From d9ae249ffe19a3c7f8dceb9bce9b33739d47c72a Mon Sep 17 00:00:00 2001 From: Oscar Zhou <100548325+oscarzhou-portainer@users.noreply.github.com> Date: Mon, 18 Mar 2024 08:55:26 +1300 Subject: [PATCH] chore(template/git): sync frontend code from ee (#11343) --- .../create-edge-stack-view.controller.js | 1 + .../docker-compose-form.html | 1 + .../git-form-auth-fieldset.controller.ts | 2 +- .../forms/git-form/git-form.controller.ts | 24 +++++++++++++++---- .../components/forms/git-form/git-form.ts | 2 ++ app/portainer/react/components/git-form.ts | 2 ++ .../gitops/AuthFieldset/AuthFieldset.tsx | 6 +++-- .../ComposePathField/ComposePathField.tsx | 3 +++ .../gitops/ComposePathField/PathSelector.tsx | 3 +++ .../portainer/gitops/GitForm.stories.tsx | 2 +- app/react/portainer/gitops/GitForm.tsx | 17 +++++++++---- .../portainer/gitops/GitFormUrlField.tsx | 9 ++++++- .../portainer/gitops/RefField/RefField.tsx | 3 +++ .../portainer/gitops/RefField/RefSelector.tsx | 3 +++ .../portainer/gitops/queries/useCheckRepo.ts | 8 ++++++- .../portainer/gitops/queries/useGitRefs.ts | 1 + .../portainer/gitops/queries/useSearch.ts | 1 + .../CreateView/useValidation.tsx | 3 ++- .../EditView/useValidation.tsx | 2 +- 19 files changed, 77 insertions(+), 16 deletions(-) diff --git a/app/edge/views/edge-stacks/createEdgeStackView/create-edge-stack-view.controller.js b/app/edge/views/edge-stacks/createEdgeStackView/create-edge-stack-view.controller.js index 33c920c17..2b8354161 100644 --- a/app/edge/views/edge-stacks/createEdgeStackView/create-edge-stack-view.controller.js +++ b/app/edge/views/edge-stacks/createEdgeStackView/create-edge-stack-view.controller.js @@ -345,6 +345,7 @@ export default class CreateEdgeStackViewController { RepositoryUsername: this.formValues.RepositoryUsername, RepositoryPassword: this.formValues.RepositoryPassword, TLSSkipVerify: this.formValues.TLSSkipVerify, + CreatedFromCustomTemplateID: this.state.templateValues.template.Id, }; return this.EdgeStackService.createStackFromGitRepository( { diff --git a/app/edge/views/edge-stacks/createEdgeStackView/docker-compose-form/docker-compose-form.html b/app/edge/views/edge-stacks/createEdgeStackView/docker-compose-form/docker-compose-form.html index 58401cd41..c0d13f8b5 100644 --- a/app/edge/views/edge-stacks/createEdgeStackView/docker-compose-form/docker-compose-form.html +++ b/app/edge/views/edge-stacks/createEdgeStackView/docker-compose-form/docker-compose-form.html @@ -35,6 +35,7 @@ on-change="($ctrl.onChangeFormValues)" base-webhook-url="{{ $ctrl.state.baseWebhookUrl }}" webhook-id="{{ $ctrl.state.webhookId }}" + created-from-custom-template-id="($ctrl.state.templateValues.type === 'custom' ? $ctrl.state.templateValues.template.Id : 0)" docs-links > diff --git a/app/portainer/components/forms/git-form/git-form-auth-fieldset.controller.ts b/app/portainer/components/forms/git-form/git-form-auth-fieldset.controller.ts index fe3e712aa..c60d7a2c0 100644 --- a/app/portainer/components/forms/git-form/git-form-auth-fieldset.controller.ts +++ b/app/portainer/components/forms/git-form/git-form-auth-fieldset.controller.ts @@ -65,7 +65,7 @@ export default class GitFormAuthFieldsetController { ); this.errors = await validateForm( - () => gitAuthValidation(this.gitCredentials, isAuthEdit), + () => gitAuthValidation(this.gitCredentials, isAuthEdit, false), value ); if (this.errors && Object.keys(this.errors).length > 0) { diff --git a/app/portainer/components/forms/git-form/git-form.controller.ts b/app/portainer/components/forms/git-form/git-form.controller.ts index 63836da31..f237b8e20 100644 --- a/app/portainer/components/forms/git-form/git-form.controller.ts +++ b/app/portainer/components/forms/git-form/git-form.controller.ts @@ -24,6 +24,8 @@ export default class GitFormController { onChange?: (value: GitFormModel) => void; + createdFromCustomTemplateId?: number; + /* @ngInject */ constructor( $async: (fn: () => Promise) => Promise, @@ -47,15 +49,26 @@ export default class GitFormController { ...newValues, }; this.onChange?.(value); - await this.runGitFormValidation(value); + + const isCreatedFromCustomTemplate = + !!this.createdFromCustomTemplateId && + this.createdFromCustomTemplateId > 0; + await this.runGitFormValidation(value, isCreatedFromCustomTemplate); } - async runGitFormValidation(value: GitFormModel) { + async runGitFormValidation( + value: GitFormModel, + isCreatedFromCustomTemplate: boolean + ) { return this.$async(async () => { this.errors = {}; this.gitForm?.$setValidity('gitForm', true, this.gitForm); - this.errors = await validateGitForm(this.gitCredentials, value); + this.errors = await validateGitForm( + this.gitCredentials, + value, + isCreatedFromCustomTemplate + ); if (this.errors && Object.keys(this.errors).length > 0) { this.gitForm?.$setValidity('gitForm', false, this.gitForm); } @@ -82,6 +95,9 @@ export default class GitFormController { throw new Error('GitFormController: value is required'); } - await this.runGitFormValidation(this.value); + const isCreatedFromCustomTemplate = + !!this.createdFromCustomTemplateId && + this.createdFromCustomTemplateId > 0; + await this.runGitFormValidation(this.value, isCreatedFromCustomTemplate); } } diff --git a/app/portainer/components/forms/git-form/git-form.ts b/app/portainer/components/forms/git-form/git-form.ts index ce7115304..699d204ce 100644 --- a/app/portainer/components/forms/git-form/git-form.ts +++ b/app/portainer/components/forms/git-form/git-form.ts @@ -17,6 +17,7 @@ export const gitForm: IComponentOptions = { base-webhook-url="$ctrl.baseWebhookUrl" webhook-id="$ctrl.webhookId" webhooks-docs="$ctrl.webhooksDocs" + created-from-custom-template-id="$ctrl.createdFromCustomTemplateId" errors="$ctrl.errors"> `, @@ -32,6 +33,7 @@ export const gitForm: IComponentOptions = { isAuthExplanationVisible: '<', webhookId: '@', webhooksDocs: '@', + createdFromCustomTemplateId: '<', }, controller, }; diff --git a/app/portainer/react/components/git-form.ts b/app/portainer/react/components/git-form.ts index e7c0a4d68..1f0bf100d 100644 --- a/app/portainer/react/components/git-form.ts +++ b/app/portainer/react/components/git-form.ts @@ -28,6 +28,7 @@ export const gitFormModule = angular 'baseWebhookUrl', 'webhookId', 'webhooksDocs', + 'createdFromCustomTemplateId', ]) ) .component( @@ -69,6 +70,7 @@ export const gitFormModule = angular 'model', 'onChange', 'stackId', + 'createdFromCustomTemplateId', 'value', 'isUrlValid', ]) diff --git a/app/react/portainer/gitops/AuthFieldset/AuthFieldset.tsx b/app/react/portainer/gitops/AuthFieldset/AuthFieldset.tsx index e8387371b..2684de206 100644 --- a/app/react/portainer/gitops/AuthFieldset/AuthFieldset.tsx +++ b/app/react/portainer/gitops/AuthFieldset/AuthFieldset.tsx @@ -147,7 +147,8 @@ export function AuthFieldset({ export function gitAuthValidation( gitCredentials: Array, - isAuthEdit: boolean + isAuthEdit: boolean, + isCreatedFromCustomTemplate: boolean ): SchemaOf { return object({ RepositoryAuthentication: boolean().default(false), @@ -160,7 +161,8 @@ export function gitAuthValidation( .default(''), RepositoryPassword: string() .when(['RepositoryAuthentication', 'RepositoryGitCredentialID'], { - is: (auth: boolean, id: number) => auth && !id && !isAuthEdit, + is: (auth: boolean, id: number) => + auth && !id && !isAuthEdit && !isCreatedFromCustomTemplate, then: string().required('Password is required'), }) .default(''), diff --git a/app/react/portainer/gitops/ComposePathField/ComposePathField.tsx b/app/react/portainer/gitops/ComposePathField/ComposePathField.tsx index 6a885130c..85c41689f 100644 --- a/app/react/portainer/gitops/ComposePathField/ComposePathField.tsx +++ b/app/react/portainer/gitops/ComposePathField/ComposePathField.tsx @@ -16,6 +16,7 @@ interface Props { isCompose: boolean; model: GitFormModel; isDockerStandalone: boolean; + createdFromCustomTemplateId?: number; } export function ComposePathField({ @@ -25,6 +26,7 @@ export function ComposePathField({ model, isDockerStandalone, errors, + createdFromCustomTemplateId, }: Props) { const [inputValue, updateInputValue] = useStateWrapper(value, onChange); @@ -64,6 +66,7 @@ export function ComposePathField({ placeholder={isCompose ? 'docker-compose.yml' : 'manifest.yml'} model={model} inputId="stack_repository_path" + createdFromCustomTemplateId={createdFromCustomTemplateId} /> ) : ( buildGitValidationSchema([])} + validationSchema={() => buildGitValidationSchema([], false)} onSubmit={() => {}} > {({ values, errors, setValues }) => ( diff --git a/app/react/portainer/gitops/GitForm.tsx b/app/react/portainer/gitops/GitForm.tsx index 3c8e42f06..94aff9ec8 100644 --- a/app/react/portainer/gitops/GitForm.tsx +++ b/app/react/portainer/gitops/GitForm.tsx @@ -34,6 +34,7 @@ interface Props { baseWebhookUrl?: string; webhookId?: string; webhooksDocs?: string; + createdFromCustomTemplateId?: number; } export function GitForm({ @@ -49,6 +50,7 @@ export function GitForm({ baseWebhookUrl, webhookId, webhooksDocs, + createdFromCustomTemplateId, }: Props) { const [value, setValue] = useState(initialValue); // TODO: remove this state when form is not inside angularjs const webhooksDocsUrl = useDocsUrl(webhooksDocs); @@ -69,6 +71,7 @@ export function GitForm({ handleChange({ RepositoryURLValid: value }) } model={value} + createdFromCustomTemplateId={createdFromCustomTemplateId} errors={errors.RepositoryURL} /> @@ -78,6 +81,7 @@ export function GitForm({ model={value} error={errors.RepositoryReferenceName} isUrlValid={value.RepositoryURLValid} + createdFromCustomTemplateId={createdFromCustomTemplateId} /> {isAdditionalFilesFieldVisible && ( @@ -137,16 +142,18 @@ export function GitForm({ export async function validateGitForm( gitCredentials: Array, - formValues: GitFormModel + formValues: GitFormModel, + isCreatedFromCustomTemplate: boolean ) { return validateForm( - () => buildGitValidationSchema(gitCredentials), + () => buildGitValidationSchema(gitCredentials, isCreatedFromCustomTemplate), formValues ); } export function buildGitValidationSchema( - gitCredentials: Array + gitCredentials: Array, + isCreatedFromCustomTemplate: boolean ): SchemaOf { return object({ RepositoryURL: string() @@ -171,5 +178,7 @@ export function buildGitValidationSchema( RepositoryURLValid: boolean().default(false), AutoUpdate: autoUpdateValidation().nullable(), TLSSkipVerify: boolean().default(false), - }).concat(gitAuthValidation(gitCredentials, false)) as SchemaOf; + }).concat( + gitAuthValidation(gitCredentials, false, isCreatedFromCustomTemplate) + ) as SchemaOf; } diff --git a/app/react/portainer/gitops/GitFormUrlField.tsx b/app/react/portainer/gitops/GitFormUrlField.tsx index 15dabc1cf..7e62f20b8 100644 --- a/app/react/portainer/gitops/GitFormUrlField.tsx +++ b/app/react/portainer/gitops/GitFormUrlField.tsx @@ -26,6 +26,7 @@ interface Props { onChange(value: string): void; onChangeRepositoryValid(value: boolean): void; model: GitFormModel; + createdFromCustomTemplateId?: number; errors?: string; } @@ -34,6 +35,7 @@ export function GitFormUrlField({ onChange, onChangeRepositoryValid, model, + createdFromCustomTemplateId, errors, }: Props) { const queryClient = useQueryClient(); @@ -42,7 +44,12 @@ export function GitFormUrlField({ const [force, setForce] = useState(false); const repoStatusQuery = useCheckRepo( value, - { creds, force, tlsSkipVerify: model.TLSSkipVerify }, + { + creds, + force, + tlsSkipVerify: model.TLSSkipVerify, + createdFromCustomTemplateId, + }, { onSettled(isValid) { onChangeRepositoryValid(!!isValid); diff --git a/app/react/portainer/gitops/RefField/RefField.tsx b/app/react/portainer/gitops/RefField/RefField.tsx index 3499d925a..3261574ef 100644 --- a/app/react/portainer/gitops/RefField/RefField.tsx +++ b/app/react/portainer/gitops/RefField/RefField.tsx @@ -20,6 +20,7 @@ interface Props { error?: string; isUrlValid?: boolean; stackId?: StackId; + createdFromCustomTemplateId?: number; } export function RefField({ @@ -29,6 +30,7 @@ export function RefField({ error, isUrlValid, stackId, + createdFromCustomTemplateId, }: Props) { const [inputValue, updateInputValue] = useStateWrapper(value, onChange); const inputId = 'repository-reference-field'; @@ -51,6 +53,7 @@ export function RefField({ model={model} isUrlValid={isUrlValid} stackId={stackId} + createdFromCustomTemplateId={createdFromCustomTemplateId} /> ) : ( diff --git a/app/react/portainer/gitops/RefField/RefSelector.tsx b/app/react/portainer/gitops/RefField/RefSelector.tsx index 0b9e520be..d7998fb5d 100644 --- a/app/react/portainer/gitops/RefField/RefSelector.tsx +++ b/app/react/portainer/gitops/RefField/RefSelector.tsx @@ -13,11 +13,13 @@ export function RefSelector({ onChange, isUrlValid, stackId, + createdFromCustomTemplateId, inputId, }: { model: RefFieldModel; value: string; stackId?: StackId; + createdFromCustomTemplateId?: number; onChange: (value: string) => void; isUrlValid?: boolean; inputId: string; @@ -26,6 +28,7 @@ export function RefSelector({ const payload = { repository: model.RepositoryURL, stackId, + createdFromCustomTemplateId, tlsSkipVerify: model.TLSSkipVerify, ...creds, }; diff --git a/app/react/portainer/gitops/queries/useCheckRepo.ts b/app/react/portainer/gitops/queries/useCheckRepo.ts index 8c1c3d854..cb58b59d4 100644 --- a/app/react/portainer/gitops/queries/useCheckRepo.ts +++ b/app/react/portainer/gitops/queries/useCheckRepo.ts @@ -15,6 +15,7 @@ interface CheckRepoOptions { creds?: Creds; force?: boolean; tlsSkipVerify?: boolean; + createdFromCustomTemplateId?: number; } export function useCheckRepo( @@ -43,7 +44,12 @@ export async function checkRepo( try { await axios.post( '/gitops/repo/refs', - { repository, tlsSkipVerify: options.tlsSkipVerify, ...options.creds }, + { + repository, + tlsSkipVerify: options.tlsSkipVerify, + createdFromCustomTemplateId: options.createdFromCustomTemplateId, + ...options.creds, + }, force ? { params: { force } } : {} ); return true; diff --git a/app/react/portainer/gitops/queries/useGitRefs.ts b/app/react/portainer/gitops/queries/useGitRefs.ts index a8658f22d..ca0e00a0f 100644 --- a/app/react/portainer/gitops/queries/useGitRefs.ts +++ b/app/react/portainer/gitops/queries/useGitRefs.ts @@ -6,6 +6,7 @@ interface RefsPayload { repository: string; username?: string; password?: string; + createdFromCustomTemplateID?: number; tlsSkipVerify?: boolean; } diff --git a/app/react/portainer/gitops/queries/useSearch.ts b/app/react/portainer/gitops/queries/useSearch.ts index 2cc0b6314..7ab8a38e3 100644 --- a/app/react/portainer/gitops/queries/useSearch.ts +++ b/app/react/portainer/gitops/queries/useSearch.ts @@ -9,6 +9,7 @@ interface SearchPayload { username?: string; password?: string; tlsSkipVerify?: boolean; + createdFromCustomTemplateId?: number; } export function useSearch(payload: SearchPayload, enabled: boolean) { diff --git a/app/react/portainer/templates/custom-templates/CreateView/useValidation.tsx b/app/react/portainer/templates/custom-templates/CreateView/useValidation.tsx index b36d0290a..b141750e7 100644 --- a/app/react/portainer/templates/custom-templates/CreateView/useValidation.tsx +++ b/app/react/portainer/templates/custom-templates/CreateView/useValidation.tsx @@ -57,7 +57,8 @@ export function useValidation({ }), Git: mixed().when('Method', { is: git.value, - then: () => buildGitValidationSchema(gitCredentialsQuery.data || []), + then: () => + buildGitValidationSchema(gitCredentialsQuery.data || [], false), }), Variables: variablesValidation(), EdgeSettings: viewType === 'edge' ? edgeFieldsetValidation() : mixed(), diff --git a/app/react/portainer/templates/custom-templates/EditView/useValidation.tsx b/app/react/portainer/templates/custom-templates/EditView/useValidation.tsx index 246c02436..d0fecd3b2 100644 --- a/app/react/portainer/templates/custom-templates/EditView/useValidation.tsx +++ b/app/react/portainer/templates/custom-templates/EditView/useValidation.tsx @@ -47,7 +47,7 @@ export function useValidation({ FileContent: string().required('Template is required.'), Git: isGit - ? buildGitValidationSchema(gitCredentialsQuery.data || []) + ? buildGitValidationSchema(gitCredentialsQuery.data || [], false) : mixed(), Variables: variablesValidation(), EdgeSettings: viewType === 'edge' ? edgeFieldsetValidation() : mixed(),