Skip to content

Commit

Permalink
Add support for perspective detection using extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
rohitkrai03 committed Jul 7, 2020
1 parent 9bf0541 commit a995d4d
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 23 deletions.
14 changes: 0 additions & 14 deletions frontend/__tests__/reducers/ui.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,6 @@ describe('getDefaultPerspective', () => {
expect(getDefaultPerspective()).toBeUndefined();
});

it('should default to perspective extension marked default', () => {
// return Perspectives extension with one marked as the default
spyOn(pluginStore, 'getAllExtensions').and.returnValue([
{
type: 'Perspective',
properties: {
id: 'admin',
default: true,
},
} as Perspective,
]);
expect(getDefaultPerspective()).toBe('admin');
});

it('should default to localStorage if perspective is a valid extension', () => {
// return Perspectives extension whose id matches that in the localStorage
spyOn(pluginStore, 'getAllExtensions').and.returnValue([
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import * as React from 'react';
import * as _ from 'lodash';
import { connect, Dispatch } from 'react-redux';
import { useExtensions, isPerspective } from '@console/plugin-sdk';
import { getActivePerspective } from '@console/internal/reducers/ui';
import { RootState } from '@console/internal/redux';
import * as UIActions from '@console/internal/actions/ui';

type OwnProps = {
children: React.ReactNode;
};

type StateProps = {
activePerspective: string;
};

type DispatchProps = {
setActivePerspective: (string) => void;
};

type DetectPerspectiveProps = OwnProps & StateProps & DispatchProps;

const DetectPerspective: React.FC<DetectPerspectiveProps> = ({
activePerspective,
children,
setActivePerspective,
}) => {
let detectedPerspective: string;
let detectionComplete: boolean;
const perspectiveExtensions = useExtensions(isPerspective);
const defaultPerspective = perspectiveExtensions.find((p) => p.properties.default);
const perspectiveDetectors = perspectiveExtensions.filter(
(p) => p.properties.usePerspectiveDetection,
);

perspectiveDetectors.some((p) => {
const isDetectedPerspective = p.properties.usePerspectiveDetection();
detectionComplete = _.isBoolean(isDetectedPerspective);
if (isDetectedPerspective) {
detectedPerspective = p.properties.id;
return true;
}
return false;
});

React.useEffect(() => {
if (!activePerspective) {
if (detectedPerspective) {
setActivePerspective(detectedPerspective);
} else if (perspectiveDetectors.length < 1 || detectionComplete) {
setActivePerspective(defaultPerspective.properties.id); // set default perspective if there are no detectors or none of the detections were successfull
}
}
}, [
activePerspective,
defaultPerspective,
detectedPerspective,
detectionComplete,
perspectiveDetectors.length,
setActivePerspective,
]);

return activePerspective ? <>{children}</> : null;
};

const mapStateToProps = (state: RootState) => ({
activePerspective: getActivePerspective(state),
});

const mapDispatchToProps = (dispatch: Dispatch) => ({
setActivePerspective: (perspective) => dispatch(UIActions.setActivePerspective(perspective)),
});

export default connect<StateProps, DispatchProps, OwnProps>(
mapStateToProps,
mapDispatchToProps,
)(DetectPerspective);
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ namespace ExtensionProperties {
getK8sLandingPageURL: GetLandingPage;
/** The function to get redirect URL for import flow. */
getImportRedirectURL: (project: string) => string;
/** The hook to detect default perspective */
usePerspectiveDetection?: () => boolean; // isDetectedPerspective
}
}

Expand Down
2 changes: 2 additions & 0 deletions frontend/packages/dev-console/src/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import {
OperatorsTopologyConsumedExtensions,
operatorsTopologyPlugin,
} from './components/topology/operators/operatorsTopologyPlugin';
import { usePerspectiveDetection } from './utils/usePerspectiveDetection';

const {
ClusterTaskModel,
Expand Down Expand Up @@ -422,6 +423,7 @@ const plugin: Plugin<ConsumedExtensions> = [
getLandingPageURL: () => '/topology',
getK8sLandingPageURL: () => '/add',
getImportRedirectURL: (project) => `/topology/ns/${project}`,
usePerspectiveDetection,
},
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore: FIXME missing exports due to out-of-sync @types/react-redux version
import { useSelector } from 'react-redux';
import { testHook } from '@console/shared/src/test-utils/hooks-utils';
import { usePerspectiveDetection } from '../usePerspectiveDetection';

jest.mock('react-redux', () => ({
useSelector: jest.fn(),
}));

describe('usePerspectiveDetection', () => {
it('should return null if CAN_GET_NS flag is pending', () => {
(useSelector as jest.Mock).mockImplementation(() => ({
CAN_GET_NS: undefined,
}));

testHook(() => {
const isDetectedPerspective = usePerspectiveDetection();

expect(isDetectedPerspective).toBe(null);
});
});

it('should return true if CAN_GET_NS flag is false', () => {
(useSelector as jest.Mock).mockImplementation(() => ({
CAN_GET_NS: false,
}));

testHook(() => {
const isDetectedPerspective = usePerspectiveDetection();

expect(isDetectedPerspective).toBe(true);
});
});
});
13 changes: 13 additions & 0 deletions frontend/packages/dev-console/src/utils/usePerspectiveDetection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore: FIXME missing exports due to out-of-sync @types/react-redux version
import { useSelector } from 'react-redux';
import { RootState } from '@console/internal/redux';
import { getFlagsObject, flagPending } from '@console/internal/reducers/features';

export const usePerspectiveDetection = () => {
const flags = useSelector((state: RootState) => getFlagsObject(state));
const canGetNS = flags.CAN_GET_NS;
const isDeveloper = !canGetNS;

return flagPending(canGetNS) ? null : isDeveloper;
};
6 changes: 4 additions & 2 deletions frontend/public/components/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { receivedResources, watchAPIServices } from '../actions/k8s';
// cloud shell imports must come later than features
import CloudShell from '@console/app/src/components/cloud-shell/CloudShell';
import CloudShellTab from '@console/app/src/components/cloud-shell/CloudShellTab';
import DetectPerspective from '@console/app/src/components/detect-perspective/DetectPerspective';

const consoleLoader = () =>
import(
'@console/kubevirt-plugin/src/components/connected-vm-console/vm-console-page' /* webpackChunkName: "kubevirt" */
Expand Down Expand Up @@ -130,7 +132,7 @@ class App extends React.PureComponent {
const { productName } = getBrandingDetails();

return (
<>
<DetectPerspective>
<Helmet titleTemplate={`%s · ${productName}`} defaultTitle={productName} />
<ConsoleNotifier location="BannerTop" />
<Page
Expand All @@ -152,7 +154,7 @@ class App extends React.PureComponent {
</Page>
<CloudShell />
<ConsoleNotifier location="BannerBottom" />
</>
</DetectPerspective>
);
}
}
Expand Down
7 changes: 0 additions & 7 deletions frontend/public/reducers/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,6 @@ export function getDefaultPerspective() {
// invalid saved perspective
activePerspective = undefined;
}
if (!activePerspective) {
// assign default perspective
const defaultPerspective = perspectiveExtensions.find((p) => p.properties.default);
if (defaultPerspective) {
activePerspective = defaultPerspective.properties.id;
}
}
return activePerspective || undefined;
}

Expand Down

0 comments on commit a995d4d

Please sign in to comment.