From cb010d90fe8ab69b1cf60aba1853e272fc209098 Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 3 Jun 2025 15:28:43 +0200 Subject: [PATCH] 998 workflow restore (#12417) Add a post hook to restore workflow sub-entities --- .../src/generated-metadata/gql.ts | 4 +- .../src/generated-metadata/graphql.ts | 36 ++- .../twenty-front/src/generated/graphql.tsx | 8 - .../constants/WorkflowActionsConfig.tsx | 2 + .../fragments/serverlessFunctionFragment.ts | 1 - ...ttingsServerlessFunctionDetail.stories.tsx | 1 - ...812894-addDeletedAtToServerlessFunction.ts | 25 ++ ...8-removeUselessServerlessFunctionColumn.ts | 25 ++ .../dtos/serverless-function.dto.ts | 19 +- .../serverless-function.entity.ts | 20 +- .../serverless-function.resolver.ts | 11 +- .../serverless-function.service.ts | 117 +++++----- .../storage/promise-memoizer.storage.ts | 12 +- .../workflow-delete-many.post-query.hook.ts | 9 +- .../workflow-delete-one.post-query.hook.ts | 9 +- .../workflow-destroy-many.pre-query.hook.ts | 29 +++ .../workflow-destroy-one.pre-query.hook.ts | 29 +++ .../query-hooks/workflow-query-hook.module.ts | 8 + .../workflow-restore-many.post-query.hook.ts | 31 +++ .../workflow-restore-one.post-query.hook.ts | 31 +++ .../workflow-common.workspace-service.ts | 106 ++++++--- ...workflow-version-step.workspace-service.ts | 1 + .../workflow-statuses-update.job.spec.ts | 9 + .../jobs/workflow-statuses-update.job.ts | 11 +- .../workflow-status/workflow-status.module.ts | 2 + .../serverless-functions.integration-spec.ts | 2 - .../workflow-resolver.integration-spec.ts | 215 ++++++++++++++++++ 27 files changed, 600 insertions(+), 173 deletions(-) create mode 100644 packages/twenty-server/src/database/typeorm/core/migrations/common/1748875812894-addDeletedAtToServerlessFunction.ts create mode 100644 packages/twenty-server/src/database/typeorm/core/migrations/common/1748942397538-removeUselessServerlessFunctionColumn.ts create mode 100644 packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-destroy-many.pre-query.hook.ts create mode 100644 packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-destroy-one.pre-query.hook.ts create mode 100644 packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-restore-many.post-query.hook.ts create mode 100644 packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-restore-one.post-query.hook.ts create mode 100644 packages/twenty-server/test/integration/graphql/suites/workflow/workflow-resolver.integration-spec.ts diff --git a/packages/twenty-front/src/generated-metadata/gql.ts b/packages/twenty-front/src/generated-metadata/gql.ts index 8f80519ec..2631b329c 100644 --- a/packages/twenty-front/src/generated-metadata/gql.ts +++ b/packages/twenty-front/src/generated-metadata/gql.ts @@ -33,7 +33,7 @@ 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 settings\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 objects(paging: { first: 1000 }) {\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 shortcut\n isLabelSyncedWithName\n isSearchable\n duplicateCriteria\n indexMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n name\n indexWhereClause\n indexType\n isUnique\n indexFieldMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n order\n fieldMetadataId\n }\n }\n }\n }\n }\n }\n fieldsList {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n isUnique\n createdAt\n updatedAt\n defaultValue\n options\n settings\n isLabelSyncedWithName\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 }\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 runtime\n timeoutSeconds\n syncStatus\n latestVersion\n latestVersionInputSchema\n publishedVersions\n createdAt\n updatedAt\n }\n": types.ServerlessFunctionFieldsFragmentDoc, + "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n timeoutSeconds\n latestVersion\n latestVersionInputSchema\n publishedVersions\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: ServerlessFunctionIdInput!) {\n deleteOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.DeleteOneServerlessFunctionDocument, "\n mutation ExecuteOneServerlessFunction(\n $input: ExecuteServerlessFunctionInput!\n ) {\n executeOneServerlessFunction(input: $input) {\n data\n logs\n duration\n status\n error\n }\n }\n": types.ExecuteOneServerlessFunctionDocument, @@ -142,7 +142,7 @@ export function graphql(source: "\n query ObjectMetadataItems {\n objects(pa /** * 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 runtime\n timeoutSeconds\n syncStatus\n latestVersion\n latestVersionInputSchema\n publishedVersions\n createdAt\n updatedAt\n }\n"): (typeof documents)["\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n timeoutSeconds\n syncStatus\n latestVersion\n latestVersionInputSchema\n publishedVersions\n createdAt\n updatedAt\n }\n"]; +export function graphql(source: "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n timeoutSeconds\n latestVersion\n latestVersionInputSchema\n publishedVersions\n createdAt\n updatedAt\n }\n"): (typeof documents)["\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n timeoutSeconds\n latestVersion\n latestVersionInputSchema\n publishedVersions\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. */ diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 83e6be4b3..81f99cc3c 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -2087,7 +2087,6 @@ export type ServerlessFunction = { name: Scalars['String']['output']; publishedVersions: Array; runtime: Scalars['String']['output']; - syncStatus: ServerlessFunctionSyncStatus; timeoutSeconds: Scalars['Float']['output']; updatedAt: Scalars['DateTime']['output']; }; @@ -2118,13 +2117,6 @@ export type ServerlessFunctionIdInput = { id: Scalars['ID']['input']; }; -/** SyncStatus of the serverlessFunction */ -export enum ServerlessFunctionSyncStatus { - BUILDING = 'BUILDING', - NOT_READY = 'NOT_READY', - READY = 'READY' -} - export type SettingPermission = { __typename?: 'SettingPermission'; id: Scalars['String']['output']; @@ -2849,21 +2841,21 @@ export type ObjectMetadataItemsQueryVariables = Exact<{ [key: string]: never; }> 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, shortcut?: string | null, isLabelSyncedWithName: boolean, isSearchable: boolean, duplicateCriteria?: Array> | null, indexMetadatas: { __typename?: 'ObjectIndexMetadatasConnection', edges: Array<{ __typename?: 'IndexEdge', node: { __typename?: 'Index', id: any, createdAt: any, updatedAt: any, name: string, indexWhereClause?: string | null, indexType: IndexType, isUnique: boolean, indexFieldMetadatas: { __typename?: 'IndexIndexFieldMetadatasConnection', edges: Array<{ __typename?: 'IndexFieldEdge', node: { __typename?: 'IndexField', id: any, createdAt: any, updatedAt: any, order: number, fieldMetadataId: any } }> } } }> }, fieldsList: Array<{ __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, isUnique?: boolean | null, createdAt: any, updatedAt: any, defaultValue?: any | null, options?: any | null, settings?: any | null, isLabelSyncedWithName?: 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 } } }; -export type ServerlessFunctionFieldsFragment = { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, timeoutSeconds: number, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any }; +export type ServerlessFunctionFieldsFragment = { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, timeoutSeconds: number, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, 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, runtime: string, timeoutSeconds: number, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; +export type CreateOneServerlessFunctionItemMutation = { __typename?: 'Mutation', createOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, timeoutSeconds: number, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; export type DeleteOneServerlessFunctionMutationVariables = Exact<{ input: ServerlessFunctionIdInput; }>; -export type DeleteOneServerlessFunctionMutation = { __typename?: 'Mutation', deleteOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, timeoutSeconds: number, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; +export type DeleteOneServerlessFunctionMutation = { __typename?: 'Mutation', deleteOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, timeoutSeconds: number, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; export type ExecuteOneServerlessFunctionMutationVariables = Exact<{ input: ExecuteServerlessFunctionInput; @@ -2877,14 +2869,14 @@ export type PublishOneServerlessFunctionMutationVariables = Exact<{ }>; -export type PublishOneServerlessFunctionMutation = { __typename?: 'Mutation', publishServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, timeoutSeconds: number, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; +export type PublishOneServerlessFunctionMutation = { __typename?: 'Mutation', publishServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, timeoutSeconds: number, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, 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, runtime: string, timeoutSeconds: number, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; +export type UpdateOneServerlessFunctionMutation = { __typename?: 'Mutation', updateOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, timeoutSeconds: number, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; export type FindManyAvailablePackagesQueryVariables = Exact<{ input: ServerlessFunctionIdInput; @@ -2896,14 +2888,14 @@ export type FindManyAvailablePackagesQuery = { __typename?: 'Query', getAvailabl export type GetManyServerlessFunctionsQueryVariables = Exact<{ [key: string]: never; }>; -export type GetManyServerlessFunctionsQuery = { __typename?: 'Query', findManyServerlessFunctions: Array<{ __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, timeoutSeconds: number, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any }> }; +export type GetManyServerlessFunctionsQuery = { __typename?: 'Query', findManyServerlessFunctions: Array<{ __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, timeoutSeconds: number, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any }> }; export type GetOneServerlessFunctionQueryVariables = Exact<{ input: ServerlessFunctionIdInput; }>; -export type GetOneServerlessFunctionQuery = { __typename?: 'Query', findOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, timeoutSeconds: number, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; +export type GetOneServerlessFunctionQuery = { __typename?: 'Query', findOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, timeoutSeconds: number, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; export type FindOneServerlessFunctionSourceCodeQueryVariables = Exact<{ input: GetServerlessFunctionSourceCodeInput; @@ -2914,7 +2906,7 @@ export type FindOneServerlessFunctionSourceCodeQuery = { __typename?: 'Query', g 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":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"timeoutSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"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":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"timeoutSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"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; @@ -2933,12 +2925,12 @@ 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"}},{"kind":"Field","name":{"kind":"Name","value":"settings"}}]}}]}}]} 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"},"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"}}]}}],"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":"shortcut"}},{"kind":"Field","name":{"kind":"Name","value":"isLabelSyncedWithName"}},{"kind":"Field","name":{"kind":"Name","value":"isSearchable"}},{"kind":"Field","name":{"kind":"Name","value":"duplicateCriteria"}},{"kind":"Field","name":{"kind":"Name","value":"indexMetadatas"},"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":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"indexWhereClause"}},{"kind":"Field","name":{"kind":"Name","value":"indexType"}},{"kind":"Field","name":{"kind":"Name","value":"isUnique"}},{"kind":"Field","name":{"kind":"Name","value":"indexFieldMetadatas"},"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":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}}]}}]}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fieldsList"},"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":"isUnique"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"defaultValue"}},{"kind":"Field","name":{"kind":"Name","value":"options"}},{"kind":"Field","name":{"kind":"Name","value":"settings"}},{"kind":"Field","name":{"kind":"Name","value":"isLabelSyncedWithName"}},{"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"}}]}}]}}]}}]} 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":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"timeoutSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"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":"ServerlessFunctionIdInput"}}}}],"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":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"timeoutSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} 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":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"timeoutSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"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":"ServerlessFunctionIdInput"}}}}],"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":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"timeoutSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"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":"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":"logs"}},{"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":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"timeoutSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"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":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"timeoutSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} 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":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"timeoutSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"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":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"timeoutSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const FindManyAvailablePackagesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindManyAvailablePackages"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunctionIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getAvailablePackages"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} 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":"findManyServerlessFunctions"},"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":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"timeoutSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"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":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunctionIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findOneServerlessFunction"},"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":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"timeoutSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"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":"findManyServerlessFunctions"},"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":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"timeoutSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"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":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunctionIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findOneServerlessFunction"},"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":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"timeoutSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"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/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 447f51a85..78df8323a 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -1882,7 +1882,6 @@ export type ServerlessFunction = { name: Scalars['String']; publishedVersions: Array; runtime: Scalars['String']; - syncStatus: ServerlessFunctionSyncStatus; timeoutSeconds: Scalars['Float']; updatedAt: Scalars['DateTime']; }; @@ -1913,13 +1912,6 @@ export type ServerlessFunctionIdInput = { id: Scalars['ID']; }; -/** SyncStatus of the serverlessFunction */ -export enum ServerlessFunctionSyncStatus { - BUILDING = 'BUILDING', - NOT_READY = 'NOT_READY', - READY = 'READY' -} - export type SettingPermission = { __typename?: 'SettingPermission'; id: Scalars['String']; diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowActionsConfig.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowActionsConfig.tsx index 2c95251c8..394c70e42 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowActionsConfig.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowActionsConfig.tsx @@ -193,9 +193,11 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({ SingleRecordActionKeys.REMOVE_FROM_FAVORITES, SingleRecordActionKeys.DELETE, SingleRecordActionKeys.DESTROY, + SingleRecordActionKeys.RESTORE, SingleRecordActionKeys.EXPORT, MultipleRecordsActionKeys.DELETE, MultipleRecordsActionKeys.DESTROY, + MultipleRecordsActionKeys.RESTORE, MultipleRecordsActionKeys.EXPORT, NoSelectionRecordActionKeys.SEE_DELETED_RECORDS, NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS, 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 27a38cbdf..5f3d28907 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 @@ -7,7 +7,6 @@ export const SERVERLESS_FUNCTION_FRAGMENT = gql` description runtime timeoutSeconds - syncStatus latestVersion latestVersionInputSchema publishedVersions diff --git a/packages/twenty-front/src/pages/settings/serverless-functions/__stories__/SettingsServerlessFunctionDetail.stories.tsx b/packages/twenty-front/src/pages/settings/serverless-functions/__stories__/SettingsServerlessFunctionDetail.stories.tsx index ea19e184d..ae4dc7904 100644 --- a/packages/twenty-front/src/pages/settings/serverless-functions/__stories__/SettingsServerlessFunctionDetail.stories.tsx +++ b/packages/twenty-front/src/pages/settings/serverless-functions/__stories__/SettingsServerlessFunctionDetail.stories.tsx @@ -36,7 +36,6 @@ const meta: Meta = { id: 'adb4bd21-7670-4c81-9f74-1fc196fe87ea', name: 'Serverless Function Name', description: '', - syncStatus: 'READY', runtime: 'nodejs18.x', updatedAt: '2024-02-24T10:23:10.673Z', createdAt: '2024-02-24T10:23:10.673Z', diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/common/1748875812894-addDeletedAtToServerlessFunction.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1748875812894-addDeletedAtToServerlessFunction.ts new file mode 100644 index 000000000..e22ca74e1 --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1748875812894-addDeletedAtToServerlessFunction.ts @@ -0,0 +1,25 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddDeletedAtToServerlessFunction1748875812894 + implements MigrationInterface +{ + name = 'AddDeletedAtToServerlessFunction1748875812894'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."serverlessFunction" ADD "deletedAt" TIMESTAMP WITH TIME ZONE`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_SERVERLESS_FUNCTION_ID_DELETED_AT" ON "core"."serverlessFunction" ("id", "deletedAt") `, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `DROP INDEX "core"."IDX_SERVERLESS_FUNCTION_ID_DELETED_AT"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."serverlessFunction" DROP COLUMN "deletedAt"`, + ); + } +} diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/common/1748942397538-removeUselessServerlessFunctionColumn.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1748942397538-removeUselessServerlessFunctionColumn.ts new file mode 100644 index 000000000..bdb0e7f08 --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1748942397538-removeUselessServerlessFunctionColumn.ts @@ -0,0 +1,25 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RemoveUselessServerlessFunctionColumn1748942397538 + implements MigrationInterface +{ + name = 'RemoveUselessServerlessFunctionColumn1748942397538'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."serverlessFunction" DROP COLUMN "syncStatus"`, + ); + await queryRunner.query( + `DROP TYPE "core"."serverlessFunction_syncstatus_enum"`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TYPE "core"."serverlessFunction_syncstatus_enum" AS ENUM('BUILDING', 'NOT_READY', 'READY')`, + ); + await queryRunner.query( + `ALTER TABLE "core"."serverlessFunction" ADD "syncStatus" "core"."serverlessFunction_syncstatus_enum" NOT NULL DEFAULT 'NOT_READY'`, + ); + } +} 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 3c2945526..4f06dde65 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 @@ -1,9 +1,4 @@ -import { - Field, - HideField, - ObjectType, - registerEnumType, -} from '@nestjs/graphql'; +import { Field, HideField, ObjectType } from '@nestjs/graphql'; import { Authorize, @@ -13,7 +8,6 @@ import { import { IsArray, IsDateString, - IsEnum, IsNotEmpty, IsNumber, IsString, @@ -22,14 +16,8 @@ import { import GraphQLJSON from 'graphql-type-json'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; -import { ServerlessFunctionSyncStatus } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity'; import { InputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/input-schema.type'; -registerEnumType(ServerlessFunctionSyncStatus, { - name: 'ServerlessFunctionSyncStatus', - description: 'SyncStatus of the serverlessFunction', -}); - @ObjectType('ServerlessFunction') @Authorize({ // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -75,11 +63,6 @@ export class ServerlessFunctionDTO { @Field(() => GraphQLJSON, { nullable: true }) latestVersionInputSchema: InputSchema; - @IsEnum(ServerlessFunctionSyncStatus) - @IsNotEmpty() - @Field(() => ServerlessFunctionSyncStatus) - syncStatus: ServerlessFunctionSyncStatus; - @HideField() workspaceId: string; 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 82e4e1cfa..13298569d 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 @@ -2,7 +2,9 @@ import { Check, Column, CreateDateColumn, + DeleteDateColumn, Entity, + Index, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -11,17 +13,12 @@ import { InputSchema } from 'src/modules/workflow/workflow-builder/workflow-sche const DEFAULT_SERVERLESS_TIMEOUT_SECONDS = 300; // 5 minutes -export enum ServerlessFunctionSyncStatus { - NOT_READY = 'NOT_READY', - BUILDING = 'BUILDING', - READY = 'READY', -} - export enum ServerlessFunctionRuntime { NODE18 = 'nodejs18.x', } @Entity('serverlessFunction') +@Index('IDX_SERVERLESS_FUNCTION_ID_DELETED_AT', ['id', 'deletedAt']) export class ServerlessFunctionEntity { @PrimaryGeneratedColumn('uuid') id: string; @@ -51,14 +48,6 @@ export class ServerlessFunctionEntity { @Column({ nullable: true }) layerVersion: number; - @Column({ - nullable: false, - default: ServerlessFunctionSyncStatus.NOT_READY, - type: 'enum', - enum: ServerlessFunctionSyncStatus, - }) - syncStatus: ServerlessFunctionSyncStatus; - @Column({ nullable: false, type: 'uuid' }) workspaceId: string; @@ -67,4 +56,7 @@ export class ServerlessFunctionEntity { @UpdateDateColumn({ type: 'timestamptz' }) updatedAt: Date; + + @DeleteDateColumn({ type: 'timestamptz' }) + deletedAt?: Date; } 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 ee4333059..6c9595bb1 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 @@ -24,6 +24,7 @@ import { } from 'src/engine/metadata-modules/serverless-function/serverless-function.exception'; 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 { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity'; @UseGuards(WorkspaceAuthGuard) @Resolver() @@ -32,6 +33,8 @@ export class ServerlessFunctionResolver { private readonly serverlessFunctionService: ServerlessFunctionService, @InjectRepository(FeatureFlag, 'core') private readonly featureFlagRepository: Repository, + @InjectRepository(ServerlessFunctionEntity, 'metadata') + private readonly serverlessFunctionRepository: Repository, ) {} async checkFeatureFlag(workspaceId: string) { @@ -57,9 +60,11 @@ export class ServerlessFunctionResolver { try { await this.checkFeatureFlag(workspaceId); - return await this.serverlessFunctionService.findOneOrFail({ - id, - workspaceId, + return await this.serverlessFunctionRepository.findOneOrFail({ + where: { + 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 d3c03ec83..0266c34e3 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 @@ -25,10 +25,7 @@ import { ThrottlerService } from 'src/engine/core-modules/throttler/throttler.se import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { CreateServerlessFunctionInput } from 'src/engine/metadata-modules/serverless-function/dtos/create-serverless-function.input'; import { UpdateServerlessFunctionInput } from 'src/engine/metadata-modules/serverless-function/dtos/update-serverless-function.input'; -import { - ServerlessFunctionEntity, - ServerlessFunctionSyncStatus, -} from 'src/engine/metadata-modules/serverless-function/serverless-function.entity'; +import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity'; import { ServerlessFunctionException, ServerlessFunctionExceptionCode, @@ -51,29 +48,6 @@ export class ServerlessFunctionService { return this.serverlessFunctionRepository.findBy(where); } - async findOneOrFail({ - workspaceId, - id, - }: { - workspaceId: string; - id: string; - }) { - const serverlessFunction = - await this.serverlessFunctionRepository.findOneBy({ - id, - workspaceId, - }); - - if (!serverlessFunction) { - throw new ServerlessFunctionException( - `Function does not exist`, - ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_NOT_FOUND, - ); - } - - return serverlessFunction; - } - async hasServerlessFunctionPublishedVersion(serverlessFunctionId: string) { return await this.serverlessFunctionRepository.exists({ where: { @@ -88,10 +62,13 @@ export class ServerlessFunctionService { id: string, version: string, ): Promise<{ [filePath: string]: string } | undefined> { - const serverlessFunction = await this.findOneOrFail({ - id, - workspaceId, - }); + const serverlessFunction = + await this.serverlessFunctionRepository.findOneOrFail({ + where: { + id, + workspaceId, + }, + }); try { const folderPath = getServerlessFolder({ @@ -129,10 +106,13 @@ export class ServerlessFunctionService { ): Promise { await this.throttleExecution(workspaceId); - const functionToExecute = await this.findOneOrFail({ - id, - workspaceId, - }); + const functionToExecute = + await this.serverlessFunctionRepository.findOneOrFail({ + where: { + id, + workspaceId, + }, + }); const resultServerlessFunction = await this.serverlessService.execute( functionToExecute, @@ -158,10 +138,13 @@ export class ServerlessFunctionService { } async publishOneServerlessFunction(id: string, workspaceId: string) { - const existingServerlessFunction = await this.findOneOrFail({ - id, - workspaceId, - }); + const existingServerlessFunction = + await this.serverlessFunctionRepository.findOneOrFail({ + where: { + id, + workspaceId, + }, + }); if (isDefined(existingServerlessFunction.latestVersion)) { const latestCode = await this.getServerlessFunctionSourceCode( @@ -222,19 +205,25 @@ export class ServerlessFunctionService { async deleteOneServerlessFunction({ id, workspaceId, - isHardDeletion = true, + softDelete = false, }: { id: string; workspaceId: string; - isHardDeletion?: boolean; + softDelete?: boolean; }) { - const existingServerlessFunction = await this.findOneOrFail({ - id, - workspaceId, - }); + const existingServerlessFunction = + await this.serverlessFunctionRepository.findOneOrFail({ + where: { + id, + workspaceId, + }, + withDeleted: true, + }); - if (isHardDeletion) { - await this.serverlessFunctionRepository.delete(id); + if (softDelete) { + await this.serverlessFunctionRepository.softDelete({ id }); + } else { + await this.serverlessFunctionRepository.delete({ id }); await this.fileStorageService.delete({ folderPath: getServerlessFolder({ serverlessFunction: existingServerlessFunction, @@ -247,14 +236,21 @@ export class ServerlessFunctionService { return existingServerlessFunction; } + async restoreOneServerlessFunction(id: string) { + await this.serverlessFunctionRepository.restore({ id }); + } + async updateOneServerlessFunction( serverlessFunctionInput: UpdateServerlessFunctionInput, workspaceId: string, ) { - const existingServerlessFunction = await this.findOneOrFail({ - id: serverlessFunctionInput.id, - workspaceId, - }); + const existingServerlessFunction = + await this.serverlessFunctionRepository.findOneOrFail({ + where: { + id: serverlessFunctionInput.id, + workspaceId, + }, + }); await this.serverlessFunctionRepository.update( existingServerlessFunction.id, @@ -316,13 +312,13 @@ export class ServerlessFunctionService { serverlessFunctionInput: CreateServerlessFunctionInput, workspaceId: string, ) { - const serverlessFunctionToCreate = - await this.serverlessFunctionRepository.create({ + const serverlessFunctionToCreate = this.serverlessFunctionRepository.create( + { ...serverlessFunctionInput, workspaceId, layerVersion: LAST_LAYER_VERSION, - syncStatus: ServerlessFunctionSyncStatus.NOT_READY, - }); + }, + ); const createdServerlessFunction = await this.serverlessFunctionRepository.save(serverlessFunctionToCreate); @@ -359,10 +355,13 @@ export class ServerlessFunctionService { return; } - const serverlessFunction = await this.findOneOrFail({ - id, - workspaceId, - }); + const serverlessFunction = + await this.serverlessFunctionRepository.findOneOrFail({ + where: { + id, + workspaceId, + }, + }); await this.fileStorageService.copy({ from: { diff --git a/packages/twenty-server/src/engine/twenty-orm/storage/promise-memoizer.storage.ts b/packages/twenty-server/src/engine/twenty-orm/storage/promise-memoizer.storage.ts index 942a1d36f..03f2ab674 100644 --- a/packages/twenty-server/src/engine/twenty-orm/storage/promise-memoizer.storage.ts +++ b/packages/twenty-server/src/engine/twenty-orm/storage/promise-memoizer.storage.ts @@ -1,6 +1,8 @@ import { Milliseconds } from 'cache-manager'; import { isDefined } from 'twenty-shared/utils'; +import { NodeEnvironment } from 'src/engine/core-modules/twenty-config/interfaces/node-environment.interface'; + import { CacheKey } from 'src/engine/twenty-orm/storage/types/cache-key.type'; type AsyncFactoryCallback = () => Promise; @@ -37,10 +39,12 @@ export class PromiseMemoizer { return existingPromise; } - // eslint-disable-next-line no-console - console.log( - `Computing new Datasource for cacheKey: ${cacheKey} out of ${this.cache.size}`, - ); + if (process.env.NODE_ENV !== NodeEnvironment.TEST) { + // eslint-disable-next-line no-console + console.log( + `Computing new Datasource for cacheKey: ${cacheKey} out of ${this.cache.size}`, + ); + } const newPromise = (async () => { try { diff --git a/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-delete-many.post-query.hook.ts b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-delete-many.post-query.hook.ts index 467e756c9..0908407a3 100644 --- a/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-delete-many.post-query.hook.ts +++ b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-delete-many.post-query.hook.ts @@ -22,9 +22,10 @@ export class WorkflowDeleteManyPostQueryHook _objectName: string, payload: WorkflowWorkspaceEntity[], ): Promise { - this.workflowCommonWorkspaceService.cleanWorkflowsSubEntities( - payload.map((workflow) => workflow.id), - authContext.workspace.id, - ); + this.workflowCommonWorkspaceService.handleWorkflowSubEntities({ + workflowIds: payload.map((workflow) => workflow.id), + workspaceId: authContext.workspace.id, + operation: 'delete', + }); } } diff --git a/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-delete-one.post-query.hook.ts b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-delete-one.post-query.hook.ts index 280537e93..675394848 100644 --- a/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-delete-one.post-query.hook.ts +++ b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-delete-one.post-query.hook.ts @@ -22,9 +22,10 @@ export class WorkflowDeleteOnePostQueryHook _objectName: string, payload: WorkflowWorkspaceEntity[], ): Promise { - this.workflowCommonWorkspaceService.cleanWorkflowsSubEntities( - payload.map((workflow) => workflow.id), - authContext.workspace.id, - ); + this.workflowCommonWorkspaceService.handleWorkflowSubEntities({ + workflowIds: payload.map((workflow) => workflow.id), + workspaceId: authContext.workspace.id, + operation: 'delete', + }); } } diff --git a/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-destroy-many.pre-query.hook.ts b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-destroy-many.pre-query.hook.ts new file mode 100644 index 000000000..efcce46eb --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-destroy-many.pre-query.hook.ts @@ -0,0 +1,29 @@ +import { WorkspacePreQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface'; +import { DestroyManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; + +import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator'; +import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; +import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; + +@WorkspaceQueryHook('workflow.destroyMany') +export class WorkflowDestroyManyPreQueryHook + implements WorkspacePreQueryHookInstance +{ + constructor( + private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService, + ) {} + + async execute( + authContext: AuthContext, + _objectName: string, + payload: DestroyManyResolverArgs<{ id: { in: string[] } }>, + ): Promise> { + await this.workflowCommonWorkspaceService.handleWorkflowSubEntities({ + workflowIds: payload.filter.id.in, + workspaceId: authContext.workspace.id, + operation: 'destroy', + }); + + return payload; + } +} diff --git a/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-destroy-one.pre-query.hook.ts b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-destroy-one.pre-query.hook.ts new file mode 100644 index 000000000..a798b3a45 --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-destroy-one.pre-query.hook.ts @@ -0,0 +1,29 @@ +import { WorkspacePreQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface'; +import { DestroyOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; + +import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator'; +import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; +import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; + +@WorkspaceQueryHook('workflow.destroyOne') +export class WorkflowDestroyOnePreQueryHook + implements WorkspacePreQueryHookInstance +{ + constructor( + private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService, + ) {} + + async execute( + authContext: AuthContext, + _objectName: string, + payload: DestroyOneResolverArgs, + ): Promise { + await this.workflowCommonWorkspaceService.handleWorkflowSubEntities({ + workflowIds: [payload.id], + workspaceId: authContext.workspace.id, + operation: 'destroy', + }); + + return payload; + } +} diff --git a/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-query-hook.module.ts b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-query-hook.module.ts index 91a1f4690..71392a7f2 100644 --- a/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-query-hook.module.ts +++ b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-query-hook.module.ts @@ -29,6 +29,10 @@ import { WorkflowVersionUpdateManyPreQueryHook } from 'src/modules/workflow/comm import { WorkflowVersionUpdateOnePreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow-version-update-one.pre-query.hook'; import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; import { WorkflowVersionValidationWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-version-validation.workspace-service'; +import { WorkflowRestoreOnePostQueryHook } from 'src/modules/workflow/common/query-hooks/workflow-restore-one.post-query.hook'; +import { WorkflowRestoreManyPostQueryHook } from 'src/modules/workflow/common/query-hooks/workflow-restore-many.post-query.hook'; +import { WorkflowDestroyOnePreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow-destroy-one.pre-query.hook'; +import { WorkflowDestroyManyPreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow-destroy-many.pre-query.hook'; @Module({ imports: [ @@ -49,6 +53,8 @@ import { WorkflowVersionValidationWorkspaceService } from 'src/modules/workflow/ WorkflowRunUpdateManyPreQueryHook, WorkflowRunDeleteOnePreQueryHook, WorkflowRunDeleteManyPreQueryHook, + WorkflowRestoreOnePostQueryHook, + WorkflowRestoreManyPostQueryHook, WorkflowVersionCreateOnePreQueryHook, WorkflowVersionCreateManyPreQueryHook, WorkflowVersionUpdateOnePreQueryHook, @@ -61,6 +67,8 @@ import { WorkflowVersionValidationWorkspaceService } from 'src/modules/workflow/ WorkflowCommonWorkspaceService, WorkflowDeleteManyPostQueryHook, WorkflowDeleteOnePostQueryHook, + WorkflowDestroyOnePreQueryHook, + WorkflowDestroyManyPreQueryHook, ], }) export class WorkflowQueryHookModule {} diff --git a/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-restore-many.post-query.hook.ts b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-restore-many.post-query.hook.ts new file mode 100644 index 000000000..e70018bba --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-restore-many.post-query.hook.ts @@ -0,0 +1,31 @@ +import { WorkspacePostQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface'; + +import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator'; +import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type'; +import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; +import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity'; +import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; + +@WorkspaceQueryHook({ + key: 'workflow.restoreMany', + type: WorkspaceQueryHookType.POST_HOOK, +}) +export class WorkflowRestoreManyPostQueryHook + implements WorkspacePostQueryHookInstance +{ + constructor( + private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService, + ) {} + + async execute( + authContext: AuthContext, + _objectName: string, + payload: WorkflowWorkspaceEntity[], + ): Promise { + this.workflowCommonWorkspaceService.handleWorkflowSubEntities({ + workflowIds: payload.map((workflow) => workflow.id), + workspaceId: authContext.workspace.id, + operation: 'restore', + }); + } +} diff --git a/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-restore-one.post-query.hook.ts b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-restore-one.post-query.hook.ts new file mode 100644 index 000000000..4512bee7b --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-restore-one.post-query.hook.ts @@ -0,0 +1,31 @@ +import { WorkspacePostQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface'; + +import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator'; +import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type'; +import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; +import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity'; +import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; + +@WorkspaceQueryHook({ + key: 'workflow.restoreOne', + type: WorkspaceQueryHookType.POST_HOOK, +}) +export class WorkflowRestoreOnePostQueryHook + implements WorkspacePostQueryHookInstance +{ + constructor( + private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService, + ) {} + + async execute( + authContext: AuthContext, + _objectName: string, + payload: WorkflowWorkspaceEntity[], + ): Promise { + this.workflowCommonWorkspaceService.handleWorkflowSubEntities({ + workflowIds: payload.map((workflow) => workflow.id), + workspaceId: authContext.workspace.id, + operation: 'restore', + }); + } +} diff --git a/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-common.workspace-service.ts b/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-common.workspace-service.ts index 67ddab366..8d90c86b9 100644 --- a/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-common.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-common.workspace-service.ts @@ -1,6 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service'; import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps'; import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps'; import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util'; @@ -19,6 +18,7 @@ import { WorkflowTriggerException, WorkflowTriggerExceptionCode, } from 'src/modules/workflow/workflow-trigger/exceptions/workflow-trigger.exception'; +import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service'; export type ObjectMetadataInfo = { objectMetadataItemWithFieldsMaps: ObjectMetadataItemWithFieldMaps; @@ -114,10 +114,15 @@ export class WorkflowCommonWorkspaceService { }; } - async cleanWorkflowsSubEntities( - workflowIds: string[], - workspaceId: string, - ): Promise { + async handleWorkflowSubEntities({ + workflowIds, + workspaceId, + operation, + }: { + workflowIds: string[]; + workspaceId: string; + operation: 'restore' | 'delete' | 'destroy'; + }): Promise { const workflowVersionRepository = await this.twentyORMManager.getRepository( 'workflowVersion', @@ -133,46 +138,91 @@ export class WorkflowCommonWorkspaceService { 'workflowAutomatedTrigger', ); - workflowIds.forEach((workflowId) => { - workflowAutomatedTriggerRepository.softDelete({ - workflowId, - }); + for (const workflowId of workflowIds) { + switch (operation) { + case 'delete': + await workflowAutomatedTriggerRepository.softDelete({ + workflowId, + }); - workflowRunRepository.softDelete({ - workflowId, - }); + await workflowRunRepository.softDelete({ + workflowId, + }); - workflowVersionRepository.softDelete({ - workflowId, - }); + await workflowVersionRepository.softDelete({ + workflowId, + }); - this.deleteServerlessFunctions( + break; + case 'restore': + await workflowAutomatedTriggerRepository.restore({ + workflowId, + }); + + await workflowRunRepository.restore({ + workflowId, + }); + + await workflowVersionRepository.restore({ + workflowId, + }); + + break; + } + + await this.handleServerlessFunctionSubEntities({ workflowVersionRepository, workflowId, workspaceId, - ); - }); + operation, + }); + } } - private async deleteServerlessFunctions( - workflowVersionRepository: WorkspaceRepository, - workflowId: string, - workspaceId: string, - ) { + async handleServerlessFunctionSubEntities({ + workflowVersionRepository, + workflowId, + workspaceId, + operation, + }: { + workflowVersionRepository: WorkspaceRepository; + + workflowId: string; + + workspaceId: string; + operation: 'restore' | 'delete' | 'destroy'; + }) { const workflowVersions = await workflowVersionRepository.find({ where: { workflowId, }, + withDeleted: true, }); workflowVersions.forEach((workflowVersion) => { workflowVersion.steps?.forEach(async (step) => { if (step.type === WorkflowActionType.CODE) { - await this.serverlessFunctionService.deleteOneServerlessFunction({ - id: step.settings.input.serverlessFunctionId, - workspaceId, - isHardDeletion: false, - }); + switch (operation) { + case 'delete': + await this.serverlessFunctionService.deleteOneServerlessFunction({ + id: step.settings.input.serverlessFunctionId, + workspaceId, + softDelete: true, + }); + break; + case 'restore': + await this.serverlessFunctionService.restoreOneServerlessFunction( + step.settings.input.serverlessFunctionId, + ); + break; + case 'destroy': + await this.serverlessFunctionService.deleteOneServerlessFunction({ + id: step.settings.input.serverlessFunctionId, + workspaceId, + softDelete: false, + }); + break; + } } }); }); diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service.ts index 0d0f0753d..621de347f 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service.ts @@ -375,6 +375,7 @@ export class WorkflowVersionStepWorkspaceService { await this.serverlessFunctionService.deleteOneServerlessFunction({ id: step.settings.input.serverlessFunctionId, workspaceId, + softDelete: false, }); } break; diff --git a/packages/twenty-server/src/modules/workflow/workflow-status/jobs/__tests__/workflow-statuses-update.job.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-status/jobs/__tests__/workflow-statuses-update.job.spec.ts index cc13a1b6e..33b4456ea 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-status/jobs/__tests__/workflow-statuses-update.job.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-status/jobs/__tests__/workflow-statuses-update.job.spec.ts @@ -12,6 +12,7 @@ import { WorkflowVersionBatchEvent, WorkflowVersionEventType, } from 'src/modules/workflow/workflow-status/jobs/workflow-statuses-update.job'; +import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity'; describe('WorkflowStatusesUpdate', () => { let job: WorkflowStatusesUpdateJob; @@ -73,6 +74,14 @@ describe('WorkflowStatusesUpdate', () => { }), }, }, + { + provide: getRepositoryToken(ServerlessFunctionEntity, 'metadata'), + useValue: { + findOneOrFail: jest.fn().mockResolvedValue({ + latestVersion: 'v2', + }), + }, + }, ], }).compile(); diff --git a/packages/twenty-server/src/modules/workflow/workflow-status/jobs/workflow-statuses-update.job.ts b/packages/twenty-server/src/modules/workflow/workflow-status/jobs/workflow-statuses-update.job.ts index 5b3e50b64..17a098ef4 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-status/jobs/workflow-statuses-update.job.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-status/jobs/workflow-statuses-update.job.ts @@ -27,6 +27,7 @@ import { WorkflowAction, WorkflowActionType, } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; +import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity'; export enum WorkflowVersionEventType { CREATE = 'CREATE', @@ -75,6 +76,8 @@ export class WorkflowStatusesUpdateJob { private readonly workspaceEventEmitter: WorkspaceEventEmitter, @InjectRepository(ObjectMetadataEntity, 'metadata') protected readonly objectMetadataRepository: Repository, + @InjectRepository(ServerlessFunctionEntity, 'metadata') + private readonly serverlessFunctionRepository: Repository, ) {} @Process(WorkflowStatusesUpdateJob.name) @@ -212,9 +215,11 @@ export class WorkflowStatusesUpdateJob { } const serverlessFunction = - await this.serverlessFunctionService.findOneOrFail({ - id: step.settings.input.serverlessFunctionId, - workspaceId, + await this.serverlessFunctionRepository.findOneOrFail({ + where: { + id: step.settings.input.serverlessFunctionId, + workspaceId, + }, }); const newStepSettings = { ...step.settings }; diff --git a/packages/twenty-server/src/modules/workflow/workflow-status/workflow-status.module.ts b/packages/twenty-server/src/modules/workflow/workflow-status/workflow-status.module.ts index ba54f1ef3..75eae8f00 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-status/workflow-status.module.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-status/workflow-status.module.ts @@ -6,12 +6,14 @@ import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless import { WorkspaceEventEmitterModule } from 'src/engine/workspace-event-emitter/workspace-event-emitter.module'; import { WorkflowStatusesUpdateJob } from 'src/modules/workflow/workflow-status/jobs/workflow-statuses-update.job'; import { WorkflowVersionStatusListener } from 'src/modules/workflow/workflow-status/listeners/workflow-version-status.listener'; +import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity'; @Module({ imports: [ ServerlessFunctionModule, WorkspaceEventEmitterModule, TypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'), + TypeOrmModule.forFeature([ServerlessFunctionEntity], 'metadata'), ], providers: [WorkflowStatusesUpdateJob, WorkflowVersionStatusListener], }) diff --git a/packages/twenty-server/test/integration/graphql/suites/object-generated/serverless-functions.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/object-generated/serverless-functions.integration-spec.ts index 10b5b66bd..2e93e1996 100644 --- a/packages/twenty-server/test/integration/graphql/suites/object-generated/serverless-functions.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/object-generated/serverless-functions.integration-spec.ts @@ -12,7 +12,6 @@ describe('serverlessFunctionsResolver (e2e)', () => { name description runtime - syncStatus latestVersion publishedVersions createdAt @@ -44,7 +43,6 @@ describe('serverlessFunctionsResolver (e2e)', () => { expect(serverlessFunction).toHaveProperty('name'); expect(serverlessFunction).toHaveProperty('description'); expect(serverlessFunction).toHaveProperty('runtime'); - expect(serverlessFunction).toHaveProperty('syncStatus'); expect(serverlessFunction).toHaveProperty('latestVersion'); expect(serverlessFunction).toHaveProperty('publishedVersions'); expect(serverlessFunction).toHaveProperty('createdAt'); diff --git a/packages/twenty-server/test/integration/graphql/suites/workflow/workflow-resolver.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/workflow/workflow-resolver.integration-spec.ts new file mode 100644 index 000000000..165585f35 --- /dev/null +++ b/packages/twenty-server/test/integration/graphql/suites/workflow/workflow-resolver.integration-spec.ts @@ -0,0 +1,215 @@ +import request from 'supertest'; + +const client = request(`http://localhost:${APP_PORT}`); + +const testWorkflowId = 'd6f9be23-c8e6-42b2-93f5-34ee0f97f1c7'; + +describe('workflowResolver', () => { + beforeAll(async () => { + const queryData = { + query: ` + mutation CreateOneWorkflow { + createWorkflow( + data: { + name: "Custom Test Workflow" + id: "${testWorkflowId}" + } + ) { + id + } + } + `, + }; + + await client + .post('/graphql') + .set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`) + .send(queryData); + }); + + afterAll(async () => { + const queryData = { + query: ` + mutation DestroyOneWorkflow { + destroyWorkflow(id: "${testWorkflowId}") { + id + } + } + `, + }; + + await client + .post('/graphql') + .set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`) + .send(queryData); + }); + + it('should create workflow subEntities', async () => { + const queryData = { + query: ` + query FindOneWorkflow { + workflow(filter: {id: {eq: "${testWorkflowId}"}}) { + id + deletedAt + versions { + edges { + node { + id + deletedAt + steps + } + } + } + } + } + `, + }; + + const response = await client + .post('/graphql') + .set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`) + .send(queryData); + + expect(response.status).toBe(200); + expect(response.body.errors).toBeUndefined(); + + const workflow = response.body.data.workflow; + + expect(workflow.id).toBe(testWorkflowId); + expect(workflow.deletedAt).toBeNull(); + expect(workflow.versions.edges.length).toBeGreaterThan(0); + expect(workflow.versions.edges[0].node.deletedAt).toBeNull(); + }); + + it('should delete workflow subEntities', async () => { + const deleteQueryData = { + query: ` + mutation DeleteOneWorkflow { + deleteWorkflow(id: "${testWorkflowId}") { + id + } + } + `, + }; + + const deleteResponse = await client + .post('/graphql') + .set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`) + .send(deleteQueryData); + + expect(deleteResponse.status).toBe(200); + + const queryData = { + query: ` + query FindWorkflow { + workflow(filter: { + id: { eq: "${testWorkflowId}" }, + not: { deletedAt: { is: NULL } } + }) { + id + deletedAt + } + } + `, + }; + + const response = await client + .post('/graphql') + .set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`) + .send(queryData); + + expect(response.status).toBe(200); + expect(response.body.errors).toBeUndefined(); + + const workflow = response.body.data.workflow; + + expect(workflow.id).toBe(testWorkflowId); + expect(workflow.deletedAt).not.toBeNull(); + + const queryWorkflowVersionsData = { + query: ` + query FindManyWorkflowVersions { + workflowVersions(filter: { + workflowId: { eq: "${testWorkflowId}" }, + not: { deletedAt: { is: NULL } } + }) { + edges { + node { + id + deletedAt + } + } + } + } + `, + }; + + const workflowVersionsResponse = await client + .post('/graphql') + .set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`) + .send(queryWorkflowVersionsData); + + expect(workflowVersionsResponse.status).toBe(200); + expect(workflowVersionsResponse.body.errors).toBeUndefined(); + + const workflowVersions = + workflowVersionsResponse.body.data.workflowVersions; + + expect(workflowVersions.edges.length).toBeGreaterThan(0); + expect(workflowVersions.edges[0].node.deletedAt).not.toBeNull(); + }); + + it('should restore workflow subEntities', async () => { + const restoreQueryData = { + query: ` + mutation RestoreOneWorkflow { + restoreWorkflow(id: "${testWorkflowId}") { + id + } + } + `, + }; + + const restoreResponse = await client + .post('/graphql') + .set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`) + .send(restoreQueryData); + + expect(restoreResponse.status).toBe(200); + + const queryData = { + query: ` + query FindOneWorkflow { + workflow(filter: {id: {eq: "${testWorkflowId}"}}) { + id + deletedAt + versions { + edges { + node { + id + deletedAt + steps + } + } + } + } + } + `, + }; + + const response = await client + .post('/graphql') + .set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`) + .send(queryData); + + expect(response.status).toBe(200); + expect(response.body.errors).toBeUndefined(); + + const workflow = response.body.data.workflow; + + expect(workflow.id).toBe(testWorkflowId); + expect(workflow.deletedAt).toBeNull(); + expect(workflow.versions.edges.length).toBeGreaterThan(0); + expect(workflow.versions.edges[0].node.deletedAt).toBeNull(); + }); +});