Skip to content

Commit

Permalink
expose reducers from SDK and consume in console
Browse files Browse the repository at this point in the history
  • Loading branch information
invincibleJai committed Nov 22, 2021
1 parent ed3bf4e commit af335e4
Show file tree
Hide file tree
Showing 41 changed files with 132 additions and 102 deletions.
Expand Up @@ -223,7 +223,7 @@ const CloudShellTerminal: React.FC<CloudShellTerminalProps &
export const InternalCloudShellTerminal = CloudShellTerminal;

const stateToProps = (state: RootState): StateProps => ({
user: state.UI.get('user'),
user: state.core.user,
});

export default connect<StateProps, null, Props>(stateToProps)(
Expand Down
Expand Up @@ -81,7 +81,7 @@ const CloudShellDeveloperSetup: React.FunctionComponent<Props> = ({
};

const mapStateToProps = (state: RootState): StateProps => ({
username: state.UI.get('user')?.metadata?.name || '',
username: state.core.user?.metadata?.name || '',
activeNamespace: state.UI.get('activeNamespace'),
});

Expand Down
1 change: 1 addition & 0 deletions frontend/packages/console-dynamic-plugin-sdk/README.md
Expand Up @@ -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`
Expand Down
2 changes: 1 addition & 1 deletion frontend/packages/console-dynamic-plugin-sdk/package.json
Expand Up @@ -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",
Expand Down
Expand Up @@ -6,6 +6,7 @@ import {
getCorePackage,
getInternalPackage,
getInternalKubevirtPackage,
getHostAppPackage,
getWebpackPackage,
} from './package-definitions';
import { resolvePath, relativePath } from './utils/path';
Expand Down Expand Up @@ -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),
];

Expand Down
Expand Up @@ -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,
Expand Down
@@ -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';
Expand Up @@ -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,
Expand Down
@@ -0,0 +1 @@
export * from './core';
@@ -0,0 +1,2 @@
export * from './actions';
export * from './reducers';
@@ -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,
});
});
Expand All @@ -35,7 +26,6 @@ describe('Core Reducer', () => {
state,
beginImpersonate('User', 'developer', ['Impersonate-User.Y29uc29sZWRldmVsb3Blcg__']),
).expectVal({
activeNamespace: 'sample-app',
impersonate: {
kind: 'User',
name: 'developer',
Expand All @@ -45,8 +35,6 @@ describe('Core Reducer', () => {
});

it('end Impersonate', () => {
reducerTest(coreReducer, state, endImpersonate()).expectVal({
activeNamespace: 'sample-app',
});
reducerTest(coreReducer, state, endImpersonate()).expectVal({});
});
});
Expand Up @@ -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,
Expand Down
@@ -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 };
};
@@ -0,0 +1,2 @@
export { default as coreReducer } from './core';
export * from './coreSelector';
@@ -1 +1,2 @@
export { default as AppInitSDK } from './AppInitSDK';
export * from './core';
@@ -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 = {
Expand Down
@@ -0,0 +1 @@
export * from './api/host-app-api';
Expand Up @@ -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'),
Expand Down
Expand Up @@ -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',
Expand Down
Expand Up @@ -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 = {
Expand Down
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig-base.json",
"compilerOptions": {
"outDir": "dist/host-app/lib"
},
"files": ["src/lib-host-app.ts"]
}
Expand Up @@ -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';

Expand Down Expand Up @@ -82,7 +83,7 @@ const ActionItem: React.FC<ActionMenuItemProps & { isAllowed: boolean }> = ({
};

const AccessReviewActionItem = connect(impersonateStateToProps)(
(props: ActionMenuItemProps & { impersonate: string }) => {
(props: ActionMenuItemProps & { impersonate: ImpersonateKind }) => {
const { action, impersonate } = props;
const isAllowed = useAccessReview(action.accessReview, impersonate);
return <ActionItem {...props} isAllowed={isAllowed} />;
Expand Down
Expand Up @@ -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' } } } }),
);
});

Expand Down Expand Up @@ -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' },
},
}),
);

Expand Down
4 changes: 2 additions & 2 deletions frontend/packages/console-shared/src/hooks/useUserSettings.ts
Expand Up @@ -38,13 +38,13 @@ export const useUserSettings = <T>(
defaultValue?: T,
sync: boolean = false,
): [T, React.Dispatch<React.SetStateAction<T>>, boolean] => {
const impersonate = useSelector((state: RootState) => !!state.UI.get('impersonate'));
const impersonate = useSelector((state: RootState) => !!state.core.impersonate);
const keyRef = React.useRef<string>(key);
const defaultValueRef = React.useRef<T>(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<boolean>(
Expand Down
Expand Up @@ -39,7 +39,7 @@ const SilenceDurationDropDown: React.FC<SilenceDurationDropDownProps> = ({
}) => {
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();
Expand Down
Expand Up @@ -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 {};
Expand Down
2 changes: 0 additions & 2 deletions frontend/public/actions/common.ts
Expand Up @@ -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 });
3 changes: 2 additions & 1 deletion frontend/public/actions/features.ts
Expand Up @@ -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';
Expand Down
4 changes: 2 additions & 2 deletions frontend/public/actions/k8s.ts
Expand Up @@ -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))),
Expand Down Expand Up @@ -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 },
Expand Down

0 comments on commit af335e4

Please sign in to comment.