From 89a9818a2ac54f37f7ad7c9fd5d1dbce62764ef1 Mon Sep 17 00:00:00 2001 From: Richard87 Date: Thu, 16 Jan 2025 14:07:05 +0100 Subject: [PATCH 01/18] init commit, extract data loading functions --- .../create-application-form.tsx} | 78 +++++++++++++------ .../page-create-application/dev.tsx | 16 ++++ .../page-create-application/index.tsx | 2 +- 3 files changed, 73 insertions(+), 23 deletions(-) rename src/components/{create-application-form/index.tsx => page-create-application/create-application-form.tsx} (85%) diff --git a/src/components/create-application-form/index.tsx b/src/components/page-create-application/create-application-form.tsx similarity index 85% rename from src/components/create-application-form/index.tsx rename to src/components/page-create-application/create-application-form.tsx index cdf815094..76b3740ba 100644 --- a/src/components/create-application-form/index.tsx +++ b/src/components/page-create-application/create-application-form.tsx @@ -14,7 +14,8 @@ import useLocalStorage from '../../effects/use-local-storage'; import { externalUrls } from '../../externalUrls'; import { type ApplicationRegistration, - type ApplicationRegistrationUpsertResponse, + type RegisterApplicationApiArg, + type RegisterApplicationApiResponse, radixApi, useRegisterApplicationMutation, } from '../../store/radix-api'; @@ -33,17 +34,50 @@ import { import type { HandleAdGroupsChangeCB } from '../graph/adGroups'; import { ExternalLink } from '../link/external-link'; -function sanitizeName(name: string): string { - // force name to lowercase, no spaces - return name?.toLowerCase().replace(/[^a-z0-9]/g, '-') ?? ''; -} - type Props = { onCreated: (application: ApplicationRegistration) => void; }; + export default function CreateApplicationForm({ onCreated }: Props) { - const [acknowledgeWarnings, setAcknowledgeWarnings] = useState(false); + const [refreshApps] = radixApi.endpoints.showApplications.useLazyQuery({}); const [createApp, creationState] = useRegisterApplicationMutation(); + + return ( + refreshApps({})} + onCreateApp={(data: RegisterApplicationApiArg) => + createApp(data).unwrap() + } + /> + ); +} + +type CreateApplicationFormLayoutProps = { + isLoading: boolean; + isSuccess: boolean; + error?: unknown; + warnings?: string[]; + onCreated: (application: ApplicationRegistration) => void; + onRefreshApps: () => unknown; + onCreateApp: ( + data: RegisterApplicationApiArg + ) => Promise; +}; +export function CreateApplicationFormLayout({ + isLoading, + isSuccess, + error, + warnings, + onCreated, + onRefreshApps, + onCreateApp, +}: CreateApplicationFormLayoutProps) { + const [acknowledgeWarnings, setAcknowledgeWarnings] = useState(false); const [applicationRegistration, setAppRegistration] = useState({ name: '', @@ -61,8 +95,6 @@ export default function CreateApplicationForm({ onCreated }: Props) { readerAdUsers: [], }); - const [refreshApps] = radixApi.endpoints.showApplications.useLazyQuery({}); - const [knownAppNames, setKnownAppNames] = useLocalStorage>( 'knownApplications', [] @@ -104,19 +136,19 @@ export default function CreateApplicationForm({ onCreated }: Props) { const handleSubmit = async (ev: FormEvent) => { try { ev.preventDefault(); - const response: ApplicationRegistrationUpsertResponse = await createApp({ + const response = await onCreateApp({ applicationRegistrationRequest: { applicationRegistration, acknowledgeWarnings, }, - }).unwrap(); + }); // the response will only contains an application if there are no warnings //Only call onCreated when created without warnings, or created with ack warnings if (response.applicationRegistration) { onCreated(response.applicationRegistration); addAppNameToLocalStorage(response.applicationRegistration.name); - refreshApps({}); + onRefreshApps(); successToast('Saved'); } else { warningToast('Registration had warnings'); @@ -154,10 +186,7 @@ export default function CreateApplicationForm({ onCreated }: Props) { -
+
- {creationState.isError && ( + {error ? ( Failed to create application.
- {getFetchErrorMessage(creationState.error)} + {getFetchErrorMessage(error)}
- )} + ) : null}
- {creationState.isLoading && ( + {isLoading && ( Creating… )} - {creationState.isSuccess && creationState.data?.warnings && ( + {isSuccess && warnings && (
- {creationState.data.warnings.map((message, i) => ( + {warnings.map((message, i) => ( {message} @@ -240,3 +269,8 @@ export default function CreateApplicationForm({ onCreated }: Props) { ); } + +function sanitizeName(name: string): string { + // force name to lowercase, no spaces + return name?.toLowerCase().replace(/[^a-z0-9]/g, '-') ?? ''; +} diff --git a/src/components/page-create-application/dev.tsx b/src/components/page-create-application/dev.tsx index 9a4263d29..427ae385d 100644 --- a/src/components/page-create-application/dev.tsx +++ b/src/components/page-create-application/dev.tsx @@ -1,4 +1,6 @@ +import { Divider } from '@equinor/eds-core-react'; import PageCreateApplication from '.'; +import { CreateApplicationFormLayout } from './create-application-form'; export default (
+ + + { + console.info(args); + return Promise.resolve({}); + }} + />
); diff --git a/src/components/page-create-application/index.tsx b/src/components/page-create-application/index.tsx index d7fa8dc64..20082338f 100644 --- a/src/components/page-create-application/index.tsx +++ b/src/components/page-create-application/index.tsx @@ -6,8 +6,8 @@ import { Link } from 'react-router-dom'; import { routes } from '../../routes'; import { routeWithParams } from '../../utils/string'; import { ConfigureApplicationGithub } from '../configure-application-github'; -import CreateApplicationForm from '../create-application-form'; import { ScrimPopup } from '../scrim-popup'; +import CreateApplicationForm from './create-application-form'; import type { ApplicationRegistration } from '../../store/radix-api'; import { NewApplyConfigPipelineLink } from '../link/apply-config-pipeline-link'; From 4c10f57d645483d8be276e036bdce6d461971fe0 Mon Sep 17 00:00:00 2001 From: Richard87 Date: Thu, 16 Jan 2025 16:57:16 +0100 Subject: [PATCH 02/18] cleanup CreateApplicationForm data --- .../create-application-form.tsx | 67 ++++++------------- .../page-create-application/index.tsx | 48 ++++++++++--- 2 files changed, 56 insertions(+), 59 deletions(-) diff --git a/src/components/page-create-application/create-application-form.tsx b/src/components/page-create-application/create-application-form.tsx index 76b3740ba..b7faefa22 100644 --- a/src/components/page-create-application/create-application-form.tsx +++ b/src/components/page-create-application/create-application-form.tsx @@ -9,15 +9,12 @@ import { } from '@equinor/eds-core-react'; import { info_circle } from '@equinor/eds-icons'; import { type ChangeEvent, type FormEvent, useState } from 'react'; - import useLocalStorage from '../../effects/use-local-storage'; import { externalUrls } from '../../externalUrls'; -import { - type ApplicationRegistration, - type RegisterApplicationApiArg, - type RegisterApplicationApiResponse, - radixApi, - useRegisterApplicationMutation, +import type { + ApplicationRegistration, + RegisterApplicationApiArg, + RegisterApplicationApiResponse, } from '../../store/radix-api'; import { getFetchErrorMessage } from '../../store/utils'; import { Alert } from '../alert'; @@ -36,48 +33,16 @@ import { ExternalLink } from '../link/external-link'; type Props = { onCreated: (application: ApplicationRegistration) => void; -}; - -export default function CreateApplicationForm({ onCreated }: Props) { - const [refreshApps] = radixApi.endpoints.showApplications.useLazyQuery({}); - const [createApp, creationState] = useRegisterApplicationMutation(); - - return ( - refreshApps({})} - onCreateApp={(data: RegisterApplicationApiArg) => - createApp(data).unwrap() - } - /> - ); -} - -type CreateApplicationFormLayoutProps = { - isLoading: boolean; - isSuccess: boolean; - error?: unknown; - warnings?: string[]; - onCreated: (application: ApplicationRegistration) => void; - onRefreshApps: () => unknown; onCreateApp: ( data: RegisterApplicationApiArg ) => Promise; }; -export function CreateApplicationFormLayout({ - isLoading, - isSuccess, - error, - warnings, - onCreated, - onRefreshApps, - onCreateApp, -}: CreateApplicationFormLayoutProps) { +export function CreateApplicationForm({ onCreated, onCreateApp }: Props) { const [acknowledgeWarnings, setAcknowledgeWarnings] = useState(false); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(); + const [warnings, setWarnings] = useState(); + const [success, setSuccess] = useState(false); const [applicationRegistration, setAppRegistration] = useState({ name: '', @@ -135,6 +100,8 @@ export function CreateApplicationFormLayout({ const handleSubmit = async (ev: FormEvent) => { try { + setLoading(true); + setSuccess(false); ev.preventDefault(); const response = await onCreateApp({ applicationRegistrationRequest: { @@ -142,19 +109,23 @@ export function CreateApplicationFormLayout({ acknowledgeWarnings, }, }); + setSuccess(true); // the response will only contains an application if there are no warnings //Only call onCreated when created without warnings, or created with ack warnings if (response.applicationRegistration) { onCreated(response.applicationRegistration); addAppNameToLocalStorage(response.applicationRegistration.name); - onRefreshApps(); successToast('Saved'); } else { + setWarnings(response.warnings); warningToast('Registration had warnings'); } } catch (e) { errorToast(`Error while saving. ${getFetchErrorMessage(e)}`); + setError(e); + } finally { + setLoading(false); } }; @@ -186,7 +157,7 @@ export function CreateApplicationFormLayout({
-
+
) : null}
- {isLoading && ( + {loading && ( Creating… )} - {isSuccess && warnings && ( + {success && warnings && (
{warnings.map((message, i) => ( diff --git a/src/components/page-create-application/index.tsx b/src/components/page-create-application/index.tsx index 20082338f..7eb8b7d21 100644 --- a/src/components/page-create-application/index.tsx +++ b/src/components/page-create-application/index.tsx @@ -7,21 +7,43 @@ import { routes } from '../../routes'; import { routeWithParams } from '../../utils/string'; import { ConfigureApplicationGithub } from '../configure-application-github'; import { ScrimPopup } from '../scrim-popup'; -import CreateApplicationForm from './create-application-form'; -import type { ApplicationRegistration } from '../../store/radix-api'; +import { + type ApplicationRegistration, + type RegisterApplicationApiArg, + type RegisterApplicationApiResponse, + radixApi, + useRegisterApplicationMutation, +} from '../../store/radix-api'; import { NewApplyConfigPipelineLink } from '../link/apply-config-pipeline-link'; import './style.css'; +import { CreateApplicationForm } from './create-application-form'; -function scrollToPosition( - elementRef: Element | null, - x: number, - y: number -): void { - elementRef?.scrollTo?.(x, y); +export default function PageCreateApplication() { + const [refreshApps] = radixApi.endpoints.showApplications.useLazyQuery({}); + const [createApp] = useRegisterApplicationMutation(); + + return ( + refreshApps({})} + onCreateApp={(data: RegisterApplicationApiArg) => + createApp(data).unwrap() + } + /> + ); } -export default function PageCreateApplication() { +type PageCreateApplicationLayoutProps = { + onRefreshApps: () => unknown; + onCreateApp: ( + data: RegisterApplicationApiArg + ) => Promise; +}; + +export function PageCreateApplicationLayout({ + onRefreshApps, + onCreateApp, +}: PageCreateApplicationLayoutProps) { const [visibleScrim, setVisibleScrim] = useState(false); const containerRef = useRef(null); const [registration, setRegistration] = @@ -33,8 +55,9 @@ export default function PageCreateApplication() { }; const onCreated = (newRegistration: ApplicationRegistration) => { - scrollToPosition(containerRef.current, 0, 0); + containerRef.current?.scrollTo(0, 0); setRegistration(newRegistration); + onRefreshApps(); }; return ( @@ -55,7 +78,10 @@ export default function PageCreateApplication() { >
{!registration ? ( - + ) : (
From 2bb3f553f4dfcbc793ee74293c7320a2e693938a Mon Sep 17 00:00:00 2001 From: Richard87 Date: Thu, 16 Jan 2025 17:36:24 +0100 Subject: [PATCH 03/18] move data out of configure github component --- .../configure-application-github/dev.tsx | 3 - .../configure-application-github/index.tsx | 160 +++++------------- src/components/page-configuration/index.tsx | 18 +- .../page-create-application/index.tsx | 146 +++++++++++++++- 4 files changed, 190 insertions(+), 137 deletions(-) diff --git a/src/components/configure-application-github/dev.tsx b/src/components/configure-application-github/dev.tsx index de85d40fd..bb69c1c9c 100644 --- a/src/components/configure-application-github/dev.tsx +++ b/src/components/configure-application-github/dev.tsx @@ -17,10 +17,7 @@ export default ( wbs: 'wbs123', radixConfigFullName: 'radixconfig.yaml', }} - onDeployKeyChange={() => void 0} refetch={() => void 0} - initialSecretPollInterval={5000} - useOtherCiToolOptionVisible={true} />
); diff --git a/src/components/configure-application-github/index.tsx b/src/components/configure-application-github/index.tsx index 189ecdb9e..bd2177736 100644 --- a/src/components/configure-application-github/index.tsx +++ b/src/components/configure-application-github/index.tsx @@ -1,76 +1,69 @@ import { Accordion, Button, - Checkbox, List, Progress, Typography, } from '@equinor/eds-core-react'; import { useState } from 'react'; -import imageDeployKey from './deploy-key02.png'; -import imageWebhook from './webhook02.png'; - -import { externalUrls } from '../../externalUrls'; -import { configVariables } from '../../utils/config'; import { Alert } from '../alert'; import { Code } from '../code'; -import { CompactCopyButton } from '../compact-copy-button'; +import imageDeployKey from './deploy-key02.png'; import './style.css'; -import { pollingInterval } from '../../store/defaults'; -import { - type ApplicationRegistration, - useGetDeployKeyAndSecretQuery, - useRegenerateDeployKeyMutation, +import type { + ApplicationRegistration, + DeployKeyAndSecret, + RegenerateDeployKeyApiArg, + RegenerateDeployKeyApiResponse, } from '../../store/radix-api'; import { getFetchErrorMessage } from '../../store/utils'; import { handlePromiseWithToast } from '../global-top-nav/styled-toaster'; import { ExternalLink } from '../link/external-link'; import { ScrimPopup } from '../scrim-popup'; -const radixZoneDNS = configVariables.RADIX_CLUSTER_BASE; - interface Props { app: ApplicationRegistration; - refetch?: () => unknown; - onDeployKeyChange: (appName: string) => void; + secrets?: DeployKeyAndSecret; + refetch: () => unknown; startVisible?: boolean; - useOtherCiToolOptionVisible?: boolean; - deployKeyTitle?: string; - webhookTitle?: string; - initialSecretPollInterval: number; + onRegenerateSecrets: ( + data: RegenerateDeployKeyApiArg + ) => Promise; + onRefreshSecrets: () => Promise; } export const ConfigureApplicationGithub = ({ app, refetch, + secrets, startVisible, - useOtherCiToolOptionVisible = false, - deployKeyTitle = 'Add deploy key', - webhookTitle = 'Add webhook', + onRegenerateSecrets, + onRefreshSecrets, }: Props) => { + const [loading, setLoading] = useState(false); + const [error, setError] = useState(); const isExpanded = !!startVisible; - const webhookURL = `https://webhook.${radixZoneDNS}/events/github?appName=${app.name}`; - const [useOtherCiTool, setUseOtherCiTool] = useState(false); const [visibleRegenerateScrim, setVisibleRegenerateScrim] = useState(false); - const [regenerateSecrets, { isLoading, error }] = - useRegenerateDeployKeyMutation(); - const { data: secrets, refetch: refetchSecrets } = - useGetDeployKeyAndSecretQuery( - { appName: app.name }, - { pollingInterval, skip: useOtherCiTool } - ); const onRegenerate = handlePromiseWithToast(async () => { - setVisibleRegenerateScrim(false); - await regenerateSecrets({ - appName: app.name, - regenerateDeployKeyAndSecretData: { sharedSecret: crypto.randomUUID() }, - }).unwrap(); - await refetchSecrets(); - await refetch?.(); + try { + setLoading(true); + setVisibleRegenerateScrim(false); + await onRegenerateSecrets({ + appName: app.name, + regenerateDeployKeyAndSecretData: { sharedSecret: crypto.randomUUID() }, + }); + await onRefreshSecrets(); + await refetch?.(); + } catch (e) { + setError(e); + throw e; + } finally { + setLoading(false); + } }, 'Successfully regenerated deploy key and webhook secret'); return ( @@ -83,7 +76,7 @@ export const ConfigureApplicationGithub = ({ - {deployKeyTitle} + Add deploy key @@ -122,13 +115,14 @@ export const ConfigureApplicationGithub = ({
- {error && ( + {error ? ( Failed to regenerate deploy key and webhook secret. +
{getFetchErrorMessage(error)}
- )} - {isLoading ? ( + ) : null} + {loading ? ( <> Regenerating… @@ -163,7 +157,7 @@ export const ConfigureApplicationGithub = ({
- {!isLoading && secrets?.publicDeployKey && ( + {!loading && secrets?.publicDeployKey && (
Regularly regenerate deploy key and webhook secret @@ -183,84 +177,6 @@ export const ConfigureApplicationGithub = ({ - - {useOtherCiToolOptionVisible && ( -
- setUseOtherCiTool(!useOtherCiTool)} - />{' '} - - - Use other CI tool than Radix - - - Select this option if your project is hosted on multiple - repositories and/or requires external control of building. Radix - will no longer need a webhook and will instead deploy your app - through the API/CLI. Read the{' '} - - Deployment Guide - {' '} - for details. - - -
- )} - - {!useOtherCiTool && ( - - - - - {webhookTitle} - - - -
- - GitHub notifies Radix using a webhook whenever a code push - is made. Open the{' '} - - Add Webhook page - {' '} - and follow the steps below - -
- 'Add webhook' steps on GitHub - - - As Payload URL, use {webhookURL}{' '} - - - - Choose application/json as Content type - - - The Shared Secret for this application is{' '} - {secrets?.sharedSecret}{' '} - - - Press "Add webhook" - -
-
-
-
-
- )}
); diff --git a/src/components/page-configuration/index.tsx b/src/components/page-configuration/index.tsx index 03cdc91c9..476f42253 100644 --- a/src/components/page-configuration/index.tsx +++ b/src/components/page-configuration/index.tsx @@ -18,7 +18,12 @@ import { ConfigureApplicationGithub } from '../configure-application-github'; import { DocumentTitle } from '../document-title'; import { pollingInterval } from '../../store/defaults'; -import { type ApplicationRegistration, radixApi } from '../../store/radix-api'; +import { + type ApplicationRegistration, + radixApi, + useGetDeployKeyAndSecretQuery, + useRegenerateDeployKeyMutation, +} from '../../store/radix-api'; import { ExternalLink } from '../link/external-link'; import { RadixConfigFileLink } from '../link/radix-config-file-link'; import './style.css'; @@ -41,6 +46,10 @@ export function PageConfiguration({ appName }: { appName: string }) { ...reqState } = radixApi.useGetApplicationQuery({ appName }, { pollingInterval }); const registration = application?.registration; + const [regenerateSecrets] = useRegenerateDeployKeyMutation(); + + const { data: secrets, refetch: refetchSecrets } = + useGetDeployKeyAndSecretQuery({ appName: appName }, { pollingInterval }); return (
@@ -78,12 +87,11 @@ export function PageConfiguration({ appName }: { appName: string }) { Config file refetchSecrets().unwrap()} + secrets={secrets} + onRegenerateSecrets={(data) => regenerateSecrets(data).unwrap()} refetch={refetch} app={registration} - deployKeyTitle="Deploy key" - webhookTitle="Webhook" - onDeployKeyChange={refetch} - initialSecretPollInterval={5000} /> diff --git a/src/components/page-create-application/index.tsx b/src/components/page-create-application/index.tsx index 7eb8b7d21..24bc6db84 100644 --- a/src/components/page-create-application/index.tsx +++ b/src/components/page-create-application/index.tsx @@ -1,4 +1,11 @@ -import { Button, Icon, Typography } from '@equinor/eds-core-react'; +import { + Accordion, + Button, + Checkbox, + Icon, + List, + Typography, +} from '@equinor/eds-core-react'; import { add } from '@equinor/eds-icons'; import { useRef, useState } from 'react'; import { Link } from 'react-router-dom'; @@ -10,39 +17,81 @@ import { ScrimPopup } from '../scrim-popup'; import { type ApplicationRegistration, + type DeployKeyAndSecret, + type RegenerateDeployKeyApiArg, + type RegenerateDeployKeyApiResponse, type RegisterApplicationApiArg, type RegisterApplicationApiResponse, radixApi, + useGetDeployKeyAndSecretQuery, + useRegenerateDeployKeyMutation, useRegisterApplicationMutation, } from '../../store/radix-api'; import { NewApplyConfigPipelineLink } from '../link/apply-config-pipeline-link'; import './style.css'; +import { externalUrls } from '../../externalUrls'; +import { pollingInterval } from '../../store/defaults'; +import { configVariables } from '../../utils/config'; +import { CompactCopyButton } from '../compact-copy-button'; +import imageWebhook from '../configure-application-github/webhook02.png'; +import { ExternalLink } from '../link/external-link'; import { CreateApplicationForm } from './create-application-form'; +const radixZoneDNS = configVariables.RADIX_CLUSTER_BASE; export default function PageCreateApplication() { const [refreshApps] = radixApi.endpoints.showApplications.useLazyQuery({}); const [createApp] = useRegisterApplicationMutation(); + const [regenerateSecrets] = useRegenerateDeployKeyMutation(); + const [useGithub, setUseGithub] = useState(false); + const [appName, setAppName] = useState(); + + const { data: secrets, refetch: refetchSecrets } = + useGetDeployKeyAndSecretQuery( + { appName: appName! }, + { pollingInterval, skip: !useGithub || !appName } + ); return ( regenerateSecrets(data).unwrap()} onRefreshApps={() => refreshApps({})} - onCreateApp={(data: RegisterApplicationApiArg) => - createApp(data).unwrap() - } + onCreateApp={async (data: RegisterApplicationApiArg) => { + const response = await createApp(data).unwrap(); + setAppName( + data.applicationRegistrationRequest?.applicationRegistration?.name + ); + return response; + }} /> ); } type PageCreateApplicationLayoutProps = { + useGithub: boolean; + setUseGithub: (show: boolean) => unknown; + secrets?: DeployKeyAndSecret; onRefreshApps: () => unknown; onCreateApp: ( data: RegisterApplicationApiArg ) => Promise; + onRegenerateSecrets: ( + data: RegenerateDeployKeyApiArg + ) => Promise; + onRefreshSecrets: () => Promise; }; export function PageCreateApplicationLayout({ + useGithub, + setUseGithub, + secrets, onRefreshApps, onCreateApp, + onRegenerateSecrets, + onRefreshSecrets, }: PageCreateApplicationLayoutProps) { const [visibleScrim, setVisibleScrim] = useState(false); const containerRef = useRef(null); @@ -89,12 +138,95 @@ export function PageCreateApplicationLayout({ set up console.error('Implement refetch registration!')} + secrets={secrets} + onRegenerateSecrets={onRegenerateSecrets} + onRefreshSecrets={onRefreshSecrets} app={registration} startVisible - onDeployKeyChange={() => void 0} - useOtherCiToolOptionVisible - initialSecretPollInterval={1500} /> + +
+ setUseGithub(e.target.checked)} + />{' '} + + + Use other CI tool than Radix + + + Select this option if your project is hosted on multiple + repositories and/or requires external control of building. + Radix will no longer need a webhook and will instead deploy + your app through the API/CLI. Read the{' '} + + Deployment Guide + {' '} + for details. + + +
+ + {!useGithub && ( + + + + + Add webhook + + + +
+ + GitHub notifies Radix using a webhook whenever a code + push is made. Open the{' '} + + Add Webhook page + {' '} + and follow the steps below + +
+ 'Add webhook' steps on GitHub + + + As Payload URL, use{' '} + {`https://webhook.${radixZoneDNS}/events/github?appName=${registration.name}`}{' '} + + + + Choose application/json as Content + type + + + The Shared Secret for this application is{' '} + {secrets?.sharedSecret}{' '} + + + Press "Add webhook" + +
+
+
+
+
+ )} Now you can run the{' '} From 20dd3738603b4039766a3c3b6f605dbe33ee35a7 Mon Sep 17 00:00:00 2001 From: Richard87 Date: Mon, 20 Jan 2025 10:10:36 +0100 Subject: [PATCH 04/18] move data out of configure github component --- src/components/configure-application-github/dev.tsx | 2 ++ .../page-create-application/create-application-form.tsx | 1 + src/components/page-create-application/dev.tsx | 9 ++------- src/components/page-create-application/index.tsx | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/configure-application-github/dev.tsx b/src/components/configure-application-github/dev.tsx index bb69c1c9c..c2c495ac6 100644 --- a/src/components/configure-application-github/dev.tsx +++ b/src/components/configure-application-github/dev.tsx @@ -18,6 +18,8 @@ export default ( radixConfigFullName: 'radixconfig.yaml', }} refetch={() => void 0} + onRefreshSecrets={() => Promise.resolve()} + onRegenerateSecrets={() => Promise.resolve()} />
); diff --git a/src/components/page-create-application/create-application-form.tsx b/src/components/page-create-application/create-application-form.tsx index b7faefa22..79aa84145 100644 --- a/src/components/page-create-application/create-application-form.tsx +++ b/src/components/page-create-application/create-application-form.tsx @@ -116,6 +116,7 @@ export function CreateApplicationForm({ onCreated, onCreateApp }: Props) { if (response.applicationRegistration) { onCreated(response.applicationRegistration); addAppNameToLocalStorage(response.applicationRegistration.name); + setAppRegistration(response.applicationRegistration); successToast('Saved'); } else { setWarnings(response.warnings); diff --git a/src/components/page-create-application/dev.tsx b/src/components/page-create-application/dev.tsx index 427ae385d..c39b71d71 100644 --- a/src/components/page-create-application/dev.tsx +++ b/src/components/page-create-application/dev.tsx @@ -1,6 +1,6 @@ import { Divider } from '@equinor/eds-core-react'; import PageCreateApplication from '.'; -import { CreateApplicationFormLayout } from './create-application-form'; +import { CreateApplicationForm } from './create-application-form'; export default (
- { console.info(args); diff --git a/src/components/page-create-application/index.tsx b/src/components/page-create-application/index.tsx index 24bc6db84..05f1392bd 100644 --- a/src/components/page-create-application/index.tsx +++ b/src/components/page-create-application/index.tsx @@ -56,7 +56,7 @@ export default function PageCreateApplication() { useGithub={useGithub} setUseGithub={setUseGithub} secrets={secrets} - onRefreshSecrets={refetchSecrets} + onRefreshSecrets={() => refetchSecrets().unwrap()} onRegenerateSecrets={(data) => regenerateSecrets(data).unwrap()} onRefreshApps={() => refreshApps({})} onCreateApp={async (data: RegisterApplicationApiArg) => { From ed9c19fe7064e9e0bbef820e64435e244972a7ab Mon Sep 17 00:00:00 2001 From: Richard87 Date: Mon, 20 Jan 2025 11:06:02 +0100 Subject: [PATCH 05/18] move layout components up a level --- .../configure-application-github/dev.tsx | 1 - .../configure-application-github/index.tsx | 270 +++++++++--------- .../configure-application-github/style.css | 12 - src/components/page-configuration/index.tsx | 56 +++- .../page-create-application/dev.tsx | 40 ++- .../page-create-application/index.tsx | 70 +---- 6 files changed, 231 insertions(+), 218 deletions(-) diff --git a/src/components/configure-application-github/dev.tsx b/src/components/configure-application-github/dev.tsx index c2c495ac6..904d03483 100644 --- a/src/components/configure-application-github/dev.tsx +++ b/src/components/configure-application-github/dev.tsx @@ -17,7 +17,6 @@ export default ( wbs: 'wbs123', radixConfigFullName: 'radixconfig.yaml', }} - refetch={() => void 0} onRefreshSecrets={() => Promise.resolve()} onRegenerateSecrets={() => Promise.resolve()} /> diff --git a/src/components/configure-application-github/index.tsx b/src/components/configure-application-github/index.tsx index bd2177736..e32d11274 100644 --- a/src/components/configure-application-github/index.tsx +++ b/src/components/configure-application-github/index.tsx @@ -1,10 +1,4 @@ -import { - Accordion, - Button, - List, - Progress, - Typography, -} from '@equinor/eds-core-react'; +import { Button, List, Progress, Typography } from '@equinor/eds-core-react'; import { useState } from 'react'; import { Alert } from '../alert'; @@ -16,48 +10,39 @@ import type { ApplicationRegistration, DeployKeyAndSecret, RegenerateDeployKeyApiArg, - RegenerateDeployKeyApiResponse, } from '../../store/radix-api'; import { getFetchErrorMessage } from '../../store/utils'; +import { configVariables } from '../../utils/config'; +import { CompactCopyButton } from '../compact-copy-button'; import { handlePromiseWithToast } from '../global-top-nav/styled-toaster'; import { ExternalLink } from '../link/external-link'; import { ScrimPopup } from '../scrim-popup'; +import imageWebhook from './webhook02.png'; interface Props { app: ApplicationRegistration; secrets?: DeployKeyAndSecret; - refetch: () => unknown; - startVisible?: boolean; - onRegenerateSecrets: ( - data: RegenerateDeployKeyApiArg - ) => Promise; + onRegenerateSecrets: (data: RegenerateDeployKeyApiArg) => Promise; onRefreshSecrets: () => Promise; } export const ConfigureApplicationGithub = ({ app, - refetch, secrets, - startVisible, onRegenerateSecrets, onRefreshSecrets, }: Props) => { const [loading, setLoading] = useState(false); const [error, setError] = useState(); - const isExpanded = !!startVisible; - const [visibleRegenerateScrim, setVisibleRegenerateScrim] = - useState(false); const onRegenerate = handlePromiseWithToast(async () => { try { setLoading(true); - setVisibleRegenerateScrim(false); await onRegenerateSecrets({ appName: app.name, regenerateDeployKeyAndSecretData: { sharedSecret: crypto.randomUUID() }, }); await onRefreshSecrets(); - await refetch?.(); } catch (e) { setError(e); throw e; @@ -67,117 +52,146 @@ export const ConfigureApplicationGithub = ({ }, 'Successfully regenerated deploy key and webhook secret'); return ( -
+
- To integrate with GitHub you must add a deploy key and a webhook + This allows Radix to clone the repository. Open the{' '} + + Add New Deploy Key page + {' '} + and follow the steps below -
- - - - - Add deploy key - - - -
- - This allows Radix to clone the repository. Open the{' '} - - Add New Deploy Key page - {' '} - and follow the steps below - -
- 'Add deploy key' steps on GitHub - - - Give the key a name, e.g. "Radix deploy key" - - - {secrets?.publicDeployKey ? ( -
- Copy and paste this key: - {secrets?.publicDeployKey} -
- ) : ( - <> - Please wait… - - )} -
- Press "Add key" -
-
-
-
- {error ? ( - - Failed to regenerate deploy key and webhook secret. -
- {getFetchErrorMessage(error)} -
- ) : null} - {loading ? ( - <> - Regenerating… - - ) : ( - <> - setVisibleRegenerateScrim(false)} - isDismissable - > -
-
- - Do you want to regenerate{' '} - deploy key and webhook secret? - - - New deploy key and webhook secret need to be put - to the GitHub repository settings - -
- - - - - -
-
- {!loading && secrets?.publicDeployKey && ( -
- - Regularly regenerate deploy key and webhook secret - - -
- )} - - )} -
-
-
-
-
-
+
+ Add deploy key' steps on GitHub + + Give the key a name, e.g. "Radix deploy key" + + {secrets?.publicDeployKey ? ( +
+ Copy and paste this key: + {secrets?.publicDeployKey} +
+ ) : ( + <> + Please wait… + + )} +
+ Press "Add key" +
+
+
+
+ {error ? ( + + Failed to regenerate deploy key and webhook secret. +
+ {getFetchErrorMessage(error)} +
+ ) : null} + {loading ? ( + <> + Regenerating… + + ) : ( + + )} +
); }; + +export function RegenerateSecretsScrim({ + onRegenerate, +}: { onRegenerate: () => Promise }) { + const [show, setShow] = useState(false); + + const onLocalRegenerate = handlePromiseWithToast(onRegenerate); + + return ( + <> + setShow(false)} + isDismissable + > +
+
+ + Do you want to regenerate deploy key and webhook + secret? + + + New deploy key and webhook secret need to be put to the GitHub + repository settings + +
+ + + + + +
+
+ +
+ + Regularly regenerate deploy key and webhook secret + + +
+ + ); +} + +export function ConfigureGithubWebhook({ + repository, + appName, + sharedSecret, +}: { repository: string; appName: string; sharedSecret?: string }) { + const radixZoneDNS = configVariables.RADIX_CLUSTER_BASE; + return ( +
+ + GitHub notifies Radix using a webhook whenever a code push is made. Open + the{' '} + + Add Webhook page + {' '} + and follow the steps below + +
+ 'Add webhook' steps on GitHub + + + As Payload URL, use{' '} + {`https://webhook.${radixZoneDNS}/events/github?appName=${appName}`}{' '} + + + + Choose application/json as Content type + + + The Shared Secret for this application is{' '} + {sharedSecret}{' '} + + + Press "Add webhook" + +
+
+ ); +} diff --git a/src/components/configure-application-github/style.css b/src/components/configure-application-github/style.css index a0a822aeb..5bc4413fb 100644 --- a/src/components/configure-application-github/style.css +++ b/src/components/configure-application-github/style.css @@ -9,18 +9,6 @@ padding-top: var(--eds_spacing_medium_small); } -.configure-application-github ol { - counter-reset: list; - padding: 0 var(--eds_spacing_medium); - margin: 0; -} - -.configure-application-github li { - display: list-item; - list-style: inherit; - padding: var(--eds_spacing_small) 0; -} - .deploy-key { margin-bottom: var(--eds_spacing_medium); } diff --git a/src/components/page-configuration/index.tsx b/src/components/page-configuration/index.tsx index 476f42253..6b1d035f9 100644 --- a/src/components/page-configuration/index.tsx +++ b/src/components/page-configuration/index.tsx @@ -1,4 +1,4 @@ -import { Typography } from '@equinor/eds-core-react'; +import { Accordion, Typography } from '@equinor/eds-core-react'; import { BuildSecretsAccordion } from './build-secrets-accordion'; import ChangeAdminForm from './change-admin-form'; import { ChangeConfigurationItemForm } from './change-ci-form'; @@ -14,7 +14,10 @@ import { withRouteParams } from '../../utils/router'; import { routeWithParams } from '../../utils/string'; import AsyncResource from '../async-resource/async-resource'; import { Breadcrumb } from '../breadcrumb'; -import { ConfigureApplicationGithub } from '../configure-application-github'; +import { + ConfigureApplicationGithub, + ConfigureGithubWebhook, +} from '../configure-application-github'; import { DocumentTitle } from '../document-title'; import { pollingInterval } from '../../store/defaults'; @@ -86,13 +89,48 @@ export function PageConfiguration({ appName }: { appName: string }) { Config file - refetchSecrets().unwrap()} - secrets={secrets} - onRegenerateSecrets={(data) => regenerateSecrets(data).unwrap()} - refetch={refetch} - app={registration} - /> + + To integrate with GitHub you must add a deploy key and a webhook + + +
+ + + + + Add deploy key + + + + refetchSecrets().unwrap()} + secrets={secrets} + onRegenerateSecrets={(data) => + regenerateSecrets(data).unwrap() + } + app={registration} + /> + + + + + + + + + Add webhook + + + + + + + +
diff --git a/src/components/page-create-application/dev.tsx b/src/components/page-create-application/dev.tsx index c39b71d71..5f299cce2 100644 --- a/src/components/page-create-application/dev.tsx +++ b/src/components/page-create-application/dev.tsx @@ -1,6 +1,10 @@ import { Divider } from '@equinor/eds-core-react'; -import PageCreateApplication from '.'; -import { CreateApplicationForm } from './create-application-form'; +import { useState } from 'react'; +import { PageCreateApplicationLayout } from '.'; +import type { + DeployKeyAndSecret, + RegisterApplicationApiArg, +} from '../../store/radix-api'; export default (
- + +
+); + +function TestComponent() { + const [useGithub, setUseGithub] = useState(false); + const [secrets, setSecrets] = useState(); - { - console.info(args); - return Promise.resolve({}); + return ( + Promise.resolve()} + onRegenerateSecrets={() => Promise.resolve()} + onRefreshApps={() => Promise.resolve()} + onCreateApp={async (data: RegisterApplicationApiArg) => { + setSecrets({ + sharedSecret: crypto.randomUUID(), + publicDeployKey: + 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDV+H/ZpDJ+gLw1oT3JaTsjR5N2LwPsAwobUKA6FnZrUwehTP1XlKG26qHv3cQZCHpRVPF7RNzCUKtyEffFU9eE5SrLoxxL/1iUaVPI7kFYZSt1TZKOhGn3WQdlb+miGZvHow6vR9K3QloNNV0L/oFQKjTJkyRf1odkkTCNzXMaEEeo0uOzBy5ZY9zaSWMOStzHMX0Ta/4LVXkGF9aJba3OJM6eipVyZ9nDP5C3UIH1y9X7ZfOTOQ338VBRMrPRKQ6cyzOaT48GNdwnJdGJpq666vYfqit5PO68c6bG5FlIfiypbaLW0mBTNYE1O+LHfYJu3Acd7CldVqcz6GP9rl9L', + }); + return data.applicationRegistrationRequest; }} /> -
-); + ); +} diff --git a/src/components/page-create-application/index.tsx b/src/components/page-create-application/index.tsx index 05f1392bd..f1a56efdd 100644 --- a/src/components/page-create-application/index.tsx +++ b/src/components/page-create-application/index.tsx @@ -12,7 +12,10 @@ import { Link } from 'react-router-dom'; import { routes } from '../../routes'; import { routeWithParams } from '../../utils/string'; -import { ConfigureApplicationGithub } from '../configure-application-github'; +import { + ConfigureApplicationGithub, + ConfigureGithubWebhook, +} from '../configure-application-github'; import { ScrimPopup } from '../scrim-popup'; import { @@ -31,12 +34,8 @@ import { NewApplyConfigPipelineLink } from '../link/apply-config-pipeline-link'; import './style.css'; import { externalUrls } from '../../externalUrls'; import { pollingInterval } from '../../store/defaults'; -import { configVariables } from '../../utils/config'; -import { CompactCopyButton } from '../compact-copy-button'; -import imageWebhook from '../configure-application-github/webhook02.png'; import { ExternalLink } from '../link/external-link'; import { CreateApplicationForm } from './create-application-form'; -const radixZoneDNS = configVariables.RADIX_CLUSTER_BASE; export default function PageCreateApplication() { const [refreshApps] = radixApi.endpoints.showApplications.useLazyQuery({}); @@ -137,13 +136,14 @@ export function PageCreateApplicationLayout({ The application {registration.name} has been set up + + To integrate with GitHub you must add a deploy key and a webhook + console.error('Implement refetch registration!')} secrets={secrets} onRegenerateSecrets={onRegenerateSecrets} onRefreshSecrets={onRefreshSecrets} app={registration} - startVisible />
@@ -175,57 +175,11 @@ export function PageCreateApplicationLayout({
{!useGithub && ( - - - - - Add webhook - - - -
- - GitHub notifies Radix using a webhook whenever a code - push is made. Open the{' '} - - Add Webhook page - {' '} - and follow the steps below - -
- 'Add webhook' steps on GitHub - - - As Payload URL, use{' '} - {`https://webhook.${radixZoneDNS}/events/github?appName=${registration.name}`}{' '} - - - - Choose application/json as Content - type - - - The Shared Secret for this application is{' '} - {secrets?.sharedSecret}{' '} - - - Press "Add webhook" - -
-
-
-
-
+ )} Now you can run the{' '} From f943571ccac763be22771c00834e57a974189134 Mon Sep 17 00:00:00 2001 From: Richard87 Date: Mon, 20 Jan 2025 11:07:55 +0100 Subject: [PATCH 06/18] move layout components up a level --- src/components/configure-application-github/dev.tsx | 7 ++++++- src/components/page-create-application/index.tsx | 9 +-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/components/configure-application-github/dev.tsx b/src/components/configure-application-github/dev.tsx index 904d03483..0a6629193 100644 --- a/src/components/configure-application-github/dev.tsx +++ b/src/components/configure-application-github/dev.tsx @@ -1,4 +1,4 @@ -import { ConfigureApplicationGithub } from '.'; +import { ConfigureApplicationGithub, ConfigureGithubWebhook } from '.'; export default (
@@ -20,5 +20,10 @@ export default ( onRefreshSecrets={() => Promise.resolve()} onRegenerateSecrets={() => Promise.resolve()} /> +
); diff --git a/src/components/page-create-application/index.tsx b/src/components/page-create-application/index.tsx index f1a56efdd..348939fe8 100644 --- a/src/components/page-create-application/index.tsx +++ b/src/components/page-create-application/index.tsx @@ -1,11 +1,4 @@ -import { - Accordion, - Button, - Checkbox, - Icon, - List, - Typography, -} from '@equinor/eds-core-react'; +import { Button, Checkbox, Icon, Typography } from '@equinor/eds-core-react'; import { add } from '@equinor/eds-icons'; import { useRef, useState } from 'react'; import { Link } from 'react-router-dom'; From 7c8ac297936f72c7e1d07eae4d122c9272115671 Mon Sep 17 00:00:00 2001 From: Richard87 Date: Mon, 20 Jan 2025 12:37:14 +0100 Subject: [PATCH 07/18] use searchparams for current page --- .../create-application-form.tsx | 9 +- .../page-create-application/dev.tsx | 3 - .../page-create-application/index.tsx | 89 ++++++++++++++----- 3 files changed, 73 insertions(+), 28 deletions(-) diff --git a/src/components/page-create-application/create-application-form.tsx b/src/components/page-create-application/create-application-form.tsx index 79aa84145..a961ff373 100644 --- a/src/components/page-create-application/create-application-form.tsx +++ b/src/components/page-create-application/create-application-form.tsx @@ -32,12 +32,17 @@ import type { HandleAdGroupsChangeCB } from '../graph/adGroups'; import { ExternalLink } from '../link/external-link'; type Props = { + created: boolean; onCreated: (application: ApplicationRegistration) => void; onCreateApp: ( data: RegisterApplicationApiArg ) => Promise; }; -export function CreateApplicationForm({ onCreated, onCreateApp }: Props) { +export function CreateApplicationForm({ + onCreated, + onCreateApp, + created, +}: Props) { const [acknowledgeWarnings, setAcknowledgeWarnings] = useState(false); const [loading, setLoading] = useState(false); const [error, setError] = useState(); @@ -158,7 +163,7 @@ export function CreateApplicationForm({ onCreated, onCreateApp }: Props) {
-
+
(); return ( Promise.resolve()} onRegenerateSecrets={() => Promise.resolve()} diff --git a/src/components/page-create-application/index.tsx b/src/components/page-create-application/index.tsx index 348939fe8..49ab77f6f 100644 --- a/src/components/page-create-application/index.tsx +++ b/src/components/page-create-application/index.tsx @@ -1,7 +1,7 @@ import { Button, Checkbox, Icon, Typography } from '@equinor/eds-core-react'; import { add } from '@equinor/eds-icons'; import { useRef, useState } from 'react'; -import { Link } from 'react-router-dom'; +import { Link, useSearchParams } from 'react-router-dom'; import { routes } from '../../routes'; import { routeWithParams } from '../../utils/string'; @@ -34,19 +34,16 @@ export default function PageCreateApplication() { const [refreshApps] = radixApi.endpoints.showApplications.useLazyQuery({}); const [createApp] = useRegisterApplicationMutation(); const [regenerateSecrets] = useRegenerateDeployKeyMutation(); - const [useGithub, setUseGithub] = useState(false); const [appName, setAppName] = useState(); const { data: secrets, refetch: refetchSecrets } = useGetDeployKeyAndSecretQuery( { appName: appName! }, - { pollingInterval, skip: !useGithub || !appName } + { pollingInterval, skip: !appName } ); return ( refetchSecrets().unwrap()} onRegenerateSecrets={(data) => regenerateSecrets(data).unwrap()} @@ -63,8 +60,6 @@ export default function PageCreateApplication() { } type PageCreateApplicationLayoutProps = { - useGithub: boolean; - setUseGithub: (show: boolean) => unknown; secrets?: DeployKeyAndSecret; onRefreshApps: () => unknown; onCreateApp: ( @@ -76,29 +71,49 @@ type PageCreateApplicationLayoutProps = { onRefreshSecrets: () => Promise; }; +type Page = 'registration' | 'deploykey' | 'ci' | 'webhook' | 'finished'; +function useCurrentPage(): [Page, (newPage: Page) => unknown] { + const [searchParams, setSearchParams] = useSearchParams(); + const page = searchParams.get('page') ?? 'registration'; + + const onNewPage = (newPage: Page) => { + setSearchParams((prev) => { + console.log({ newPage }); + prev.set('page', newPage); + return prev; + }); + }; + + return [page as Page, onNewPage]; +} + export function PageCreateApplicationLayout({ - useGithub, - setUseGithub, secrets, onRefreshApps, onCreateApp, onRegenerateSecrets, onRefreshSecrets, }: PageCreateApplicationLayoutProps) { - const [visibleScrim, setVisibleScrim] = useState(false); + const [visibleScrim, setVisibleScrim] = useState(true); + const [created, setCreated] = useState(false); + const [useGithub, setUseGithub] = useState(false); const containerRef = useRef(null); const [registration, setRegistration] = useState(null); + const [page, setNewPage] = useCurrentPage(); const onCloseScrim = () => { setVisibleScrim(false); setRegistration(null); + setCreated(false); }; const onCreated = (newRegistration: ApplicationRegistration) => { containerRef.current?.scrollTo(0, 0); setRegistration(newRegistration); + setCreated(true); onRefreshApps(); + setNewPage('deploykey'); }; return ( @@ -118,13 +133,15 @@ export function PageCreateApplicationLayout({ onClose={onCloseScrim} >
- {!registration ? ( + {(page === 'registration' || !registration) && ( - ) : ( -
+ )} + {page === 'deploykey' && registration && ( + <> The application {registration.name} has been set up @@ -138,12 +155,20 @@ export function PageCreateApplicationLayout({ onRefreshSecrets={onRefreshSecrets} app={registration} /> + + + + )} + {page === 'ci' && registration && ( + <>
setUseGithub(e.target.checked)} + checked={!useGithub} + onChange={(e) => setUseGithub(!e.target.checked)} />{' '}
+ + + + )} + + {page === 'webhook' && registration && ( + <> + + + + + )} - {!useGithub && ( - - )} + {page === 'webhook' && registration && ( + <> Now you can run the{' '} @@ -190,7 +230,10 @@ export function PageCreateApplicationLayout({ your application's page
-
+ + )}
From a51a616087fda271ffeb90272bbd89e28782373e Mon Sep 17 00:00:00 2001 From: Richard87 Date: Tue, 21 Jan 2025 10:52:27 +0100 Subject: [PATCH 08/18] move regenerate secret out of github deploykey component --- .../configure-application-github/index.tsx | 117 ++++++++---------- src/components/page-configuration/index.tsx | 11 +- .../page-create-application/dev.tsx | 1 + 3 files changed, 59 insertions(+), 70 deletions(-) diff --git a/src/components/configure-application-github/index.tsx b/src/components/configure-application-github/index.tsx index e32d11274..9b447c0b6 100644 --- a/src/components/configure-application-github/index.tsx +++ b/src/components/configure-application-github/index.tsx @@ -1,18 +1,17 @@ import { Button, List, Progress, Typography } from '@equinor/eds-core-react'; import { useState } from 'react'; - -import { Alert } from '../alert'; import { Code } from '../code'; import imageDeployKey from './deploy-key02.png'; import './style.css'; -import type { - ApplicationRegistration, - DeployKeyAndSecret, - RegenerateDeployKeyApiArg, +import { + type ApplicationRegistration, + type DeployKeyAndSecret, + useRegenerateDeployKeyMutation, } from '../../store/radix-api'; import { getFetchErrorMessage } from '../../store/utils'; import { configVariables } from '../../utils/config'; +import { Alert } from '../alert'; import { CompactCopyButton } from '../compact-copy-button'; import { handlePromiseWithToast } from '../global-top-nav/styled-toaster'; import { ExternalLink } from '../link/external-link'; @@ -22,35 +21,9 @@ import imageWebhook from './webhook02.png'; interface Props { app: ApplicationRegistration; secrets?: DeployKeyAndSecret; - onRegenerateSecrets: (data: RegenerateDeployKeyApiArg) => Promise; - onRefreshSecrets: () => Promise; } -export const ConfigureApplicationGithub = ({ - app, - secrets, - onRegenerateSecrets, - onRefreshSecrets, -}: Props) => { - const [loading, setLoading] = useState(false); - const [error, setError] = useState(); - - const onRegenerate = handlePromiseWithToast(async () => { - try { - setLoading(true); - await onRegenerateSecrets({ - appName: app.name, - regenerateDeployKeyAndSecretData: { sharedSecret: crypto.randomUUID() }, - }); - await onRefreshSecrets(); - } catch (e) { - setError(e); - throw e; - } finally { - setLoading(false); - } - }, 'Successfully regenerated deploy key and webhook secret'); - +export const ConfigureApplicationGithub = ({ app, secrets }: Props) => { return (
@@ -61,11 +34,6 @@ export const ConfigureApplicationGithub = ({ and follow the steps below
- Add deploy key' steps on GitHub Give the key a name, e.g. "Radix deploy key" @@ -82,35 +50,39 @@ export const ConfigureApplicationGithub = ({ Press "Add key" -
-
-
- {error ? ( - - Failed to regenerate deploy key and webhook secret. -
- {getFetchErrorMessage(error)} -
- ) : null} - {loading ? ( - <> - Regenerating… - - ) : ( - - )} -
+ Add deploy key' steps on GitHub
); }; +type RegenerateSecretsScrimProps = { + appName: string; + refetchSecrets: () => Promise; +}; + export function RegenerateSecretsScrim({ - onRegenerate, -}: { onRegenerate: () => Promise }) { + appName, + refetchSecrets, +}: RegenerateSecretsScrimProps) { const [show, setShow] = useState(false); - const onLocalRegenerate = handlePromiseWithToast(onRegenerate); + const [regenerateSecrets, regenerateSecretState] = + useRegenerateDeployKeyMutation(); + + const onRegenerate = handlePromiseWithToast(async () => { + await regenerateSecrets({ + appName, + regenerateDeployKeyAndSecretData: { + sharedSecret: crypto.randomUUID(), + }, + }).unwrap(); + await refetchSecrets(); + }); return ( <> @@ -133,7 +105,7 @@ export function RegenerateSecretsScrim({
- + @@ -142,10 +114,27 @@ export function RegenerateSecretsScrim({
- - Regularly regenerate deploy key and webhook secret - - +
+ {regenerateSecretState.isError ? ( + + Failed to regenerate deploy key and webhook secret. +
+ {getFetchErrorMessage(regenerateSecretState.error)} +
+ ) : null} + {regenerateSecretState.isLoading ? ( + <> + Regenerating… + + ) : ( +
+ + Regularly regenerate deploy key and webhook secret + + +
+ )} +
); diff --git a/src/components/page-configuration/index.tsx b/src/components/page-configuration/index.tsx index 6b1d035f9..aab6e00f0 100644 --- a/src/components/page-configuration/index.tsx +++ b/src/components/page-configuration/index.tsx @@ -17,6 +17,7 @@ import { Breadcrumb } from '../breadcrumb'; import { ConfigureApplicationGithub, ConfigureGithubWebhook, + RegenerateSecretsScrim, } from '../configure-application-github'; import { DocumentTitle } from '../document-title'; @@ -25,7 +26,6 @@ import { type ApplicationRegistration, radixApi, useGetDeployKeyAndSecretQuery, - useRegenerateDeployKeyMutation, } from '../../store/radix-api'; import { ExternalLink } from '../link/external-link'; import { RadixConfigFileLink } from '../link/radix-config-file-link'; @@ -49,7 +49,6 @@ export function PageConfiguration({ appName }: { appName: string }) { ...reqState } = radixApi.useGetApplicationQuery({ appName }, { pollingInterval }); const registration = application?.registration; - const [regenerateSecrets] = useRegenerateDeployKeyMutation(); const { data: secrets, refetch: refetchSecrets } = useGetDeployKeyAndSecretQuery({ appName: appName }, { pollingInterval }); @@ -103,13 +102,13 @@ export function PageConfiguration({ appName }: { appName: string }) { refetchSecrets().unwrap()} secrets={secrets} - onRegenerateSecrets={(data) => - regenerateSecrets(data).unwrap() - } app={registration} /> + diff --git a/src/components/page-create-application/dev.tsx b/src/components/page-create-application/dev.tsx index dab319108..050160eb9 100644 --- a/src/components/page-create-application/dev.tsx +++ b/src/components/page-create-application/dev.tsx @@ -1,5 +1,6 @@ import { Divider } from '@equinor/eds-core-react'; import { useState } from 'react'; +import { BrowserRouter } from 'react-router-dom'; import { PageCreateApplicationLayout } from '.'; import type { DeployKeyAndSecret, From 45ddeb0052be03e30be7d0f2233644488f80b3b2 Mon Sep 17 00:00:00 2001 From: Richard87 Date: Tue, 21 Jan 2025 10:55:00 +0100 Subject: [PATCH 09/18] move regenerate secret out of github deploykey component --- .../configure-application-github/index.tsx | 10 ++-- .../page-create-application/index.tsx | 55 +++++++------------ 2 files changed, 25 insertions(+), 40 deletions(-) diff --git a/src/components/configure-application-github/index.tsx b/src/components/configure-application-github/index.tsx index 9b447c0b6..455682623 100644 --- a/src/components/configure-application-github/index.tsx +++ b/src/components/configure-application-github/index.tsx @@ -157,11 +157,6 @@ export function ConfigureGithubWebhook({ and follow the steps below
- 'Add webhook' steps on GitHub As Payload URL, use{' '} @@ -180,6 +175,11 @@ export function ConfigureGithubWebhook({ Press "Add webhook" + 'Add webhook' steps on GitHub
); diff --git a/src/components/page-create-application/index.tsx b/src/components/page-create-application/index.tsx index 49ab77f6f..b4588a563 100644 --- a/src/components/page-create-application/index.tsx +++ b/src/components/page-create-application/index.tsx @@ -14,13 +14,10 @@ import { ScrimPopup } from '../scrim-popup'; import { type ApplicationRegistration, type DeployKeyAndSecret, - type RegenerateDeployKeyApiArg, - type RegenerateDeployKeyApiResponse, type RegisterApplicationApiArg, type RegisterApplicationApiResponse, radixApi, useGetDeployKeyAndSecretQuery, - useRegenerateDeployKeyMutation, useRegisterApplicationMutation, } from '../../store/radix-api'; import { NewApplyConfigPipelineLink } from '../link/apply-config-pipeline-link'; @@ -33,20 +30,16 @@ import { CreateApplicationForm } from './create-application-form'; export default function PageCreateApplication() { const [refreshApps] = radixApi.endpoints.showApplications.useLazyQuery({}); const [createApp] = useRegisterApplicationMutation(); - const [regenerateSecrets] = useRegenerateDeployKeyMutation(); const [appName, setAppName] = useState(); - const { data: secrets, refetch: refetchSecrets } = - useGetDeployKeyAndSecretQuery( - { appName: appName! }, - { pollingInterval, skip: !appName } - ); + const { data: secrets } = useGetDeployKeyAndSecretQuery( + { appName: appName! }, + { pollingInterval, skip: !appName } + ); return ( refetchSecrets().unwrap()} - onRegenerateSecrets={(data) => regenerateSecrets(data).unwrap()} onRefreshApps={() => refreshApps({})} onCreateApp={async (data: RegisterApplicationApiArg) => { const response = await createApp(data).unwrap(); @@ -65,34 +58,12 @@ type PageCreateApplicationLayoutProps = { onCreateApp: ( data: RegisterApplicationApiArg ) => Promise; - onRegenerateSecrets: ( - data: RegenerateDeployKeyApiArg - ) => Promise; - onRefreshSecrets: () => Promise; }; -type Page = 'registration' | 'deploykey' | 'ci' | 'webhook' | 'finished'; -function useCurrentPage(): [Page, (newPage: Page) => unknown] { - const [searchParams, setSearchParams] = useSearchParams(); - const page = searchParams.get('page') ?? 'registration'; - - const onNewPage = (newPage: Page) => { - setSearchParams((prev) => { - console.log({ newPage }); - prev.set('page', newPage); - return prev; - }); - }; - - return [page as Page, onNewPage]; -} - export function PageCreateApplicationLayout({ secrets, onRefreshApps, onCreateApp, - onRegenerateSecrets, - onRefreshSecrets, }: PageCreateApplicationLayoutProps) { const [visibleScrim, setVisibleScrim] = useState(true); const [created, setCreated] = useState(false); @@ -151,8 +122,6 @@ export function PageCreateApplicationLayout({
-
+
{error ? ( @@ -217,7 +141,7 @@ export function CreateApplicationForm({ Creating… )} - {success && warnings && ( + {warnings.length > 0 && (
{warnings.map((message, i) => ( @@ -229,10 +153,6 @@ export function CreateApplicationForm({ ) => - setAcknowledgeWarnings(e.target.checked) - } />
)} diff --git a/src/components/page-create-application/dev.tsx b/src/components/page-create-application/dev.tsx index 050160eb9..2a0d4ff61 100644 --- a/src/components/page-create-application/dev.tsx +++ b/src/components/page-create-application/dev.tsx @@ -1,10 +1,9 @@ import { Divider } from '@equinor/eds-core-react'; import { useState } from 'react'; -import { BrowserRouter } from 'react-router-dom'; import { PageCreateApplicationLayout } from '.'; import type { + ApplicationRegistration, DeployKeyAndSecret, - RegisterApplicationApiArg, } from '../../store/radix-api'; export default ( @@ -27,16 +26,22 @@ function TestComponent() { return ( Promise.resolve()} - onRegenerateSecrets={() => Promise.resolve()} - onRefreshApps={() => Promise.resolve()} - onCreateApp={async (data: RegisterApplicationApiArg) => { + onRefreshApps={() => console.log('Refresh apps...')} + onCreateApplication={async ( + data: ApplicationRegistration, + ackWarnings + ) => { + if (!ackWarnings) + return [ + 'This is a warning, you should acknowledge it before continuing', + ]; + setSecrets({ sharedSecret: crypto.randomUUID(), publicDeployKey: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDV+H/ZpDJ+gLw1oT3JaTsjR5N2LwPsAwobUKA6FnZrUwehTP1XlKG26qHv3cQZCHpRVPF7RNzCUKtyEffFU9eE5SrLoxxL/1iUaVPI7kFYZSt1TZKOhGn3WQdlb+miGZvHow6vR9K3QloNNV0L/oFQKjTJkyRf1odkkTCNzXMaEEeo0uOzBy5ZY9zaSWMOStzHMX0Ta/4LVXkGF9aJba3OJM6eipVyZ9nDP5C3UIH1y9X7ZfOTOQ338VBRMrPRKQ6cyzOaT48GNdwnJdGJpq666vYfqit5PO68c6bG5FlIfiypbaLW0mBTNYE1O+LHfYJu3Acd7CldVqcz6GP9rl9L', }); - return data.applicationRegistrationRequest; + return [] as string[]; }} /> ); diff --git a/src/components/page-create-application/index.tsx b/src/components/page-create-application/index.tsx index b4588a563..d573ce667 100644 --- a/src/components/page-create-application/index.tsx +++ b/src/components/page-create-application/index.tsx @@ -1,4 +1,10 @@ -import { Button, Checkbox, Icon, Typography } from '@equinor/eds-core-react'; +import { + Button, + Checkbox, + Divider, + Icon, + Typography, +} from '@equinor/eds-core-react'; import { add } from '@equinor/eds-icons'; import { useRef, useState } from 'react'; import { Link, useSearchParams } from 'react-router-dom'; @@ -14,14 +20,13 @@ import { ScrimPopup } from '../scrim-popup'; import { type ApplicationRegistration, type DeployKeyAndSecret, - type RegisterApplicationApiArg, - type RegisterApplicationApiResponse, radixApi, useGetDeployKeyAndSecretQuery, useRegisterApplicationMutation, } from '../../store/radix-api'; import { NewApplyConfigPipelineLink } from '../link/apply-config-pipeline-link'; import './style.css'; +import useLocalStorage from '../../effects/use-local-storage'; import { externalUrls } from '../../externalUrls'; import { pollingInterval } from '../../store/defaults'; import { ExternalLink } from '../link/external-link'; @@ -37,17 +42,29 @@ export default function PageCreateApplication() { { pollingInterval, skip: !appName } ); + const onCreateApplication = async ( + applicationRegistration: ApplicationRegistration, + acknowledgeWarnings: boolean + ) => { + const response = await createApp({ + applicationRegistrationRequest: { + applicationRegistration, + acknowledgeWarnings, + }, + }).unwrap(); + + if (response.applicationRegistration) { + setAppName(response.applicationRegistration.name); + } + + return response.warnings ?? []; + }; + return ( refreshApps({})} - onCreateApp={async (data: RegisterApplicationApiArg) => { - const response = await createApp(data).unwrap(); - setAppName( - data.applicationRegistrationRequest?.applicationRegistration?.name - ); - return response; - }} /> ); } @@ -55,36 +72,57 @@ export default function PageCreateApplication() { type PageCreateApplicationLayoutProps = { secrets?: DeployKeyAndSecret; onRefreshApps: () => unknown; - onCreateApp: ( - data: RegisterApplicationApiArg - ) => Promise; + onCreateApplication: ( + application: ApplicationRegistration, + acknowledgeWarnings: boolean + ) => Promise; }; export function PageCreateApplicationLayout({ secrets, onRefreshApps, - onCreateApp, + onCreateApplication, }: PageCreateApplicationLayoutProps) { const [visibleScrim, setVisibleScrim] = useState(true); - const [created, setCreated] = useState(false); - const [useGithub, setUseGithub] = useState(false); + const [useGithub, setUseGithub] = useState(true); const containerRef = useRef(null); - const [registration, setRegistration] = - useState(null); + const [registration, setRegistration] = useState(); const [page, setNewPage] = useCurrentPage(); const onCloseScrim = () => { setVisibleScrim(false); - setRegistration(null); - setCreated(false); + setRegistration(undefined); }; - const onCreated = (newRegistration: ApplicationRegistration) => { + const [knownAppNames, setKnownAppNames] = useLocalStorage>( + 'knownApplications', + [] + ); + + const addAppNameToLocalStorage = (appName: string) => { + if (knownAppNames.some((knownAppName) => knownAppName === appName)) { + return; + } + setKnownAppNames([...knownAppNames, appName].sort()); + }; + + const onLocalCreateApplication = async ( + newRegistration: ApplicationRegistration, + acknowledgeWarnings: boolean + ) => { + const warnings = await onCreateApplication( + newRegistration, + acknowledgeWarnings + ); + if (warnings.length > 0) { + return warnings; + } + addAppNameToLocalStorage(newRegistration.name); containerRef.current?.scrollTo(0, 0); setRegistration(newRegistration); - setCreated(true); onRefreshApps(); setNewPage('deploykey'); + return []; }; return ( @@ -106,11 +144,11 @@ export function PageCreateApplicationLayout({
{(page === 'registration' || !registration) && ( )} + {page === 'deploykey' && registration && ( <> @@ -120,12 +158,14 @@ export function PageCreateApplicationLayout({ To integrate with GitHub you must add a deploy key and a webhook + + @@ -160,9 +200,7 @@ export function PageCreateApplicationLayout({
- + @@ -176,7 +214,7 @@ export function PageCreateApplicationLayout({ repository={registration.repository} sharedSecret={secrets?.sharedSecret} /> - + )} @@ -199,9 +237,6 @@ export function PageCreateApplicationLayout({ your application's page - )} @@ -217,7 +252,6 @@ function useCurrentPage(): [Page, (newPage: Page) => unknown] { const onNewPage = (newPage: Page) => { setSearchParams((prev) => { - console.log({ newPage }); prev.set('page', newPage); return prev; }); From 6f85e6b00b516dec4258f14ea712a4846d264539 Mon Sep 17 00:00:00 2001 From: Richard87 Date: Tue, 21 Jan 2025 14:44:47 +0100 Subject: [PATCH 12/18] controlled AD Groups --- .../configure-application-github/index.tsx | 25 ++++++----- .../link/apply-config-pipeline-link.tsx | 41 ++++++++++------- .../create-application-form.tsx | 10 ++++- .../page-create-application/index.tsx | 45 ++++++++++--------- 4 files changed, 70 insertions(+), 51 deletions(-) diff --git a/src/components/configure-application-github/index.tsx b/src/components/configure-application-github/index.tsx index 9734e4b45..8479c7ed6 100644 --- a/src/components/configure-application-github/index.tsx +++ b/src/components/configure-application-github/index.tsx @@ -153,15 +153,16 @@ export function ConfigureGithubWebhook({ return (
- GitHub notifies Radix using a webhook whenever a code push is made. Open - the{' '} - - Add Webhook page - {' '} - and follow the steps below + GitHub notifies Radix using a webhook whenever a code push is made.
+ + Open the{' '} + + Add Webhook page + {' '} + As Payload URL, use{' '} {`https://webhook.${radixZoneDNS}/events/github?appName=${appName}`}{' '} @@ -179,11 +180,13 @@ export function ConfigureGithubWebhook({ Press "Add webhook" - 'Add webhook' steps on GitHub +
+ 'Add webhook' steps on GitHub +
); diff --git a/src/components/link/apply-config-pipeline-link.tsx b/src/components/link/apply-config-pipeline-link.tsx index c69d36964..d9379c15d 100644 --- a/src/components/link/apply-config-pipeline-link.tsx +++ b/src/components/link/apply-config-pipeline-link.tsx @@ -1,26 +1,33 @@ -import { Typography } from '@equinor/eds-core-react'; +import { Button, Typography } from '@equinor/eds-core-react'; import type { PropsWithChildren } from 'react'; import { Link } from 'react-router-dom'; import { routes } from '../../routes'; import { routeWithParams } from '../../utils/string'; -type Props = { appName: string }; +type Props = { appName: string; button?: boolean }; export const NewApplyConfigPipelineLink = ({ + button, appName, children, -}: PropsWithChildren) => ( - - {children} - -); +}: PropsWithChildren) => { + const to = routeWithParams( + routes.appJobNew, + { appName: appName }, + { pipeline: 'apply-config' } + ); + + if (button) { + return ( + + ); + } + + return ( + + {children} + + ); +}; diff --git a/src/components/page-create-application/create-application-form.tsx b/src/components/page-create-application/create-application-form.tsx index 8eae847af..1f6d8c4a0 100644 --- a/src/components/page-create-application/create-application-form.tsx +++ b/src/components/page-create-application/create-application-form.tsx @@ -16,6 +16,7 @@ import { Alert } from '../alert'; import { AppConfigAdGroups } from '../app-config-ad-groups'; import { AppConfigConfigurationItem } from '../app-config-ci'; import { errorToast } from '../global-top-nav/styled-toaster'; +import type { AdGroupItem } from '../graph/adGroups'; import { ExternalLink } from '../link/external-link'; type Props = { @@ -29,6 +30,7 @@ export function CreateApplicationForm({ registration, onCreate }: Props) { const [loading, setLoading] = useState(false); const [error, setError] = useState(); const [warnings, setWarnings] = useState([]); + const [adUsers, setAdUsers] = useState[]>(); const handleSubmit = async (ev: FormEvent) => { ev.preventDefault(); @@ -38,7 +40,10 @@ export function CreateApplicationForm({ registration, onCreate }: Props) { const applicationRegistration: Partial = { name: sanitizeName(data.get('name')?.toString() ?? ''), repository: data.get('repository')?.toString(), - adGroups: data.getAll('adGroups').map((x) => x.toString()), + adGroups: adUsers?.filter((x) => x.type === 'Group')?.map((x) => x.id), + adUsers: adUsers + ?.filter((x) => x.type === 'ServicePrincipal') + ?.map((x) => x.id), configBranch: data.get('configBranch')?.toString(), radixConfigFullName: data.get('radixConfigFullName')?.toString(), configurationItem: data.get('configurationItem')?.toString(), @@ -125,7 +130,8 @@ export function CreateApplicationForm({ registration, onCreate }: Props) { /> x.id) || registration?.adGroups} + onChange={(items) => setAdUsers([...items])} labeling="Administrators" /> {error ? ( diff --git a/src/components/page-create-application/index.tsx b/src/components/page-create-application/index.tsx index d573ce667..dd90cad81 100644 --- a/src/components/page-create-application/index.tsx +++ b/src/components/page-create-application/index.tsx @@ -83,7 +83,7 @@ export function PageCreateApplicationLayout({ onRefreshApps, onCreateApplication, }: PageCreateApplicationLayoutProps) { - const [visibleScrim, setVisibleScrim] = useState(true); + const [visibleScrim, setVisibleScrim] = useState(false); const [useGithub, setUseGithub] = useState(true); const containerRef = useRef(null); const [registration, setRegistration] = useState(); @@ -164,20 +164,18 @@ export function PageCreateApplicationLayout({ app={registration} /> - - + )} {page === 'ci' && registration && ( <> -
+
+ - + {useGithub ? ( + + ) : ( + + )} )} @@ -215,18 +217,15 @@ export function PageCreateApplicationLayout({ sharedSecret={secrets?.sharedSecret} /> - + )} - {page === 'webhook' && registration && ( + {page === 'finished' && registration && ( <> - Now you can run the{' '} - - apply-config pipeline job - - to apply radixconfig.yaml, or go to{' '} + Now you can run the apply-config pipeline job, or or go + to{' '} your application's page + + + Apply Config + )} From 260038999abd17db865cd1f2b665b7e3928acba4 Mon Sep 17 00:00:00 2001 From: Richard87 Date: Tue, 21 Jan 2025 14:57:49 +0100 Subject: [PATCH 13/18] split components out in seperate files --- src/components/app-list/index.tsx | 4 +- .../configure-github-deploykey.tsx | 56 +++++ .../configure-github-webhook.tsx | 55 +++++ .../configure-application-github/dev.tsx | 5 +- .../configure-application-github/index.tsx | 193 ------------------ .../regenerate-secrets-scrim.tsx | 84 ++++++++ .../create-application-form.tsx | 0 .../create-application-scrim.tsx} | 116 ++--------- .../dev.tsx | 4 +- src/components/create-application/index.tsx | 47 +++++ .../style.css | 0 .../create-application/use-github-ci-form.tsx | 40 ++++ src/components/page-configuration/index.tsx | 10 +- 13 files changed, 311 insertions(+), 303 deletions(-) create mode 100644 src/components/configure-application-github/configure-github-deploykey.tsx create mode 100644 src/components/configure-application-github/configure-github-webhook.tsx delete mode 100644 src/components/configure-application-github/index.tsx create mode 100644 src/components/configure-application-github/regenerate-secrets-scrim.tsx rename src/components/{page-create-application => create-application}/create-application-form.tsx (100%) rename src/components/{page-create-application/index.tsx => create-application/create-application-scrim.tsx} (63%) rename src/components/{page-create-application => create-application}/dev.tsx (93%) create mode 100644 src/components/create-application/index.tsx rename src/components/{page-create-application => create-application}/style.css (100%) create mode 100644 src/components/create-application/use-github-ci-form.tsx diff --git a/src/components/app-list/index.tsx b/src/components/app-list/index.tsx index 27a4d6662..8e9553643 100644 --- a/src/components/app-list/index.tsx +++ b/src/components/app-list/index.tsx @@ -10,7 +10,7 @@ import { radixApi, useGetSearchApplicationsQuery } from '../../store/radix-api'; import { dataSorter, sortCompareString } from '../../utils/sort-utils'; import { AppListItem } from '../app-list-item'; import AsyncResource from '../async-resource/async-resource'; -import PageCreateApplication from '../page-create-application'; +import CreateApplication from '../create-application'; import './style.css'; import { refresh } from '@equinor/eds-icons'; @@ -124,7 +124,7 @@ export default function AppList() {
Favourites - +
{favouriteNames?.length > 0 ? ( diff --git a/src/components/configure-application-github/configure-github-deploykey.tsx b/src/components/configure-application-github/configure-github-deploykey.tsx new file mode 100644 index 000000000..c9acf40ef --- /dev/null +++ b/src/components/configure-application-github/configure-github-deploykey.tsx @@ -0,0 +1,56 @@ +import { List, Progress, Typography } from '@equinor/eds-core-react'; +import { Code } from '../code'; +import imageDeployKey from './deploy-key02.png'; + +import './style.css'; +import type { + ApplicationRegistration, + DeployKeyAndSecret, +} from '../../store/radix-api'; +import { ExternalLink } from '../link/external-link'; + +interface Props { + app: ApplicationRegistration; + secrets?: DeployKeyAndSecret; +} + +export const ConfigureGithubDeploykey = ({ app, secrets }: Props) => { + return ( +
+ + This allows Radix to clone the repository. Open the and follow the steps + below + +
+ + + {secrets?.publicDeployKey ? ( +
+ Copy this key: + {secrets?.publicDeployKey} +
+ ) : ( + <> + Please wait… + + )} +
+ Give the key a name, e.g. "Radix deploy key" + + Press "Add key" on Github's{' '} + + Add New Deploy Key page + + +
+
+ Add deploy key' steps on GitHub +
+
+
+ ); +}; diff --git a/src/components/configure-application-github/configure-github-webhook.tsx b/src/components/configure-application-github/configure-github-webhook.tsx new file mode 100644 index 000000000..adf7e413e --- /dev/null +++ b/src/components/configure-application-github/configure-github-webhook.tsx @@ -0,0 +1,55 @@ +import { List, Typography } from '@equinor/eds-core-react'; +import { configVariables } from '../../utils/config'; +import { CompactCopyButton } from '../compact-copy-button'; +import { ExternalLink } from '../link/external-link'; +import imageWebhook from './webhook02.png'; + +type Props = { repository: string; appName: string; sharedSecret?: string }; + +export function ConfigureGithubWebhook({ + repository, + appName, + sharedSecret, +}: Props) { + const radixZoneDNS = configVariables.RADIX_CLUSTER_BASE; + return ( +
+ + GitHub notifies Radix using a webhook whenever a code push is made. + +
+ + + Open the{' '} + + Add Webhook page + {' '} + + + As Payload URL, use{' '} + {`https://webhook.${radixZoneDNS}/events/github?appName=${appName}`}{' '} + + + + Choose application/json as Content type + + + The Shared Secret for this application is{' '} + {sharedSecret}{' '} + + + Press "Add webhook" + +
+ 'Add webhook' steps on GitHub +
+
+
+ ); +} diff --git a/src/components/configure-application-github/dev.tsx b/src/components/configure-application-github/dev.tsx index 2f6f3d545..86b7d9519 100644 --- a/src/components/configure-application-github/dev.tsx +++ b/src/components/configure-application-github/dev.tsx @@ -1,8 +1,9 @@ -import { ConfigureApplicationGithub, ConfigureGithubWebhook } from '.'; +import { ConfigureGithubDeploykey } from './configure-github-deploykey'; +import { ConfigureGithubWebhook } from './configure-github-webhook'; export default (
- { - return ( -
- - This allows Radix to clone the repository. Open the and follow the steps - below - -
- - - {secrets?.publicDeployKey ? ( -
- Copy this key: - {secrets?.publicDeployKey} -
- ) : ( - <> - Please wait… - - )} -
- Give the key a name, e.g. "Radix deploy key" - - Press "Add key" on Github's{' '} - - Add New Deploy Key page - - -
-
- Add deploy key' steps on GitHub -
-
-
- ); -}; - -type RegenerateSecretsScrimProps = { - appName: string; - refetchSecrets: () => Promise; -}; - -export function RegenerateSecretsScrim({ - appName, - refetchSecrets, -}: RegenerateSecretsScrimProps) { - const [show, setShow] = useState(false); - - const [regenerateSecrets, regenerateSecretState] = - useRegenerateDeployKeyMutation(); - - const onRegenerate = handlePromiseWithToast(async () => { - await regenerateSecrets({ - appName, - regenerateDeployKeyAndSecretData: { - sharedSecret: crypto.randomUUID(), - }, - }).unwrap(); - await refetchSecrets(); - }); - - return ( - <> - setShow(false)} - isDismissable - > -
-
- - Do you want to regenerate deploy key and webhook - secret? - - - New deploy key and webhook secret need to be put to the GitHub - repository settings - -
- - - - - -
-
- -
-
- {regenerateSecretState.isError ? ( - - Failed to regenerate deploy key and webhook secret. -
- {getFetchErrorMessage(regenerateSecretState.error)} -
- ) : null} - {regenerateSecretState.isLoading ? ( - <> - Regenerating… - - ) : ( -
- - Regularly regenerate deploy key and webhook secret - - -
- )} -
-
- - ); -} - -export function ConfigureGithubWebhook({ - repository, - appName, - sharedSecret, -}: { repository: string; appName: string; sharedSecret?: string }) { - const radixZoneDNS = configVariables.RADIX_CLUSTER_BASE; - return ( -
- - GitHub notifies Radix using a webhook whenever a code push is made. - -
- - - Open the{' '} - - Add Webhook page - {' '} - - - As Payload URL, use{' '} - {`https://webhook.${radixZoneDNS}/events/github?appName=${appName}`}{' '} - - - - Choose application/json as Content type - - - The Shared Secret for this application is{' '} - {sharedSecret}{' '} - - - Press "Add webhook" - -
- 'Add webhook' steps on GitHub -
-
-
- ); -} diff --git a/src/components/configure-application-github/regenerate-secrets-scrim.tsx b/src/components/configure-application-github/regenerate-secrets-scrim.tsx new file mode 100644 index 000000000..2e585175d --- /dev/null +++ b/src/components/configure-application-github/regenerate-secrets-scrim.tsx @@ -0,0 +1,84 @@ +import { Button, Progress, Typography } from '@equinor/eds-core-react'; +import { useState } from 'react'; +import { useRegenerateDeployKeyMutation } from '../../store/radix-api'; +import { getFetchErrorMessage } from '../../store/utils'; +import { Alert } from '../alert'; +import { handlePromiseWithToast } from '../global-top-nav/styled-toaster'; +import { ScrimPopup } from '../scrim-popup'; + +type Props = { + appName: string; + refetchSecrets: () => Promise; +}; + +export function RegenerateSecretsScrim({ appName, refetchSecrets }: Props) { + const [show, setShow] = useState(false); + + const [regenerateSecrets, regenerateSecretState] = + useRegenerateDeployKeyMutation(); + + const onRegenerate = handlePromiseWithToast(async () => { + await regenerateSecrets({ + appName, + regenerateDeployKeyAndSecretData: { + sharedSecret: crypto.randomUUID(), + }, + }).unwrap(); + await refetchSecrets(); + }); + + return ( + <> + setShow(false)} + isDismissable + > +
+
+ + Do you want to regenerate deploy key and webhook + secret? + + + New deploy key and webhook secret need to be put to the GitHub + repository settings + +
+ + + + + +
+
+ +
+
+ {regenerateSecretState.isError ? ( + + Failed to regenerate deploy key and webhook secret. +
+ {getFetchErrorMessage(regenerateSecretState.error)} +
+ ) : null} + {regenerateSecretState.isLoading ? ( + <> + Regenerating… + + ) : ( +
+ + Regularly regenerate deploy key and webhook secret + + +
+ )} +
+
+ + ); +} diff --git a/src/components/page-create-application/create-application-form.tsx b/src/components/create-application/create-application-form.tsx similarity index 100% rename from src/components/page-create-application/create-application-form.tsx rename to src/components/create-application/create-application-form.tsx diff --git a/src/components/page-create-application/index.tsx b/src/components/create-application/create-application-scrim.tsx similarity index 63% rename from src/components/page-create-application/index.tsx rename to src/components/create-application/create-application-scrim.tsx index dd90cad81..1b8dd48ee 100644 --- a/src/components/page-create-application/index.tsx +++ b/src/components/create-application/create-application-scrim.tsx @@ -1,75 +1,22 @@ -import { - Button, - Checkbox, - Divider, - Icon, - Typography, -} from '@equinor/eds-core-react'; +import { Button, Divider, Icon, Typography } from '@equinor/eds-core-react'; import { add } from '@equinor/eds-icons'; import { useRef, useState } from 'react'; import { Link, useSearchParams } from 'react-router-dom'; - +import useLocalStorage from '../../effects/use-local-storage'; import { routes } from '../../routes'; -import { routeWithParams } from '../../utils/string'; -import { - ConfigureApplicationGithub, - ConfigureGithubWebhook, -} from '../configure-application-github'; -import { ScrimPopup } from '../scrim-popup'; - -import { - type ApplicationRegistration, - type DeployKeyAndSecret, - radixApi, - useGetDeployKeyAndSecretQuery, - useRegisterApplicationMutation, +import type { + ApplicationRegistration, + DeployKeyAndSecret, } from '../../store/radix-api'; +import { routeWithParams } from '../../utils/string'; +import { ConfigureGithubDeploykey } from '../configure-application-github/configure-github-deploykey'; +import { ConfigureGithubWebhook } from '../configure-application-github/configure-github-webhook'; import { NewApplyConfigPipelineLink } from '../link/apply-config-pipeline-link'; -import './style.css'; -import useLocalStorage from '../../effects/use-local-storage'; -import { externalUrls } from '../../externalUrls'; -import { pollingInterval } from '../../store/defaults'; -import { ExternalLink } from '../link/external-link'; +import { ScrimPopup } from '../scrim-popup'; import { CreateApplicationForm } from './create-application-form'; +import { UseGithubCIForm } from './use-github-ci-form'; -export default function PageCreateApplication() { - const [refreshApps] = radixApi.endpoints.showApplications.useLazyQuery({}); - const [createApp] = useRegisterApplicationMutation(); - const [appName, setAppName] = useState(); - - const { data: secrets } = useGetDeployKeyAndSecretQuery( - { appName: appName! }, - { pollingInterval, skip: !appName } - ); - - const onCreateApplication = async ( - applicationRegistration: ApplicationRegistration, - acknowledgeWarnings: boolean - ) => { - const response = await createApp({ - applicationRegistrationRequest: { - applicationRegistration, - acknowledgeWarnings, - }, - }).unwrap(); - - if (response.applicationRegistration) { - setAppName(response.applicationRegistration.name); - } - - return response.warnings ?? []; - }; - - return ( - refreshApps({})} - /> - ); -} - -type PageCreateApplicationLayoutProps = { +type Props = { secrets?: DeployKeyAndSecret; onRefreshApps: () => unknown; onCreateApplication: ( @@ -78,11 +25,11 @@ type PageCreateApplicationLayoutProps = { ) => Promise; }; -export function PageCreateApplicationLayout({ +export function CreateApplicationScrim({ secrets, onRefreshApps, onCreateApplication, -}: PageCreateApplicationLayoutProps) { +}: Props) { const [visibleScrim, setVisibleScrim] = useState(false); const [useGithub, setUseGithub] = useState(true); const containerRef = useRef(null); @@ -159,10 +106,7 @@ export function PageCreateApplicationLayout({ To integrate with GitHub you must add a deploy key and a webhook - + @@ -170,34 +114,10 @@ export function PageCreateApplicationLayout({ {page === 'ci' && registration && ( <> - + {useGithub ? ( - - )} - - {page === 'ci' && registration && ( - <> - - - {useGithub ? ( - - ) : ( - - )} + )} @@ -167,7 +150,7 @@ export function CreateApplicationScrim({ ); } -type Page = 'registration' | 'deploykey' | 'ci' | 'webhook' | 'finished'; +type Page = 'registration' | 'deploykey' | 'webhook' | 'finished'; function useCurrentPage(): [Page, (newPage: Page) => unknown] { const [searchParams, setSearchParams] = useSearchParams(); const page = searchParams.get('page') ?? 'registration'; diff --git a/src/components/create-application/use-github-ci-form.tsx b/src/components/create-application/use-github-ci-form.tsx deleted file mode 100644 index 7842a2840..000000000 --- a/src/components/create-application/use-github-ci-form.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Checkbox, Typography } from '@equinor/eds-core-react'; -import { externalUrls } from '../../externalUrls'; -import { ExternalLink } from '../link/external-link'; - -type Props = { - useGithub: boolean; - setUseGithub: (useGithub: boolean) => unknown; -}; -export function UseGithubCIForm({ useGithub, setUseGithub }: Props) { - return ( - - ); -}