address review comments

pull/11212/head
testa113 2024-01-03 15:42:30 +13:00
parent 218a32a49f
commit 17971a10cc
13 changed files with 326 additions and 354 deletions

View File

@ -1,6 +1,6 @@
import { StackId } from '../types';
export const stacksQueryKeys = {
stackFile: (stackId: StackId) => ['stacks', stackId, 'file'],
stacks: ['stacks'],
stackFile: (stackId: StackId) => ['stacks', stackId, 'file'] as const,
stacks: ['stacks'] as const,
};

View File

@ -0,0 +1,29 @@
import { useQuery } from 'react-query';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { withError } from '@/react-tools/react-query';
import { Stack, StackId } from '../types';
import { stacksQueryKeys } from './query-keys';
import { buildStackUrl } from './buildUrl';
export function useStack(stackId?: StackId) {
return useQuery(
stacksQueryKeys.stackFile(stackId || 0),
() => getStack(stackId!),
{
...withError('Unable to retrieve stack'),
enabled: !!stackId,
}
);
}
async function getStack(stackId: StackId) {
try {
const { data } = await axios.get<Stack>(buildStackUrl(stackId));
return data;
} catch (err) {
throw parseAxiosError(err, 'Unable to retrieve stack');
}
}

View File

@ -0,0 +1,29 @@
import { useQuery } from 'react-query';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { withError } from '@/react-tools/react-query';
import { StackFile, StackId } from '../types';
import { stacksQueryKeys } from './query-keys';
import { buildStackUrl } from './buildUrl';
export function useStackFile(stackId?: StackId) {
return useQuery(
stacksQueryKeys.stackFile(stackId || 0),
() => getStackFile(stackId!),
{
...withError('Unable to retrieve stack'),
enabled: !!stackId,
}
);
}
async function getStackFile(stackId: StackId) {
try {
const { data } = await axios.get<StackFile>(buildStackUrl(stackId, 'file'));
return data;
} catch (err) {
throw parseAxiosError(err, 'Unable to retrieve stack file');
}
}

View File

@ -1,47 +0,0 @@
import { useQuery } from 'react-query';
import axios from '@/portainer/services/axios';
import { withError } from '@/react-tools/react-query';
import { Stack, StackFile, StackId } from '../types';
import { stacksQueryKeys } from './query-keys';
import { buildStackUrl } from './buildUrl';
export function useStackQuery(stackId?: StackId) {
return useQuery(
stacksQueryKeys.stackFile(stackId || 0),
() => getStack(stackId),
{
...withError('Unable to retrieve stack'),
enabled: !!stackId,
}
);
}
async function getStack(stackId?: StackId) {
if (!stackId) {
return Promise.resolve(undefined);
}
const { data } = await axios.get<Stack>(buildStackUrl(stackId));
return data;
}
export function useStackFileQuery(stackId?: StackId) {
return useQuery(
stacksQueryKeys.stackFile(stackId || 0),
() => getStackFile(stackId),
{
...withError('Unable to retrieve stack'),
enabled: !!stackId,
}
);
}
async function getStackFile(stackId?: StackId) {
if (!stackId) {
return Promise.resolve(undefined);
}
const { data } = await axios.get<StackFile>(buildStackUrl(stackId, 'file'));
return data;
}

View File

@ -1,44 +1,65 @@
import { useMutation } from 'react-query';
import axios from '@/portainer/services/axios';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import {
withError,
withInvalidate,
queryClient,
} from '@/react-tools/react-query';
import { StackId } from '@/react/common/stacks/types';
import { Stack, StackId } from '@/react/common/stacks/types';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { AutoUpdateModel } from '@/react/portainer/gitops/types';
import { stacksQueryKeys } from '@/react/common/stacks/queries/query-keys';
import { buildStackUrl } from '@/react/common/stacks/queries/buildUrl';
import {
AutoUpdateResponse,
GitAuthModel,
GitCredentialsModel,
} from '@/react/portainer/gitops/types';
import { saveGitCredentialsIfNeeded } from '@/react/portainer/account/git-credentials/queries/useCreateGitCredentialsMutation';
import { stacksQueryKeys } from './query-keys';
import { buildStackUrl } from './buildUrl';
type UpdateKubeGitStackPayload = {
AutoUpdate: AutoUpdateModel;
RepositoryAuthentication: boolean;
RepositoryGitCredentialID: number;
RepositoryPassword: string;
export interface UpdateKubeGitStackPayload extends GitCredentialsModel {
AutoUpdate: AutoUpdateResponse | null;
RepositoryReferenceName: string;
RepositoryUsername: string;
TLSSkipVerify: boolean;
};
}
// update a stack from a git repository
export function useUpdateKubeGitStackMutation(
stackId: StackId,
environmentId: EnvironmentId
environmentId: EnvironmentId,
userId: number
) {
return useMutation(
(stack: UpdateKubeGitStackPayload) =>
updateGitStack({ stack, stackId, environmentId }),
async ({
stack,
authentication,
}: {
stack: UpdateKubeGitStackPayload;
authentication: GitAuthModel;
}) => {
// save the new git credentials if the user has selected to save them
const newGitAuth = await saveGitCredentialsIfNeeded(
userId,
authentication
);
const stackWithUpdatedAuth: UpdateKubeGitStackPayload = {
...stack,
...newGitAuth,
};
return updateGitStack({
stack: stackWithUpdatedAuth,
stackId,
environmentId,
});
},
{
...withError('Unable to update stack'),
...withError('Unable to update application'),
...withInvalidate(queryClient, [stacksQueryKeys.stackFile(stackId)]),
}
);
}
function updateGitStack({
async function updateGitStack({
stackId,
stack,
environmentId,
@ -47,7 +68,11 @@ function updateGitStack({
stack: UpdateKubeGitStackPayload;
environmentId: EnvironmentId;
}) {
return axios.put(buildStackUrl(stackId), stack, {
params: { endpointId: environmentId },
});
try {
return await axios.post<Stack>(buildStackUrl(stackId, 'git'), stack, {
params: { endpointId: environmentId },
});
} catch (e) {
throw parseAxiosError(e, 'Unable to update stack');
}
}

View File

@ -1,4 +1,4 @@
import { PropsWithChildren } from 'react';
import { PropsWithChildren, ReactNode } from 'react';
import { AutomationTestingProps } from '@/types';
@ -11,6 +11,7 @@ interface Props extends AutomationTestingProps {
loadingText: string;
isLoading: boolean;
isValid: boolean;
submitButtonIcon?: ReactNode;
}
export function FormActions({
@ -19,6 +20,7 @@ export function FormActions({
isLoading,
children,
isValid,
submitButtonIcon,
'data-cy': dataCy,
}: PropsWithChildren<Props>) {
return (
@ -30,6 +32,7 @@ export function FormActions({
loadingText={loadingText}
isLoading={isLoading}
disabled={!isValid}
icon={submitButtonIcon}
data-cy={dataCy}
>
{submitLabel}

View File

@ -3,7 +3,7 @@ import { useCurrentStateAndParams } from '@uirouter/react';
import { Pod } from 'kubernetes-types/core/v1';
import { Authorized } from '@/react/hooks/useUser';
import { useStackFileQuery } from '@/react/common/stacks/queries/useStackQuery';
import { useStackFile } from '@/react/common/stacks/queries/useStackFile';
import { Widget, WidgetBody } from '@@/Widget';
import { Button } from '@@/buttons';
@ -48,7 +48,7 @@ export function ApplicationDetailsWidget() {
);
const externalApp = app && isExternalApplication(app);
const appStackId = Number(app?.metadata?.labels?.[appStackIdLabel]);
const appStackFileQuery = useStackFileQuery(appStackId);
const appStackFileQuery = useStackFile(appStackId);
const { data: appServices } = useApplicationServices(
environmentId,
namespace,

View File

@ -1,53 +1,33 @@
import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
import { useRef, useState } from 'react';
import { RefreshCw } from 'lucide-react';
import { useRouter } from '@uirouter/react';
import { Formik, FormikHelpers } from 'formik';
import { useRef } from 'react';
import {
UpdateKubeGitStackPayload,
useUpdateKubeGitStackMutation,
} from '@/react/common/stacks/queries/useUpdateKubeGitStackMutation';
import { Stack, StackId } from '@/react/common/stacks/types';
import {
parseAutoUpdateResponse,
transformAutoUpdateViewModel,
} from '@/react/portainer/gitops/AutoUpdateFieldset/utils';
import { AutoUpdateFieldset } from '@/react/portainer/gitops/AutoUpdateFieldset';
import {
AutoUpdateMechanism,
AutoUpdateModel,
} from '@/react/portainer/gitops/types';
import {
baseStackWebhookUrl,
createWebhookId,
} from '@/portainer/helpers/webhookHelper';
import { TimeWindowDisplay } from '@/react/portainer/gitops/TimeWindowDisplay';
import { RefField } from '@/react/portainer/gitops/RefField';
import { AutoUpdateMechanism } from '@/react/portainer/gitops/types';
import { createWebhookId } from '@/portainer/helpers/webhookHelper';
import { parseAuthResponse } from '@/react/portainer/gitops/AuthFieldset/utils';
import { confirmEnableTLSVerify } from '@/react/portainer/gitops/utils';
import { AuthFieldset } from '@/react/portainer/gitops/AuthFieldset';
import { useGitCredentials } from '@/react/portainer/account/git-credentials/git-credentials.service';
import { useCurrentUser } from '@/react/hooks/useUser';
import { useAnalytics } from '@/react/hooks/useAnalytics';
import { RepositoryMechanismTypes } from '@/kubernetes/models/deploy';
import { useCreateGitCredentialMutation } from '@/react/portainer/account/git-credentials/queries/useCreateGitCredentialsMutation';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import { notifySuccess } from '@/portainer/services/notifications';
import { useStackQuery } from '@/react/common/stacks/queries/useStackQuery';
import { useStack } from '@/react/common/stacks/queries/useStack';
import { FormSection } from '@@/form-components/FormSection';
import { SwitchField } from '@@/form-components/SwitchField';
import { LoadingButton } from '@@/buttons';
import { buildConfirmButton } from '@@/modals/utils';
import { confirm } from '@@/modals/confirm';
import { ModalType } from '@@/modals';
import { InlineLoader } from '@@/InlineLoader';
import { Alert } from '@@/Alert';
import { useUpdateKubeGitStackMutation } from './queries/useUpdateKubeGitStackMutation';
import {
KubeAppGitFormValues,
RedeployGitStackPayload,
UpdateKubeGitStackPayload,
} from './types';
import { KubeAppGitFormValues } from './types';
import { redeployGitAppFormValidationSchema } from './redeployGitAppFormValidationSchema';
import { useRedeployKubeGitStackMutation } from './queries/useRedeployKubeGitStackMutation';
import { RedeployGitAppInnerForm } from './RedeployGitAppInnerForm';
type Props = {
stackId: StackId;
@ -55,7 +35,7 @@ type Props = {
};
export function RedeployGitAppForm({ stackId, namespaceName }: Props) {
const { data: stack, ...stackQuery } = useStackQuery(stackId);
const { data: stack, ...stackQuery } = useStack(stackId);
const { trackEvent } = useAnalytics();
const initialValues = getInitialValues(stack);
const { user } = useCurrentUser();
@ -63,10 +43,10 @@ export function RedeployGitAppForm({ stackId, namespaceName }: Props) {
user.Id
);
const environmentId = useEnvironmentId();
const createGitCredentialMutation = useCreateGitCredentialMutation();
const updateKubeGitStackMutation = useUpdateKubeGitStackMutation(
stack?.Id || 0,
environmentId
environmentId,
user.Id
);
// keep a single generated webhook id, so that it doesn't change on every render
const generatedWebhookId = useRef(createWebhookId());
@ -122,40 +102,24 @@ export function RedeployGitAppForm({ stackId, namespaceName }: Props) {
) {
trackSubmit(values);
// save the new git credentials if the user has selected to save them
if (
values.authentication.SaveCredential &&
values.authentication.NewCredentialName &&
values.authentication.RepositoryPassword &&
values.authentication.RepositoryUsername
) {
createGitCredentialMutation.mutate({
name: values.authentication.NewCredentialName,
username: values.authentication.RepositoryUsername,
password: values.authentication.RepositoryPassword,
userId: user.Id,
});
}
// update the kubernetes git stack
const { authentication } = values;
const updateKubeGitStack: UpdateKubeGitStackPayload = {
RepositoryAuthentication:
!!values.authentication.RepositoryAuthentication,
RepositoryReferenceName: values.repositoryReferenceName,
AutoUpdate: transformAutoUpdateViewModel(values.autoUpdate, webhookId),
TLSSkipVerify: values.tlsSkipVerify,
RepositoryGitCredentialID: authentication.RepositoryGitCredentialID,
RepositoryPassword: authentication.RepositoryPassword,
RepositoryUsername: authentication.RepositoryUsername,
...authentication,
};
await updateKubeGitStackMutation.mutateAsync(updateKubeGitStack, {
onSuccess: ({ data: newStack }) => {
const newInitialValues = getInitialValues(newStack);
notifySuccess('Success', 'Application saved successfully.');
resetForm({ values: newInitialValues });
},
});
await updateKubeGitStackMutation.mutateAsync(
{ stack: updateKubeGitStack, authentication },
{
onSuccess: ({ data: newStack }) => {
const newInitialValues = getInitialValues(newStack);
notifySuccess('Success', 'Application saved successfully.');
resetForm({ values: newInitialValues });
},
}
);
}
function trackSubmit(values: KubeAppGitFormValues) {
@ -184,168 +148,6 @@ export function RedeployGitAppForm({ stackId, namespaceName }: Props) {
}
}
function RedeployGitAppInnerForm({
stack,
namespaceName,
webhookId,
}: {
stack: Stack;
namespaceName: string;
webhookId: string;
}) {
const router = useRouter();
const environmentId = useEnvironmentId();
const redeployKubeGitStackMutation = useRedeployKubeGitStackMutation(
stack.Id,
environmentId
);
const [isRedeployLoading, setIsRedeployLoading] = useState(false);
const { trackEvent } = useAnalytics();
const {
values,
errors,
setFieldValue,
handleSubmit,
dirty,
isValid,
isSubmitting,
setFieldTouched,
} = useFormikContext<KubeAppGitFormValues>();
return (
<Form className="form-horizontal" onSubmit={handleSubmit}>
<AutoUpdateFieldset
value={values.autoUpdate}
onChange={(value: AutoUpdateModel) =>
setFieldValue('autoUpdate', value)
}
baseWebhookUrl={baseStackWebhookUrl()}
webhookId={webhookId}
errors={errors.autoUpdate}
environmentType="KUBERNETES"
isForcePullVisible={false}
webhooksDocs="https://docs.portainer.io/user/kubernetes/applications/webhooks"
/>
<TimeWindowDisplay />
<FormSection title="Advanced configuration" isFoldable>
<RefField
value={values.repositoryReferenceName}
onChange={(refName) =>
setFieldValue('repositoryReferenceName', refName)
}
model={{
...values.authentication,
RepositoryURL: values.repositoryURL,
}}
error={errors.repositoryReferenceName}
stackId={stack.Id}
isUrlValid
/>
<AuthFieldset
value={values.authentication}
isAuthExplanationVisible
onChange={(value) =>
Object.entries(value).forEach(([key, value]) => {
setFieldValue(key, value);
// set touched after a delay to revalidate debounced username and access token fields
setTimeout(() => setFieldTouched(key, true), 400);
})
}
errors={errors.authentication}
/>
<SwitchField
name="TLSSkipVerify"
label="Skip TLS verification"
labelClass="col-sm-3 col-lg-2"
tooltip="Enabling this will allow skipping TLS validation for any self-signed certificate."
checked={values.tlsSkipVerify}
onChange={onChangeTLSSkipVerify}
/>
</FormSection>
<FormSection title="Actions">
<div className="form-group">
<div className="col-sm-12">
<LoadingButton
type="button"
onClick={handleRedeploy}
className="!ml-0"
loadingText="In progress..."
isLoading={isRedeployLoading}
disabled={dirty}
icon={RefreshCw}
data-cy="application-redeploy-button"
>
Pull and update application
</LoadingButton>
<LoadingButton
type="submit"
loadingText="In progress..."
isLoading={isSubmitting}
disabled={!dirty || !isValid}
data-cy="application-redeploy-button"
>
Save settings
</LoadingButton>
</div>
</div>
</FormSection>
</Form>
);
async function handleRedeploy() {
const confirmed = await confirm({
title: 'Are you sure?',
message:
'Any changes to this application will be overridden by the definition in git and may cause a service interruption. Do you wish to continue?',
confirmButton: buildConfirmButton('Update', 'warning'),
modalType: ModalType.Warn,
});
if (!confirmed) {
return;
}
setIsRedeployLoading(true);
// track the redeploy event
trackEvent('kubernetes-application-edit-git-pull', {
category: 'kubernetes',
});
// redeploy the application
const redeployPayload: RedeployGitStackPayload = {
RepositoryReferenceName: values.repositoryReferenceName,
RepositoryAuthentication:
!!values.authentication.RepositoryAuthentication,
RepositoryGitCredentialID:
values.authentication.RepositoryGitCredentialID,
RepositoryPassword: values.authentication.RepositoryPassword,
RepositoryUsername: values.authentication.RepositoryUsername,
Namespace: namespaceName,
};
await redeployKubeGitStackMutation.mutateAsync(redeployPayload, {
onSuccess: () => {
notifySuccess('Success', 'Application redeployed successfully.');
router.stateService.go('kubernetes.applications.application', {
id: stack.Id,
});
},
});
setIsRedeployLoading(false);
}
async function onChangeTLSSkipVerify(value: boolean) {
// If the user is disabling TLS verification, ask for confirmation
if (stack.GitConfig?.TLSSkipVerify && !value) {
const confirmed = await confirmEnableTLSVerify();
if (!confirmed) {
return;
}
}
setFieldValue('tlsSkipVerify', value);
}
}
function getInitialValues(stack?: Stack): KubeAppGitFormValues | undefined {
if (!stack || !stack.GitConfig) {
return undefined;

View File

@ -0,0 +1,184 @@
import { Form, useFormikContext } from 'formik';
import { useState } from 'react';
import { RefreshCw } from 'lucide-react';
import { useRouter } from '@uirouter/react';
import { Stack } from '@/react/common/stacks/types';
import { AutoUpdateFieldset } from '@/react/portainer/gitops/AutoUpdateFieldset';
import { AutoUpdateModel } from '@/react/portainer/gitops/types';
import { baseStackWebhookUrl } from '@/portainer/helpers/webhookHelper';
import { TimeWindowDisplay } from '@/react/portainer/gitops/TimeWindowDisplay';
import { RefField } from '@/react/portainer/gitops/RefField';
import { confirmEnableTLSVerify } from '@/react/portainer/gitops/utils';
import { AuthFieldset } from '@/react/portainer/gitops/AuthFieldset';
import { useAnalytics } from '@/react/hooks/useAnalytics';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import { notifySuccess } from '@/portainer/services/notifications';
import { FormSection } from '@@/form-components/FormSection';
import { SwitchField } from '@@/form-components/SwitchField';
import { LoadingButton } from '@@/buttons';
import { buildConfirmButton } from '@@/modals/utils';
import { confirm } from '@@/modals/confirm';
import { ModalType } from '@@/modals';
import { FormActions } from '@@/form-components/FormActions';
import { KubeAppGitFormValues, RedeployGitStackPayload } from './types';
import { useRedeployKubeGitStackMutation } from './queries/useRedeployKubeGitStackMutation';
type Props = {
stack: Stack;
namespaceName: string;
webhookId: string;
};
export function RedeployGitAppInnerForm({
stack,
namespaceName,
webhookId,
}: Props) {
const router = useRouter();
const environmentId = useEnvironmentId();
const redeployKubeGitStackMutation = useRedeployKubeGitStackMutation(
stack.Id,
environmentId
);
const [isRedeployLoading, setIsRedeployLoading] = useState(false);
const { trackEvent } = useAnalytics();
const {
values,
errors,
setFieldValue,
handleSubmit,
dirty,
isValid,
isSubmitting,
setFieldTouched,
} = useFormikContext<KubeAppGitFormValues>();
return (
<Form className="form-horizontal" onSubmit={handleSubmit}>
<AutoUpdateFieldset
value={values.autoUpdate}
onChange={(value: AutoUpdateModel) =>
setFieldValue('autoUpdate', value)
}
baseWebhookUrl={baseStackWebhookUrl()}
webhookId={webhookId}
errors={errors.autoUpdate}
environmentType="KUBERNETES"
isForcePullVisible={false}
webhooksDocs="https://docs.portainer.io/user/kubernetes/applications/webhooks"
/>
<TimeWindowDisplay />
<FormSection title="Advanced configuration" isFoldable>
<RefField
value={values.repositoryReferenceName}
onChange={(refName) =>
setFieldValue('repositoryReferenceName', refName)
}
model={{
...values.authentication,
RepositoryURL: values.repositoryURL,
}}
error={errors.repositoryReferenceName}
stackId={stack.Id}
isUrlValid
/>
<AuthFieldset
value={values.authentication}
isAuthExplanationVisible
onChange={(value) =>
Object.entries(value).forEach(([key, value]) => {
setFieldValue(key, value);
// set touched after a delay to revalidate debounced username and access token fields
setTimeout(() => setFieldTouched(key, true), 400);
})
}
errors={errors.authentication}
/>
<SwitchField
name="TLSSkipVerify"
label="Skip TLS verification"
labelClass="col-sm-3 col-lg-2"
tooltip="Enabling this will allow skipping TLS validation for any self-signed certificate."
checked={values.tlsSkipVerify}
onChange={onChangeTLSSkipVerify}
/>
</FormSection>
<FormActions
submitLabel="Save settings"
loadingText="In progress..."
isLoading={isSubmitting}
isValid={dirty && isValid}
data-cy="application-git-save-button"
>
<LoadingButton
type="button"
color="secondary"
onClick={handleRedeploy}
loadingText="In progress..."
isLoading={isRedeployLoading}
disabled={dirty}
icon={RefreshCw}
data-cy="application-redeploy-button"
>
Pull and update application
</LoadingButton>
</FormActions>
</Form>
);
async function handleRedeploy() {
const confirmed = await confirm({
title: 'Are you sure?',
message:
'Any changes to this application will be overridden by the definition in git and may cause a service interruption. Do you wish to continue?',
confirmButton: buildConfirmButton('Update', 'warning'),
modalType: ModalType.Warn,
});
if (!confirmed) {
return;
}
setIsRedeployLoading(true);
// track the redeploy event
trackEvent('kubernetes-application-edit-git-pull', {
category: 'kubernetes',
});
// redeploy the application
const redeployPayload: RedeployGitStackPayload = {
RepositoryReferenceName: values.repositoryReferenceName,
RepositoryAuthentication:
!!values.authentication.RepositoryAuthentication,
RepositoryGitCredentialID:
values.authentication.RepositoryGitCredentialID,
RepositoryPassword: values.authentication.RepositoryPassword,
RepositoryUsername: values.authentication.RepositoryUsername,
Namespace: namespaceName,
};
await redeployKubeGitStackMutation.mutateAsync(redeployPayload, {
onSuccess: () => {
notifySuccess('Success', 'Application redeployed successfully.');
router.stateService.go('kubernetes.applications.application', {
id: stack.Id,
});
},
});
setIsRedeployLoading(false);
}
async function onChangeTLSSkipVerify(value: boolean) {
// If the user is disabling TLS verification, ask for confirmation
if (stack.GitConfig?.TLSSkipVerify && !value) {
const confirmed = await confirmEnableTLSVerify();
if (!confirmed) {
return;
}
}
setFieldValue('tlsSkipVerify', value);
}
}

View File

@ -1,47 +0,0 @@
import { useMutation } from 'react-query';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import {
withError,
withInvalidate,
queryClient,
} from '@/react-tools/react-query';
import { Stack, StackId } from '@/react/common/stacks/types';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { stacksQueryKeys } from '@/react/common/stacks/queries/query-keys';
import { buildStackUrl } from '@/react/common/stacks/queries/buildUrl';
import { UpdateKubeGitStackPayload } from '../types';
// update a stack from a git repository
export function useUpdateKubeGitStackMutation(
stackId: StackId,
environmentId: EnvironmentId
) {
return useMutation(
(stack: UpdateKubeGitStackPayload) =>
updateGitStack({ stack, stackId, environmentId }),
{
...withError('Unable to update application.'),
...withInvalidate(queryClient, [stacksQueryKeys.stackFile(stackId)]),
}
);
}
async function updateGitStack({
stackId,
stack,
environmentId,
}: {
stackId: StackId;
stack: UpdateKubeGitStackPayload;
environmentId: EnvironmentId;
}) {
try {
return await axios.post<Stack>(buildStackUrl(stackId, 'git'), stack, {
params: { endpointId: environmentId },
});
} catch (e) {
throw parseAxiosError(e);
}
}

View File

@ -1,6 +1,5 @@
import {
AutoUpdateModel,
AutoUpdateResponse,
GitAuthModel,
GitCredentialsModel,
} from '@/react/portainer/gitops/types';
@ -13,12 +12,6 @@ export interface KubeAppGitFormValues {
autoUpdate: AutoUpdateModel;
}
export interface UpdateKubeGitStackPayload extends GitCredentialsModel {
AutoUpdate: AutoUpdateResponse | null;
RepositoryReferenceName: string;
TLSSkipVerify: boolean;
}
export interface RedeployGitStackPayload extends GitCredentialsModel {
RepositoryReferenceName: string;
Namespace: string;

View File

@ -2,7 +2,7 @@ import { useQueryClient, useMutation } from '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';
@ -84,8 +84,8 @@ export function useSaveCredentialsIfRequired() {
export async function saveGitCredentialsIfNeeded(
userId: UserId,
gitModel: GitFormModel
) {
gitModel: GitAuthModel
): Promise<GitAuthModel> {
let credentialsId = gitModel.RepositoryGitCredentialID;
let username = gitModel.RepositoryUsername;
let password = gitModel.RepositoryPassword;

View File

@ -84,7 +84,8 @@ async function createTemplateAndGitCredential(
userId: UserId,
{ Git: gitModel, ...values }: CreateTemplatePayload
) {
const newGitModel = await saveGitCredentialsIfNeeded(userId, gitModel);
const newGitAuthModel = await saveGitCredentialsIfNeeded(userId, gitModel);
const newGitModel = { ...gitModel, ...newGitAuthModel };
return createTemplateFromGit({
...values,