From 34b5cff2c696fc324239a70d9620e9a5e0b0a486 Mon Sep 17 00:00:00 2001 From: flyinghermit Date: Tue, 2 Jul 2024 18:10:18 -0400 Subject: [PATCH 1/2] feat: update Discover flow with update mechanism --- .../teleport/src/Discover/Discover.test.tsx | 104 +++++++++++++++++- .../teleport/src/Discover/Discover.tsx | 24 +++- .../teleport/src/Discover/useDiscover.tsx | 21 ++++ 3 files changed, 144 insertions(+), 5 deletions(-) diff --git a/web/packages/teleport/src/Discover/Discover.test.tsx b/web/packages/teleport/src/Discover/Discover.test.tsx index 6b60e44d15935..2beb2ce25db90 100644 --- a/web/packages/teleport/src/Discover/Discover.test.tsx +++ b/web/packages/teleport/src/Discover/Discover.test.tsx @@ -25,7 +25,8 @@ import { render, screen } from 'design/utils/testing'; import { Resource } from 'gen-proto-ts/teleport/userpreferences/v1/onboard_pb'; import TeleportContextProvider from 'teleport/TeleportContextProvider'; -import { Discover } from 'teleport/Discover/Discover'; +import { Discover, DiscoverComponent } from 'teleport/Discover/Discover'; +import { ResourceViewConfig } from 'teleport/Discover/flow'; import { FeaturesContextProvider } from 'teleport/FeaturesContext'; import { createTeleportContext, getAcl } from 'teleport/mocks/contexts'; import { getOSSFeatures } from 'teleport/features'; @@ -47,6 +48,9 @@ import { makeTestUserContext } from 'teleport/User/testHelpers/makeTestUserConte import { makeDefaultUserPreferences } from 'teleport/services/userPreferences/userPreferences'; import { ResourceKind } from './Shared'; +import { useDiscover } from './useDiscover'; + +import type { ResourceSpec } from 'teleport/Discover/SelectResource/types'; beforeEach(() => { jest.restoreAllMocks(); @@ -204,3 +208,101 @@ describe('location state', () => { ).not.toBeInTheDocument(); }); }); + +type updateProps = { + resourceName?: string; + resourceSpecForUpdate?: ResourceSpec; +}; + +const renderUpdate = (props: updateProps) => { + const defaultPref = makeDefaultUserPreferences(); + defaultPref.onboard.preferredResources = [Resource.WEB_APPLICATIONS]; + + mockUserContextProviderWith( + makeTestUserContext({ preferences: defaultPref }) + ); + + const userAcl = getAcl(); + const ctx = createTeleportContext({ customAcl: userAcl }); + + const MockComponent1 = () => { + const { agentMeta } = useDiscover(); + return ( + <> + {agentMeta.resourceName === 'saml2' ? agentMeta.resourceName : 'saml1'} + + ); + }; + + const testViews: ResourceViewConfig[] = [ + { + kind: ResourceKind.SamlApplication, + views() { + return [ + { + title: 'MockComponent1', + component: MockComponent1, + }, + ]; + }, + }, + ]; + + return render( + + + + + + ); +}; + +test('update flow: renders single component based on resourceSpecForUpdatvalue', () => { + const resourceSpecForUpdate: ResourceSpec = { + name: 'Connect My Computer', + kind: ResourceKind.ConnectMyComputer, + event: null, + icon: 'Laptop', + keywords: '', + hasAccess: true, + }; + + renderUpdate({ resourceSpecForUpdate: resourceSpecForUpdate }); + + expect(screen.queryAllByTestId(ResourceKind.Server).length).toBeFalsy(); + + expect(screen.queryAllByTestId(ResourceKind.Database).length).toBeFalsy(); + + expect(screen.queryAllByTestId(ResourceKind.Application).length).toBeFalsy(); + + expect(screen.queryAllByTestId(ResourceKind.Kubernetes).length).toBeFalsy(); + + expect(screen.getByText('Sign In & Connect My Computer')).toBeInTheDocument(); +}); + +test('update flow: agentMeta is prepopulated based on agentMetaForUpdate', () => { + const resourceSpecForUpdate: ResourceSpec = { + name: 'MockComponent1', + kind: ResourceKind.SamlApplication, + event: null, + icon: 'Application', + keywords: '', + hasAccess: true, + }; + + renderUpdate({ + resourceName: 'saml2', + resourceSpecForUpdate: resourceSpecForUpdate, + }); + + expect(screen.getByText('saml2')).toBeInTheDocument(); +}); diff --git a/web/packages/teleport/src/Discover/Discover.tsx b/web/packages/teleport/src/Discover/Discover.tsx index 3e56e8dcb72fa..50f81f3fbf7a5 100644 --- a/web/packages/teleport/src/Discover/Discover.tsx +++ b/web/packages/teleport/src/Discover/Discover.tsx @@ -29,9 +29,11 @@ import { findViewAtIndex } from 'teleport/components/Wizard/flow'; import { EViewConfigs } from './types'; -import { DiscoverProvider, useDiscover } from './useDiscover'; +import { DiscoverProvider, useDiscover, AgentMeta } from './useDiscover'; import { DiscoverIcon } from './SelectResource/icons'; +import type { ResourceSpec } from './SelectResource'; + function DiscoverContent() { const { currentStep, @@ -94,10 +96,21 @@ function DiscoverContent() { ); } -export function DiscoverComponent({ eViewConfigs = [] }: Props) { +export function DiscoverComponent({ + eViewConfigs = [], + isUpdateFlow, + resourceSpecForUpdate, + agentMetaForUpdate, +}: DiscoverComponentProps) { const location = useLocation(); return ( - + ); @@ -107,6 +120,9 @@ export function Discover() { return ; } -type Props = { +export type DiscoverComponentProps = { eViewConfigs?: EViewConfigs; + isUpdateFlow?: boolean; + resourceSpecForUpdate?: ResourceSpec; + agentMetaForUpdate?: AgentMeta; }; diff --git a/web/packages/teleport/src/Discover/useDiscover.tsx b/web/packages/teleport/src/Discover/useDiscover.tsx index 55b09eaa1cf67..4b6177800b4ca 100644 --- a/web/packages/teleport/src/Discover/useDiscover.tsx +++ b/web/packages/teleport/src/Discover/useDiscover.tsx @@ -70,6 +70,7 @@ export interface DiscoverContextState { emitErrorEvent(errorStr: string): void; emitEvent(status: DiscoverEventStepStatus, custom?: CustomEventInput): void; eventState: EventState; + isUpdateFlow?: boolean; } type EventState = { @@ -93,6 +94,14 @@ type DiscoverProviderProps = { mockCtx?: DiscoverContextState; // Extra view configs that are passed in. This is used to add view configs from Enterprise. eViewConfigs?: EViewConfigs; + // isUpdateFlow indicates if the Discover flow is an update resource flow. + isUpdateFlow?: boolean; + // resourceSpecForUpdate specifies ResourceSpec which should be used to + // start a Discover flow. + resourceSpecForUpdate?: ResourceSpec; + // agentMetaForUpdate includes data that will be used to prepopulate input fields + // in the respective Discover compnents. + agentMetaForUpdate?: AgentMeta; }; // DiscoverUrlLocationState define fields to preserve state between @@ -117,6 +126,9 @@ export function DiscoverProvider({ mockCtx, children, eViewConfigs = [], + isUpdateFlow, + resourceSpecForUpdate, + agentMetaForUpdate, }: React.PropsWithChildren) { const history = useHistory(); const location = useLocation(); @@ -229,6 +241,14 @@ export function DiscoverProvider({ }); } + // trigger update Discover flow. + useEffect(() => { + if (isUpdateFlow) { + onSelectResource(resourceSpecForUpdate); + updateAgentMeta(agentMetaForUpdate); + } + }, [agentMetaForUpdate]); + // If a location.state.discover was provided, that means the user is // coming back from another location to resume the flow. // Users will resume at the step that is +1 from the step they left from. @@ -453,6 +473,7 @@ export function DiscoverProvider({ emitErrorEvent, emitEvent, eventState, + isUpdateFlow, }; return ( From c72e310604260f6fab6e5b6f0c06354a84dbb590 Mon Sep 17 00:00:00 2001 From: flyinghermit Date: Wed, 3 Jul 2024 11:45:03 -0400 Subject: [PATCH 2/2] use updateFlow type object to hold update props --- .../teleport/src/Discover/Discover.test.tsx | 30 ++++++----------- .../teleport/src/Discover/Discover.tsx | 20 +++++------ .../teleport/src/Discover/useDiscover.tsx | 33 ++++++++++--------- 3 files changed, 35 insertions(+), 48 deletions(-) diff --git a/web/packages/teleport/src/Discover/Discover.test.tsx b/web/packages/teleport/src/Discover/Discover.test.tsx index 2beb2ce25db90..aa8327d5612c0 100644 --- a/web/packages/teleport/src/Discover/Discover.test.tsx +++ b/web/packages/teleport/src/Discover/Discover.test.tsx @@ -48,7 +48,7 @@ import { makeTestUserContext } from 'teleport/User/testHelpers/makeTestUserConte import { makeDefaultUserPreferences } from 'teleport/services/userPreferences/userPreferences'; import { ResourceKind } from './Shared'; -import { useDiscover } from './useDiscover'; +import { useDiscover, DiscoverUpdateProps } from './useDiscover'; import type { ResourceSpec } from 'teleport/Discover/SelectResource/types'; @@ -209,12 +209,7 @@ describe('location state', () => { }); }); -type updateProps = { - resourceName?: string; - resourceSpecForUpdate?: ResourceSpec; -}; - -const renderUpdate = (props: updateProps) => { +const renderUpdate = (props: DiscoverUpdateProps) => { const defaultPref = makeDefaultUserPreferences(); defaultPref.onboard.preferredResources = [Resource.WEB_APPLICATIONS]; @@ -255,19 +250,14 @@ const renderUpdate = (props: updateProps) => { ]} > - + ); }; -test('update flow: renders single component based on resourceSpecForUpdatvalue', () => { - const resourceSpecForUpdate: ResourceSpec = { +test('update flow: renders single component based on resourceSpec', () => { + const resourceSpec: ResourceSpec = { name: 'Connect My Computer', kind: ResourceKind.ConnectMyComputer, event: null, @@ -276,7 +266,7 @@ test('update flow: renders single component based on resourceSpecForUpdatvalue', hasAccess: true, }; - renderUpdate({ resourceSpecForUpdate: resourceSpecForUpdate }); + renderUpdate({ resourceSpec: resourceSpec, agentMeta: { resourceName: '' } }); expect(screen.queryAllByTestId(ResourceKind.Server).length).toBeFalsy(); @@ -289,8 +279,8 @@ test('update flow: renders single component based on resourceSpecForUpdatvalue', expect(screen.getByText('Sign In & Connect My Computer')).toBeInTheDocument(); }); -test('update flow: agentMeta is prepopulated based on agentMetaForUpdate', () => { - const resourceSpecForUpdate: ResourceSpec = { +test('update flow: agentMeta is prepopulated based on agentMeta', () => { + const resourceSpec: ResourceSpec = { name: 'MockComponent1', kind: ResourceKind.SamlApplication, event: null, @@ -300,8 +290,8 @@ test('update flow: agentMeta is prepopulated based on agentMetaForUpdate', () => }; renderUpdate({ - resourceName: 'saml2', - resourceSpecForUpdate: resourceSpecForUpdate, + resourceSpec: resourceSpec, + agentMeta: { resourceName: 'saml2' }, }); expect(screen.getByText('saml2')).toBeInTheDocument(); diff --git a/web/packages/teleport/src/Discover/Discover.tsx b/web/packages/teleport/src/Discover/Discover.tsx index 50f81f3fbf7a5..3730f132c282c 100644 --- a/web/packages/teleport/src/Discover/Discover.tsx +++ b/web/packages/teleport/src/Discover/Discover.tsx @@ -29,11 +29,13 @@ import { findViewAtIndex } from 'teleport/components/Wizard/flow'; import { EViewConfigs } from './types'; -import { DiscoverProvider, useDiscover, AgentMeta } from './useDiscover'; +import { + DiscoverProvider, + useDiscover, + DiscoverUpdateProps, +} from './useDiscover'; import { DiscoverIcon } from './SelectResource/icons'; -import type { ResourceSpec } from './SelectResource'; - function DiscoverContent() { const { currentStep, @@ -98,18 +100,14 @@ function DiscoverContent() { export function DiscoverComponent({ eViewConfigs = [], - isUpdateFlow, - resourceSpecForUpdate, - agentMetaForUpdate, + updateFlow, }: DiscoverComponentProps) { const location = useLocation(); return ( @@ -122,7 +120,5 @@ export function Discover() { export type DiscoverComponentProps = { eViewConfigs?: EViewConfigs; - isUpdateFlow?: boolean; - resourceSpecForUpdate?: ResourceSpec; - agentMetaForUpdate?: AgentMeta; + updateFlow?: DiscoverUpdateProps; }; diff --git a/web/packages/teleport/src/Discover/useDiscover.tsx b/web/packages/teleport/src/Discover/useDiscover.tsx index 4b6177800b4ca..8ab099843af62 100644 --- a/web/packages/teleport/src/Discover/useDiscover.tsx +++ b/web/packages/teleport/src/Discover/useDiscover.tsx @@ -89,19 +89,22 @@ type CustomEventInput = { discoveryConfigMethod?: DiscoverDiscoveryConfigMethod; }; +export type DiscoverUpdateProps = { + // resourceSpecForUpdate specifies ResourceSpec which should be used to + // start a Discover flow. + resourceSpec: ResourceSpec; + // agentMetaForUpdate includes data that will be used to prepopulate input fields + // in the respective Discover compnents. + agentMeta: AgentMeta; +}; + type DiscoverProviderProps = { // mockCtx used for testing purposes. mockCtx?: DiscoverContextState; // Extra view configs that are passed in. This is used to add view configs from Enterprise. eViewConfigs?: EViewConfigs; - // isUpdateFlow indicates if the Discover flow is an update resource flow. - isUpdateFlow?: boolean; - // resourceSpecForUpdate specifies ResourceSpec which should be used to - // start a Discover flow. - resourceSpecForUpdate?: ResourceSpec; - // agentMetaForUpdate includes data that will be used to prepopulate input fields - // in the respective Discover compnents. - agentMetaForUpdate?: AgentMeta; + // updateFlow holds properties used in Discover update flow. + updateFlow?: DiscoverUpdateProps; }; // DiscoverUrlLocationState define fields to preserve state between @@ -126,9 +129,7 @@ export function DiscoverProvider({ mockCtx, children, eViewConfigs = [], - isUpdateFlow, - resourceSpecForUpdate, - agentMetaForUpdate, + updateFlow, }: React.PropsWithChildren) { const history = useHistory(); const location = useLocation(); @@ -243,11 +244,11 @@ export function DiscoverProvider({ // trigger update Discover flow. useEffect(() => { - if (isUpdateFlow) { - onSelectResource(resourceSpecForUpdate); - updateAgentMeta(agentMetaForUpdate); + if (updateFlow) { + onSelectResource(updateFlow.resourceSpec); + updateAgentMeta(updateFlow.agentMeta); } - }, [agentMetaForUpdate]); + }, [updateFlow]); // If a location.state.discover was provided, that means the user is // coming back from another location to resume the flow. @@ -473,7 +474,7 @@ export function DiscoverProvider({ emitErrorEvent, emitEvent, eventState, - isUpdateFlow, + isUpdateFlow: Boolean(updateFlow), }; return (