app-connections-hooks.ts•8.22 kB
import { useMutation, useQuery } from '@tanstack/react-query';
import { t } from 'i18next';
import { UseFormReturn } from 'react-hook-form';
import { useEmbedding } from '@/components/embed-provider';
import { INTERNAL_ERROR_TOAST, toast } from '@/components/ui/use-toast';
import { projectMembersApi } from '@/features/team/lib/project-members-api';
import { api } from '@/lib/api';
import { authenticationSession } from '@/lib/authentication-session';
import {
ApErrorParams,
AppConnectionScope,
AppConnectionWithoutSensitiveData,
ErrorCode,
isNil,
ListAppConnectionsRequestQuery,
ReplaceAppConnectionsRequestBody,
UpsertAppConnectionRequestBody,
} from '@activepieces/shared';
import { appConnectionsApi } from './api/app-connections';
import { globalConnectionsApi } from './api/global-connections';
import {
ConnectionNameAlreadyExists,
NoProjectSelected,
isConnectionNameUnique,
} from './utils';
type UseReplaceConnectionsProps = {
setDialogOpen: (isOpen: boolean) => void;
refetch: () => void;
};
type UseRenameAppConnectionProps = {
currentName: string;
setIsRenameDialogOpen: (isOpen: boolean) => void;
renameConnectionForm: UseFormReturn<{
displayName: string;
}>;
refetch: () => void;
};
type UseUpsertAppConnectionProps = {
isGlobalConnection: boolean;
reconnectConnection: AppConnectionWithoutSensitiveData | null;
externalIdComingFromSdk?: string | null;
setErrorMessage: (message: string) => void;
form: UseFormReturn<{
request: UpsertAppConnectionRequestBody & {
projectIds: string[];
};
}>;
setOpen: (
open: boolean,
connection?: AppConnectionWithoutSensitiveData,
) => void;
};
export const appConnectionsMutations = {
useUpsertAppConnection: ({
isGlobalConnection,
reconnectConnection,
externalIdComingFromSdk,
setErrorMessage,
form,
setOpen,
}: UseUpsertAppConnectionProps) => {
return useMutation({
mutationFn: async () => {
setErrorMessage('');
const formValues = form.getValues().request;
const isNameUnique = await isConnectionNameUnique(
isGlobalConnection,
formValues.displayName,
);
if (
!isNameUnique &&
reconnectConnection?.displayName !== formValues.displayName &&
(isNil(externalIdComingFromSdk) || externalIdComingFromSdk === '')
) {
throw new ConnectionNameAlreadyExists();
}
if (isGlobalConnection) {
if (formValues.projectIds.length === 0) {
throw new NoProjectSelected();
}
return globalConnectionsApi.upsert({
...formValues,
projectIds: formValues.projectIds,
scope: AppConnectionScope.PLATFORM,
});
}
return appConnectionsApi.upsert(formValues);
},
onSuccess: (connection) => {
setOpen(false, connection);
setErrorMessage('');
},
onError: (err) => {
if (err instanceof ConnectionNameAlreadyExists) {
form.setError('request.displayName', {
message: err.message,
});
} else if (err instanceof NoProjectSelected) {
form.setError('request.projectIds', {
message: err.message,
});
} else if (api.isError(err)) {
const apError = err.response?.data as ApErrorParams;
switch (apError.code) {
case ErrorCode.INVALID_CLOUD_CLAIM: {
setErrorMessage(
t(
'Could not claim the authorization code, make sure you have correct settings and try again.',
),
);
break;
}
case ErrorCode.INVALID_CLAIM: {
setErrorMessage(
t('Connection failed with error {msg}', {
msg: apError.params.message,
}),
);
break;
}
case ErrorCode.INVALID_APP_CONNECTION: {
setErrorMessage(
t('Connection failed with error {msg}', {
msg: apError.params.error,
}),
);
break;
}
// can happen in embedding sdk connect method
case ErrorCode.PERMISSION_DENIED: {
setErrorMessage(
t(`You don't have the permission to create a connection.`),
);
break;
}
default: {
setErrorMessage('Unexpected error, please contact support');
toast(INTERNAL_ERROR_TOAST);
console.error(err);
}
}
}
},
});
},
useBulkDeleteAppConnections: (refetch: () => void) => {
return useMutation({
mutationFn: async (ids: string[]) => {
await Promise.all(ids.map((id) => appConnectionsApi.delete(id)));
},
onSuccess: () => {
refetch();
},
onError: () => {
toast({
title: t('Error deleting connections'),
variant: 'destructive',
});
},
});
},
useRenameAppConnection: ({
currentName,
setIsRenameDialogOpen,
renameConnectionForm,
refetch,
}: UseRenameAppConnectionProps) => {
return useMutation({
mutationFn: async ({
connectionId,
displayName,
}: {
connectionId: string;
displayName: string;
}) => {
const existingConnection = await isConnectionNameUnique(
false,
displayName,
);
if (!existingConnection && displayName !== currentName) {
throw new ConnectionNameAlreadyExists();
}
return appConnectionsApi.update(connectionId, { displayName });
},
onSuccess: () => {
refetch();
toast({
title: t('Success'),
description: t('Connection has been renamed.'),
duration: 3000,
});
setIsRenameDialogOpen(false);
},
onError: (error) => {
if (error instanceof ConnectionNameAlreadyExists) {
renameConnectionForm.setError('displayName', {
message: error.message,
});
} else {
toast(INTERNAL_ERROR_TOAST);
}
},
});
},
useReplaceConnections: ({
setDialogOpen,
refetch,
}: UseReplaceConnectionsProps) => {
return useMutation({
mutationFn: async (request: ReplaceAppConnectionsRequestBody) => {
await appConnectionsApi.replace(request);
},
onSuccess: () => {
toast({
title: t('Success'),
description: t('Connections replaced successfully'),
});
setDialogOpen(false);
refetch();
},
onError: () => {
toast({
title: t('Error'),
description: t('Failed to replace connections'),
variant: 'destructive',
});
},
});
},
};
type UseConnectionsProps = {
request: ListAppConnectionsRequestQuery;
extraKeys: any[];
enabled?: boolean;
staleTime?: number;
};
export const appConnectionsQueries = {
useAppConnections: ({
request,
extraKeys,
enabled,
staleTime,
}: UseConnectionsProps) => {
return useQuery({
queryKey: ['app-connections', ...extraKeys],
queryFn: () => appConnectionsApi.list(request),
enabled,
staleTime,
});
},
useConnectionsOwners: () => {
const projectId = authenticationSession.getProjectId() ?? '';
const isEmbedding = useEmbedding().embedState.isEmbedded;
return useQuery({
queryKey: ['app-connections-owners', projectId],
queryFn: async () => {
const { data: owners } = await appConnectionsApi.getOwners({
projectId,
});
const { data: projectMembers } = await projectMembersApi.list({
projectId,
});
if (isEmbedding) {
return owners.filter(
(owner) =>
!isNil(
projectMembers.find(
(member) => member.user.email === owner.email,
),
),
);
}
return owners;
},
});
},
};