diff --git a/frontend/packages/console-app/src/components/cloud-shell/CloudShellTerminal.tsx b/frontend/packages/console-app/src/components/cloud-shell/CloudShellTerminal.tsx index 8b602d73e588..f6c6d836c440 100644 --- a/frontend/packages/console-app/src/components/cloud-shell/CloudShellTerminal.tsx +++ b/frontend/packages/console-app/src/components/cloud-shell/CloudShellTerminal.tsx @@ -223,7 +223,7 @@ const CloudShellTerminal: React.FC ({ - user: state.UI.get('user'), + user: state.core.user, }); export default connect(stateToProps)( diff --git a/frontend/packages/console-app/src/components/cloud-shell/setup/CloudShellDeveloperSetup.tsx b/frontend/packages/console-app/src/components/cloud-shell/setup/CloudShellDeveloperSetup.tsx index 0a33c011702b..7f015ff2a416 100644 --- a/frontend/packages/console-app/src/components/cloud-shell/setup/CloudShellDeveloperSetup.tsx +++ b/frontend/packages/console-app/src/components/cloud-shell/setup/CloudShellDeveloperSetup.tsx @@ -81,7 +81,7 @@ const CloudShellDeveloperSetup: React.FunctionComponent = ({ }; const mapStateToProps = (state: RootState): StateProps => ({ - username: state.UI.get('user')?.metadata?.name || '', + username: state.core.user?.metadata?.name || '', activeNamespace: state.UI.get('activeNamespace'), }); diff --git a/frontend/packages/console-dynamic-plugin-sdk/README.md b/frontend/packages/console-dynamic-plugin-sdk/README.md index ab435dcde8c5..d67ffcf3b72d 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/README.md +++ b/frontend/packages/console-dynamic-plugin-sdk/README.md @@ -31,6 +31,7 @@ dynamic-demo-plugin/ | `@openshift-console/dynamic-plugin-sdk` | Provides core APIs, types and utilities used by dynamic plugins at runtime. | | `@openshift-console/dynamic-plugin-sdk-webpack` | Provides webpack plugin `ConsoleRemotePlugin` used to build all dynamic plugin assets. | | `@openshift-console/dynamic-plugin-sdk-internal` | Internal package exposing additional code. | +| `@openshift-console/dynamic-plugin-sdk-host-app` | Provides APIs, Component, and utilities for host applications i.e reducers, actions, etc | | `@openshift-console/dynamic-plugin-sdk-internal-kubevirt` | Internal package to support KubeVirt plugin migration. | ## `package.json` diff --git a/frontend/packages/console-dynamic-plugin-sdk/package.json b/frontend/packages/console-dynamic-plugin-sdk/package.json index 2e863ff00011..40379ae79542 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/package.json +++ b/frontend/packages/console-dynamic-plugin-sdk/package.json @@ -7,7 +7,7 @@ "scripts": { "clean": "rm -rf dist generated", "build": "yarn clean && yarn validate && yarn compile && yarn generate", - "compile": "for ext in '' '-internal' '-internal-kubevirt' '-webpack' ; do yarn tsc -p tsconfig${ext}.json || exit $? ; done", + "compile": "for ext in '' '-internal' '-host-app' '-internal-kubevirt' '-webpack' ; do yarn tsc -p tsconfig${ext}.json || exit $? ; done", "generate": "yarn generate-schema && yarn generate-doc && yarn generate-pkg-assets", "generate-schema": "yarn ts-node scripts/generate-schema.ts", "generate-doc": "yarn ts-node scripts/generate-doc.ts", diff --git a/frontend/packages/console-dynamic-plugin-sdk/scripts/generate-pkg-assets.ts b/frontend/packages/console-dynamic-plugin-sdk/scripts/generate-pkg-assets.ts index 9154eaafdfbb..f2f5fed6f090 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/scripts/generate-pkg-assets.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/scripts/generate-pkg-assets.ts @@ -6,6 +6,7 @@ import { getCorePackage, getInternalPackage, getInternalKubevirtPackage, + getHostAppPackage, getWebpackPackage, } from './package-definitions'; import { resolvePath, relativePath } from './utils/path'; @@ -33,6 +34,7 @@ const outPackages = [ getCorePackage(sdkPackage, rootPackage, missingDepCallback), getInternalPackage(sdkPackage, rootPackage, missingDepCallback), getInternalKubevirtPackage(sdkPackage, rootPackage, missingDepCallback), + getHostAppPackage(sdkPackage, rootPackage, missingDepCallback), getWebpackPackage(sdkPackage, rootPackage, missingDepCallback), ]; diff --git a/frontend/packages/console-dynamic-plugin-sdk/scripts/package-definitions.ts b/frontend/packages/console-dynamic-plugin-sdk/scripts/package-definitions.ts index 3e6c580c8f22..77d5583d371e 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/scripts/package-definitions.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/scripts/package-definitions.ts @@ -112,6 +112,24 @@ export const getInternalKubevirtPackage: GetPackageDefinition = ( }, }); +export const getHostAppPackage: GetPackageDefinition = ( + sdkPackage, + rootPackage, + missingDepCallback, +) => ({ + outDir: 'dist/host-app', + manifest: { + name: '@openshift-console/dynamic-plugin-sdk-host-app', + version: sdkPackage.version, + main: 'lib/lib-host-app.js', + ...commonManifestFields, + dependencies: parseSharedModuleDeps(rootPackage, missingDepCallback), + }, + filesToCopy: { + ...commonFiles, + }, +}); + export const getWebpackPackage: GetPackageDefinition = ( sdkPackage, rootPackage, diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/api/host-app-api.ts b/frontend/packages/console-dynamic-plugin-sdk/src/api/host-app-api.ts new file mode 100644 index 000000000000..c0aeeb1ffc94 --- /dev/null +++ b/frontend/packages/console-dynamic-plugin-sdk/src/api/host-app-api.ts @@ -0,0 +1,11 @@ +export { default as AppInitSDK } from '@console/dynamic-plugin-sdk/src/app/AppInitSDK'; +export { default as coreReducer } from '@console/dynamic-plugin-sdk/src/app/core/reducers/core'; +export { + setUser, + beginImpersonate, + endImpersonate, +} from '@console/dynamic-plugin-sdk/src/app/core/actions/core'; +export { + getUser, + getImpersonate, +} from '@console/dynamic-plugin-sdk/src/app/core/reducers/coreSelector'; diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/app/core/actions/core.ts b/frontend/packages/console-dynamic-plugin-sdk/src/app/core/actions/core.ts index d829f66081c2..8bf752aaedfb 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/app/core/actions/core.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/app/core/actions/core.ts @@ -2,21 +2,17 @@ import { action, ActionType as Action } from 'typesafe-actions'; import { UserKind } from '../../redux-types'; export enum ActionType { - SetNamespace = 'setNamespace', SetUser = 'setUser', BeginImpersonate = 'beginImpersonate', EndImpersonate = 'endImpersonate', } -export const setNamespace = (namespace: string = '') => - action(ActionType.SetNamespace, { namespace: namespace.trim() }); export const setUser = (user: UserKind) => action(ActionType.SetUser, { user }); export const beginImpersonate = (kind: string, name: string, subprotocols: string[]) => action(ActionType.BeginImpersonate, { kind, name, subprotocols }); export const endImpersonate = () => action(ActionType.EndImpersonate); const coreActions = { - setNamespace, setUser, beginImpersonate, endImpersonate, diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/app/core/actions/index.ts b/frontend/packages/console-dynamic-plugin-sdk/src/app/core/actions/index.ts new file mode 100644 index 000000000000..4b0e04137627 --- /dev/null +++ b/frontend/packages/console-dynamic-plugin-sdk/src/app/core/actions/index.ts @@ -0,0 +1 @@ +export * from './core'; diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/app/core/index.ts b/frontend/packages/console-dynamic-plugin-sdk/src/app/core/index.ts new file mode 100644 index 000000000000..43988367b1ac --- /dev/null +++ b/frontend/packages/console-dynamic-plugin-sdk/src/app/core/index.ts @@ -0,0 +1,2 @@ +export * from './actions'; +export * from './reducers'; diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/__tests__/core.spec.ts b/frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/__tests__/core.spec.ts index 812dad5431cb..9a51c5b36937 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/__tests__/core.spec.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/__tests__/core.spec.ts @@ -1,30 +1,21 @@ import { CoreState } from '../../../redux-types'; -import { setUser, setNamespace, beginImpersonate, endImpersonate } from '../../actions/core'; +import { setUser, beginImpersonate, endImpersonate } from '../../actions/core'; import coreReducer from '../core'; import reducerTest from './utils/reducerTest'; describe('Core Reducer', () => { - const state: CoreState = { - activeNamespace: 'sample-app', - }; - - it('set namespace', () => { - reducerTest(coreReducer, state, setNamespace('my-app')).expectVal({ - activeNamespace: 'my-app', - }); - }); + const state: CoreState = {}; it('set user', () => { const mockUser = { apiVersion: 'user.openshift.io/v1', kind: 'User', - groups: ['system:authenticated'], + identities: [], metadata: { name: 'kube:admin', }, }; reducerTest(coreReducer, state, setUser(mockUser)).expectVal({ - activeNamespace: 'sample-app', user: mockUser, }); }); @@ -35,7 +26,6 @@ describe('Core Reducer', () => { state, beginImpersonate('User', 'developer', ['Impersonate-User.Y29uc29sZWRldmVsb3Blcg__']), ).expectVal({ - activeNamespace: 'sample-app', impersonate: { kind: 'User', name: 'developer', @@ -45,8 +35,6 @@ describe('Core Reducer', () => { }); it('end Impersonate', () => { - reducerTest(coreReducer, state, endImpersonate()).expectVal({ - activeNamespace: 'sample-app', - }); + reducerTest(coreReducer, state, endImpersonate()).expectVal({}); }); }); diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/core.ts b/frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/core.ts index 804b64220f99..8c5243a95c2e 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/core.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/core.ts @@ -10,24 +10,8 @@ import { ActionType, CoreAction } from '../actions/core'; * @see CoreAction * @return The the updated state. * * */ -const coreReducer = ( - state: CoreState = { - activeNamespace: '', - }, - action: CoreAction, -): CoreState => { +const coreReducer = (state: CoreState = {}, action: CoreAction): CoreState => { switch (action.type) { - case ActionType.SetNamespace: - if (!action.payload.namespace) { - // eslint-disable-next-line no-console - console.warn('setNamespace: Not setting to falsy!'); - return state; - } - return { - ...state, - activeNamespace: action.payload.namespace, - }; - case ActionType.BeginImpersonate: return { ...state, diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/coreSelector.ts b/frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/coreSelector.ts new file mode 100644 index 000000000000..3cbf517c0fdf --- /dev/null +++ b/frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/coreSelector.ts @@ -0,0 +1,22 @@ +import { ImpersonateKind, RootState, UserKind } from '../../redux-types'; + +type GetImpersonate = (state: RootState) => { impersonate: ImpersonateKind }; +type GetUser = (state: RootState) => { user: UserKind }; + +/** + * It provides impersonation details from the redux store. + * @param state the root state + * @return The the impersonate state. + * * */ +export const getImpersonate: GetImpersonate = (state) => { + return { impersonate: state.core.impersonate }; +}; + +/** + * It provides user details from the redux store. + * @param state the root state + * @return The the user state. + * * */ +export const getUser: GetUser = (state) => { + return { user: state.core.user }; +}; diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/index.ts b/frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/index.ts new file mode 100644 index 000000000000..d7ee29aa51d1 --- /dev/null +++ b/frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/index.ts @@ -0,0 +1,2 @@ +export { default as coreReducer } from './core'; +export * from './coreSelector'; diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/app/index.ts b/frontend/packages/console-dynamic-plugin-sdk/src/app/index.ts index a46c03cdb16b..bae26bab7309 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/app/index.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/app/index.ts @@ -1 +1,2 @@ export { default as AppInitSDK } from './AppInitSDK'; +export * from './core'; diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/app/redux-types.ts b/frontend/packages/console-dynamic-plugin-sdk/src/app/redux-types.ts index 0f107a96f044..58c9144285a8 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/app/redux-types.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/app/redux-types.ts @@ -1,10 +1,18 @@ import { K8sResourceCommon } from '../extensions/console-types'; -export type UserKind = K8sResourceCommon & { groups: string[] }; +export type UserKind = { + fullName?: string; + identities: string[]; +} & K8sResourceCommon; + +export type ImpersonateKind = { + kind: string; + name: string; + subprotocols: string[]; +}; export type CoreState = { - activeNamespace: string; user?: UserKind; - impersonate?: { kind: string; name: string; subprotocols: string[] }; + impersonate?: ImpersonateKind; }; export type RootState = { diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/lib-host-app.ts b/frontend/packages/console-dynamic-plugin-sdk/src/lib-host-app.ts new file mode 100644 index 000000000000..2c02ee570465 --- /dev/null +++ b/frontend/packages/console-dynamic-plugin-sdk/src/lib-host-app.ts @@ -0,0 +1 @@ +export * from './api/host-app-api'; diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/shared-modules-init.ts b/frontend/packages/console-dynamic-plugin-sdk/src/shared-modules-init.ts index a81a2b3e1f0f..1819b84bc8fb 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/shared-modules-init.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/shared-modules-init.ts @@ -10,6 +10,8 @@ const modules: SharedModuleResolution = { require('@console/dynamic-plugin-sdk/src/lib-internal'), '@openshift-console/dynamic-plugin-sdk-internal-kubevirt': async () => () => require('@console/dynamic-plugin-sdk/src/lib-internal-kubevirt'), + '@openshift-console/dynamic-plugin-sdk-host-app': async () => () => + require('@console/dynamic-plugin-sdk/src/lib-host-app'), '@patternfly/react-core': async () => () => require('@patternfly/react-core'), '@patternfly/react-table': async () => () => require('@patternfly/react-table'), react: async () => () => require('react'), diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/shared-modules.ts b/frontend/packages/console-dynamic-plugin-sdk/src/shared-modules.ts index 3756cd7d1040..8434f412eb38 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/shared-modules.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/shared-modules.ts @@ -5,6 +5,7 @@ export const sharedPluginModules = [ '@openshift-console/dynamic-plugin-sdk', '@openshift-console/dynamic-plugin-sdk-internal', '@openshift-console/dynamic-plugin-sdk-internal-kubevirt', + '@openshift-console/dynamic-plugin-sdk-host-app', '@patternfly/react-core', '@patternfly/react-table', 'react', diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/utils/fetch/console-fetch-utils.ts b/frontend/packages/console-dynamic-plugin-sdk/src/utils/fetch/console-fetch-utils.ts index a2f01624e407..b077fbcf980c 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/utils/fetch/console-fetch-utils.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/utils/fetch/console-fetch-utils.ts @@ -106,7 +106,7 @@ export const getImpersonateHeaders = (): ImpersonateHeaders => { const store = storeHandler.getStore(); if (!store) return undefined; - const { kind, name } = store.getState().UI.get('impersonate', {}); + const { kind, name } = store.getState().core.impersonate || {}; if ((kind === 'User' || kind === 'Group') && name) { // Even if we are impersonating a group, we still need to set Impersonate-User to something or k8s will complain const headers = { diff --git a/frontend/packages/console-dynamic-plugin-sdk/tsconfig-host-app.json b/frontend/packages/console-dynamic-plugin-sdk/tsconfig-host-app.json new file mode 100644 index 000000000000..03d808507f81 --- /dev/null +++ b/frontend/packages/console-dynamic-plugin-sdk/tsconfig-host-app.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig-base.json", + "compilerOptions": { + "outDir": "dist/host-app/lib" + }, + "files": ["src/lib-host-app.ts"] +} diff --git a/frontend/packages/console-shared/src/components/actions/menu/ActionMenuItem.tsx b/frontend/packages/console-shared/src/components/actions/menu/ActionMenuItem.tsx index bd2ee98cf1e4..a1d71828d79c 100644 --- a/frontend/packages/console-shared/src/components/actions/menu/ActionMenuItem.tsx +++ b/frontend/packages/console-shared/src/components/actions/menu/ActionMenuItem.tsx @@ -10,6 +10,7 @@ import * as classNames from 'classnames'; import * as _ from 'lodash'; import { connect } from 'react-redux'; import { Action } from '@console/dynamic-plugin-sdk'; +import { ImpersonateKind } from '@console/dynamic-plugin-sdk/src/app/redux-types'; import { useAccessReview, history } from '@console/internal/components/utils'; import { impersonateStateToProps } from '@console/internal/reducers/ui'; @@ -82,7 +83,7 @@ const ActionItem: React.FC = ({ }; const AccessReviewActionItem = connect(impersonateStateToProps)( - (props: ActionMenuItemProps & { impersonate: string }) => { + (props: ActionMenuItemProps & { impersonate: ImpersonateKind }) => { const { action, impersonate } = props; const isAllowed = useAccessReview(action.accessReview, impersonate); return ; diff --git a/frontend/packages/console-shared/src/hooks/__tests__/useUserSettings.spec.ts b/frontend/packages/console-shared/src/hooks/__tests__/useUserSettings.spec.ts index 8d8a38d2c3bb..c8ea83e15594 100644 --- a/frontend/packages/console-shared/src/hooks/__tests__/useUserSettings.spec.ts +++ b/frontend/packages/console-shared/src/hooks/__tests__/useUserSettings.spec.ts @@ -66,7 +66,7 @@ describe('useUserSettings', () => { updateConfigMapMock.mockClear(); useSelectorMock.mockClear(); useSelectorMock.mockImplementation((selector) => - selector({ UI: new Map([['user', { metadata: { uid: 'foo' } }]]) }), + selector({ core: { user: { metadata: { uid: 'foo' } } } }), ); }); @@ -531,10 +531,10 @@ describe('useUserSettings', () => { it('should use session storage when impersonating', () => { useSelectorMock.mockImplementation((selector) => selector({ - UI: new Map([ - ['user', { metadata: { uid: 'foo' } }], - ['impersonate', { name: 'imposter' }], - ]), + core: { + user: { metadata: { uid: 'foo' } }, + impersonate: { name: 'imposter' }, + }, }), ); diff --git a/frontend/packages/console-shared/src/hooks/useUserSettings.ts b/frontend/packages/console-shared/src/hooks/useUserSettings.ts index 8d9bd45d91e8..7729b9a3c065 100644 --- a/frontend/packages/console-shared/src/hooks/useUserSettings.ts +++ b/frontend/packages/console-shared/src/hooks/useUserSettings.ts @@ -38,13 +38,13 @@ export const useUserSettings = ( defaultValue?: T, sync: boolean = false, ): [T, React.Dispatch>, boolean] => { - const impersonate = useSelector((state: RootState) => !!state.UI.get('impersonate')); + const impersonate = useSelector((state: RootState) => !!state.core.impersonate); const keyRef = React.useRef(key); const defaultValueRef = React.useRef(defaultValue); const [isRequestPending, increaseRequest, decreaseRequest] = useCounterRef(); const userUid = useSelector( (state: RootState) => - state.UI.get('impersonate')?.name ?? state.UI.get('user')?.metadata?.uid ?? 'kubeadmin', + state.core.impersonate?.name ?? state.core.user?.metadata?.uid ?? 'kubeadmin', ); const [fallbackLocalStorage, setFallbackLocalStorage] = React.useState( diff --git a/frontend/packages/dev-console/src/components/monitoring/alerts/SilenceDurationDropdown.tsx b/frontend/packages/dev-console/src/components/monitoring/alerts/SilenceDurationDropdown.tsx index caca54159565..f62850a6cb92 100644 --- a/frontend/packages/dev-console/src/components/monitoring/alerts/SilenceDurationDropdown.tsx +++ b/frontend/packages/dev-console/src/components/monitoring/alerts/SilenceDurationDropdown.tsx @@ -39,7 +39,7 @@ const SilenceDurationDropDown: React.FC = ({ }) => { const { t } = useTranslation(); const [silencing, setSilencing] = React.useState(false); - const createdBy = useSelector((state: RootState) => state.UI.get('user')?.metadata?.name); + const createdBy = useSelector((state: RootState) => state.core.user?.metadata?.name); const rules = useSelector((state: RootState) => state.UI.getIn(['monitoring', 'devRules'])); const ruleMatchers = _.map(rule?.labels, (value, key) => ({ isRegex: false, name: key, value })); const dispatch = useDispatch(); diff --git a/frontend/packages/pipelines-plugin/src/components/pipelineruns/triggered-by/hooks.ts b/frontend/packages/pipelines-plugin/src/components/pipelineruns/triggered-by/hooks.ts index 2ce39434c8e2..4beb1bde8135 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelineruns/triggered-by/hooks.ts +++ b/frontend/packages/pipelines-plugin/src/components/pipelineruns/triggered-by/hooks.ts @@ -15,7 +15,7 @@ const mergeAnnotationsWithResource = (annotations: AnnotationMap, resource: K8sR }; export const useUserAnnotationForManualStart = (): AnnotationMap => { - const user = useSelector((state) => state.UI.get('user')); + const user = useSelector((state) => state.core.user); if (!user?.metadata?.name) { return {}; diff --git a/frontend/public/actions/common.ts b/frontend/public/actions/common.ts index 198fc2b674d4..5239dc0b3489 100644 --- a/frontend/public/actions/common.ts +++ b/frontend/public/actions/common.ts @@ -3,10 +3,8 @@ import { action } from 'typesafe-actions'; enum ActionType { SetCreateProjectMessage = 'setCreateProjectMessage', SetClusterID = 'setClusterID', - SetUser = 'setUser', } export const setClusterID = (clusterID: string) => action(ActionType.SetClusterID, { clusterID }); -export const setUser = (user: any) => action(ActionType.SetUser, { user }); export const setCreateProjectMessage = (message: string) => action(ActionType.SetCreateProjectMessage, { message }); diff --git a/frontend/public/actions/features.ts b/frontend/public/actions/features.ts index 62eb4441dad9..24b731cd43db 100644 --- a/frontend/public/actions/features.ts +++ b/frontend/public/actions/features.ts @@ -12,13 +12,14 @@ import { FeatureFlag as DynamicFeatureFlag, isFeatureFlag as isDynamicFeatureFlag, SetFeatureFlag, + setUser, } from '@console/dynamic-plugin-sdk'; import { resolveExtension } from '@console/dynamic-plugin-sdk/src/coderefs/coderef-resolver'; import store from '../redux'; import { GroupModel, UserModel, VolumeSnapshotContentModel } from '../models'; import { ClusterVersionKind } from '../module/k8s'; import { receivedResources } from './k8s'; -import { setClusterID, setCreateProjectMessage, setUser } from './common'; +import { setClusterID, setCreateProjectMessage } from './common'; import client, { fetchURL } from '../graphql/client'; import { SSARQuery } from './features.gql'; import { SSARQueryType, SSARQueryVariables } from '../../@types/console/generated/graphql-schema'; diff --git a/frontend/public/actions/k8s.ts b/frontend/public/actions/k8s.ts index 19b0ef757ca6..ce6e29970b04 100644 --- a/frontend/public/actions/k8s.ts +++ b/frontend/public/actions/k8s.ts @@ -118,7 +118,7 @@ export const watchK8sObject = ( return; } - const { subprotocols } = getState().UI.get('impersonate', {}); + const { subprotocols } = getState().core.impersonate || {}; WS[id] = k8sWatch(k8sType, query, { subprotocols }).onbulkmessage((events) => events.forEach((e) => dispatch(modifyObject(id, e.object))), @@ -233,7 +233,7 @@ export const watchK8sList = ( return; } - const { subprotocols } = getState().UI.get('impersonate', {}); + const { subprotocols } = getState().core.impersonate || {}; WS[id] = k8sWatch( k8skind, { ...query, resourceVersion }, diff --git a/frontend/public/actions/ui.ts b/frontend/public/actions/ui.ts index ae96ec545388..41f553d6869f 100644 --- a/frontend/public/actions/ui.ts +++ b/frontend/public/actions/ui.ts @@ -14,9 +14,10 @@ import { K8sResourceKind, PodKind, NodeKind } from '../module/k8s'; import { allModels } from '../module/k8s/k8s-models'; import { detectFeatures, clearSSARFlags } from './features'; import { OverviewSpecialGroup } from '../components/overview/constants'; -import { setClusterID, setCreateProjectMessage, setUser } from './common'; +import { setClusterID, setCreateProjectMessage } from './common'; import { Rule } from '../components/monitoring/types'; import { subsClient } from '../graphql/client'; +import { beginImpersonate, endImpersonate } from '@console/dynamic-plugin-sdk'; export enum ActionType { DismissOverviewDetails = 'dismissOverviewDetails', @@ -50,10 +51,7 @@ export enum ActionType { QueryBrowserToggleIsEnabled = 'queryBrowserToggleIsEnabled', QueryBrowserToggleSeries = 'queryBrowserToggleSeries', SetClusterID = 'setClusterID', - SetUser = 'setUser', SortList = 'sortList', - BeginImpersonate = 'beginImpersonate', - EndImpersonate = 'endImpersonate', UpdateOverviewMetrics = 'updateOverviewMetrics', UpdateOverviewResources = 'updateOverviewResources', UpdateOverviewSelectedGroup = 'updateOverviewSelectedGroup', @@ -124,7 +122,7 @@ export const getNamespacedResources = () => { }; export const getActiveNamespace = (): string => store.getState().UI.get('activeNamespace'); -export const getActiveUserName = (): string => store.getState().UI.get('user')?.metadata?.name; +export const getActiveUserName = (): string => store.getState().core.user?.metadata?.name; export const getNamespaceMetric = (ns: K8sResourceKind, metric: string): number => { const metrics = store.getState().UI.getIn(['metrics', 'namespace']); @@ -216,9 +214,6 @@ export const setActiveNamespace = (namespace: string = '') => { return action(ActionType.SetActiveNamespace, { namespace }); }; -export const beginImpersonate = (kind: string, name: string, subprotocols: string[]) => - action(ActionType.BeginImpersonate, { kind, name, subprotocols }); -export const endImpersonate = () => action(ActionType.EndImpersonate); export const startImpersonate = (kind: string, name: string) => async (dispatch, getState) => { let textEncoder; try { @@ -232,7 +227,7 @@ export const startImpersonate = (kind: string, name: string) => async (dispatch, textEncoder = await import('text-encoding').then((module) => new module.TextEncoder('utf-8')); } - const imp = getState().UI.get('impersonate', {}); + const imp = getState().core.impersonate || {}; if ((imp.name && imp.name !== name) || (imp.kin && imp.kind !== kind)) { // eslint-disable-next-line no-console console.warn(`Impersonate race detected: ${name} vs ${imp.name} / ${kind} ${imp.kind}`); @@ -400,12 +395,9 @@ const uiActions = { setCurrentLocation, setActiveApplication, setActiveNamespace, - beginImpersonate, - endImpersonate, sortList, setCreateProjectMessage, setClusterID, - setUser, selectOverviewItem, selectOverviewDetailsTab, updateOverviewMetrics, diff --git a/frontend/public/components/app.jsx b/frontend/public/components/app.jsx index 5703d5eef036..f3fab07592a7 100644 --- a/frontend/public/components/app.jsx +++ b/frontend/public/components/app.jsx @@ -270,7 +270,7 @@ const CaptureTelemetry = React.memo(() => { const fireTelemetryEvent = useTelemetry(); // notify of identity change - const user = useSelector(({ UI }) => UI.get('user')); + const user = useSelector(({ core }) => core.user || {}); React.useEffect(() => { if (user.metadata?.uid || user.metadata?.name) { fireTelemetryEvent('identify', { user }); diff --git a/frontend/public/components/edit-yaml.jsx b/frontend/public/components/edit-yaml.jsx index 0d3bf97447c3..3a99e9450955 100644 --- a/frontend/public/components/edit-yaml.jsx +++ b/frontend/public/components/edit-yaml.jsx @@ -47,9 +47,9 @@ const generateObjToLoad = (templateExtensions, kind, id, yaml, namespace = 'defa return sampleObj; }; -const stateToProps = ({ k8s, UI }) => ({ +const stateToProps = ({ k8s, UI, core }) => ({ activeNamespace: UI.get('activeNamespace'), - impersonate: UI.get('impersonate'), + impersonate: core.impersonate, models: k8s.getIn(['RESOURCES', 'models']), }); diff --git a/frontend/public/components/environment.jsx b/frontend/public/components/environment.jsx index c11d0096c9bd..db73bf897ca3 100644 --- a/frontend/public/components/environment.jsx +++ b/frontend/public/components/environment.jsx @@ -285,11 +285,11 @@ class CurrentEnvVars { } /** @type {(state: any, props: {obj?: object, rawEnvData?: any, readOnly: boolean, envPath: any, onChange?: (env: any) => void, addConfigMapSecret?: boolean, useLoadingInline?: boolean}) => {model: K8sKind}} */ -const stateToProps = ({ k8s, UI }, { obj }) => ({ +const stateToProps = ({ k8s, core }, { obj }) => ({ model: k8s.getIn(['RESOURCES', 'models', referenceFor(obj)]) || k8s.getIn(['RESOURCES', 'models', obj.kind]), - impersonate: UI.get('impersonate'), + impersonate: core.impersonate, }); export class UnconnectedEnvironmentPage extends PromiseComponent { diff --git a/frontend/public/components/impersonate-notifier.jsx b/frontend/public/components/impersonate-notifier.jsx index d3b3ba59a272..ab67faf7e075 100644 --- a/frontend/public/components/impersonate-notifier.jsx +++ b/frontend/public/components/impersonate-notifier.jsx @@ -6,7 +6,7 @@ import { Button } from '@patternfly/react-core'; import * as UIActions from '../actions/ui'; import { modelFor } from '../module/k8s'; -export const ImpersonateNotifier = connect(({ UI }) => ({ impersonate: UI.get('impersonate') }), { +export const ImpersonateNotifier = connect(({ core }) => ({ impersonate: core.impersonate }), { stopImpersonate: UIActions.stopImpersonate, })(({ stopImpersonate, impersonate }) => { const { t } = useTranslation(); diff --git a/frontend/public/components/masthead-toolbar.jsx b/frontend/public/components/masthead-toolbar.jsx index 35267dbdd165..d9fd8fb4021f 100644 --- a/frontend/public/components/masthead-toolbar.jsx +++ b/frontend/public/components/masthead-toolbar.jsx @@ -721,7 +721,7 @@ class MastheadToolbarContents_ extends React.Component { const mastheadToolbarStateToProps = (state) => ({ activeNamespace: state.UI.get('activeNamespace'), clusterID: state.UI.get('clusterID'), - user: state.UI.get('user'), + user: state.core.user, alertCount: state.UI.getIn(['monitoring', 'alertCount']), canAccessNS: !!state[featureReducerName].get(FLAGS.CAN_GET_NS), }); diff --git a/frontend/public/components/monitoring/silence-form.tsx b/frontend/public/components/monitoring/silence-form.tsx index cac16e8008a0..95fe215eda75 100644 --- a/frontend/public/components/monitoring/silence-form.tsx +++ b/frontend/public/components/monitoring/silence-form.tsx @@ -109,7 +109,7 @@ const SilenceForm_: React.FC = ({ defaults, Info, title }) => ); const [startsAt, setStartsAt] = React.useState(defaults.startsAt ?? formatDate(now)); - const user = useSelector(({ UI }: RootState) => UI.get('user')); + const user = useSelector(({ core }: RootState) => core.user); React.useEffect(() => { if (_.isEmpty(createdBy)) { diff --git a/frontend/public/components/pod-exec.jsx b/frontend/public/components/pod-exec.jsx index 323575e96f57..6aaba3d68309 100644 --- a/frontend/public/components/pod-exec.jsx +++ b/frontend/public/components/pod-exec.jsx @@ -78,7 +78,7 @@ const PodExec_ = connectToFlags(FLAGS.OPENSHIFT)( current && current.onConnectionClosed(`connecting to ${activeContainer}`); } - const impersonate = store.getState().UI.get('impersonate', {}); + const impersonate = store.getState().core.impersonate || {}; const subprotocols = (impersonate.subprotocols || []).concat('base64.channel.k8s.io'); let previous; diff --git a/frontend/public/components/utils/kebab.tsx b/frontend/public/components/utils/kebab.tsx index 977861eac23b..6aab7a40e023 100644 --- a/frontend/public/components/utils/kebab.tsx +++ b/frontend/public/components/utils/kebab.tsx @@ -39,6 +39,7 @@ import { DeploymentModel, VolumeSnapshotModel, } from '../../models'; +import { ImpersonateKind } from '@console/dynamic-plugin-sdk/src/app/redux-types'; export const kebabOptionsToMenu = (options: KebabOption[]): KebabMenuOption[] => { const subs: { [key: string]: KebabSubMenu } = {}; @@ -106,7 +107,7 @@ const KebabItem_: React.FC = ({ ); }; -const KebabItemAccessReview_ = (props: KebabItemProps & { impersonate: string }) => { +const KebabItemAccessReview_ = (props: KebabItemProps & { impersonate: ImpersonateKind }) => { const { option, impersonate } = props; const isAllowed = useAccessReview(option.accessReview, impersonate); return ; diff --git a/frontend/public/components/utils/rbac.tsx b/frontend/public/components/utils/rbac.tsx index cb5d6ec2d57f..ca50b6326a03 100644 --- a/frontend/public/components/utils/rbac.tsx +++ b/frontend/public/components/utils/rbac.tsx @@ -16,6 +16,7 @@ import { } from '../../module/k8s'; import { ProjectModel, SelfSubjectAccessReviewModel } from '../../models'; import { useSafetyFirst } from '../../components/safety-first'; +import { ImpersonateKind } from '@console/dynamic-plugin-sdk/src/app/redux-types'; // Memoize the result so we only make the request once for each access review. // This does mean that the user will have to refresh the page to see updates. @@ -59,7 +60,7 @@ const checkAccessInternal = _.memoize( ); const getImpersonateKey = (impersonate): string => { - impersonate = impersonate || store.getState().UI.get('impersonate'); + impersonate = impersonate || store.getState().core.impersonate; return impersonate ? `${impersonate.kind}~{impersonate.user}` : ''; }; @@ -183,7 +184,7 @@ RequireCreatePermission.displayName = 'RequireCreatePermission'; type RequireCreatePermissionProps = { model: K8sKind; namespace?: string; - impersonate?: string; + impersonate?: ImpersonateKind; children: React.ReactNode; }; diff --git a/frontend/public/reducers/ui.ts b/frontend/public/reducers/ui.ts index 78709066d6e4..d8a0b2dbc76f 100644 --- a/frontend/public/reducers/ui.ts +++ b/frontend/public/reducers/ui.ts @@ -117,15 +117,6 @@ export default (state: UIState, action: UIAction): UIState => { } return state.set('activeNamespace', ns); } - case ActionType.BeginImpersonate: - return state.set('impersonate', { - kind: action.payload.kind, - name: action.payload.name, - subprotocols: action.payload.subprotocols, - }); - - case ActionType.EndImpersonate: - return state.delete('impersonate'); case ActionType.SortList: return state.mergeIn( @@ -139,9 +130,6 @@ export default (state: UIState, action: UIAction): UIState => { case ActionType.SetClusterID: return state.set('clusterID', action.payload.clusterID); - case ActionType.SetUser: - return state.set('user', action.payload.user); - case ActionType.MonitoringDashboardsPatchVariable: return state.mergeIn( ['monitoringDashboards', action.payload.perspective, 'variables', action.payload.key], @@ -376,13 +364,10 @@ export const createProjectMessageStateToProps = ({ UI }: RootState) => { return { createProjectMessage: UI.get('createProjectMessage') as string }; }; -export const userStateToProps = ({ UI }: RootState) => { - return { user: UI.get('user') }; -}; - -export const impersonateStateToProps = ({ UI }: RootState) => { - return { impersonate: UI.get('impersonate') }; -}; +export { + getImpersonate as impersonateStateToProps, + getUser as userStateToProps, +} from '@console/dynamic-plugin-sdk/src/app/core/reducers/coreSelector'; export const getActiveNamespace = ({ UI }: RootState): string => UI.get('activeNamespace'); diff --git a/frontend/public/redux.ts b/frontend/public/redux.ts index 81731cf1c58a..db936047a5c1 100644 --- a/frontend/public/redux.ts +++ b/frontend/public/redux.ts @@ -6,6 +6,8 @@ import { featureReducer, featureReducerName, FeatureState } from './reducers/fea import k8sReducers, { K8sState } from './reducers/k8s'; import UIReducers, { UIState } from './reducers/ui'; import { dashboardsReducer, DashboardsState } from './reducers/dashboards'; +import coreReducer from '@console/dynamic-plugin-sdk/src/app/core/reducers/core'; +import { CoreState } from '@console/dynamic-plugin-sdk/src/app/redux-types'; const composeEnhancers = (process.env.NODE_ENV !== 'production' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose; @@ -18,6 +20,7 @@ export type RootState = { plugins?: { [namespace: string]: any; }; + core: CoreState; }; const baseReducers = Object.freeze({ @@ -25,6 +28,7 @@ const baseReducers = Object.freeze({ UI: UIReducers, [featureReducerName]: featureReducer, dashboards: dashboardsReducer, + core: coreReducer, }); const store = createStore(