1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-09 15:55:23 +02:00

refactor(edge/stacks): migrate create view to react [EE-2223] (#11575)
Some checks are pending
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
ci / build_images (map[arch:arm platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:s390x platform:linux version:]) (push) Waiting to run
ci / build_manifests (push) Blocked by required conditions
/ triage (push) Waiting to run
Lint / Run linters (push) Waiting to run
Test / test-client (push) Waiting to run
Test / test-server (map[arch:amd64 platform:linux]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
Test / test-server (map[arch:arm64 platform:linux]) (push) Waiting to run

This commit is contained in:
Chaim Lev-Ari 2024-05-06 08:08:03 +03:00 committed by GitHub
parent f22aed34b5
commit 8a81d95253
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
64 changed files with 1878 additions and 1005 deletions

View file

@ -2,7 +2,7 @@ import { useQueryClient, useMutation } from '@tanstack/react-query';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { notifyError, notifySuccess } from '@/portainer/services/notifications';
import { GitAuthModel, GitFormModel } from '@/react/portainer/gitops/types';
import { GitAuthModel } from '@/react/portainer/gitops/types';
import { useCurrentUser } from '@/react/hooks/useUser';
import { UserId } from '@/portainer/users/types';
@ -82,9 +82,9 @@ export function useSaveCredentialsIfRequired() {
}
}
export async function saveGitCredentialsIfNeeded(
export async function saveGitCredentialsIfNeeded<TGit extends GitAuthModel>(
userId: UserId,
gitModel: GitFormModel
gitModel: TGit
) {
let credentialsId = gitModel.RepositoryGitCredentialID;
let username = gitModel.RepositoryUsername;

View file

@ -2,7 +2,9 @@ import { VariableDefinition } from '../CustomTemplatesVariablesDefinitionField';
import { Values } from './CustomTemplatesVariablesField';
export function getDefaultValues(definitions: VariableDefinition[]): Values {
export function getDefaultValues(
definitions: VariableDefinition[] | undefined = []
): Values {
return definitions.map((v) => ({
key: v.name,
value: v.defaultValue,

View file

@ -1,9 +1,8 @@
import { useCallback } from 'react';
import { FormikErrors } from 'formik';
import { GitFormModel } from '@/react/portainer/gitops/types';
import { PathSelector } from '@/react/portainer/gitops/ComposePathField/PathSelector';
import { dummyGitForm } from '@/react/portainer/gitops/RelativePathFieldset/utils';
import { useValidation } from '@/react/portainer/gitops/RelativePathFieldset/useValidation';
import { useEnableFsPath } from '@/react/portainer/gitops/RelativePathFieldset/useEnableFsPath';
import { SwitchField } from '@@/form-components/SwitchField';
@ -15,27 +14,22 @@ import { useDocsUrl } from '@@/PageHeader/ContextHelp/ContextHelp';
import { RelativePathModel, getPerDevConfigsFilterType } from './types';
interface Props {
value: RelativePathModel;
values: RelativePathModel;
gitModel?: GitFormModel;
onChange?: (value: Partial<RelativePathModel>) => void;
onChange: (value: RelativePathModel) => void;
isEditing?: boolean;
hideEdgeConfigs?: boolean;
errors?: FormikErrors<RelativePathModel>;
}
export function RelativePathFieldset({
value,
values: value,
gitModel,
onChange,
onChange = () => {},
isEditing,
hideEdgeConfigs,
errors,
}: Props) {
const innerOnChange = useCallback(
(value: Partial<RelativePathModel>) => onChange && onChange(value),
[onChange]
);
const { errors } = useValidation(value);
const { enableFsPath0, enableFsPath1, toggleFsPath } = useEnableFsPath(value);
const gitoptsEdgeConfigDocUrl = useDocsUrl(
@ -63,7 +57,7 @@ export function RelativePathFieldset({
checked={value.SupportRelativePath}
onChange={(value) => {
toggleFsPath(0, value);
innerOnChange({ SupportRelativePath: value });
handleChange({ SupportRelativePath: value });
}}
/>
</div>
@ -83,7 +77,7 @@ export function RelativePathFieldset({
<div className="col-sm-12">
<FormControl
label="Local filesystem path"
errors={errors.FilesystemPath}
errors={errors?.FilesystemPath}
>
<Input
name="FilesystemPath"
@ -92,7 +86,7 @@ export function RelativePathFieldset({
disabled={isEditing || !enableFsPath0}
value={value.FilesystemPath}
onChange={(e) =>
innerOnChange({ FilesystemPath: e.target.value })
handleChange({ FilesystemPath: e.target.value })
}
/>
</FormControl>
@ -124,7 +118,7 @@ export function RelativePathFieldset({
checked={!!value.SupportPerDeviceConfigs}
onChange={(value) => {
toggleFsPath(1, value);
innerOnChange({ SupportPerDeviceConfigs: value });
handleChange({ SupportPerDeviceConfigs: value });
}}
/>
</div>
@ -147,7 +141,7 @@ export function RelativePathFieldset({
<div className="col-sm-12">
<FormControl
label="Local filesystem path"
errors={errors.FilesystemPath}
errors={errors?.FilesystemPath}
>
<Input
name="FilesystemPath"
@ -156,7 +150,7 @@ export function RelativePathFieldset({
disabled={isEditing || !enableFsPath1}
value={value.FilesystemPath}
onChange={(e) =>
innerOnChange({ FilesystemPath: e.target.value })
handleChange({ FilesystemPath: e.target.value })
}
/>
</FormControl>
@ -178,13 +172,13 @@ export function RelativePathFieldset({
<div className="col-sm-12">
<FormControl
label="Directory"
errors={errors.PerDeviceConfigsPath}
errors={errors?.PerDeviceConfigsPath}
inputId="per_device_configs_path_input"
>
<PathSelector
value={value.PerDeviceConfigsPath || ''}
onChange={(value) =>
innerOnChange({ PerDeviceConfigsPath: value })
handleChange({ PerDeviceConfigsPath: value })
}
placeholder="config"
model={gitModel || dummyGitForm}
@ -216,7 +210,7 @@ export function RelativePathFieldset({
value={value.PerDeviceConfigsMatchType}
data-cy="per-device-configs-match-type-select"
onChange={(e) =>
innerOnChange({
handleChange({
PerDeviceConfigsMatchType: getPerDevConfigsFilterType(
e.target.value
),
@ -249,7 +243,7 @@ export function RelativePathFieldset({
value={value.PerDeviceConfigsGroupMatchType}
data-cy="per-device-configs-group-match-type-select"
onChange={(e) =>
innerOnChange({
handleChange({
PerDeviceConfigsGroupMatchType:
getPerDevConfigsFilterType(e.target.value),
})
@ -301,4 +295,8 @@ export function RelativePathFieldset({
)}
</>
);
function handleChange(newValue: Partial<RelativePathModel>) {
onChange({ ...value, ...newValue });
}
}

View file

@ -1,27 +0,0 @@
import { useEffect, useState } from 'react';
import { FormikErrors, yupToFormErrors } from 'formik';
import { RelativePathModel } from '@/react/portainer/gitops/types';
import { relativePathValidation } from '@/react/portainer/gitops/RelativePathFieldset/validation';
export function useValidation(value: RelativePathModel) {
const [errors, setErrors] = useState<FormikErrors<RelativePathModel>>({});
useEffect(() => {
async function valide() {
try {
await relativePathValidation().validate(value, {
strict: true,
abortEarly: false,
});
setErrors({});
} catch (error) {
setErrors(yupToFormErrors(error));
}
}
valide();
}, [value]);
return { errors };
}

View file

@ -1,22 +1,20 @@
import angular from 'angular';
import { r2a } from '@/react-tools/react2angular';
import { withUIRouter } from '@/react-tools/withUIRouter';
import { withReactQuery } from '@/react-tools/withReactQuery';
import { RelativePathFieldset } from '@/react/portainer/gitops/RelativePathFieldset/RelativePathFieldset';
import { withFormValidation } from '@/react-tools/withFormValidation';
export const ngModule = angular
.module('portainer.app.react.gitops', [])
import { relativePathValidation } from './RelativePathFieldset/validation';
.component(
'relativePathFieldset',
r2a(withUIRouter(withReactQuery(RelativePathFieldset)), [
'value',
'gitModel',
'onChange',
'isEditing',
'hideEdgeConfigs',
])
);
export const ngModule = angular.module('portainer.app.react.gitops', []);
withFormValidation(
ngModule,
withUIRouter(withReactQuery(RelativePathFieldset)),
'relativePathFieldset',
['gitModel', 'hideEdgeConfigs', 'isEditing', 'onChange'],
relativePathValidation
);
export const gitopsModule = ngModule.name;

View file

@ -59,8 +59,6 @@ export interface GitFormModel extends GitAuthModel {
RepositoryReferenceName?: string;
AdditionalFiles?: string[];
SaveCredential?: boolean;
NewCredentialName?: string;
TLSSkipVerify?: boolean;
/**

View file

@ -78,6 +78,7 @@ export function useGenericRegistriesQuery<T = Registry[]>(
export async function getRegistries() {
try {
const { data } = await axios.get<Registry[]>('/registries');
return data;
} catch (e) {
throw parseAxiosError(e as Error, 'Unable to retrieve registries');

View file

@ -12,19 +12,38 @@ import { buildUrl } from './build-url';
export function useAppTemplates<T = Array<TemplateViewModel>>({
select,
}: { select?: (templates: Array<TemplateViewModel>) => T } = {}) {
const registriesQuery = useRegistries();
enabled = true,
}: {
select?: (templates: Array<TemplateViewModel>) => T;
enabled?: boolean;
} = {}) {
const registriesQuery = useRegistries({ enabled });
return useQuery(
['templates'],
() => getTemplatesWithRegistry(registriesQuery.data),
{
enabled: !!registriesQuery.data,
enabled: !!registriesQuery.data && enabled,
select,
}
);
}
export function useAppTemplate(
id: AppTemplate['id'] | undefined,
{ enabled = true }: { enabled?: boolean } = {}
) {
const templateListQuery = useAppTemplates({ enabled: !!id && enabled });
const template = templateListQuery.data?.find((t) => t.Id === id);
return {
data: template,
isLoading: templateListQuery.isLoading,
error: templateListQuery.error,
};
}
async function getTemplatesWithRegistry(
registries: Array<Registry> | undefined
) {

View file

@ -34,7 +34,7 @@ export function EdgeSettingsFieldset({
{isGit && (
<FormSection title="Advanced settings">
<RelativePathFieldset
value={values.RelativePathSettings}
values={values.RelativePathSettings}
gitModel={gitConfig}
onChange={(newValues) =>
setValues((values) => ({

View file

@ -1,5 +1,6 @@
import { SchemaOf, boolean, mixed, number, object } from 'yup';
import { staggerConfigValidation } from '@/react/edge/edge-stacks/components/StaggerFieldset';
import { relativePathValidation } from '@/react/portainer/gitops/RelativePathFieldset/validation';
import { EdgeTemplateSettings } from '@/react/portainer/templates/custom-templates/types';
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
@ -14,6 +15,6 @@ export function edgeFieldsetValidation(): SchemaOf<EdgeTemplateSettings> {
PrePullImage: boolean().default(false),
RetryDeploy: boolean().default(false),
PrivateRegistryId: number().default(undefined),
StaggerConfig: mixed(),
StaggerConfig: staggerConfigValidation(),
});
}

View file

@ -160,7 +160,7 @@ export function InnerForm({
...values,
EdgeSettings: applySetStateAction(
edgeSetValues,
values.EdgeSettings
values.EdgeSettings!
),
}))
}

View file

@ -167,7 +167,7 @@ export function InnerForm({
setValues={(edgeValues) =>
setFieldValue(
'EdgeSettings',
applySetStateAction(edgeValues, values.EdgeSettings)
applySetStateAction(edgeValues, values.EdgeSettings!)
)
}
gitConfig={values.Git}

View file

@ -17,9 +17,12 @@ export async function getCustomTemplate(id: CustomTemplate['Id']) {
}
}
export function useCustomTemplate(id?: CustomTemplate['Id']) {
export function useCustomTemplate(
id?: CustomTemplate['Id'],
{ enabled = true }: { enabled?: boolean } = {}
) {
return useQuery(queryKeys.item(id!), () => getCustomTemplate(id!), {
...withGlobalError('Unable to retrieve custom template'),
enabled: !!id,
enabled: !!id && enabled,
});
}

View file

@ -1,5 +1,9 @@
import { UserId } from '@/portainer/users/types';
import { StackType } from '@/react/common/stacks/types';
import {
StaggerConfig,
getDefaultStaggerConfig,
} from '@/react/edge/edge-stacks/components/StaggerFieldset.types';
import { ResourceControlResponse } from '../../access-control/types';
import { RelativePathModel, RepoConfigResponse } from '../../gitops/types';
@ -105,6 +109,12 @@ export type EdgeTemplateSettings = {
PrivateRegistryId: RegistryId | undefined;
RelativePathSettings: RelativePathModel;
/**
* StaggerConfig is the configuration for staggered update
* required only on BE
*/
StaggerConfig: StaggerConfig;
};
export type CustomTemplateFileContent = {
@ -113,7 +123,9 @@ export type CustomTemplateFileContent = {
export const CustomTemplateKubernetesType = StackType.Kubernetes;
export function getDefaultEdgeTemplateSettings() {
export function getDefaultEdgeTemplateSettings():
| EdgeTemplateSettings
| undefined {
if (!isBE) {
return undefined;
}
@ -123,5 +135,6 @@ export function getDefaultEdgeTemplateSettings() {
RetryDeploy: false,
PrivateRegistryId: undefined,
RelativePathSettings: getDefaultRelativePathModel(),
StaggerConfig: getDefaultStaggerConfig(),
};
}