github.com/grafana/pyroscope@v1.18.0/public/app/redux/reducers/tenant.ts (about) 1 import { createSlice, type PayloadAction } from '@reduxjs/toolkit'; 2 import { createAsyncThunk } from '@pyroscope/redux/async-thunk'; 3 import type { RootState } from '@pyroscope/redux/store'; 4 import { isMultiTenancyEnabled } from '@pyroscope/services/tenant'; 5 import storage from 'redux-persist/lib/storage'; 6 import { PersistConfig } from 'redux-persist/lib/types'; 7 import { tenantIDFromStorage } from '@pyroscope/services/storage'; 8 9 export const persistConfig: PersistConfig<TenantState> = { 10 key: 'pyroscope:tenant', 11 version: 0, 12 storage, 13 whitelist: ['tenantID'], 14 }; 15 16 interface TenantState { 17 tenancy: 18 | 'unknown' 19 | 'loading' 20 | 'needs_tenant_id' 21 | 'wants_to_change' 22 | 'single_tenant' 23 | 'multi_tenant'; 24 tenantID?: string; 25 } 26 27 const initialState: TenantState = { 28 tenancy: 'unknown', 29 tenantID: undefined, 30 }; 31 32 export const checkTenancyIsRequired = createAsyncThunk< 33 { tenancy: TenantState['tenancy']; tenantID?: string }, 34 void, 35 { state: { tenant: TenantState } } 36 >( 37 'checkTenancyIsRequired', 38 async () => { 39 const tenantID = tenantIDFromStorage(); 40 41 // Try to hit the server and see the response 42 const multitenancy = await isMultiTenancyEnabled(); 43 44 if (multitenancy && !tenantID) { 45 return Promise.resolve({ tenancy: 'needs_tenant_id', tenantID }); 46 } 47 48 if (multitenancy && tenantID) { 49 return Promise.resolve({ tenancy: 'multi_tenant', tenantID }); 50 } 51 52 return Promise.resolve({ tenancy: 'single_tenant', tenantID: undefined }); 53 }, 54 { 55 // This check is only valid if we don't know what's the tenancy status yet 56 condition: (query, thunkAPI) => { 57 const state = thunkAPI.getState().tenant; 58 59 return state.tenancy === 'unknown'; 60 }, 61 } 62 ); 63 64 const tenantSlice = createSlice({ 65 name: 'tenant', 66 initialState, 67 reducers: { 68 deleteTenancy(state) { 69 state.tenancy = 'unknown'; 70 state.tenantID = undefined; 71 }, 72 setTenantID(state, action: PayloadAction<string>) { 73 state.tenancy = 'multi_tenant'; 74 state.tenantID = action.payload; 75 }, 76 setWantsToChange(state) { 77 state.tenancy = 'wants_to_change'; 78 }, 79 }, 80 extraReducers: (builder) => { 81 // This thunk will never reject 82 builder.addCase(checkTenancyIsRequired.fulfilled, (state, action) => { 83 state.tenancy = action.payload?.tenancy; 84 state.tenantID = action.payload?.tenantID; 85 }); 86 builder.addCase(checkTenancyIsRequired.pending, (state) => { 87 state.tenancy = 'loading'; 88 }); 89 }, 90 }); 91 92 export const { actions } = tenantSlice; 93 94 export const selectTenancy = (state: RootState) => state.tenant?.tenancy; 95 96 export const selectIsMultiTenant = (state: RootState) => 97 state.tenant?.tenancy === 'multi_tenant' || 98 state.tenant?.tenancy === 'wants_to_change'; 99 100 export const selectTenantID = (state: RootState) => state.tenant?.tenantID; 101 102 export default tenantSlice.reducer;