From eff5c331abaa9a2df8b4d5aa3e8382c65db8bb3b Mon Sep 17 00:00:00 2001 From: Richard Hagen Date: Wed, 10 Jan 2024 12:25:08 +0100 Subject: [PATCH] migrate create-application-form (#920) * migrate create-application-form * await onCreated * fix bug --------- Co-authored-by: Richard Hagen --- src/components/app-list/index.tsx | 11 +- .../configure-application-github/index.tsx | 3 +- .../create-application-form/index.tsx | 449 ++++++++---------- .../page-create-application/index.tsx | 111 +---- 4 files changed, 217 insertions(+), 357 deletions(-) diff --git a/src/components/app-list/index.tsx b/src/components/app-list/index.tsx index bb3279609..d887076c9 100644 --- a/src/components/app-list/index.tsx +++ b/src/components/app-list/index.tsx @@ -65,10 +65,11 @@ export const AppList: FunctionComponent = ({ setFavourites(favouriteAppNames); } - const { data: appsData, ...appsState } = useShowApplicationsQuery( - {}, - { pollingInterval: pollAppsInterval } - ); + const { + data: appsData, + refetch, + ...appsState + } = useShowApplicationsQuery({}, { pollingInterval: pollAppsInterval }); const { data: favsData, ...favsState } = useGetSearchApplicationsQuery( { apps: favourites?.join(','), @@ -99,7 +100,7 @@ export const AppList: FunctionComponent = ({
Favourites
- +
diff --git a/src/components/configure-application-github/index.tsx b/src/components/configure-application-github/index.tsx index d8df3b05b..83405b5f4 100644 --- a/src/components/configure-application-github/index.tsx +++ b/src/components/configure-application-github/index.tsx @@ -27,11 +27,12 @@ import { RequestState } from '../../state/state-utils/request-states'; import { configVariables } from '../../utils/config'; import './style.css'; +import { ApplicationRegistration } from '../../store/radix-api'; const radixZoneDNS = configVariables.RADIX_CLUSTER_BASE; export interface ConfigureApplicationGithubProps { - app: ApplicationRegistrationModel; + app: ApplicationRegistration | ApplicationRegistrationModel; onDeployKeyChange: (appName: string) => void; startVisible?: boolean; useOtherCiToolOptionVisible?: boolean; diff --git a/src/components/create-application-form/index.tsx b/src/components/create-application-form/index.tsx index ac78fa6b7..0a81245b8 100644 --- a/src/components/create-application-form/index.tsx +++ b/src/components/create-application-form/index.tsx @@ -8,10 +8,7 @@ import { Typography, } from '@equinor/eds-core-react'; import { info_circle } from '@equinor/eds-icons'; -import * as PropTypes from 'prop-types'; -import { ChangeEvent, Component, FormEvent } from 'react'; -import { connect } from 'react-redux'; -import { Dispatch } from 'redux'; +import { ChangeEvent, FormEvent, useState } from 'react'; import { Alert } from '../alert'; import { AppConfigAdGroups } from '../app-config-ad-groups'; @@ -20,288 +17,218 @@ import { OnConfigurationItemChangeCallback, } from '../app-config-ci'; import { HandleAdGroupsChangeCB } from '../graph/adGroups'; -import { AppCreateProps } from '../../api/apps'; import { externalUrls } from '../../externalUrls'; -import { RootState } from '../../init/store'; import { ApplicationRegistrationModel } from '../../models/radix-api/applications/application-registration'; import { - ApplicationRegistrationUpsertResponseModelValidationMap, - ApplicationRegistrationUpsertResponseModel, -} from '../../models/radix-api/applications/application-registration-upsert-response'; + ApplicationRegistration, + ApplicationRegistrationUpsertResponse, + useRegisterApplicationMutation, +} from '../../store/radix-api'; import { - getCreationError, - getCreationState, - getCreationResult, -} from '../../state/application-creation'; -import { actions as appsActions } from '../../state/application-creation/action-creators'; -import { RequestState } from '../../state/state-utils/request-states'; - -interface CreateApplicationFormState { - creationState: RequestState; - creationError?: string; - creationResponse?: ApplicationRegistrationUpsertResponseModel; -} - -interface CreateApplicationFormDispatch { - requestCreate: (form: AppCreateProps) => void; -} - -export interface CreateApplicationFormProps - extends CreateApplicationFormState, - CreateApplicationFormDispatch {} + errorToast, + successToast, + warningToast, +} from '../global-top-nav/styled-toaster'; +import { getFetchErrorMessage } from '../../store/utils'; function sanitizeName(name: string): string { // force name to lowercase, no spaces return name?.toLowerCase().replace(/[^a-z0-9]/g, '-') ?? ''; } -export class CreateApplicationForm extends Component< - CreateApplicationFormProps, - AppCreateProps -> { - static readonly propTypes: PropTypes.ValidationMap = - { - creationState: PropTypes.oneOf(Object.values(RequestState)).isRequired, - requestCreate: PropTypes.func.isRequired, - creationError: PropTypes.string, - creationResponse: PropTypes.shape( - ApplicationRegistrationUpsertResponseModelValidationMap - ), - }; - - constructor(props: CreateApplicationFormProps) { - super(props); - this.state = { - appRegistrationRequest: { - applicationRegistration: { - name: '', - repository: '', - sharedSecret: '', - adGroups: [], - owner: '', - creator: '', - wbs: '', - configBranch: '', - radixConfigFullName: 'radixconfig.yaml', - }, - acknowledgeWarnings: false, - }, - }; - - this.handleAdGroupsChange = this.handleAdGroupsChange.bind(this); - this.handleConfigurationItemChange = - this.handleConfigurationItemChange.bind(this); - this.handleAppRegistrationChange = - this.handleAppRegistrationChange.bind(this); - this.handleSubmit = this.handleSubmit.bind(this); - this.handleAcknowledgeWarnings = this.handleAcknowledgeWarnings.bind(this); - } +type Props = { + onCreated: (application: ApplicationRegistration) => Promise; +}; +export default function CreateApplicationForm({ onCreated }: Props) { + const [acknowledgeWarnings, setAcknowledgeWarnings] = useState(false); + const [createApp, creationState] = useRegisterApplicationMutation(); + const [applicationRegistration, setAppRegistration] = + useState({ + name: '', + repository: '', + sharedSecret: '', + adGroups: [], + owner: '', + creator: '', + wbs: '', + configBranch: '', + radixConfigFullName: 'radixconfig.yaml', + configurationItem: '', + readerAdGroups: [], + }); - private handleAdGroupsChange( - ...[value]: Parameters - ): ReturnType { - this.setState(({ appRegistrationRequest }) => ({ - appRegistrationRequest: { - ...appRegistrationRequest, - applicationRegistration: { - ...appRegistrationRequest.applicationRegistration, - adGroups: value.map(({ id }) => id), - }, - }, + const handleAdGroupsChange: HandleAdGroupsChangeCB = (value) => { + setAppRegistration((current) => ({ + ...current, + adGroups: value.map((x) => x.id), })); - } + }; - private handleConfigurationItemChange( - ...[value]: Parameters - ): ReturnType { - this.setState(({ appRegistrationRequest }) => ({ - appRegistrationRequest: { - ...appRegistrationRequest, - applicationRegistration: { - ...appRegistrationRequest.applicationRegistration, - configurationItem: value?.id, - }, - }, + const handleConfigurationItemChange: OnConfigurationItemChangeCallback = ( + value + ) => { + setAppRegistration((current) => ({ + ...current, + configurationItem: value?.id, })); - } + }; - private handleAppRegistrationChange({ + const handleAppRegistrationChange = ({ target: { name, value }, - }: ChangeEvent): void { + }: ChangeEvent) => { const key = name as keyof ApplicationRegistrationModel; - this.setState(({ appRegistrationRequest }) => ({ - appRegistrationRequest: { - ...appRegistrationRequest, - applicationRegistration: { - ...appRegistrationRequest.applicationRegistration, - [key]: key === 'name' ? sanitizeName(value) : value, - }, - }, - })); - } - - private handleSubmit(ev: FormEvent): void { - ev.preventDefault(); - const { appRegistrationRequest } = this.state; - this.props.requestCreate({ - appRegistrationRequest: appRegistrationRequest, - }); - } + if (key === 'name') value = sanitizeName(value); - private handleAcknowledgeWarnings(): void { - this.setState(({ appRegistrationRequest }) => ({ - appRegistrationRequest: { - ...appRegistrationRequest, - acknowledgeWarnings: !appRegistrationRequest.acknowledgeWarnings, - }, - })); - } + setAppRegistration((current) => ({ ...current, [key]: value })); + }; - override render() { - const { acknowledgeWarnings, applicationRegistration } = - this.state.appRegistrationRequest; + const handleSubmit = async (ev: FormEvent) => { + try { + ev.preventDefault(); + const response: ApplicationRegistrationUpsertResponse = await createApp({ + applicationRegistrationRequest: { + applicationRegistration, + acknowledgeWarnings, + }, + }).unwrap(); + + //Only call onCreated when created without warnings, or created with ack warnings + if (response.warnings?.length === 0 || acknowledgeWarnings) { + await onCreated(response.applicationRegistration); + successToast('Saved'); + } else { + warningToast('Registration had warnings'); + } + } catch (e) { + errorToast(`Error while saving. ${getFetchErrorMessage(e)}`); + } + }; - return ( -
- - -
- - Your application needs a GitHub repository with a radixconfig.yaml - file and a Dockerfile. + return ( + + + +
+ + Your application needs a GitHub repository with a radixconfig.yaml + file and a Dockerfile. + + + You can read about{' '} + + radixconfig.yaml + {' '} + and{' '} + + Dockerfile best practices - - You can read about{' '} - - radixconfig.yaml - {' '} - and{' '} - - Dockerfile best practices - - . + . + + + Need help? Get in touch on our{' '} + + Slack support channel + +
+
+
+ + + + + + + {creationState.isError && ( + + Failed to create application.{' '} + {getFetchErrorMessage(creationState.error)} + + )} +
+ {creationState.isLoading && ( - Need help? Get in touch on our{' '} - - Slack support channel - + Creating… -
- -
- - - - - - - {this.props.creationState === RequestState.FAILURE && ( - - Failed to create application. {this.props.creationError} - )} -
- {this.props.creationState === RequestState.IN_PROGRESS && ( - - Creating… - - )} - {this.props.creationState === RequestState.SUCCESS && - this.props.creationResponse.warnings && ( -
- - {this.props.creationResponse.warnings?.map((message, i) => ( - - {message} - - ))} - - -
- )} -
- + {creationState.isSuccess && creationState.data?.warnings && ( +
+ + {creationState.data.warnings.map((message, i) => ( + + {message} + + ))} + + ) => + setAcknowledgeWarnings(e.target.checked) + } + />
+ )} +
+
-
- - ); - } -} - -function mapStateToProps(state: RootState): CreateApplicationFormState { - return { - creationState: getCreationState(state), - creationError: getCreationError(state), - creationResponse: getCreationResult(state), - }; -} - -function mapDispatchToProps(dispatch: Dispatch): CreateApplicationFormDispatch { - return { requestCreate: (form) => dispatch(appsActions.addAppRequest(form)) }; +
+ + + ); } - -export default connect( - mapStateToProps, - mapDispatchToProps -)(CreateApplicationForm); diff --git a/src/components/page-create-application/index.tsx b/src/components/page-create-application/index.tsx index 5da242fe1..85435dc1d 100644 --- a/src/components/page-create-application/index.tsx +++ b/src/components/page-create-application/index.tsx @@ -1,79 +1,37 @@ import { Button, Icon, Typography } from '@equinor/eds-core-react'; import { add } from '@equinor/eds-icons'; -import * as PropTypes from 'prop-types'; -import { FunctionComponent, useEffect, useRef, useState } from 'react'; -import { connect } from 'react-redux'; +import { useRef, useState } from 'react'; import { Link } from 'react-router-dom'; -import { Dispatch } from 'redux'; import { ConfigureApplicationGithub } from '../configure-application-github'; import CreateApplicationForm from '../create-application-form'; import { ScrimPopup } from '../scrim-popup'; -import { RootState } from '../../init/store'; -import { - ApplicationRegistrationUpsertResponseModel, - ApplicationRegistrationUpsertResponseModelValidationMap, -} from '../../models/radix-api/applications/application-registration-upsert-response'; import { routes } from '../../routes'; -import { - getCreationResult, - getCreationState, -} from '../../state/application-creation'; -import { actions as appsActions } from '../../state/application-creation/action-creators'; -import { RequestState } from '../../state/state-utils/request-states'; import { routeWithParams } from '../../utils/string'; import './style.css'; - -interface PageCreateApplicationState { - creationState: RequestState; - creationResponse?: ApplicationRegistrationUpsertResponseModel; -} - -interface PageCreateApplicationDispatch { - resetCreate: () => void; -} - -export interface PageCreateApplicationProps - extends PageCreateApplicationState, - PageCreateApplicationDispatch {} +import { ApplicationRegistration } from '../../store/radix-api'; function scrollToPosition(elementRef: Element, x: number, y: number): void { elementRef.scrollTo?.(x, y); } -const PageCreateApplication: FunctionComponent = ({ - creationState, - creationResponse, - resetCreate, -}) => { +type Props = { refetch: Function }; +export default function PageCreateApplication({ refetch }: Props) { const [visibleScrim, setVisibleScrim] = useState(false); const containerRef = useRef(null); + const [registration, setRegistration] = + useState(null); - useEffect(() => { - if (!visibleScrim) { - resetCreate(); - } - }, [resetCreate, visibleScrim]); - - useEffect(() => { - if (!visibleScrim) return; + const onCloseScrim = () => { + setVisibleScrim(false); + }; - if (creationState === RequestState.FAILURE) { - scrollToPosition( - containerRef.current, - 0, - containerRef.current?.scrollHeight - ); - } else if (creationState === RequestState.SUCCESS) { - if ( - creationResponse.applicationRegistration && - !creationResponse.warnings - ) { - scrollToPosition(containerRef.current, 0, 0); - } - } - }, [creationState, creationResponse, visibleScrim]); + const onCreated = async (newRegistration: ApplicationRegistration) => { + setRegistration(newRegistration); + await refetch(); + scrollToPosition(containerRef.current, 0, 0); + }; return ( <> @@ -89,22 +47,19 @@ const PageCreateApplication: FunctionComponent = ({ setVisibleScrim(false)} + onClose={onCloseScrim} >
- {creationState !== RequestState.SUCCESS || - !creationResponse.applicationRegistration || - creationResponse.warnings ? ( - + {!registration ? ( + ) : (
- The application{' '} - {creationResponse.applicationRegistration.name}{' '} - has been set up + The application {registration.name} has been + set up void 0} useOtherCiToolOptionVisible @@ -115,7 +70,7 @@ const PageCreateApplication: FunctionComponent = ({ @@ -128,28 +83,4 @@ const PageCreateApplication: FunctionComponent = ({ ); -}; - -PageCreateApplication.propTypes = { - creationState: PropTypes.oneOf(Object.values(RequestState)).isRequired, - resetCreate: PropTypes.func.isRequired, - creationResponse: PropTypes.shape( - ApplicationRegistrationUpsertResponseModelValidationMap - ), -}; - -function mapStateToProps(state: RootState): PageCreateApplicationState { - return { - creationState: getCreationState(state), - creationResponse: getCreationResult(state), - }; } - -function mapDispatchToProps(dispatch: Dispatch): PageCreateApplicationDispatch { - return { resetCreate: () => dispatch(appsActions.addAppReset()) }; -} - -export default connect( - mapStateToProps, - mapDispatchToProps -)(PageCreateApplication);