From 10857440360702fd3c94e7c59bbe5373f515cb33 Mon Sep 17 00:00:00 2001 From: martmull Date: Mon, 19 Aug 2024 16:58:08 +0200 Subject: [PATCH 01/21] Fix twenty-front --- packages/twenty-front/src/hooks/useIsMatchingLocation.ts | 2 +- .../emails/components/MessageThreadSubscribersChip.tsx | 2 +- .../utils/generateDefaultRecordChipData.ts | 2 +- .../modules/object-record/hooks/useRestoreManyRecords.ts | 4 +++- .../input/components/MultiSelectFieldInput.tsx | 4 ++-- .../components/RecordDetailRelationSection.tsx | 2 +- .../utils/findUnmatchedRequiredFields.ts | 9 +++++---- .../src/modules/ui/input/components/Toggle.tsx | 2 +- 8 files changed, 15 insertions(+), 12 deletions(-) diff --git a/packages/twenty-front/src/hooks/useIsMatchingLocation.ts b/packages/twenty-front/src/hooks/useIsMatchingLocation.ts index aa57dbb432e3..044a344068a3 100644 --- a/packages/twenty-front/src/hooks/useIsMatchingLocation.ts +++ b/packages/twenty-front/src/hooks/useIsMatchingLocation.ts @@ -9,7 +9,7 @@ export const useIsMatchingLocation = () => { return useCallback( (path: string, basePath?: AppBasePath) => { const constructedPath = basePath - ? (new URL(basePath + path, document.location.origin).pathname ?? '') + ? new URL(basePath + path, document.location.origin).pathname ?? '' : path; return !!matchPath(constructedPath, location.pathname); diff --git a/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersChip.tsx b/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersChip.tsx index 6ef97d07b28f..d10827215835 100644 --- a/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersChip.tsx +++ b/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersChip.tsx @@ -45,7 +45,7 @@ export const MessageThreadSubscribersChip = ({ ? `+${numberOfMessageThreadSubscribers - MAX_NUMBER_OF_AVATARS}` : null; - const label = isPrivateThread ? privateLabel : (moreAvatarsLabel ?? ''); + const label = isPrivateThread ? privateLabel : moreAvatarsLabel ?? ''; return ( { const name = isFieldFullNameValue(record.name) ? record.name.firstName + ' ' + record.name.lastName - : (record.name ?? ''); + : record.name ?? ''; return { name, diff --git a/packages/twenty-front/src/modules/object-record/hooks/useRestoreManyRecords.ts b/packages/twenty-front/src/modules/object-record/hooks/useRestoreManyRecords.ts index f1fb1c17c13c..55bd5cc5e865 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useRestoreManyRecords.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useRestoreManyRecords.ts @@ -58,7 +58,9 @@ export const useRestoreManyRecords = ({ // TODO: fix optimistic effect const findOneQueryName = `FindOne${capitalize(objectNameSingular)}`; - const findManyQueryName = `FindMany${capitalize(objectMetadataItem.namePlural)}`; + const findManyQueryName = `FindMany${capitalize( + objectMetadataItem.namePlural, + )}`; const restoredRecordsResponse = await apolloClient.mutate({ mutation: restoreManyRecordsMutation, diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiSelectFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiSelectFieldInput.tsx index c6134f911a2d..d36b2b9fbfc4 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiSelectFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiSelectFieldInput.tsx @@ -42,8 +42,8 @@ export const MultiSelectFieldInput = ({ const [searchFilter, setSearchFilter] = useState(''); const containerRef = useRef(null); - const selectedOptions = fieldDefinition.metadata.options.filter((option) => - fieldValues?.includes(option.value), + const selectedOptions = fieldDefinition.metadata.options.filter( + (option) => fieldValues?.includes(option.value), ); const optionsInDropDown = fieldDefinition.metadata.options; diff --git a/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx b/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx index 426078f807b4..e59ca3be75fe 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx @@ -69,7 +69,7 @@ export const RecordDetailRelationSection = ({ const relationRecords: ObjectRecord[] = fieldValue && isToOneObject ? [fieldValue as ObjectRecord] - : ((fieldValue as ObjectRecord[]) ?? []); + : (fieldValue as ObjectRecord[]) ?? []; const relationRecordIds = relationRecords.map(({ id }) => id); diff --git a/packages/twenty-front/src/modules/spreadsheet-import/utils/findUnmatchedRequiredFields.ts b/packages/twenty-front/src/modules/spreadsheet-import/utils/findUnmatchedRequiredFields.ts index 71af1840263d..e7219f70bddf 100644 --- a/packages/twenty-front/src/modules/spreadsheet-import/utils/findUnmatchedRequiredFields.ts +++ b/packages/twenty-front/src/modules/spreadsheet-import/utils/findUnmatchedRequiredFields.ts @@ -6,10 +6,11 @@ export const findUnmatchedRequiredFields = ( columns: Columns, ) => fields - .filter((field) => - field.fieldValidationDefinitions?.some( - (validation) => validation.rule === 'required', - ), + .filter( + (field) => + field.fieldValidationDefinitions?.some( + (validation) => validation.rule === 'required', + ), ) .filter( (field) => diff --git a/packages/twenty-front/src/modules/ui/input/components/Toggle.tsx b/packages/twenty-front/src/modules/ui/input/components/Toggle.tsx index f723e93e1c5a..9aadab0eb028 100644 --- a/packages/twenty-front/src/modules/ui/input/components/Toggle.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/Toggle.tsx @@ -16,7 +16,7 @@ type ContainerProps = { const StyledContainer = styled.div` align-items: center; background-color: ${({ theme, isOn, color }) => - isOn ? (color ?? theme.color.blue) : theme.background.quaternary}; + isOn ? color ?? theme.color.blue : theme.background.quaternary}; border-radius: 10px; cursor: pointer; display: flex; From de213efc60a94943e4de2b8bdda2e884f46e5411 Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 20 Aug 2024 09:40:00 +0200 Subject: [PATCH 02/21] Remove console errors --- ...ettingsServerlessFunctionTestTabEffect.tsx | 19 ++++++++++++------- .../ResetServerlessFunctionStatesEffect.tsx | 13 ++++++++++--- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTestTabEffect.tsx b/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTestTabEffect.tsx index 64cff208b491..0bd86e692078 100644 --- a/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTestTabEffect.tsx +++ b/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTestTabEffect.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { useRecoilValue, useSetRecoilState } from 'recoil'; import { DEFAULT_OUTPUT_VALUE, @@ -13,11 +13,16 @@ export const SettingsServerlessFunctionTestTabEffect = () => { const setSettingsServerlessFunctionCodeEditorOutputParams = useSetRecoilState( settingsServerlessFunctionCodeEditorOutputParamsState, ); - if (settingsServerlessFunctionOutput.data !== DEFAULT_OUTPUT_VALUE) { - setSettingsServerlessFunctionCodeEditorOutputParams({ - language: 'json', - height: 300, - }); - } + useEffect(() => { + if (settingsServerlessFunctionOutput.data !== DEFAULT_OUTPUT_VALUE) { + setSettingsServerlessFunctionCodeEditorOutputParams({ + language: 'json', + height: 300, + }); + } + }, [ + settingsServerlessFunctionOutput.data, + setSettingsServerlessFunctionCodeEditorOutputParams, + ]); return <>; }; diff --git a/packages/twenty-front/src/pages/settings/serverless-functions/ResetServerlessFunctionStatesEffect.tsx b/packages/twenty-front/src/pages/settings/serverless-functions/ResetServerlessFunctionStatesEffect.tsx index 595f2b2a1d47..95f243cbe910 100644 --- a/packages/twenty-front/src/pages/settings/serverless-functions/ResetServerlessFunctionStatesEffect.tsx +++ b/packages/twenty-front/src/pages/settings/serverless-functions/ResetServerlessFunctionStatesEffect.tsx @@ -2,6 +2,7 @@ import { settingsServerlessFunctionCodeEditorOutputParamsState } from '@/setting import { settingsServerlessFunctionInputState } from '@/settings/serverless-functions/states/settingsServerlessFunctionInputState'; import { settingsServerlessFunctionOutputState } from '@/settings/serverless-functions/states/settingsServerlessFunctionOutputState'; import { useResetRecoilState } from 'recoil'; +import { useEffect } from 'react'; export const ResetServerlessFunctionStatesEffect = () => { const resetSettingsServerlessFunctionInput = useResetRecoilState( @@ -13,8 +14,14 @@ export const ResetServerlessFunctionStatesEffect = () => { const resetSettingsServerlessFunctionCodeEditorOutputParamsState = useResetRecoilState(settingsServerlessFunctionCodeEditorOutputParamsState); - resetSettingsServerlessFunctionInput(); - resetSettingsServerlessFunctionOutput(); - resetSettingsServerlessFunctionCodeEditorOutputParamsState(); + useEffect(() => { + resetSettingsServerlessFunctionInput(); + resetSettingsServerlessFunctionOutput(); + resetSettingsServerlessFunctionCodeEditorOutputParamsState(); + }, [ + resetSettingsServerlessFunctionInput, + resetSettingsServerlessFunctionOutput, + resetSettingsServerlessFunctionCodeEditorOutputParamsState, + ]); return <>; }; From b497fe9210e6985a3062e4bd786e62398a6cdf62 Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 20 Aug 2024 10:30:39 +0200 Subject: [PATCH 03/21] Fix carret move when autosave --- .../hooks/useServerlessFunctionUpdateFormState.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState.ts b/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState.ts index f10557518798..42ae51bfd587 100644 --- a/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState.ts +++ b/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState.ts @@ -19,6 +19,7 @@ type SetServerlessFunctionFormValues = Dispatch< export const useServerlessFunctionUpdateFormState = ( serverlessFunctionId: string, ): [ServerlessFunctionFormValues, SetServerlessFunctionFormValues] => { + const [isInitialized, setIsInitialized] = useState(false); const [formValues, setFormValues] = useState({ name: '', description: '', @@ -46,12 +47,13 @@ export const useServerlessFunctionUpdateFormState = ( ...prevState, ...newState, })); + setIsInitialized(true); } }; - if (isDefined(serverlessFunction?.sourceCodeFullPath)) { + if (isDefined(serverlessFunction?.sourceCodeFullPath) && !isInitialized) { getFileContent(); } - }, [serverlessFunction, setFormValues]); + }, [serverlessFunction, setFormValues, isInitialized, setIsInitialized]); return [formValues, setFormValues]; }; From 90824a41ad850ae32181bdbb2796fca942fdaf02 Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 20 Aug 2024 11:42:28 +0200 Subject: [PATCH 04/21] Update build --- .../SettingsServerlessFunctionDetail.tsx | 1 - .../serverless/drivers/lambda.driver.ts | 7 +++++++ .../serverless-function.service.ts | 18 ++++++------------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/twenty-front/src/pages/settings/serverless-functions/SettingsServerlessFunctionDetail.tsx b/packages/twenty-front/src/pages/settings/serverless-functions/SettingsServerlessFunctionDetail.tsx index b67cd4655479..600f3d6ee440 100644 --- a/packages/twenty-front/src/pages/settings/serverless-functions/SettingsServerlessFunctionDetail.tsx +++ b/packages/twenty-front/src/pages/settings/serverless-functions/SettingsServerlessFunctionDetail.tsx @@ -71,7 +71,6 @@ export const SettingsServerlessFunctionDetail = () => { }; const handleExecute = async () => { - await handleSave(); try { const result = await executeOneServerlessFunction( serverlessFunctionId, diff --git a/packages/twenty-server/src/engine/integrations/serverless/drivers/lambda.driver.ts b/packages/twenty-server/src/engine/integrations/serverless/drivers/lambda.driver.ts index 4889ff0fcb13..07ab8590429b 100644 --- a/packages/twenty-server/src/engine/integrations/serverless/drivers/lambda.driver.ts +++ b/packages/twenty-server/src/engine/integrations/serverless/drivers/lambda.driver.ts @@ -9,6 +9,7 @@ import { UpdateFunctionCodeCommand, DeleteFunctionCommand, ResourceNotFoundException, + waitUntilFunctionUpdatedV2, } from '@aws-sdk/client-lambda'; import { CreateFunctionCommandInput } from '@aws-sdk/client-lambda/dist-types/commands/CreateFunctionCommand'; import { UpdateFunctionCodeCommandInput } from '@aws-sdk/client-lambda/dist-types/commands/UpdateFunctionCodeCommand'; @@ -130,6 +131,12 @@ export class LambdaDriver const command = new UpdateFunctionCodeCommand(params); await this.lambdaClient.send(command); + const waitParams = { FunctionName: serverlessFunction.id }; + + await waitUntilFunctionUpdatedV2( + { client: this.lambdaClient, maxWaitTime: 2 }, + waitParams, + ); } await this.buildDirectoryManagerService.clean(); diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts index 38768d44a711..7d6330e375e6 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts @@ -60,10 +60,10 @@ export class ServerlessFunctionService extends TypeOrmQueryService Date: Tue, 20 Aug 2024 18:12:47 +0200 Subject: [PATCH 05/21] Add versionning --- .../src/generated-metadata/gql.ts | 13 +- .../src/generated-metadata/graphql.ts | 51 ++++-- .../fragments/serverlessFunctionFragment.ts | 2 +- .../mutations/executeOneServerlessFunction.ts | 12 +- .../findOneServerlessFunctionSourceCode.ts | 9 + .../hooks/useExecuteOneServerlessFunction.ts | 2 + .../useGetOneServerlessFunctionSourceCode.ts | 30 +++ .../useServerlessFunctionUpdateFormState.ts | 47 ++--- .../SettingsServerlessFunctionDetail.tsx | 1 + .../1724171584314-updateServerlessFunction.ts | 25 +++ .../interfaces/storage-driver.interface.ts | 4 + .../file-storage/drivers/local.driver.ts | 37 ++++ .../file-storage/drivers/s3.driver.ts | 36 ++++ .../file-storage/file-storage.service.ts | 7 + .../drivers/base-serverless.driver.ts | 9 +- .../interfaces/serverless-driver.interface.ts | 9 +- .../serverless/drivers/lambda.driver.ts | 103 ++++++++--- .../serverless/drivers/local.driver.ts | 173 +++++++++++------- .../serverless/serverless.service.ts | 16 +- .../dtos/execute-serverless-function.input.ts | 7 + ...t-serverless-function-source-code.input.ts | 17 ++ .../dtos/publish-serverless-function.input.ts | 15 ++ .../dtos/serverless-function.dto.ts | 13 +- .../serverless-function.entity.ts | 6 +- .../serverless-function.exception.ts | 1 + .../serverless-function.resolver.ts | 45 ++++- .../serverless-function.service.ts | 168 ++++++++++++----- ...ion-graphql-api-exception-handler.utils.ts | 1 + .../code-workflow-action-runner.ts | 2 +- 29 files changed, 653 insertions(+), 208 deletions(-) create mode 100644 packages/twenty-front/src/modules/settings/serverless-functions/graphql/queries/findOneServerlessFunctionSourceCode.ts create mode 100644 packages/twenty-front/src/modules/settings/serverless-functions/hooks/useGetOneServerlessFunctionSourceCode.ts create mode 100644 packages/twenty-server/src/database/typeorm/metadata/migrations/1724171584314-updateServerlessFunction.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/get-serverless-function-source-code.input.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/publish-serverless-function.input.ts diff --git a/packages/twenty-front/src/generated-metadata/gql.ts b/packages/twenty-front/src/generated-metadata/gql.ts index 1aca61a965d2..2676763d8476 100644 --- a/packages/twenty-front/src/generated-metadata/gql.ts +++ b/packages/twenty-front/src/generated-metadata/gql.ts @@ -33,13 +33,14 @@ const documents = { "\n mutation DeleteOneFieldMetadataItem($idToDelete: UUID!) {\n deleteOneField(input: { id: $idToDelete }) {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n }\n }\n": types.DeleteOneFieldMetadataItemDocument, "\n mutation DeleteOneRelationMetadataItem($idToDelete: UUID!) {\n deleteOneRelation(input: { id: $idToDelete }) {\n id\n }\n }\n": types.DeleteOneRelationMetadataItemDocument, "\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isRemote\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n isSystem\n isRemote\n }\n toFieldMetadataId\n }\n toRelationMetadata {\n id\n relationType\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n isSystem\n isRemote\n }\n fromFieldMetadataId\n }\n defaultValue\n options\n relationDefinition {\n relationId\n direction\n sourceObjectMetadata {\n id\n nameSingular\n namePlural\n }\n sourceFieldMetadata {\n id\n name\n }\n targetObjectMetadata {\n id\n nameSingular\n namePlural\n }\n targetFieldMetadata {\n id\n name\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n": types.ObjectMetadataItemsDocument, - "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n sourceCodeHash\n sourceCodeFullPath\n runtime\n syncStatus\n createdAt\n updatedAt\n }\n": types.ServerlessFunctionFieldsFragmentDoc, + "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n sourceCodeHash\n runtime\n syncStatus\n latestVersion\n createdAt\n updatedAt\n }\n": types.ServerlessFunctionFieldsFragmentDoc, "\n \n mutation CreateOneServerlessFunctionItem(\n $input: CreateServerlessFunctionInput!\n ) {\n createOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.CreateOneServerlessFunctionItemDocument, "\n \n mutation DeleteOneServerlessFunction($input: DeleteServerlessFunctionInput!) {\n deleteOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.DeleteOneServerlessFunctionDocument, - "\n mutation ExecuteOneServerlessFunction($id: UUID!, $payload: JSON!) {\n executeOneServerlessFunction(id: $id, payload: $payload) {\n data\n duration\n status\n error\n }\n }\n": types.ExecuteOneServerlessFunctionDocument, + "\n mutation ExecuteOneServerlessFunction(\n $id: UUID!\n $payload: JSON!\n $version: String\n ) {\n executeOneServerlessFunction(\n id: $id\n payload: $payload\n version: $version\n ) {\n data\n duration\n status\n error\n }\n }\n": types.ExecuteOneServerlessFunctionDocument, "\n \n mutation UpdateOneServerlessFunction($input: UpdateServerlessFunctionInput!) {\n updateOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.UpdateOneServerlessFunctionDocument, "\n \n query GetManyServerlessFunctions {\n serverlessFunctions(paging: { first: 100 }) {\n edges {\n node {\n ...ServerlessFunctionFields\n }\n }\n }\n }\n": types.GetManyServerlessFunctionsDocument, "\n \n query GetOneServerlessFunction($id: UUID!) {\n serverlessFunction(id: $id) {\n ...ServerlessFunctionFields\n }\n }\n": types.GetOneServerlessFunctionDocument, + "\n query FindOneServerlessFunctionSourceCode(\n $input: GetServerlessFunctionSourceCodeInput!\n ) {\n getServerlessFunctionSourceCode(input: $input)\n }\n": types.FindOneServerlessFunctionSourceCodeDocument, }; /** @@ -139,7 +140,7 @@ export function graphql(source: "\n query ObjectMetadataItems(\n $objectFilt /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n sourceCodeHash\n sourceCodeFullPath\n runtime\n syncStatus\n createdAt\n updatedAt\n }\n"): (typeof documents)["\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n sourceCodeHash\n sourceCodeFullPath\n runtime\n syncStatus\n createdAt\n updatedAt\n }\n"]; +export function graphql(source: "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n sourceCodeHash\n runtime\n syncStatus\n latestVersion\n createdAt\n updatedAt\n }\n"): (typeof documents)["\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n sourceCodeHash\n runtime\n syncStatus\n latestVersion\n createdAt\n updatedAt\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -151,7 +152,7 @@ export function graphql(source: "\n \n mutation DeleteOneServerlessFunction($i /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n mutation ExecuteOneServerlessFunction($id: UUID!, $payload: JSON!) {\n executeOneServerlessFunction(id: $id, payload: $payload) {\n data\n duration\n status\n error\n }\n }\n"): (typeof documents)["\n mutation ExecuteOneServerlessFunction($id: UUID!, $payload: JSON!) {\n executeOneServerlessFunction(id: $id, payload: $payload) {\n data\n duration\n status\n error\n }\n }\n"]; +export function graphql(source: "\n mutation ExecuteOneServerlessFunction(\n $id: UUID!\n $payload: JSON!\n $version: String\n ) {\n executeOneServerlessFunction(\n id: $id\n payload: $payload\n version: $version\n ) {\n data\n duration\n status\n error\n }\n }\n"): (typeof documents)["\n mutation ExecuteOneServerlessFunction(\n $id: UUID!\n $payload: JSON!\n $version: String\n ) {\n executeOneServerlessFunction(\n id: $id\n payload: $payload\n version: $version\n ) {\n data\n duration\n status\n error\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -164,6 +165,10 @@ export function graphql(source: "\n \n query GetManyServerlessFunctions {\n * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "\n \n query GetOneServerlessFunction($id: UUID!) {\n serverlessFunction(id: $id) {\n ...ServerlessFunctionFields\n }\n }\n"): (typeof documents)["\n \n query GetOneServerlessFunction($id: UUID!) {\n serverlessFunction(id: $id) {\n ...ServerlessFunctionFields\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n query FindOneServerlessFunctionSourceCode(\n $input: GetServerlessFunctionSourceCodeInput!\n ) {\n getServerlessFunctionSourceCode(input: $input)\n }\n"): (typeof documents)["\n query FindOneServerlessFunctionSourceCode(\n $input: GetServerlessFunctionSourceCodeInput!\n ) {\n getServerlessFunctionSourceCode(input: $input)\n }\n"]; export function graphql(source: string) { return (documents as any)[source] ?? {}; diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index f5e61e07afd5..d19c3cb4b119 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -389,6 +389,13 @@ export type FullName = { lastName: Scalars['String']['output']; }; +export type GetServerlessFunctionSourceCodeInput = { + /** The id of the function. */ + id: Scalars['ID']['input']; + /** The version of the function */ + version?: Scalars['String']['input']; +}; + export type InvalidatePassword = { __typename?: 'InvalidatePassword'; /** Boolean that confirms query was dispatched */ @@ -586,6 +593,7 @@ export type MutationExchangeAuthorizationCodeArgs = { export type MutationExecuteOneServerlessFunctionArgs = { id: Scalars['UUID']['input']; payload?: InputMaybe; + version?: Scalars['String']['input']; }; @@ -789,6 +797,7 @@ export type Query = { getAISQLQuery: AisqlQueryResult; getPostgresCredentials?: Maybe; getProductPrices: ProductPricesEntity; + getServerlessFunctionSourceCode: Scalars['String']['output']; getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal; getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal; getTimelineThreadsFromCompanyId: TimelineThreadsWithTotal; @@ -860,6 +869,11 @@ export type QueryGetProductPricesArgs = { }; +export type QueryGetServerlessFunctionSourceCodeArgs = { + input: GetServerlessFunctionSourceCodeInput; +}; + + export type QueryGetTimelineCalendarEventsFromCompanyIdArgs = { companyId: Scalars['UUID']['input']; page: Scalars['Int']['input']; @@ -1026,9 +1040,9 @@ export type ServerlessFunction = { createdAt: Scalars['DateTime']['output']; description?: Maybe; id: Scalars['UUID']['output']; + latestVersion?: Maybe; name: Scalars['String']['output']; runtime: Scalars['String']['output']; - sourceCodeFullPath: Scalars['String']['output']; sourceCodeHash: Scalars['String']['output']; syncStatus: ServerlessFunctionSyncStatus; updatedAt: Scalars['DateTime']['output']; @@ -1690,25 +1704,26 @@ export type ObjectMetadataItemsQueryVariables = Exact<{ export type ObjectMetadataItemsQuery = { __typename?: 'Query', objects: { __typename?: 'ObjectConnection', edges: Array<{ __typename?: 'objectEdge', node: { __typename?: 'object', id: any, dataSourceId: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isRemote: boolean, isActive: boolean, isSystem: boolean, createdAt: any, updatedAt: any, labelIdentifierFieldMetadataId?: string | null, imageIdentifierFieldMetadataId?: string | null, fields: { __typename?: 'ObjectFieldsConnection', edges: Array<{ __typename?: 'fieldEdge', node: { __typename?: 'field', id: any, type: FieldMetadataType, name: string, label: string, description?: string | null, icon?: string | null, isCustom?: boolean | null, isActive?: boolean | null, isSystem?: boolean | null, isNullable?: boolean | null, createdAt: any, updatedAt: any, defaultValue?: any | null, options?: any | null, fromRelationMetadata?: { __typename?: 'relation', id: any, relationType: RelationMetadataType, toFieldMetadataId: string, toObjectMetadata: { __typename?: 'object', id: any, dataSourceId: string, nameSingular: string, namePlural: string, isSystem: boolean, isRemote: boolean } } | null, toRelationMetadata?: { __typename?: 'relation', id: any, relationType: RelationMetadataType, fromFieldMetadataId: string, fromObjectMetadata: { __typename?: 'object', id: any, dataSourceId: string, nameSingular: string, namePlural: string, isSystem: boolean, isRemote: boolean } } | null, relationDefinition?: { __typename?: 'RelationDefinition', relationId: any, direction: RelationDefinitionType, sourceObjectMetadata: { __typename?: 'object', id: any, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'field', id: any, name: string }, targetObjectMetadata: { __typename?: 'object', id: any, nameSingular: string, namePlural: string }, targetFieldMetadata: { __typename?: 'field', id: any, name: string } } | null } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } }; -export type ServerlessFunctionFieldsFragment = { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, sourceCodeFullPath: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, createdAt: any, updatedAt: any }; +export type ServerlessFunctionFieldsFragment = { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: number | null, createdAt: any, updatedAt: any }; export type CreateOneServerlessFunctionItemMutationVariables = Exact<{ input: CreateServerlessFunctionInput; }>; -export type CreateOneServerlessFunctionItemMutation = { __typename?: 'Mutation', createOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, sourceCodeFullPath: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, createdAt: any, updatedAt: any } }; +export type CreateOneServerlessFunctionItemMutation = { __typename?: 'Mutation', createOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: number | null, createdAt: any, updatedAt: any } }; export type DeleteOneServerlessFunctionMutationVariables = Exact<{ input: DeleteServerlessFunctionInput; }>; -export type DeleteOneServerlessFunctionMutation = { __typename?: 'Mutation', deleteOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, sourceCodeFullPath: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, createdAt: any, updatedAt: any } }; +export type DeleteOneServerlessFunctionMutation = { __typename?: 'Mutation', deleteOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: number | null, createdAt: any, updatedAt: any } }; export type ExecuteOneServerlessFunctionMutationVariables = Exact<{ id: Scalars['UUID']['input']; payload: Scalars['JSON']['input']; + version?: InputMaybe; }>; @@ -1719,23 +1734,30 @@ export type UpdateOneServerlessFunctionMutationVariables = Exact<{ }>; -export type UpdateOneServerlessFunctionMutation = { __typename?: 'Mutation', updateOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, sourceCodeFullPath: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, createdAt: any, updatedAt: any } }; +export type UpdateOneServerlessFunctionMutation = { __typename?: 'Mutation', updateOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: number | null, createdAt: any, updatedAt: any } }; export type GetManyServerlessFunctionsQueryVariables = Exact<{ [key: string]: never; }>; -export type GetManyServerlessFunctionsQuery = { __typename?: 'Query', serverlessFunctions: { __typename?: 'ServerlessFunctionConnection', edges: Array<{ __typename?: 'ServerlessFunctionEdge', node: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, sourceCodeFullPath: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, createdAt: any, updatedAt: any } }> } }; +export type GetManyServerlessFunctionsQuery = { __typename?: 'Query', serverlessFunctions: { __typename?: 'ServerlessFunctionConnection', edges: Array<{ __typename?: 'ServerlessFunctionEdge', node: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: number | null, createdAt: any, updatedAt: any } }> } }; export type GetOneServerlessFunctionQueryVariables = Exact<{ id: Scalars['UUID']['input']; }>; -export type GetOneServerlessFunctionQuery = { __typename?: 'Query', serverlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, sourceCodeFullPath: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, createdAt: any, updatedAt: any } }; +export type GetOneServerlessFunctionQuery = { __typename?: 'Query', serverlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: number | null, createdAt: any, updatedAt: any } }; + +export type FindOneServerlessFunctionSourceCodeQueryVariables = Exact<{ + input: GetServerlessFunctionSourceCodeInput; +}>; + + +export type FindOneServerlessFunctionSourceCodeQuery = { __typename?: 'Query', getServerlessFunctionSourceCode: string }; export const RemoteServerFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteServerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperId"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperOptions"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperType"}},{"kind":"Field","name":{"kind":"Name","value":"userMappingOptions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"}}]}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}}]} as unknown as DocumentNode; export const RemoteTableFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteTableFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTable"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"schemaPendingUpdates"}}]}}]} as unknown as DocumentNode; -export const ServerlessFunctionFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeFullPath"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const ServerlessFunctionFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const CreateServerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"createServer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateRemoteServerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneRemoteServer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteServerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteServerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperId"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperOptions"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperType"}},{"kind":"Field","name":{"kind":"Name","value":"userMappingOptions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"}}]}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}}]} as unknown as DocumentNode; export const DeleteServerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"deleteServer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServerIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneRemoteServer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; export const SyncRemoteTableDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"syncRemoteTable"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTableInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"syncRemoteTable"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteTableFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteTableFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTable"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"schemaPendingUpdates"}}]}}]} as unknown as DocumentNode; @@ -1754,9 +1776,10 @@ export const DeleteOneObjectMetadataItemDocument = {"kind":"Document","definitio export const DeleteOneFieldMetadataItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneFieldMetadataItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneField"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode; export const DeleteOneRelationMetadataItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneRelationMetadataItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneRelation"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; export const ObjectMetadataItemsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ObjectMetadataItems"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"objectFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"objectFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fieldFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"fieldFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"objects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"objectFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isRemote"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"labelIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"imageIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"fields"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fieldFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"fromRelationMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"relationType"}},{"kind":"Field","name":{"kind":"Name","value":"toObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"isRemote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"toFieldMetadataId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"toRelationMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"relationType"}},{"kind":"Field","name":{"kind":"Name","value":"fromObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"isRemote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fromFieldMetadataId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"defaultValue"}},{"kind":"Field","name":{"kind":"Name","value":"options"}},{"kind":"Field","name":{"kind":"Name","value":"relationDefinition"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"relationId"}},{"kind":"Field","name":{"kind":"Name","value":"direction"}},{"kind":"Field","name":{"kind":"Name","value":"sourceObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sourceFieldMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"targetObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}}]}},{"kind":"Field","name":{"kind":"Name","value":"targetFieldMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]} as unknown as DocumentNode; -export const CreateOneServerlessFunctionItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOneServerlessFunctionItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeFullPath"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; -export const DeleteOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DeleteServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeFullPath"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; -export const ExecuteOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ExecuteOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"payload"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"executeOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"payload"},"value":{"kind":"Variable","name":{"kind":"Name","value":"payload"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"data"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]} as unknown as DocumentNode; -export const UpdateOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeFullPath"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; -export const GetManyServerlessFunctionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetManyServerlessFunctions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverlessFunctions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeFullPath"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; -export const GetOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeFullPath"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; \ No newline at end of file +export const CreateOneServerlessFunctionItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOneServerlessFunctionItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const DeleteOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DeleteServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const ExecuteOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ExecuteOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"payload"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"version"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"executeOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"payload"},"value":{"kind":"Variable","name":{"kind":"Name","value":"payload"}}},{"kind":"Argument","name":{"kind":"Name","value":"version"},"value":{"kind":"Variable","name":{"kind":"Name","value":"version"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"data"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]} as unknown as DocumentNode; +export const UpdateOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const GetManyServerlessFunctionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetManyServerlessFunctions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverlessFunctions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const GetOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const FindOneServerlessFunctionSourceCodeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindOneServerlessFunctionSourceCode"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GetServerlessFunctionSourceCodeInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getServerlessFunctionSourceCode"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/graphql/fragments/serverlessFunctionFragment.ts b/packages/twenty-front/src/modules/settings/serverless-functions/graphql/fragments/serverlessFunctionFragment.ts index dc48df97d08d..2fd1e2506614 100644 --- a/packages/twenty-front/src/modules/settings/serverless-functions/graphql/fragments/serverlessFunctionFragment.ts +++ b/packages/twenty-front/src/modules/settings/serverless-functions/graphql/fragments/serverlessFunctionFragment.ts @@ -6,9 +6,9 @@ export const SERVERLESS_FUNCTION_FRAGMENT = gql` name description sourceCodeHash - sourceCodeFullPath runtime syncStatus + latestVersion createdAt updatedAt } diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/graphql/mutations/executeOneServerlessFunction.ts b/packages/twenty-front/src/modules/settings/serverless-functions/graphql/mutations/executeOneServerlessFunction.ts index 9054e26abcb6..dde79a3d8e52 100644 --- a/packages/twenty-front/src/modules/settings/serverless-functions/graphql/mutations/executeOneServerlessFunction.ts +++ b/packages/twenty-front/src/modules/settings/serverless-functions/graphql/mutations/executeOneServerlessFunction.ts @@ -1,8 +1,16 @@ import { gql } from '@apollo/client'; export const EXECUTE_ONE_SERVERLESS_FUNCTION = gql` - mutation ExecuteOneServerlessFunction($id: UUID!, $payload: JSON!) { - executeOneServerlessFunction(id: $id, payload: $payload) { + mutation ExecuteOneServerlessFunction( + $id: UUID! + $payload: JSON! + $version: String! + ) { + executeOneServerlessFunction( + id: $id + payload: $payload + version: $version + ) { data duration status diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/graphql/queries/findOneServerlessFunctionSourceCode.ts b/packages/twenty-front/src/modules/settings/serverless-functions/graphql/queries/findOneServerlessFunctionSourceCode.ts new file mode 100644 index 000000000000..510eeb33a658 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/serverless-functions/graphql/queries/findOneServerlessFunctionSourceCode.ts @@ -0,0 +1,9 @@ +import { gql } from '@apollo/client'; + +export const FIND_ONE_SERVERLESS_FUNCTION_SOURCE_CODE = gql` + query FindOneServerlessFunctionSourceCode( + $input: GetServerlessFunctionSourceCodeInput! + ) { + getServerlessFunctionSourceCode(input: $input) + } +`; diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useExecuteOneServerlessFunction.ts b/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useExecuteOneServerlessFunction.ts index 84394ac20a62..9ef8e7bac15d 100644 --- a/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useExecuteOneServerlessFunction.ts +++ b/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useExecuteOneServerlessFunction.ts @@ -18,11 +18,13 @@ export const useExecuteOneServerlessFunction = () => { const executeOneServerlessFunction = async ( id: string, payload: object = {}, + version: string, ) => { return await mutate({ variables: { id, payload, + version, }, }); }; diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useGetOneServerlessFunctionSourceCode.ts b/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useGetOneServerlessFunctionSourceCode.ts new file mode 100644 index 000000000000..b39f919039bd --- /dev/null +++ b/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useGetOneServerlessFunctionSourceCode.ts @@ -0,0 +1,30 @@ +import { useQuery } from '@apollo/client'; +import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient'; +import { FIND_ONE_SERVERLESS_FUNCTION_SOURCE_CODE } from '@/settings/serverless-functions/graphql/queries/findOneServerlessFunctionSourceCode'; +import { + FindOneServerlessFunctionSourceCodeQuery, + FindOneServerlessFunctionSourceCodeQueryVariables, +} from '~/generated-metadata/graphql'; + +export const useGetOneServerlessFunctionSourceCode = ({ + id, + version, + onCompleted, +}: { + id: string; + version: string; + onCompleted?: (data: FindOneServerlessFunctionSourceCodeQuery) => void; +}) => { + const apolloMetadataClient = useApolloMetadataClient(); + const { data, loading } = useQuery< + FindOneServerlessFunctionSourceCodeQuery, + FindOneServerlessFunctionSourceCodeQueryVariables + >(FIND_ONE_SERVERLESS_FUNCTION_SOURCE_CODE, { + client: apolloMetadataClient ?? undefined, + variables: { + input: { id, version }, + }, + onCompleted, + }); + return { code: data?.getServerlessFunctionSourceCode, loading }; +}; diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState.ts b/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState.ts index 42ae51bfd587..aa336b977162 100644 --- a/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState.ts +++ b/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState.ts @@ -1,7 +1,7 @@ -import { Dispatch, SetStateAction, useEffect, useState } from 'react'; -import { getFileAbsoluteURI } from '~/utils/file/getFileAbsoluteURI'; -import { isDefined } from '~/utils/isDefined'; +import { Dispatch, SetStateAction, useState } from 'react'; import { useGetOneServerlessFunction } from '@/settings/serverless-functions/hooks/useGetOneServerlessFunction'; +import { useGetOneServerlessFunctionSourceCode } from '@/settings/serverless-functions/hooks/useGetOneServerlessFunctionSourceCode'; +import { FindOneServerlessFunctionSourceCodeQuery } from '~/generated-metadata/graphql'; export type ServerlessFunctionNewFormValues = { name: string; @@ -19,7 +19,6 @@ type SetServerlessFunctionFormValues = Dispatch< export const useServerlessFunctionUpdateFormState = ( serverlessFunctionId: string, ): [ServerlessFunctionFormValues, SetServerlessFunctionFormValues] => { - const [isInitialized, setIsInitialized] = useState(false); const [formValues, setFormValues] = useState({ name: '', description: '', @@ -29,31 +28,21 @@ export const useServerlessFunctionUpdateFormState = ( const { serverlessFunction } = useGetOneServerlessFunction(serverlessFunctionId); - useEffect(() => { - const getFileContent = async () => { - const resp = await fetch( - getFileAbsoluteURI(serverlessFunction?.sourceCodeFullPath), - ); - if (resp.status !== 200) { - throw new Error('Network response was not ok'); - } else { - const result = await resp.text(); - const newState = { - code: result, - name: serverlessFunction?.name || '', - description: serverlessFunction?.description || '', - }; - setFormValues((prevState) => ({ - ...prevState, - ...newState, - })); - setIsInitialized(true); - } - }; - if (isDefined(serverlessFunction?.sourceCodeFullPath) && !isInitialized) { - getFileContent(); - } - }, [serverlessFunction, setFormValues, isInitialized, setIsInitialized]); + useGetOneServerlessFunctionSourceCode({ + id: serverlessFunctionId, + version: 'draft', + onCompleted: (data: FindOneServerlessFunctionSourceCodeQuery) => { + const newState = { + code: data?.getServerlessFunctionSourceCode || '', + name: serverlessFunction?.name || '', + description: serverlessFunction?.description || '', + }; + setFormValues((prevState) => ({ + ...prevState, + ...newState, + })); + }, + }); return [formValues, setFormValues]; }; diff --git a/packages/twenty-front/src/pages/settings/serverless-functions/SettingsServerlessFunctionDetail.tsx b/packages/twenty-front/src/pages/settings/serverless-functions/SettingsServerlessFunctionDetail.tsx index 600f3d6ee440..f3e3d510573e 100644 --- a/packages/twenty-front/src/pages/settings/serverless-functions/SettingsServerlessFunctionDetail.tsx +++ b/packages/twenty-front/src/pages/settings/serverless-functions/SettingsServerlessFunctionDetail.tsx @@ -75,6 +75,7 @@ export const SettingsServerlessFunctionDetail = () => { const result = await executeOneServerlessFunction( serverlessFunctionId, JSON.parse(settingsServerlessFunctionInput), + 'draft', ); setSettingsServerlessFunctionOutput({ data: result?.data?.executeOneServerlessFunction?.data diff --git a/packages/twenty-server/src/database/typeorm/metadata/migrations/1724171584314-updateServerlessFunction.ts b/packages/twenty-server/src/database/typeorm/metadata/migrations/1724171584314-updateServerlessFunction.ts new file mode 100644 index 000000000000..ee134cb07733 --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/metadata/migrations/1724171584314-updateServerlessFunction.ts @@ -0,0 +1,25 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateServerlessFunction1724171584314 + implements MigrationInterface +{ + name = 'UpdateServerlessFunction1724171584314'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "metadata"."serverlessFunction" DROP COLUMN "sourceCodeFullPath"`, + ); + await queryRunner.query( + `ALTER TABLE "metadata"."serverlessFunction" ADD "latestVersion" character varying`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "metadata"."serverlessFunction" DROP COLUMN "latestVersion"`, + ); + await queryRunner.query( + `ALTER TABLE "metadata"."serverlessFunction" ADD "sourceCodeFullPath" character varying NOT NULL`, + ); + } +} diff --git a/packages/twenty-server/src/engine/integrations/file-storage/drivers/interfaces/storage-driver.interface.ts b/packages/twenty-server/src/engine/integrations/file-storage/drivers/interfaces/storage-driver.interface.ts index 08c9f0708066..84cf20a21894 100644 --- a/packages/twenty-server/src/engine/integrations/file-storage/drivers/interfaces/storage-driver.interface.ts +++ b/packages/twenty-server/src/engine/integrations/file-storage/drivers/interfaces/storage-driver.interface.ts @@ -13,4 +13,8 @@ export interface StorageDriver { from: { folderPath: string; filename: string }; to: { folderPath: string; filename: string }; }): Promise; + copy(params: { + from: { folderPath: string; filename?: string }; + to: { folderPath: string; filename?: string }; + }): Promise; } diff --git a/packages/twenty-server/src/engine/integrations/file-storage/drivers/local.driver.ts b/packages/twenty-server/src/engine/integrations/file-storage/drivers/local.driver.ts index 3a2e46f93963..245b7af82951 100644 --- a/packages/twenty-server/src/engine/integrations/file-storage/drivers/local.driver.ts +++ b/packages/twenty-server/src/engine/integrations/file-storage/drivers/local.driver.ts @@ -70,6 +70,13 @@ export class LocalDriver implements StorageDriver { params.filename, ); + if (!existsSync(filePath)) { + throw new FileStorageException( + 'File not found', + FileStorageExceptionCode.FILE_NOT_FOUND, + ); + } + try { return createReadStream(filePath); } catch (error) { @@ -115,4 +122,34 @@ export class LocalDriver implements StorageDriver { throw error; } } + + async copy(params: { + from: { folderPath: string; filename?: string }; + to: { folderPath: string; filename?: string }; + }): Promise { + const fromPath = join( + `${this.options.storagePath}/`, + params.from.folderPath, + params.from.filename || '', + ); + + const toPath = join( + `${this.options.storagePath}/`, + params.to.folderPath, + params.from.filename || '', + ); + + try { + await fs.cp(fromPath, toPath, { recursive: true }); + } catch (error) { + if (error.code === 'ENOENT') { + throw new FileStorageException( + 'File not found', + FileStorageExceptionCode.FILE_NOT_FOUND, + ); + } + + throw error; + } + } } diff --git a/packages/twenty-server/src/engine/integrations/file-storage/drivers/s3.driver.ts b/packages/twenty-server/src/engine/integrations/file-storage/drivers/s3.driver.ts index 7ced7608d9a8..d882db903b6e 100644 --- a/packages/twenty-server/src/engine/integrations/file-storage/drivers/s3.driver.ts +++ b/packages/twenty-server/src/engine/integrations/file-storage/drivers/s3.driver.ts @@ -187,6 +187,42 @@ export class S3Driver implements StorageDriver { } } + async copy(params: { + from: { folderPath: string; filename?: string }; + to: { folderPath: string; filename?: string }; + }): Promise { + const fromKey = `${params.from.folderPath}/${params.from.filename || ''}`; + const toKey = `${params.to.folderPath}/${params.to.filename || ''}`; + + try { + // Check if the source file exists + await this.s3Client.send( + new HeadObjectCommand({ + Bucket: this.bucketName, + Key: fromKey, + }), + ); + + // Copy the object to the new location + await this.s3Client.send( + new CopyObjectCommand({ + CopySource: `${this.bucketName}/${fromKey}`, + Bucket: this.bucketName, + Key: toKey, + }), + ); + } catch (error) { + if (error.name === 'NotFound') { + throw new FileStorageException( + 'File not found', + FileStorageExceptionCode.FILE_NOT_FOUND, + ); + } + // For other errors, throw the original error + throw error; + } + } + async checkBucketExists(args: HeadBucketCommandInput) { try { await this.s3Client.headBucket(args); diff --git a/packages/twenty-server/src/engine/integrations/file-storage/file-storage.service.ts b/packages/twenty-server/src/engine/integrations/file-storage/file-storage.service.ts index 65133f991f31..a6eeb6c2041c 100644 --- a/packages/twenty-server/src/engine/integrations/file-storage/file-storage.service.ts +++ b/packages/twenty-server/src/engine/integrations/file-storage/file-storage.service.ts @@ -33,4 +33,11 @@ export class FileStorageService implements StorageDriver { }): Promise { return this.driver.move(params); } + + copy(params: { + from: { folderPath: string; filename?: string }; + to: { folderPath: string; filename?: string }; + }): Promise { + return this.driver.copy(params); + } } diff --git a/packages/twenty-server/src/engine/integrations/serverless/drivers/base-serverless.driver.ts b/packages/twenty-server/src/engine/integrations/serverless/drivers/base-serverless.driver.ts index e26666dc5315..d66a6c948696 100644 --- a/packages/twenty-server/src/engine/integrations/serverless/drivers/base-serverless.driver.ts +++ b/packages/twenty-server/src/engine/integrations/serverless/drivers/base-serverless.driver.ts @@ -9,19 +9,24 @@ import { compileTypescript } from 'src/engine/integrations/serverless/drivers/ut import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity'; export class BaseServerlessDriver { - getFolderPath(serverlessFunction: ServerlessFunctionEntity) { + getFolderPath( + serverlessFunction: ServerlessFunctionEntity, + version = 'draft', + ) { return join( 'workspace-' + serverlessFunction.workspaceId, FileFolder.ServerlessFunction, serverlessFunction.id, + `${version}`, ); } async getCompiledCode( serverlessFunction: ServerlessFunctionEntity, fileStorageService: FileStorageService, + version='draft', ) { - const folderPath = this.getFolderPath(serverlessFunction); + const folderPath = this.getFolderPath(serverlessFunction, version); const fileStream = await fileStorageService.read({ folderPath, filename: SOURCE_FILE_NAME, diff --git a/packages/twenty-server/src/engine/integrations/serverless/drivers/interfaces/serverless-driver.interface.ts b/packages/twenty-server/src/engine/integrations/serverless/drivers/interfaces/serverless-driver.interface.ts index 21f87ef926d4..e9ac575f6085 100644 --- a/packages/twenty-server/src/engine/integrations/serverless/drivers/interfaces/serverless-driver.interface.ts +++ b/packages/twenty-server/src/engine/integrations/serverless/drivers/interfaces/serverless-driver.interface.ts @@ -16,9 +16,16 @@ export type ServerlessExecuteResult = { export interface ServerlessDriver { delete(serverlessFunction: ServerlessFunctionEntity): Promise; - build(serverlessFunction: ServerlessFunctionEntity): Promise; + build( + serverlessFunction: ServerlessFunctionEntity, + version: string, + ): Promise; + publish( + serverlessFunction: ServerlessFunctionEntity, + ): Promise<{ newVersion: string }>; execute( serverlessFunction: ServerlessFunctionEntity, payload: object | undefined, + version: string, ): Promise; } diff --git a/packages/twenty-server/src/engine/integrations/serverless/drivers/lambda.driver.ts b/packages/twenty-server/src/engine/integrations/serverless/drivers/lambda.driver.ts index 07ab8590429b..1e043a864cfc 100644 --- a/packages/twenty-server/src/engine/integrations/serverless/drivers/lambda.driver.ts +++ b/packages/twenty-server/src/engine/integrations/serverless/drivers/lambda.driver.ts @@ -10,6 +10,8 @@ import { DeleteFunctionCommand, ResourceNotFoundException, waitUntilFunctionUpdatedV2, + PublishVersionCommandInput, + PublishVersionCommand, } from '@aws-sdk/client-lambda'; import { CreateFunctionCommandInput } from '@aws-sdk/client-lambda/dist-types/commands/CreateFunctionCommand'; import { UpdateFunctionCodeCommandInput } from '@aws-sdk/client-lambda/dist-types/commands/UpdateFunctionCodeCommand'; @@ -25,6 +27,10 @@ import { FileStorageService } from 'src/engine/integrations/file-storage/file-st import { BaseServerlessDriver } from 'src/engine/integrations/serverless/drivers/base-serverless.driver'; import { BuildDirectoryManagerService } from 'src/engine/integrations/serverless/drivers/services/build-directory-manager.service'; import { ServerlessFunctionExecutionStatus } from 'src/engine/metadata-modules/serverless-function/dtos/serverless-function-execution-result.dto'; +import { + ServerlessFunctionException, + ServerlessFunctionExceptionCode, +} from 'src/engine/metadata-modules/serverless-function/serverless-function.exception'; export interface LambdaDriverOptions extends LambdaClientConfig { fileStorageService: FileStorageService; @@ -52,12 +58,10 @@ export class LambdaDriver this.buildDirectoryManagerService = options.buildDirectoryManagerService; } - private async checkFunctionExists( - serverlessFunctionId: string, - ): Promise { + private async checkFunctionExists(functionName: string): Promise { try { const getFunctionCommand = new GetFunctionCommand({ - FunctionName: serverlessFunctionId, + FunctionName: functionName, }); await this.lambdaClient.send(getFunctionCommand); @@ -131,50 +135,97 @@ export class LambdaDriver const command = new UpdateFunctionCodeCommand(params); await this.lambdaClient.send(command); - const waitParams = { FunctionName: serverlessFunction.id }; - - await waitUntilFunctionUpdatedV2( - { client: this.lambdaClient, maxWaitTime: 2 }, - waitParams, - ); } + const waitParams = { FunctionName: serverlessFunction.id }; + + await waitUntilFunctionUpdatedV2( + { client: this.lambdaClient, maxWaitTime: 5 }, + waitParams, + ); + await this.buildDirectoryManagerService.clean(); } + async publish(serverlessFunction: ServerlessFunctionEntity) { + await this.build(serverlessFunction); + const params: PublishVersionCommandInput = { + FunctionName: serverlessFunction.id, + }; + + const command = new PublishVersionCommand(params); + + const result = await this.lambdaClient.send(command); + const newVersion = result.Version; + + if (!newVersion) { + throw new Error('New published version is undefined'); + } + + const draftFolderPath = this.getFolderPath(serverlessFunction); + const newFolderPath = this.getFolderPath(serverlessFunction, newVersion); + + await this.fileStorageService.copy({ + from: { folderPath: draftFolderPath }, + to: { folderPath: newFolderPath }, + }); + + return { + newVersion, + }; + } + async execute( functionToExecute: ServerlessFunctionEntity, payload: object | undefined = undefined, + version: string, ): Promise { + const computedVersion = + version === 'latest' ? functionToExecute.latestVersion : version; + + const functionName = + computedVersion === 'draft' + ? functionToExecute.id + : `${functionToExecute.id}:${computedVersion}`; const startTime = Date.now(); const params = { - FunctionName: functionToExecute.id, + FunctionName: functionName, Payload: JSON.stringify(payload), }; const command = new InvokeCommand(params); - const result = await this.lambdaClient.send(command); + try { + const result = await this.lambdaClient.send(command); - const parsedResult = result.Payload - ? JSON.parse(result.Payload.transformToString()) - : {}; + const parsedResult = result.Payload + ? JSON.parse(result.Payload.transformToString()) + : {}; - const duration = Date.now() - startTime; + const duration = Date.now() - startTime; + + if (result.FunctionError) { + return { + data: null, + duration, + status: ServerlessFunctionExecutionStatus.ERROR, + error: parsedResult, + }; + } - if (result.FunctionError) { return { - data: null, + data: parsedResult, duration, - status: ServerlessFunctionExecutionStatus.ERROR, - error: parsedResult, + status: ServerlessFunctionExecutionStatus.SUCCESS, }; + } catch (error) { + if (error instanceof ResourceNotFoundException) { + throw new ServerlessFunctionException( + `Function Version '${version}' does not exist`, + ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_NOT_FOUND, + ); + } + throw error; } - - return { - data: parsedResult, - duration, - status: ServerlessFunctionExecutionStatus.SUCCESS, - }; } } diff --git a/packages/twenty-server/src/engine/integrations/serverless/drivers/local.driver.ts b/packages/twenty-server/src/engine/integrations/serverless/drivers/local.driver.ts index c811426b3cd8..9aa225999705 100644 --- a/packages/twenty-server/src/engine/integrations/serverless/drivers/local.driver.ts +++ b/packages/twenty-server/src/engine/integrations/serverless/drivers/local.driver.ts @@ -10,6 +10,7 @@ import { ServerlessExecuteError, ServerlessExecuteResult, } from 'src/engine/integrations/serverless/drivers/interfaces/serverless-driver.interface'; +import { FileStorageExceptionCode } from 'src/engine/integrations/file-storage/interfaces/file-storage-exception'; import { FileStorageService } from 'src/engine/integrations/file-storage/file-storage.service'; import { readFileContent } from 'src/engine/integrations/file-storage/utils/read-file-content'; @@ -17,6 +18,10 @@ import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless import { BUILD_FILE_NAME } from 'src/engine/integrations/serverless/drivers/constants/build-file-name'; import { BaseServerlessDriver } from 'src/engine/integrations/serverless/drivers/base-serverless.driver'; import { ServerlessFunctionExecutionStatus } from 'src/engine/metadata-modules/serverless-function/dtos/serverless-function-execution-result.dto'; +import { + ServerlessFunctionException, + ServerlessFunctionExceptionCode, +} from 'src/engine/metadata-modules/serverless-function/serverless-function.exception'; export interface LocalDriverOptions { fileStorageService: FileStorageService; @@ -53,20 +58,43 @@ export class LocalDriver }); } + async publish(serverlessFunction: ServerlessFunctionEntity) { + await this.build(serverlessFunction); + + const newVersion = serverlessFunction.latestVersion + ? `${parseInt(serverlessFunction.latestVersion, 10) + 1}` + : '1'; + + const draftFolderPath = this.getFolderPath(serverlessFunction); + const newFolderPath = this.getFolderPath(serverlessFunction, newVersion); + + await this.fileStorageService.copy({ + from: { folderPath: draftFolderPath }, + to: { folderPath: newFolderPath }, + }); + + return { + newVersion, + }; + } + async execute( serverlessFunction: ServerlessFunctionEntity, payload: object | undefined = undefined, + version: string, ): Promise { - const startTime = Date.now(); - const fileStream = await this.fileStorageService.read({ - folderPath: this.getFolderPath(serverlessFunction), - filename: BUILD_FILE_NAME, - }); - const fileContent = await readFileContent(fileStream); + try { + const startTime = Date.now(); + const fileStream = await this.fileStorageService.read({ + folderPath: this.getFolderPath(serverlessFunction, version), + filename: BUILD_FILE_NAME, + }); + + const fileContent = await readFileContent(fileStream); - const tmpFilePath = join(tmpdir(), `${v4()}.js`); + const tmpFilePath = join(tmpdir(), `${v4()}.js`); - const modifiedContent = ` + const modifiedContent = ` process.on('message', async (message) => { const { event, context } = message; try { @@ -84,78 +112,87 @@ export class LocalDriver ${fileContent} `; - await fs.writeFile(tmpFilePath, modifiedContent); + await fs.writeFile(tmpFilePath, modifiedContent); + + return await new Promise((resolve, reject) => { + const child = fork(tmpFilePath, { silent: true }); + + child.on('message', (message: object | ServerlessExecuteError) => { + const duration = Date.now() - startTime; + + if ('errorType' in message) { + resolve({ + data: null, + duration, + error: message, + status: ServerlessFunctionExecutionStatus.ERROR, + }); + } else { + resolve({ + data: message, + duration, + status: ServerlessFunctionExecutionStatus.SUCCESS, + }); + } + child.kill(); + fs.unlink(tmpFilePath); + }); + + child.stderr?.on('data', (data) => { + const stackTrace = data + .toString() + .split('\n') + .filter((line: string) => line.trim() !== ''); + const errorTrace = stackTrace.filter((line: string) => + line.includes('Error: '), + )?.[0]; - return await new Promise((resolve, reject) => { - const child = fork(tmpFilePath, { silent: true }); + let errorType = 'Unknown'; + let errorMessage = ''; - child.on('message', (message: object | ServerlessExecuteError) => { - const duration = Date.now() - startTime; + if (errorTrace) { + errorType = errorTrace.split(':')[0]; + errorMessage = errorTrace.split(': ')[1]; + } + const duration = Date.now() - startTime; - if ('errorType' in message) { resolve({ data: null, duration, - error: message, status: ServerlessFunctionExecutionStatus.ERROR, + error: { + errorType, + errorMessage, + stackTrace: stackTrace, + }, }); - } else { - resolve({ - data: message, - duration, - status: ServerlessFunctionExecutionStatus.SUCCESS, - }); - } - child.kill(); - fs.unlink(tmpFilePath); - }); + child.kill(); + fs.unlink(tmpFilePath); + }); - child.stderr?.on('data', (data) => { - const stackTrace = data - .toString() - .split('\n') - .filter((line: string) => line.trim() !== ''); - const errorTrace = stackTrace.filter((line: string) => - line.includes('Error: '), - )?.[0]; - - let errorType = 'Unknown'; - let errorMessage = ''; - - if (errorTrace) { - errorType = errorTrace.split(':')[0]; - errorMessage = errorTrace.split(': ')[1]; - } - const duration = Date.now() - startTime; - - resolve({ - data: null, - duration, - status: ServerlessFunctionExecutionStatus.ERROR, - error: { - errorType, - errorMessage, - stackTrace: stackTrace, - }, + child.on('error', (error) => { + reject(error); + child.kill(); + fs.unlink(tmpFilePath); }); - child.kill(); - fs.unlink(tmpFilePath); - }); - child.on('error', (error) => { - reject(error); - child.kill(); - fs.unlink(tmpFilePath); - }); + child.on('exit', (code) => { + if (code && code !== 0) { + reject(new Error(`Child process exited with code ${code}`)); + fs.unlink(tmpFilePath); + } + }); - child.on('exit', (code) => { - if (code && code !== 0) { - reject(new Error(`Child process exited with code ${code}`)); - fs.unlink(tmpFilePath); - } + child.send({ event: payload }); }); - - child.send({ event: payload }); - }); + } catch (error) { + if (error.code === FileStorageExceptionCode.FILE_NOT_FOUND) { + throw new ServerlessFunctionException( + `Function Version '${version}' does not exist`, + ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_NOT_FOUND, + ); + } + throw error; + } } } diff --git a/packages/twenty-server/src/engine/integrations/serverless/serverless.service.ts b/packages/twenty-server/src/engine/integrations/serverless/serverless.service.ts index 4ca596245752..5ec8fe1bc3a5 100644 --- a/packages/twenty-server/src/engine/integrations/serverless/serverless.service.ts +++ b/packages/twenty-server/src/engine/integrations/serverless/serverless.service.ts @@ -16,14 +16,24 @@ export class ServerlessService implements ServerlessDriver { return this.driver.delete(serverlessFunction); } - async build(serverlessFunction: ServerlessFunctionEntity): Promise { - return this.driver.build(serverlessFunction); + async build( + serverlessFunction: ServerlessFunctionEntity, + version: string, + ): Promise { + return this.driver.build(serverlessFunction, version); + } + + async publish( + serverlessFunction: ServerlessFunctionEntity, + ): Promise<{ newVersion: string }> { + return this.driver.publish(serverlessFunction); } async execute( serverlessFunction: ServerlessFunctionEntity, payload: object | undefined = undefined, + version: string, ): Promise { - return this.driver.execute(serverlessFunction, payload); + return this.driver.execute(serverlessFunction, payload, version); } } diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/execute-serverless-function.input.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/execute-serverless-function.input.ts index 08071b04f5e3..94052ff28a9b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/execute-serverless-function.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/execute-serverless-function.input.ts @@ -21,4 +21,11 @@ export class ExecuteServerlessFunctionInput { @IsObject() @IsOptional() payload?: JSON; + + @Field(() => String, { + nullable: false, + description: 'Version of the serverless function to execute', + defaultValue: 'latest', + }) + version: string; } diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/get-serverless-function-source-code.input.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/get-serverless-function-source-code.input.ts new file mode 100644 index 000000000000..99c6fc242d80 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/get-serverless-function-source-code.input.ts @@ -0,0 +1,17 @@ +import { Field, ID, InputType } from '@nestjs/graphql'; + +import { IDField } from '@ptc-org/nestjs-query-graphql'; + + +@InputType() +export class GetServerlessFunctionSourceCodeInput { + @IDField(() => ID, { description: 'The id of the function.' }) + id!: string; + + @Field(() => String, { + nullable: false, + description: 'The version of the function', + defaultValue: 'draft', + }) + version: string; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/publish-serverless-function.input.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/publish-serverless-function.input.ts new file mode 100644 index 000000000000..4a2c484d3735 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/publish-serverless-function.input.ts @@ -0,0 +1,15 @@ +import { ArgsType, Field } from '@nestjs/graphql'; + +import { IsNotEmpty, IsUUID } from 'class-validator'; + +import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; + +@ArgsType() +export class PublishServerlessFunctionInput { + @Field(() => UUIDScalarType, { + description: 'Id of the serverless function to publish', + }) + @IsNotEmpty() + @IsUUID() + id: string; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto.ts index 3d348fdb336d..b911e50f447f 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto.ts @@ -13,9 +13,9 @@ import { import { IsDateString, IsEnum, - IsNotEmpty, + IsNotEmpty, IsNumber, IsOptional, IsString, - IsUUID, + IsUUID } from 'class-validator'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; @@ -56,16 +56,15 @@ export class ServerlessFunctionDTO { @Field() sourceCodeHash: string; - @IsString() - @IsNotEmpty() - @Field() - sourceCodeFullPath: string; - @IsString() @IsNotEmpty() @Field() runtime: string; + @IsNumber() + @Field({ nullable: true }) + latestVersion: string; + @IsEnum(ServerlessFunctionSyncStatus) @IsNotEmpty() @Field(() => ServerlessFunctionSyncStatus) diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.entity.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.entity.ts index a5893abe2aee..3e985e5bd95f 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.entity.ts @@ -28,11 +28,11 @@ export class ServerlessFunctionEntity { @Column({ nullable: true }) description: string; - @Column({ nullable: false }) - sourceCodeHash: string; + @Column({ nullable: true }) + latestVersion: string; @Column({ nullable: false }) - sourceCodeFullPath: string; + sourceCodeHash: string; @Column({ nullable: false, default: ServerlessFunctionRuntime.NODE18 }) runtime: ServerlessFunctionRuntime; diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.exception.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.exception.ts index 6f0430431a3a..e76f76cc7dda 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.exception.ts @@ -9,6 +9,7 @@ export class ServerlessFunctionException extends CustomException { export enum ServerlessFunctionExceptionCode { SERVERLESS_FUNCTION_NOT_FOUND = 'SERVERLESS_FUNCTION_NOT_FOUND', + SERVERLESS_FUNCTION_VERSION_NOT_FOUND = 'SERVERLESS_FUNCTION_VERSION_NOT_FOUND', FEATURE_FLAG_INVALID = 'FEATURE_FLAG_INVALID', SERVERLESS_FUNCTION_ALREADY_EXIST = 'SERVERLESS_FUNCTION_ALREADY_EXIST', SERVERLESS_FUNCTION_NOT_READY = 'SERVERLESS_FUNCTION_NOT_READY', diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.resolver.ts index b49956344210..5d55d3abc587 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.resolver.ts @@ -1,5 +1,5 @@ import { UseGuards, UseInterceptors } from '@nestjs/common'; -import { Args, Mutation, Resolver } from '@nestjs/graphql'; +import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; import { InjectRepository } from '@nestjs/typeorm'; import { FileUpload, GraphQLUpload } from 'graphql-upload'; @@ -24,6 +24,8 @@ import { import { ServerlessFunctionInterceptor } from 'src/engine/metadata-modules/serverless-function/serverless-function.interceptor'; import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service'; import { serverlessFunctionGraphQLApiExceptionHandler } from 'src/engine/metadata-modules/serverless-function/utils/serverless-function-graphql-api-exception-handler.utils'; +import { GetServerlessFunctionSourceCodeInput } from 'src/engine/metadata-modules/serverless-function/dtos/get-serverless-function-source-code.input'; +import { PublishServerlessFunctionInput } from 'src/engine/metadata-modules/serverless-function/dtos/publish-serverless-function.input'; @UseGuards(JwtAuthGuard) @Resolver() @@ -50,6 +52,24 @@ export class ServerlessFunctionResolver { } } + @Query(() => String) + async getServerlessFunctionSourceCode( + @Args('input') input: GetServerlessFunctionSourceCodeInput, + @AuthWorkspace() { id: workspaceId }: Workspace, + ) { + try { + await this.checkFeatureFlag(workspaceId); + + return await this.serverlessFunctionService.getServerlessFunctionSourceCode( + workspaceId, + input.id, + input.version, + ); + } catch (error) { + serverlessFunctionGraphQLApiExceptionHandler(error); + } + } + @Mutation(() => ServerlessFunctionDTO) async deleteOneServerlessFunction( @Args('input') input: DeleteServerlessFunctionInput, @@ -138,12 +158,31 @@ export class ServerlessFunctionResolver { ) { try { await this.checkFeatureFlag(workspaceId); - const { id, payload } = executeServerlessFunctionInput; + const { id, payload, version } = executeServerlessFunctionInput; - return await this.serverlessFunctionService.executeOne( + return await this.serverlessFunctionService.executeOneServerlessFunction( id, workspaceId, payload, + version, + ); + } catch (error) { + serverlessFunctionGraphQLApiExceptionHandler(error); + } + } + + @Mutation(() => ServerlessFunctionDTO) + async publishServerlessFunction( + @Args() publishServerlessFunctionInput: PublishServerlessFunctionInput, + @AuthWorkspace() { id: workspaceId }: Workspace, + ) { + try { + await this.checkFeatureFlag(workspaceId); + const { id } = publishServerlessFunctionInput; + + return await this.serverlessFunctionService.publishOneServerlessFunction( + id, + workspaceId, ); } catch (error) { serverlessFunctionGraphQLApiExceptionHandler(error); diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts index 7d6330e375e6..fc7e16c233e0 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts @@ -6,7 +6,6 @@ import { join } from 'path'; import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm'; import { FileUpload } from 'graphql-upload'; import { Repository } from 'typeorm'; -import { v4 } from 'uuid'; import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface'; import { ServerlessExecuteResult } from 'src/engine/integrations/serverless/drivers/interfaces/serverless-driver.interface'; @@ -26,6 +25,7 @@ import { ServerlessFunctionExceptionCode, } from 'src/engine/metadata-modules/serverless-function/serverless-function.exception'; import { serverlessFunctionCreateHash } from 'src/engine/metadata-modules/serverless-function/utils/serverless-function-create-hash.utils'; +import { isDefined } from 'src/utils/is-defined'; @Injectable() export class ServerlessFunctionService extends TypeOrmQueryService { @@ -38,10 +38,64 @@ export class ServerlessFunctionService extends TypeOrmQueryService { const functionToExecute = await this.serverlessFunctionRepository.findOne({ where: { @@ -60,13 +114,59 @@ export class ServerlessFunctionService extends TypeOrmQueryService { if (error instanceof ServerlessFunctionException) { switch (error.code) { case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_NOT_FOUND: + case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_VERSION_NOT_FOUND: throw new NotFoundError(error.message); case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_ALREADY_EXIST: throw new ConflictError(error.message); diff --git a/packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runners/code-workflow-action-runner.ts b/packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runners/code-workflow-action-runner.ts index 2afedcb21402..37f4b0416d3b 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runners/code-workflow-action-runner.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runners/code-workflow-action-runner.ts @@ -33,7 +33,7 @@ export class CodeWorkflowActionRunner implements WorkflowActionRunner { ); } - const result = await this.serverlessFunctionService.executeOne( + const result = await this.serverlessFunctionService.executeOneServerlessFunction( action.settings.serverlessFunctionId, workspaceId, payload, From d189ac1597fff2af0af415f1dee91a8f0b8256cf Mon Sep 17 00:00:00 2001 From: martmull Date: Thu, 22 Aug 2024 15:17:09 +0200 Subject: [PATCH 06/21] Fix lint --- .../serverless/drivers/base-serverless.driver.ts | 2 +- .../dtos/get-serverless-function-source-code.input.ts | 1 - .../dtos/serverless-function.dto.ts | 5 +++-- .../code-workflow-action-executor.ts | 11 ++++++----- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/twenty-server/src/engine/integrations/serverless/drivers/base-serverless.driver.ts b/packages/twenty-server/src/engine/integrations/serverless/drivers/base-serverless.driver.ts index d66a6c948696..770dbfca856a 100644 --- a/packages/twenty-server/src/engine/integrations/serverless/drivers/base-serverless.driver.ts +++ b/packages/twenty-server/src/engine/integrations/serverless/drivers/base-serverless.driver.ts @@ -24,7 +24,7 @@ export class BaseServerlessDriver { async getCompiledCode( serverlessFunction: ServerlessFunctionEntity, fileStorageService: FileStorageService, - version='draft', + version = 'draft', ) { const folderPath = this.getFolderPath(serverlessFunction, version); const fileStream = await fileStorageService.read({ diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/get-serverless-function-source-code.input.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/get-serverless-function-source-code.input.ts index 99c6fc242d80..47b552fd1fd4 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/get-serverless-function-source-code.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/get-serverless-function-source-code.input.ts @@ -2,7 +2,6 @@ import { Field, ID, InputType } from '@nestjs/graphql'; import { IDField } from '@ptc-org/nestjs-query-graphql'; - @InputType() export class GetServerlessFunctionSourceCodeInput { @IDField(() => ID, { description: 'The id of the function.' }) diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto.ts index b911e50f447f..de4255f4fc74 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto.ts @@ -13,9 +13,10 @@ import { import { IsDateString, IsEnum, - IsNotEmpty, IsNumber, IsOptional, + IsNotEmpty, + IsNumber, IsString, - IsUUID + IsUUID, } from 'class-validator'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; diff --git a/packages/twenty-server/src/modules/workflow/workflow-action-executor/workflow-action-executors/code-workflow-action-executor.ts b/packages/twenty-server/src/modules/workflow/workflow-action-executor/workflow-action-executors/code-workflow-action-executor.ts index 10cf6001d594..73d69c025a24 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-action-executor/workflow-action-executors/code-workflow-action-executor.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-action-executor/workflow-action-executors/code-workflow-action-executor.ts @@ -33,11 +33,12 @@ export class CodeWorkflowActionExecutor implements WorkflowActionExecutor { ); } - const result = await this.serverlessFunctionService.executeOneServerlessFunction( - action.settings.serverlessFunctionId, - workspaceId, - payload, - ); + const result = + await this.serverlessFunctionService.executeOneServerlessFunction( + action.settings.serverlessFunctionId, + workspaceId, + payload, + ); return { data: result.data, ...(result.error && { error: result.error }) }; } From b3cebae88b48624aaf0e0dc4b36aba26550e1995 Mon Sep 17 00:00:00 2001 From: martmull Date: Thu, 22 Aug 2024 16:28:39 +0200 Subject: [PATCH 07/21] Improvements --- .../interfaces/serverless-driver.interface.ts | 4 +-- .../serverless/drivers/lambda.driver.ts | 12 +------ .../serverless/drivers/local.driver.ts | 20 ++--------- .../serverless/serverless.service.ts | 4 +-- .../serverless-function.service.ts | 33 ++++++++++++++----- 5 files changed, 29 insertions(+), 44 deletions(-) diff --git a/packages/twenty-server/src/engine/integrations/serverless/drivers/interfaces/serverless-driver.interface.ts b/packages/twenty-server/src/engine/integrations/serverless/drivers/interfaces/serverless-driver.interface.ts index e9ac575f6085..549289707fb9 100644 --- a/packages/twenty-server/src/engine/integrations/serverless/drivers/interfaces/serverless-driver.interface.ts +++ b/packages/twenty-server/src/engine/integrations/serverless/drivers/interfaces/serverless-driver.interface.ts @@ -20,9 +20,7 @@ export interface ServerlessDriver { serverlessFunction: ServerlessFunctionEntity, version: string, ): Promise; - publish( - serverlessFunction: ServerlessFunctionEntity, - ): Promise<{ newVersion: string }>; + publish(serverlessFunction: ServerlessFunctionEntity): Promise; execute( serverlessFunction: ServerlessFunctionEntity, payload: object | undefined, diff --git a/packages/twenty-server/src/engine/integrations/serverless/drivers/lambda.driver.ts b/packages/twenty-server/src/engine/integrations/serverless/drivers/lambda.driver.ts index 1e043a864cfc..719b4934b155 100644 --- a/packages/twenty-server/src/engine/integrations/serverless/drivers/lambda.driver.ts +++ b/packages/twenty-server/src/engine/integrations/serverless/drivers/lambda.driver.ts @@ -162,17 +162,7 @@ export class LambdaDriver throw new Error('New published version is undefined'); } - const draftFolderPath = this.getFolderPath(serverlessFunction); - const newFolderPath = this.getFolderPath(serverlessFunction, newVersion); - - await this.fileStorageService.copy({ - from: { folderPath: draftFolderPath }, - to: { folderPath: newFolderPath }, - }); - - return { - newVersion, - }; + return newVersion; } async execute( diff --git a/packages/twenty-server/src/engine/integrations/serverless/drivers/local.driver.ts b/packages/twenty-server/src/engine/integrations/serverless/drivers/local.driver.ts index 9aa225999705..cd5b617f2e19 100644 --- a/packages/twenty-server/src/engine/integrations/serverless/drivers/local.driver.ts +++ b/packages/twenty-server/src/engine/integrations/serverless/drivers/local.driver.ts @@ -38,11 +38,7 @@ export class LocalDriver this.fileStorageService = options.fileStorageService; } - async delete(serverlessFunction: ServerlessFunctionEntity) { - await this.fileStorageService.delete({ - folderPath: this.getFolderPath(serverlessFunction), - }); - } + async delete() {} async build(serverlessFunction: ServerlessFunctionEntity) { const javascriptCode = await this.getCompiledCode( @@ -61,21 +57,9 @@ export class LocalDriver async publish(serverlessFunction: ServerlessFunctionEntity) { await this.build(serverlessFunction); - const newVersion = serverlessFunction.latestVersion + return serverlessFunction.latestVersion ? `${parseInt(serverlessFunction.latestVersion, 10) + 1}` : '1'; - - const draftFolderPath = this.getFolderPath(serverlessFunction); - const newFolderPath = this.getFolderPath(serverlessFunction, newVersion); - - await this.fileStorageService.copy({ - from: { folderPath: draftFolderPath }, - to: { folderPath: newFolderPath }, - }); - - return { - newVersion, - }; } async execute( diff --git a/packages/twenty-server/src/engine/integrations/serverless/serverless.service.ts b/packages/twenty-server/src/engine/integrations/serverless/serverless.service.ts index 5ec8fe1bc3a5..747feae9a1c8 100644 --- a/packages/twenty-server/src/engine/integrations/serverless/serverless.service.ts +++ b/packages/twenty-server/src/engine/integrations/serverless/serverless.service.ts @@ -23,9 +23,7 @@ export class ServerlessService implements ServerlessDriver { return this.driver.build(serverlessFunction, version); } - async publish( - serverlessFunction: ServerlessFunctionEntity, - ): Promise<{ newVersion: string }> { + async publish(serverlessFunction: ServerlessFunctionEntity): Promise { return this.driver.publish(serverlessFunction); } diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts index fc7e16c233e0..d9fccfe41d6c 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts @@ -40,21 +40,19 @@ export class ServerlessFunctionService extends TypeOrmQueryService Date: Thu, 22 Aug 2024 16:50:43 +0200 Subject: [PATCH 08/21] Factorize --- .../drivers/base-serverless.driver.ts | 23 +++---------- .../serverless/drivers/local.driver.ts | 11 ++++-- .../utils/serverless-get-folder.utils.ts | 23 +++++++++++++ .../serverless-function.service.ts | 34 ++++--------------- 4 files changed, 44 insertions(+), 47 deletions(-) create mode 100644 packages/twenty-server/src/engine/integrations/serverless/utils/serverless-get-folder.utils.ts diff --git a/packages/twenty-server/src/engine/integrations/serverless/drivers/base-serverless.driver.ts b/packages/twenty-server/src/engine/integrations/serverless/drivers/base-serverless.driver.ts index 770dbfca856a..bce3c8e42d13 100644 --- a/packages/twenty-server/src/engine/integrations/serverless/drivers/base-serverless.driver.ts +++ b/packages/twenty-server/src/engine/integrations/serverless/drivers/base-serverless.driver.ts @@ -1,32 +1,19 @@ -import { join } from 'path'; - -import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface'; - import { FileStorageService } from 'src/engine/integrations/file-storage/file-storage.service'; import { readFileContent } from 'src/engine/integrations/file-storage/utils/read-file-content'; import { SOURCE_FILE_NAME } from 'src/engine/integrations/serverless/drivers/constants/source-file-name'; import { compileTypescript } from 'src/engine/integrations/serverless/drivers/utils/compile-typescript'; import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity'; +import { getServerlessFolder } from 'src/engine/integrations/serverless/utils/serverless-get-folder.utils'; export class BaseServerlessDriver { - getFolderPath( - serverlessFunction: ServerlessFunctionEntity, - version = 'draft', - ) { - return join( - 'workspace-' + serverlessFunction.workspaceId, - FileFolder.ServerlessFunction, - serverlessFunction.id, - `${version}`, - ); - } - async getCompiledCode( serverlessFunction: ServerlessFunctionEntity, fileStorageService: FileStorageService, - version = 'draft', ) { - const folderPath = this.getFolderPath(serverlessFunction, version); + const folderPath = getServerlessFolder({ + serverlessFunction, + version: 'draft', + }); const fileStream = await fileStorageService.read({ folderPath, filename: SOURCE_FILE_NAME, diff --git a/packages/twenty-server/src/engine/integrations/serverless/drivers/local.driver.ts b/packages/twenty-server/src/engine/integrations/serverless/drivers/local.driver.ts index cd5b617f2e19..6c94dd468962 100644 --- a/packages/twenty-server/src/engine/integrations/serverless/drivers/local.driver.ts +++ b/packages/twenty-server/src/engine/integrations/serverless/drivers/local.driver.ts @@ -22,6 +22,7 @@ import { ServerlessFunctionException, ServerlessFunctionExceptionCode, } from 'src/engine/metadata-modules/serverless-function/serverless-function.exception'; +import { getServerlessFolder } from 'src/engine/integrations/serverless/utils/serverless-get-folder.utils'; export interface LocalDriverOptions { fileStorageService: FileStorageService; @@ -50,7 +51,10 @@ export class LocalDriver file: javascriptCode, name: BUILD_FILE_NAME, mimeType: undefined, - folder: this.getFolderPath(serverlessFunction), + folder: getServerlessFolder({ + serverlessFunction, + version: 'draft', + }), }); } @@ -70,7 +74,10 @@ export class LocalDriver try { const startTime = Date.now(); const fileStream = await this.fileStorageService.read({ - folderPath: this.getFolderPath(serverlessFunction, version), + folderPath: getServerlessFolder({ + serverlessFunction, + version, + }), filename: BUILD_FILE_NAME, }); diff --git a/packages/twenty-server/src/engine/integrations/serverless/utils/serverless-get-folder.utils.ts b/packages/twenty-server/src/engine/integrations/serverless/utils/serverless-get-folder.utils.ts new file mode 100644 index 000000000000..8dc4a97bf70f --- /dev/null +++ b/packages/twenty-server/src/engine/integrations/serverless/utils/serverless-get-folder.utils.ts @@ -0,0 +1,23 @@ +import { join } from 'path'; + +import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface'; + +import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity'; + +export const getServerlessFolder = ({ + serverlessFunction, + version, +}: { + serverlessFunction: ServerlessFunctionEntity; + version?: string; +}) => { + const computedVersion = + version === 'latest' ? serverlessFunction.latestVersion : version; + + return join( + 'workspace-' + serverlessFunction.workspaceId, + FileFolder.ServerlessFunction, + serverlessFunction.id, + computedVersion || '', + ); +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts index d9fccfe41d6c..dea977b338f8 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts @@ -1,13 +1,10 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { join } from 'path'; - import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm'; import { FileUpload } from 'graphql-upload'; import { Repository } from 'typeorm'; -import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface'; import { ServerlessExecuteResult } from 'src/engine/integrations/serverless/drivers/interfaces/serverless-driver.interface'; import { FileStorageService } from 'src/engine/integrations/file-storage/file-storage.service'; @@ -26,6 +23,7 @@ import { } from 'src/engine/metadata-modules/serverless-function/serverless-function.exception'; import { serverlessFunctionCreateHash } from 'src/engine/metadata-modules/serverless-function/utils/serverless-function-create-hash.utils'; import { isDefined } from 'src/utils/is-defined'; +import { getServerlessFolder } from 'src/engine/integrations/serverless/utils/serverless-get-folder.utils'; @Injectable() export class ServerlessFunctionService extends TypeOrmQueryService { @@ -38,24 +36,6 @@ export class ServerlessFunctionService extends TypeOrmQueryService Date: Thu, 22 Aug 2024 16:53:49 +0200 Subject: [PATCH 09/21] Remove interceptor --- .../serverless-function.interceptor.ts | 57 ------------------- .../serverless-function.module.ts | 2 - .../serverless-function.resolver.ts | 4 -- 3 files changed, 63 deletions(-) delete mode 100644 packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.interceptor.ts diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.interceptor.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.interceptor.ts deleted file mode 100644 index 7120a5c5f37f..000000000000 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.interceptor.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { - CallHandler, - ExecutionContext, - Injectable, - NestInterceptor, -} from '@nestjs/common'; - -import { Observable, map } from 'rxjs'; - -import { FileService } from 'src/engine/core-modules/file/services/file.service'; - -@Injectable() -export class ServerlessFunctionInterceptor implements NestInterceptor { - constructor(private readonly fileService: FileService) {} - - intercept(context: ExecutionContext, next: CallHandler): Observable { - return next.handle().pipe( - map(async (data) => { - if (data.edges && Array.isArray(data.edges)) { - return { - ...data, - edges: Promise.all( - data.edges.map((item) => ({ - ...item, - node: this.processItem(item.node), - })), - ), - }; - } else { - return this.processItem(data); - } - }), - ); - } - - private async processItem(item: any): Promise { - if (item && item.sourceCodeFullPath) { - const workspaceId = item.workspace?.id || item.workspaceId; - - if (!workspaceId) { - return item; - } - - const signedPayload = await this.fileService.encodeFileToken({ - serverlessFunctionId: item.id, - workspace_id: workspaceId, - }); - - return { - ...item, - sourceCodeFullPath: `${item.sourceCodeFullPath}?token=${signedPayload}`, - }; - } - - return item; - } -} diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.module.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.module.ts index 1fdbdfe5a5ac..bbea9dac81fe 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.module.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.module.ts @@ -15,7 +15,6 @@ import { JwtAuthGuard } from 'src/engine/guards/jwt.auth.guard'; import { ServerlessModule } from 'src/engine/integrations/serverless/serverless.module'; import { ServerlessFunctionDTO } from 'src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto'; import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity'; -import { ServerlessFunctionInterceptor } from 'src/engine/metadata-modules/serverless-function/serverless-function.interceptor'; import { ServerlessFunctionResolver } from 'src/engine/metadata-modules/serverless-function/serverless-function.resolver'; import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service'; @@ -45,7 +44,6 @@ import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverles update: { disabled: true }, delete: { disabled: true }, guards: [JwtAuthGuard], - interceptors: [ServerlessFunctionInterceptor], }, ], }), diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.resolver.ts index 5d55d3abc587..0c19d9805288 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.resolver.ts @@ -21,7 +21,6 @@ import { ServerlessFunctionException, ServerlessFunctionExceptionCode, } from 'src/engine/metadata-modules/serverless-function/serverless-function.exception'; -import { ServerlessFunctionInterceptor } from 'src/engine/metadata-modules/serverless-function/serverless-function.interceptor'; import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service'; import { serverlessFunctionGraphQLApiExceptionHandler } from 'src/engine/metadata-modules/serverless-function/utils/serverless-function-graphql-api-exception-handler.utils'; import { GetServerlessFunctionSourceCodeInput } from 'src/engine/metadata-modules/serverless-function/dtos/get-serverless-function-source-code.input'; @@ -87,7 +86,6 @@ export class ServerlessFunctionResolver { } } - @UseInterceptors(ServerlessFunctionInterceptor) @Mutation(() => ServerlessFunctionDTO) async updateOneServerlessFunction( @Args('input') @@ -106,7 +104,6 @@ export class ServerlessFunctionResolver { } } - @UseInterceptors(ServerlessFunctionInterceptor) @Mutation(() => ServerlessFunctionDTO) async createOneServerlessFunction( @Args('input') @@ -129,7 +126,6 @@ export class ServerlessFunctionResolver { } } - @UseInterceptors(ServerlessFunctionInterceptor) @Mutation(() => ServerlessFunctionDTO) async createOneServerlessFunctionFromFile( @Args({ name: 'file', type: () => GraphQLUpload }) From 09fcf256e127d2d60df46ad9989ae6a2ff77b515 Mon Sep 17 00:00:00 2001 From: martmull Date: Thu, 22 Aug 2024 17:38:07 +0200 Subject: [PATCH 10/21] WIP: front --- .../src/generated-metadata/gql.ts | 9 ++- .../src/generated-metadata/graphql.ts | 62 +++++++++++++------ ...ettingsServerlessFunctionCodeEditorTab.tsx | 30 ++++++++- .../mutations/executeOneServerlessFunction.ts | 10 +-- .../mutations/publishOneServerlessFunction.ts | 13 ++++ .../hooks/useDeleteOneServerlessFunction.ts | 6 +- .../hooks/useExecuteOneServerlessFunction.ts | 9 +-- .../hooks/usePublishOneServerlessFunction.ts | 36 +++++++++++ .../SettingsServerlessFunctionDetail.tsx | 58 +++++++++++++++-- .../dtos/execute-serverless-function.input.ts | 4 +- .../dtos/publish-serverless-function.input.ts | 16 ++--- .../serverless-function.resolver.ts | 10 +-- .../display/icon/components/TablerIcons.ts | 1 + 13 files changed, 200 insertions(+), 64 deletions(-) create mode 100644 packages/twenty-front/src/modules/settings/serverless-functions/graphql/mutations/publishOneServerlessFunction.ts create mode 100644 packages/twenty-front/src/modules/settings/serverless-functions/hooks/usePublishOneServerlessFunction.ts diff --git a/packages/twenty-front/src/generated-metadata/gql.ts b/packages/twenty-front/src/generated-metadata/gql.ts index 2676763d8476..4c43b04b58b2 100644 --- a/packages/twenty-front/src/generated-metadata/gql.ts +++ b/packages/twenty-front/src/generated-metadata/gql.ts @@ -36,7 +36,8 @@ const documents = { "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n sourceCodeHash\n runtime\n syncStatus\n latestVersion\n createdAt\n updatedAt\n }\n": types.ServerlessFunctionFieldsFragmentDoc, "\n \n mutation CreateOneServerlessFunctionItem(\n $input: CreateServerlessFunctionInput!\n ) {\n createOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.CreateOneServerlessFunctionItemDocument, "\n \n mutation DeleteOneServerlessFunction($input: DeleteServerlessFunctionInput!) {\n deleteOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.DeleteOneServerlessFunctionDocument, - "\n mutation ExecuteOneServerlessFunction(\n $id: UUID!\n $payload: JSON!\n $version: String\n ) {\n executeOneServerlessFunction(\n id: $id\n payload: $payload\n version: $version\n ) {\n data\n duration\n status\n error\n }\n }\n": types.ExecuteOneServerlessFunctionDocument, + "\n mutation ExecuteOneServerlessFunction(\n $input: ExecuteServerlessFunctionInput!\n ) {\n executeOneServerlessFunction(input: $input) {\n data\n duration\n status\n error\n }\n }\n": types.ExecuteOneServerlessFunctionDocument, + "\n \n mutation PublishOneServerlessFunction(\n $input: PublishServerlessFunctionInput!\n ) {\n publishServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.PublishOneServerlessFunctionDocument, "\n \n mutation UpdateOneServerlessFunction($input: UpdateServerlessFunctionInput!) {\n updateOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.UpdateOneServerlessFunctionDocument, "\n \n query GetManyServerlessFunctions {\n serverlessFunctions(paging: { first: 100 }) {\n edges {\n node {\n ...ServerlessFunctionFields\n }\n }\n }\n }\n": types.GetManyServerlessFunctionsDocument, "\n \n query GetOneServerlessFunction($id: UUID!) {\n serverlessFunction(id: $id) {\n ...ServerlessFunctionFields\n }\n }\n": types.GetOneServerlessFunctionDocument, @@ -152,7 +153,11 @@ export function graphql(source: "\n \n mutation DeleteOneServerlessFunction($i /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n mutation ExecuteOneServerlessFunction(\n $id: UUID!\n $payload: JSON!\n $version: String\n ) {\n executeOneServerlessFunction(\n id: $id\n payload: $payload\n version: $version\n ) {\n data\n duration\n status\n error\n }\n }\n"): (typeof documents)["\n mutation ExecuteOneServerlessFunction(\n $id: UUID!\n $payload: JSON!\n $version: String\n ) {\n executeOneServerlessFunction(\n id: $id\n payload: $payload\n version: $version\n ) {\n data\n duration\n status\n error\n }\n }\n"]; +export function graphql(source: "\n mutation ExecuteOneServerlessFunction(\n $input: ExecuteServerlessFunctionInput!\n ) {\n executeOneServerlessFunction(input: $input) {\n data\n duration\n status\n error\n }\n }\n"): (typeof documents)["\n mutation ExecuteOneServerlessFunction(\n $input: ExecuteServerlessFunctionInput!\n ) {\n executeOneServerlessFunction(input: $input) {\n data\n duration\n status\n error\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n \n mutation PublishOneServerlessFunction(\n $input: PublishServerlessFunctionInput!\n ) {\n publishServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n"): (typeof documents)["\n \n mutation PublishOneServerlessFunction(\n $input: PublishServerlessFunctionInput!\n ) {\n publishServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 4612e00256e8..7ee649b5c5ca 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -310,6 +310,15 @@ export type ExchangeAuthCode = { refreshToken: AuthToken; }; +export type ExecuteServerlessFunctionInput = { + /** Id of the serverless function to execute */ + id: Scalars['UUID']['input']; + /** Payload in JSON format */ + payload?: InputMaybe; + /** Version of the serverless function to execute */ + version?: Scalars['String']['input']; +}; + export type FeatureFlag = { __typename?: 'FeatureFlag'; id: Scalars['UUID']['output']; @@ -458,8 +467,9 @@ export type Mutation = { generateJWT: AuthTokens; generateTransientToken: TransientToken; impersonate: Verify; + publishServerlessFunction: ServerlessFunction; renewToken: AuthTokens; - runWorkflowVersion: WorkflowTriggerResult; + runWorkflowVersion: WorkflowRun; sendInviteLink: SendInviteLink; signUp: LoginToken; skipSyncEmailOnboardingStep: OnboardingStepSuccess; @@ -591,9 +601,7 @@ export type MutationExchangeAuthorizationCodeArgs = { export type MutationExecuteOneServerlessFunctionArgs = { - id: Scalars['UUID']['input']; - payload?: InputMaybe; - version?: Scalars['String']['input']; + input: ExecuteServerlessFunctionInput; }; @@ -613,6 +621,11 @@ export type MutationImpersonateArgs = { }; +export type MutationPublishServerlessFunctionArgs = { + input: PublishServerlessFunctionInput; +}; + + export type MutationRenewTokenArgs = { appToken: Scalars['String']['input']; }; @@ -780,6 +793,11 @@ export type ProductPricesEntity = { totalNumberOfPrices: Scalars['Int']['output']; }; +export type PublishServerlessFunctionInput = { + /** The id of the function. */ + id: Scalars['ID']['input']; +}; + export type Query = { __typename?: 'Query'; billingPortalSession: SessionEntity; @@ -1040,7 +1058,7 @@ export type ServerlessFunction = { createdAt: Scalars['DateTime']['output']; description?: Maybe; id: Scalars['UUID']['output']; - latestVersion?: Maybe; + latestVersion?: Maybe; name: Scalars['String']['output']; runtime: Scalars['String']['output']; sourceCodeHash: Scalars['String']['output']; @@ -1381,10 +1399,9 @@ export type Verify = { user: User; }; -export type WorkflowTriggerResult = { - __typename?: 'WorkflowTriggerResult'; - /** Execution result in JSON format */ - result?: Maybe; +export type WorkflowRun = { + __typename?: 'WorkflowRun'; + workflowRunId: Scalars['UUID']['output']; }; export type Workspace = { @@ -1394,7 +1411,6 @@ export type Workspace = { billingSubscriptions?: Maybe>; createdAt: Scalars['DateTime']['output']; currentBillingSubscription?: Maybe; - currentMetadataVersion: Scalars['Float']['output']; databaseSchema: Scalars['String']['output']; databaseUrl: Scalars['String']['output']; deletedAt?: Maybe; @@ -1707,49 +1723,54 @@ export type ObjectMetadataItemsQueryVariables = Exact<{ export type ObjectMetadataItemsQuery = { __typename?: 'Query', objects: { __typename?: 'ObjectConnection', edges: Array<{ __typename?: 'objectEdge', node: { __typename?: 'object', id: any, dataSourceId: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isRemote: boolean, isActive: boolean, isSystem: boolean, createdAt: any, updatedAt: any, labelIdentifierFieldMetadataId?: string | null, imageIdentifierFieldMetadataId?: string | null, fields: { __typename?: 'ObjectFieldsConnection', edges: Array<{ __typename?: 'fieldEdge', node: { __typename?: 'field', id: any, type: FieldMetadataType, name: string, label: string, description?: string | null, icon?: string | null, isCustom?: boolean | null, isActive?: boolean | null, isSystem?: boolean | null, isNullable?: boolean | null, createdAt: any, updatedAt: any, defaultValue?: any | null, options?: any | null, fromRelationMetadata?: { __typename?: 'relation', id: any, relationType: RelationMetadataType, toFieldMetadataId: string, toObjectMetadata: { __typename?: 'object', id: any, dataSourceId: string, nameSingular: string, namePlural: string, isSystem: boolean, isRemote: boolean } } | null, toRelationMetadata?: { __typename?: 'relation', id: any, relationType: RelationMetadataType, fromFieldMetadataId: string, fromObjectMetadata: { __typename?: 'object', id: any, dataSourceId: string, nameSingular: string, namePlural: string, isSystem: boolean, isRemote: boolean } } | null, relationDefinition?: { __typename?: 'RelationDefinition', relationId: any, direction: RelationDefinitionType, sourceObjectMetadata: { __typename?: 'object', id: any, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'field', id: any, name: string }, targetObjectMetadata: { __typename?: 'object', id: any, nameSingular: string, namePlural: string }, targetFieldMetadata: { __typename?: 'field', id: any, name: string } } | null } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } }; -export type ServerlessFunctionFieldsFragment = { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: number | null, createdAt: any, updatedAt: any }; +export type ServerlessFunctionFieldsFragment = { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, createdAt: any, updatedAt: any }; export type CreateOneServerlessFunctionItemMutationVariables = Exact<{ input: CreateServerlessFunctionInput; }>; -export type CreateOneServerlessFunctionItemMutation = { __typename?: 'Mutation', createOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: number | null, createdAt: any, updatedAt: any } }; +export type CreateOneServerlessFunctionItemMutation = { __typename?: 'Mutation', createOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, createdAt: any, updatedAt: any } }; export type DeleteOneServerlessFunctionMutationVariables = Exact<{ input: DeleteServerlessFunctionInput; }>; -export type DeleteOneServerlessFunctionMutation = { __typename?: 'Mutation', deleteOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: number | null, createdAt: any, updatedAt: any } }; +export type DeleteOneServerlessFunctionMutation = { __typename?: 'Mutation', deleteOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, createdAt: any, updatedAt: any } }; export type ExecuteOneServerlessFunctionMutationVariables = Exact<{ - id: Scalars['UUID']['input']; - payload: Scalars['JSON']['input']; - version?: InputMaybe; + input: ExecuteServerlessFunctionInput; }>; export type ExecuteOneServerlessFunctionMutation = { __typename?: 'Mutation', executeOneServerlessFunction: { __typename?: 'ServerlessFunctionExecutionResult', data?: any | null, duration: number, status: ServerlessFunctionExecutionStatus, error?: any | null } }; +export type PublishOneServerlessFunctionMutationVariables = Exact<{ + input: PublishServerlessFunctionInput; +}>; + + +export type PublishOneServerlessFunctionMutation = { __typename?: 'Mutation', publishServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, createdAt: any, updatedAt: any } }; + export type UpdateOneServerlessFunctionMutationVariables = Exact<{ input: UpdateServerlessFunctionInput; }>; -export type UpdateOneServerlessFunctionMutation = { __typename?: 'Mutation', updateOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: number | null, createdAt: any, updatedAt: any } }; +export type UpdateOneServerlessFunctionMutation = { __typename?: 'Mutation', updateOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, createdAt: any, updatedAt: any } }; export type GetManyServerlessFunctionsQueryVariables = Exact<{ [key: string]: never; }>; -export type GetManyServerlessFunctionsQuery = { __typename?: 'Query', serverlessFunctions: { __typename?: 'ServerlessFunctionConnection', edges: Array<{ __typename?: 'ServerlessFunctionEdge', node: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: number | null, createdAt: any, updatedAt: any } }> } }; +export type GetManyServerlessFunctionsQuery = { __typename?: 'Query', serverlessFunctions: { __typename?: 'ServerlessFunctionConnection', edges: Array<{ __typename?: 'ServerlessFunctionEdge', node: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, createdAt: any, updatedAt: any } }> } }; export type GetOneServerlessFunctionQueryVariables = Exact<{ id: Scalars['UUID']['input']; }>; -export type GetOneServerlessFunctionQuery = { __typename?: 'Query', serverlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: number | null, createdAt: any, updatedAt: any } }; +export type GetOneServerlessFunctionQuery = { __typename?: 'Query', serverlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, sourceCodeHash: string, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, createdAt: any, updatedAt: any } }; export type FindOneServerlessFunctionSourceCodeQueryVariables = Exact<{ input: GetServerlessFunctionSourceCodeInput; @@ -1781,7 +1802,8 @@ export const DeleteOneRelationMetadataItemDocument = {"kind":"Document","definit export const ObjectMetadataItemsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ObjectMetadataItems"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"objectFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"objectFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fieldFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"fieldFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"objects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"objectFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isRemote"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"labelIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"imageIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"fields"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fieldFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"fromRelationMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"relationType"}},{"kind":"Field","name":{"kind":"Name","value":"toObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"isRemote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"toFieldMetadataId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"toRelationMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"relationType"}},{"kind":"Field","name":{"kind":"Name","value":"fromObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"isRemote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fromFieldMetadataId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"defaultValue"}},{"kind":"Field","name":{"kind":"Name","value":"options"}},{"kind":"Field","name":{"kind":"Name","value":"relationDefinition"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"relationId"}},{"kind":"Field","name":{"kind":"Name","value":"direction"}},{"kind":"Field","name":{"kind":"Name","value":"sourceObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sourceFieldMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"targetObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}}]}},{"kind":"Field","name":{"kind":"Name","value":"targetFieldMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateOneServerlessFunctionItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOneServerlessFunctionItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const DeleteOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DeleteServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; -export const ExecuteOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ExecuteOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"payload"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"version"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"executeOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"payload"},"value":{"kind":"Variable","name":{"kind":"Name","value":"payload"}}},{"kind":"Argument","name":{"kind":"Name","value":"version"},"value":{"kind":"Variable","name":{"kind":"Name","value":"version"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"data"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]} as unknown as DocumentNode; +export const ExecuteOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ExecuteOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ExecuteServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"executeOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"data"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]} as unknown as DocumentNode; +export const PublishOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"PublishOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PublishServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"publishServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const UpdateOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const GetManyServerlessFunctionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetManyServerlessFunctions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverlessFunctions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const GetOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"sourceCodeHash"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionCodeEditorTab.tsx b/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionCodeEditorTab.tsx index a74a0889dc2e..f654358203c4 100644 --- a/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionCodeEditorTab.tsx +++ b/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionCodeEditorTab.tsx @@ -1,4 +1,4 @@ -import { H2Title, IconPlayerPlay } from 'twenty-ui'; +import { H2Title, IconPlayerPlay, IconGitCommit, IconRestore } from 'twenty-ui'; import { CodeEditor } from '@/ui/input/code-editor/components/CodeEditor'; import { Section } from '@/ui/layout/section/components/Section'; import { ServerlessFunctionFormValues } from '@/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState'; @@ -14,13 +14,16 @@ const StyledTabList = styled(TabList)` export const SettingsServerlessFunctionCodeEditorTab = ({ formValues, handleExecute, + handlePublish,handleReset, onChange, }: { formValues: ServerlessFunctionFormValues; handleExecute: () => void; + handlePublish: () => void; + handleReset: () => void; onChange: (key: string) => (value: string) => void; }) => { - const HeaderButton = ( + const TestButton = (