From 354ee86cb9db547c1bb701dfee5d533619f1b251 Mon Sep 17 00:00:00 2001 From: martmull Date: Fri, 8 Nov 2024 17:15:27 +0100 Subject: [PATCH] 8311 serverless function functions can be executed with any input (#8380) - remove ts-morph - update inputSchema shape ![image](https://github.com/user-attachments/assets/e62f3fdb-5be8-4666-8172-44f73a1981b9) https://github.com/user-attachments/assets/913cd305-9e7c-48da-b20f-c974a8ac7cea ## TODO - have inputTypes to match the inputSchema type (string, number, boolean, etc...), only string for now - handle required/optional inputs - handle case when inputSchema changes, fix data reset when switching function --- .../src/generated-metadata/gql.ts | 4 +- .../src/generated-metadata/graphql.ts | 36 ++- .../twenty-front/src/generated/graphql.tsx | 8 +- .../fragments/serverlessFunctionFragment.ts | 5 +- .../modules/ui/input/components/Select.tsx | 4 +- ...rkflowEditActionFormServerlessFunction.tsx | 153 +++++++----- .../modules/workflow/types/FunctionInput.ts | 5 + .../src/modules/workflow/types/InputSchema.ts | 18 ++ ...efaultFunctionInputFromInputSchema.test.ts | 29 +++ ...faultFunctionInputAndFunctionInput.test.ts | 21 ++ .../utils/__tests__/setNestedValue.test.ts | 11 + .../getDefaultFunctionInputFromInputSchema.ts | 22 ++ ...rgeDefaultFunctionInputAndFunctionInput.ts | 32 +++ .../modules/workflow/utils/setNestedValue.ts | 10 + .../base-typescript-project/src/index.ts | 11 +- .../serverless/drivers/lambda.driver.ts | 2 +- .../serverless/drivers/local.driver.ts | 6 +- .../dtos/function-parameter.dto.ts | 14 -- .../dtos/serverless-function.dto.ts | 8 +- ...erverless-function-publication.listener.ts | 6 +- .../serverless-function.entity.ts | 4 +- .../code-introspection.service.spec.ts | 164 ++++++------- .../code-introspection.service.ts | 218 +++++++++++------- .../types/input-schema.type.ts | 18 ++ .../workflow-builder.workspace-service.ts | 10 +- .../types/workflow-step-settings.type.ts | 1 - 26 files changed, 529 insertions(+), 291 deletions(-) create mode 100644 packages/twenty-front/src/modules/workflow/types/FunctionInput.ts create mode 100644 packages/twenty-front/src/modules/workflow/types/InputSchema.ts create mode 100644 packages/twenty-front/src/modules/workflow/utils/__tests__/getDefaultFunctionInputFromInputSchema.test.ts create mode 100644 packages/twenty-front/src/modules/workflow/utils/__tests__/mergeDefaultFunctionInputAndFunctionInput.test.ts create mode 100644 packages/twenty-front/src/modules/workflow/utils/__tests__/setNestedValue.test.ts create mode 100644 packages/twenty-front/src/modules/workflow/utils/getDefaultFunctionInputFromInputSchema.ts create mode 100644 packages/twenty-front/src/modules/workflow/utils/mergeDefaultFunctionInputAndFunctionInput.ts create mode 100644 packages/twenty-front/src/modules/workflow/utils/setNestedValue.ts delete mode 100644 packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/function-parameter.dto.ts create mode 100644 packages/twenty-server/src/modules/code-introspection/types/input-schema.type.ts diff --git a/packages/twenty-front/src/generated-metadata/gql.ts b/packages/twenty-front/src/generated-metadata/gql.ts index 54153628a72b..5d8e10a5ec05 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 $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isRemote\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n shortcut\n isLabelSyncedWithName\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 fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n isUnique\n createdAt\n updatedAt\n defaultValue\n options\n settings\n relationDefinition {\n relationId\n direction\n sourceObjectMetadata {\n id\n nameSingular\n namePlural\n }\n sourceFieldMetadata {\n id\n name\n }\n targetObjectMetadata {\n id\n nameSingular\n namePlural\n }\n targetFieldMetadata {\n id\n name\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n": types.ObjectMetadataItemsDocument, - "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n latestVersionInputSchema {\n name\n type\n }\n publishedVersions\n createdAt\n updatedAt\n }\n": types.ServerlessFunctionFieldsFragmentDoc, + "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\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 duration\n status\n error\n }\n }\n": types.ExecuteOneServerlessFunctionDocument, @@ -142,7 +142,7 @@ export function graphql(source: "\n query ObjectMetadataItems(\n $objectFilt /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n latestVersionInputSchema {\n name\n type\n }\n publishedVersions\n createdAt\n updatedAt\n }\n"): (typeof documents)["\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n latestVersionInputSchema {\n name\n type\n }\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 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 syncStatus\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 59787435f224..b26b39ddc122 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -436,12 +436,6 @@ export type FullName = { lastName: Scalars['String']['output']; }; -export type FunctionParameter = { - __typename?: 'FunctionParameter'; - name: Scalars['String']['output']; - type: Scalars['String']['output']; -}; - export type GenerateJwt = GenerateJwtOutputWithAuthTokens | GenerateJwtOutputWithSsoauth; export type GenerateJwtOutputWithAuthTokens = { @@ -1243,7 +1237,7 @@ export type ServerlessFunction = { description?: Maybe; id: Scalars['UUID']['output']; latestVersion?: Maybe; - latestVersionInputSchema?: Maybe>; + latestVersionInputSchema?: Maybe; name: Scalars['String']['output']; publishedVersions: Array; runtime: Scalars['String']['output']; @@ -1997,21 +1991,21 @@ export type ObjectMetadataItemsQueryVariables = Exact<{ export type ObjectMetadataItemsQuery = { __typename?: 'Query', objects: { __typename?: 'ObjectConnection', edges: Array<{ __typename?: 'objectEdge', node: { __typename?: 'object', id: any, dataSourceId: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isRemote: boolean, isActive: boolean, isSystem: boolean, createdAt: any, updatedAt: any, labelIdentifierFieldMetadataId?: string | null, imageIdentifierFieldMetadataId?: string | null, shortcut?: string | null, isLabelSyncedWithName: boolean, 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 } }> } } }> }, fields: { __typename?: 'ObjectFieldsConnection', edges: Array<{ __typename?: 'fieldEdge', node: { __typename?: 'field', id: any, type: FieldMetadataType, name: string, label: string, description?: string | null, icon?: string | null, isCustom?: boolean | null, isActive?: boolean | null, isSystem?: boolean | null, isNullable?: boolean | null, isUnique?: boolean | null, createdAt: any, updatedAt: any, defaultValue?: any | null, options?: any | null, settings?: any | null, relationDefinition?: { __typename?: 'RelationDefinition', relationId: any, direction: RelationDefinitionType, sourceObjectMetadata: { __typename?: 'object', id: any, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'field', id: any, name: string }, targetObjectMetadata: { __typename?: 'object', id: any, nameSingular: string, namePlural: string }, targetFieldMetadata: { __typename?: 'field', id: any, name: string } } | null } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } }; -export type ServerlessFunctionFieldsFragment = { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, publishedVersions: Array, createdAt: any, updatedAt: any, latestVersionInputSchema?: Array<{ __typename?: 'FunctionParameter', name: string, type: string }> | null }; +export type ServerlessFunctionFieldsFragment = { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, 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, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, publishedVersions: Array, createdAt: any, updatedAt: any, latestVersionInputSchema?: Array<{ __typename?: 'FunctionParameter', name: string, type: string }> | null } }; +export type CreateOneServerlessFunctionItemMutation = { __typename?: 'Mutation', createOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, 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, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, publishedVersions: Array, createdAt: any, updatedAt: any, latestVersionInputSchema?: Array<{ __typename?: 'FunctionParameter', name: string, type: string }> | null } }; +export type DeleteOneServerlessFunctionMutation = { __typename?: 'Mutation', deleteOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; export type ExecuteOneServerlessFunctionMutationVariables = Exact<{ input: ExecuteServerlessFunctionInput; @@ -2025,14 +2019,14 @@ export type PublishOneServerlessFunctionMutationVariables = Exact<{ }>; -export type PublishOneServerlessFunctionMutation = { __typename?: 'Mutation', publishServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, publishedVersions: Array, createdAt: any, updatedAt: any, latestVersionInputSchema?: Array<{ __typename?: 'FunctionParameter', name: string, type: string }> | null } }; +export type PublishOneServerlessFunctionMutation = { __typename?: 'Mutation', publishServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, 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, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, publishedVersions: Array, createdAt: any, updatedAt: any, latestVersionInputSchema?: Array<{ __typename?: 'FunctionParameter', name: string, type: string }> | null } }; +export type UpdateOneServerlessFunctionMutation = { __typename?: 'Mutation', updateOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; export type FindManyAvailablePackagesQueryVariables = Exact<{ [key: string]: never; }>; @@ -2042,14 +2036,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, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, publishedVersions: Array, createdAt: any, updatedAt: any, latestVersionInputSchema?: Array<{ __typename?: 'FunctionParameter', name: string, type: string }> | null }> }; +export type GetManyServerlessFunctionsQuery = { __typename?: 'Query', findManyServerlessFunctions: Array<{ __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, 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, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, publishedVersions: Array, createdAt: any, updatedAt: any, latestVersionInputSchema?: Array<{ __typename?: 'FunctionParameter', name: string, type: string }> | null } }; +export type GetOneServerlessFunctionQuery = { __typename?: 'Query', findOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; export type FindOneServerlessFunctionSourceCodeQueryVariables = Exact<{ input: GetServerlessFunctionSourceCodeInput; @@ -2060,7 +2054,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":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"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":"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 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; @@ -2079,12 +2073,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"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"objectFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"objectFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fieldFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"fieldFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"objects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"objectFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isRemote"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"labelIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"imageIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"shortcut"}},{"kind":"Field","name":{"kind":"Name","value":"isLabelSyncedWithName"}},{"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":"fields"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fieldFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"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":"relationDefinition"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"relationId"}},{"kind":"Field","name":{"kind":"Name","value":"direction"}},{"kind":"Field","name":{"kind":"Name","value":"sourceObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sourceFieldMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"targetObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}}]}},{"kind":"Field","name":{"kind":"Name","value":"targetFieldMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]} as unknown as DocumentNode; -export const CreateOneServerlessFunctionItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOneServerlessFunctionItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"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":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"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":"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":"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 ExecuteOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ExecuteOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ExecuteServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"executeOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"data"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]} as unknown as DocumentNode; -export const PublishOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"PublishOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PublishServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"publishServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"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":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"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":"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":"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 FindManyAvailablePackagesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindManyAvailablePackages"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getAvailablePackages"}}]}}]} 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":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"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":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"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":"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":"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 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 5cd0f8983b35..de35163609bc 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -331,12 +331,6 @@ export type FullName = { lastName: Scalars['String']; }; -export type FunctionParameter = { - __typename?: 'FunctionParameter'; - name: Scalars['String']; - type: Scalars['String']; -}; - export type GenerateJwt = GenerateJwtOutputWithAuthTokens | GenerateJwtOutputWithSsoauth; export type GenerateJwtOutputWithAuthTokens = { @@ -983,7 +977,7 @@ export type ServerlessFunction = { description?: Maybe; id: Scalars['UUID']; latestVersion?: Maybe; - latestVersionInputSchema?: Maybe>; + latestVersionInputSchema?: Maybe; name: Scalars['String']; publishedVersions: Array; runtime: Scalars['String']; 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 af00ef49b608..a0b688badb3d 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 @@ -8,10 +8,7 @@ export const SERVERLESS_FUNCTION_FRAGMENT = gql` runtime syncStatus latestVersion - latestVersionInputSchema { - name - type - } + latestVersionInputSchema publishedVersions createdAt updatedAt diff --git a/packages/twenty-front/src/modules/ui/input/components/Select.tsx b/packages/twenty-front/src/modules/ui/input/components/Select.tsx index bbf53e6688bd..1895cbb0b97b 100644 --- a/packages/twenty-front/src/modules/ui/input/components/Select.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/Select.tsx @@ -91,7 +91,9 @@ export const Select = ({ const isDisabled = disabledFromProps || - (options.length <= 1 && !isDefined(callToActionButton)); + (options.length <= 1 && + !isDefined(callToActionButton) && + (!isDefined(emptyOption) || selectedOption !== emptyOption)); const { closeDropdown } = useDropdown(dropdownId); diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormServerlessFunction.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormServerlessFunction.tsx index db6aec133f30..c1e67d2a9f1e 100644 --- a/packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormServerlessFunction.tsx +++ b/packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormServerlessFunction.tsx @@ -1,13 +1,39 @@ +import { ReactNode } from 'react'; +import styled from '@emotion/styled'; + import { useGetManyServerlessFunctions } from '@/settings/serverless-functions/hooks/useGetManyServerlessFunctions'; +import { setNestedValue } from '@/workflow/utils/setNestedValue'; import { Select, SelectOption } from '@/ui/input/components/Select'; import { WorkflowEditGenericFormBase } from '@/workflow/components/WorkflowEditGenericFormBase'; import VariableTagInput from '@/workflow/search-variables/components/VariableTagInput'; import { WorkflowCodeStep } from '@/workflow/types/Workflow'; import { useTheme } from '@emotion/react'; -import { useState } from 'react'; import { IconCode, isDefined } from 'twenty-ui'; import { useDebouncedCallback } from 'use-debounce'; -import { capitalize } from '~/utils/string/capitalize'; +import { getDefaultFunctionInputFromInputSchema } from '@/workflow/utils/getDefaultFunctionInputFromInputSchema'; +import { FunctionInput } from '@/workflow/types/FunctionInput'; +import { mergeDefaultFunctionInputAndFunctionInput } from '@/workflow/utils/mergeDefaultFunctionInputAndFunctionInput'; + +const StyledContainer = styled.div` + display: inline-flex; + flex-direction: column; +`; + +const StyledLabel = styled.div` + color: ${({ theme }) => theme.font.color.light}; + font-size: ${({ theme }) => theme.font.size.md}; + font-weight: ${({ theme }) => theme.font.weight.semiBold}; + margin-top: ${({ theme }) => theme.spacing(3)}; + margin-bottom: ${({ theme }) => theme.spacing(2)}; +`; + +const StyledInputContainer = styled.div` + display: flex; + flex-direction: column; + position: relative; + gap: ${({ theme }) => theme.spacing(4)}; + padding-left: ${({ theme }) => theme.spacing(4)}; +`; type WorkflowEditActionFormServerlessFunctionProps = | { @@ -24,16 +50,30 @@ export const WorkflowEditActionFormServerlessFunction = ( props: WorkflowEditActionFormServerlessFunctionProps, ) => { const theme = useTheme(); - const { serverlessFunctions } = useGetManyServerlessFunctions(); - const defaultFunctionInput = - props.action.settings.input.serverlessFunctionInput; + const getFunctionInput = (serverlessFunctionId: string) => { + if (!serverlessFunctionId) { + return {}; + } + + const serverlessFunction = serverlessFunctions.find( + (f) => f.id === serverlessFunctionId, + ); + const inputSchema = serverlessFunction?.latestVersionInputSchema; + const defaultFunctionInput = + getDefaultFunctionInputFromInputSchema(inputSchema); - const [functionInput, setFunctionInput] = - useState>(defaultFunctionInput); + const existingFunctionInput = + props.action.settings.input.serverlessFunctionInput; + + return mergeDefaultFunctionInputAndFunctionInput({ + defaultFunctionInput, + functionInput: existingFunctionInput, + }); + }; - const [serverlessFunctionId, setServerlessFunctionId] = useState( + const functionInput = getFunctionInput( props.action.settings.input.serverlessFunctionId, ); @@ -48,14 +88,8 @@ export const WorkflowEditActionFormServerlessFunction = ( settings: { ...props.action.settings, input: { - serverlessFunctionId: - props.action.settings.input.serverlessFunctionId, - serverlessFunctionVersion: - props.action.settings.input.serverlessFunctionVersion, - serverlessFunctionInput: { - ...props.action.settings.input.serverlessFunctionInput, - ...newFunctionInput, - }, + ...props.action.settings.input, + serverlessFunctionInput: newFunctionInput, }, }, }); @@ -63,14 +97,11 @@ export const WorkflowEditActionFormServerlessFunction = ( 1_000, ); - const handleInputChange = (key: string, value: any) => { - const newFunctionInput = { ...functionInput, [key]: value }; - setFunctionInput(newFunctionInput); - updateFunctionInput(newFunctionInput); + const handleInputChange = (value: any, path: string[]) => { + updateFunctionInput(setNestedValue(functionInput, path, value)); }; const availableFunctions: Array> = [ - { label: 'None', value: '' }, ...serverlessFunctions .filter((serverlessFunction) => isDefined(serverlessFunction.latestVersion), @@ -83,36 +114,58 @@ export const WorkflowEditActionFormServerlessFunction = ( ]; const handleFunctionChange = (newServerlessFunctionId: string) => { - setServerlessFunctionId(newServerlessFunctionId); - const serverlessFunction = serverlessFunctions.find( (f) => f.id === newServerlessFunctionId, ); - const serverlessFunctionVersion = - serverlessFunction?.latestVersion || 'latest'; - - const defaultFunctionInput = serverlessFunction?.latestVersionInputSchema - ? serverlessFunction.latestVersionInputSchema - .map((parameter) => parameter.name) - .reduce((acc, name) => ({ ...acc, [name]: null }), {}) - : {}; + const newProps = { + ...props.action, + settings: { + ...props.action.settings, + input: { + serverlessFunctionId: newServerlessFunctionId, + serverlessFunctionVersion: + serverlessFunction?.latestVersion || 'latest', + serverlessFunctionInput: getFunctionInput(newServerlessFunctionId), + }, + }, + }; if (!props.readonly) { - props.onActionUpdate({ - ...props.action, - settings: { - ...props.action.settings, - input: { - serverlessFunctionId: newServerlessFunctionId, - serverlessFunctionVersion, - serverlessFunctionInput: defaultFunctionInput, - }, - }, - }); + props.onActionUpdate(newProps); } + }; - setFunctionInput(defaultFunctionInput); + const renderFields = ( + functionInput: FunctionInput, + path: string[] = [], + ): ReactNode | undefined => { + return Object.entries(functionInput).map(([inputKey, inputValue]) => { + const currentPath = [...path, inputKey]; + const pathKey = currentPath.join('.'); + + if (inputValue !== null && typeof inputValue === 'object') { + return ( + + {inputKey} + + {renderFields(inputValue, currentPath)} + + + ); + } else { + return ( + handleInputChange(value, currentPath)} + /> + ); + } + }); }; return ( @@ -125,21 +178,13 @@ export const WorkflowEditActionFormServerlessFunction = ( dropdownId="select-serverless-function-id" label="Function" fullWidth - value={serverlessFunctionId} + value={props.action.settings.input.serverlessFunctionId} options={availableFunctions} + emptyOption={{ label: 'None', value: '' }} disabled={props.readonly} onChange={handleFunctionChange} /> - {functionInput && - Object.entries(functionInput).map(([inputKey, inputValue]) => ( - handleInputChange(inputKey, value)} - /> - ))} + {functionInput && renderFields(functionInput)} ); }; diff --git a/packages/twenty-front/src/modules/workflow/types/FunctionInput.ts b/packages/twenty-front/src/modules/workflow/types/FunctionInput.ts new file mode 100644 index 000000000000..62780021378b --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/types/FunctionInput.ts @@ -0,0 +1,5 @@ +export type FunctionInput = + | { + [name: string]: FunctionInput; + } + | any; diff --git a/packages/twenty-front/src/modules/workflow/types/InputSchema.ts b/packages/twenty-front/src/modules/workflow/types/InputSchema.ts new file mode 100644 index 000000000000..5970bc56fec3 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/types/InputSchema.ts @@ -0,0 +1,18 @@ +type InputSchemaPropertyType = + | 'string' + | 'number' + | 'boolean' + | 'object' + | 'array' + | 'unknown'; + +type InputSchemaProperty = { + type: InputSchemaPropertyType; + enum?: string[]; + items?: InputSchemaProperty; + properties?: InputSchema; +}; + +export type InputSchema = { + [name: string]: InputSchemaProperty; +}; diff --git a/packages/twenty-front/src/modules/workflow/utils/__tests__/getDefaultFunctionInputFromInputSchema.test.ts b/packages/twenty-front/src/modules/workflow/utils/__tests__/getDefaultFunctionInputFromInputSchema.test.ts new file mode 100644 index 000000000000..ec0bb682f9d0 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/utils/__tests__/getDefaultFunctionInputFromInputSchema.test.ts @@ -0,0 +1,29 @@ +import { getDefaultFunctionInputFromInputSchema } from '@/workflow/utils/getDefaultFunctionInputFromInputSchema'; +import { InputSchema } from '@/workflow/types/InputSchema'; + +describe('getDefaultFunctionInputFromInputSchema', () => { + it('should init function input properly', () => { + const inputSchema = { + params: { + type: 'object', + properties: { + a: { + type: 'string', + }, + b: { + type: 'number', + }, + }, + }, + } as InputSchema; + const expectedResult = { + params: { + a: null, + b: null, + }, + }; + expect(getDefaultFunctionInputFromInputSchema(inputSchema)).toEqual( + expectedResult, + ); + }); +}); diff --git a/packages/twenty-front/src/modules/workflow/utils/__tests__/mergeDefaultFunctionInputAndFunctionInput.test.ts b/packages/twenty-front/src/modules/workflow/utils/__tests__/mergeDefaultFunctionInputAndFunctionInput.test.ts new file mode 100644 index 000000000000..9513a4fe55e1 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/utils/__tests__/mergeDefaultFunctionInputAndFunctionInput.test.ts @@ -0,0 +1,21 @@ +import { mergeDefaultFunctionInputAndFunctionInput } from '@/workflow/utils/mergeDefaultFunctionInputAndFunctionInput'; + +describe('mergeDefaultFunctionInputAndFunctionInput', () => { + it('should merge properly', () => { + const defaultFunctionInput = { + params: { a: null, b: null, c: { cc: null } }, + }; + const functionInput = { + params: { a: 'a', c: 'c' }, + }; + const expectedResult = { + params: { a: 'a', b: null, c: { cc: null } }, + }; + expect( + mergeDefaultFunctionInputAndFunctionInput({ + defaultFunctionInput, + functionInput, + }), + ).toEqual(expectedResult); + }); +}); diff --git a/packages/twenty-front/src/modules/workflow/utils/__tests__/setNestedValue.test.ts b/packages/twenty-front/src/modules/workflow/utils/__tests__/setNestedValue.test.ts new file mode 100644 index 000000000000..1c2bf307fc6a --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/utils/__tests__/setNestedValue.test.ts @@ -0,0 +1,11 @@ +import { setNestedValue } from '@/workflow/utils/setNestedValue'; + +describe('setNestedValue', () => { + it('should set nested value properly', () => { + const obj = { a: { b: 'b' } }; + const path = ['a', 'b']; + const newValue = 'bb'; + const expectedResult = { a: { b: newValue } }; + expect(setNestedValue(obj, path, newValue)).toEqual(expectedResult); + }); +}); diff --git a/packages/twenty-front/src/modules/workflow/utils/getDefaultFunctionInputFromInputSchema.ts b/packages/twenty-front/src/modules/workflow/utils/getDefaultFunctionInputFromInputSchema.ts new file mode 100644 index 000000000000..65c5e1e3a6bf --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/utils/getDefaultFunctionInputFromInputSchema.ts @@ -0,0 +1,22 @@ +import { InputSchema } from '@/workflow/types/InputSchema'; +import { FunctionInput } from '@/workflow/types/FunctionInput'; +import { isDefined } from '~/utils/isDefined'; + +export const getDefaultFunctionInputFromInputSchema = ( + inputSchema: InputSchema | undefined, +): FunctionInput => { + return isDefined(inputSchema) + ? Object.entries(inputSchema).reduce((acc, [key, value]) => { + if (['string', 'number', 'boolean'].includes(value.type)) { + acc[key] = null; + } else if (value.type === 'object') { + acc[key] = isDefined(value.properties) + ? getDefaultFunctionInputFromInputSchema(value.properties) + : {}; + } else if (value.type === 'array' && isDefined(value.items)) { + acc[key] = []; + } + return acc; + }, {} as FunctionInput) + : {}; +}; diff --git a/packages/twenty-front/src/modules/workflow/utils/mergeDefaultFunctionInputAndFunctionInput.ts b/packages/twenty-front/src/modules/workflow/utils/mergeDefaultFunctionInputAndFunctionInput.ts new file mode 100644 index 000000000000..d7241477cedd --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/utils/mergeDefaultFunctionInputAndFunctionInput.ts @@ -0,0 +1,32 @@ +import { FunctionInput } from '@/workflow/types/FunctionInput'; + +export const mergeDefaultFunctionInputAndFunctionInput = ({ + defaultFunctionInput, + functionInput, +}: { + defaultFunctionInput: FunctionInput; + functionInput: FunctionInput; +}): FunctionInput => { + const result: FunctionInput = {}; + + for (const key of Object.keys(defaultFunctionInput)) { + if (!(key in functionInput)) { + result[key] = defaultFunctionInput[key]; + } else { + if ( + defaultFunctionInput[key] !== null && + typeof defaultFunctionInput[key] === 'object' + ) { + result[key] = mergeDefaultFunctionInputAndFunctionInput({ + defaultFunctionInput: defaultFunctionInput[key], + functionInput: + typeof functionInput[key] === 'object' ? functionInput[key] : {}, + }); + } else { + result[key] = functionInput[key]; + } + } + } + + return result; +}; diff --git a/packages/twenty-front/src/modules/workflow/utils/setNestedValue.ts b/packages/twenty-front/src/modules/workflow/utils/setNestedValue.ts new file mode 100644 index 000000000000..a3b351cfbc87 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/utils/setNestedValue.ts @@ -0,0 +1,10 @@ +export const setNestedValue = (obj: any, path: string[], value: any) => { + const newObj = { ...obj }; + path.reduce((o, key, index) => { + if (index === path.length - 1) { + o[key] = value; + } + return o[key] || {}; + }, newObj); + return newObj; +}; diff --git a/packages/twenty-server/src/engine/core-modules/serverless/drivers/constants/base-typescript-project/src/index.ts b/packages/twenty-server/src/engine/core-modules/serverless/drivers/constants/base-typescript-project/src/index.ts index da19597d5cfb..a897e86c928f 100644 --- a/packages/twenty-server/src/engine/core-modules/serverless/drivers/constants/base-typescript-project/src/index.ts +++ b/packages/twenty-server/src/engine/core-modules/serverless/drivers/constants/base-typescript-project/src/index.ts @@ -1,7 +1,10 @@ -export const handler = async ( - event: object, - context: object, -): Promise => { +export const main = async (params: { + a: string; + b: number; +}): Promise => { + const { a, b } = params; + // Your code here + return {}; }; diff --git a/packages/twenty-server/src/engine/core-modules/serverless/drivers/lambda.driver.ts b/packages/twenty-server/src/engine/core-modules/serverless/drivers/lambda.driver.ts index cf6315ca9a84..5364a30d3aec 100644 --- a/packages/twenty-server/src/engine/core-modules/serverless/drivers/lambda.driver.ts +++ b/packages/twenty-server/src/engine/core-modules/serverless/drivers/lambda.driver.ts @@ -227,7 +227,7 @@ export class LambdaDriver implements ServerlessDriver { ZipFile: await fs.readFile(lambdaZipPath), }, FunctionName: serverlessFunction.id, - Handler: 'src/index.handler', + Handler: 'src/index.main', Layers: [layerArn], Environment: { Variables: envVariables, diff --git a/packages/twenty-server/src/engine/core-modules/serverless/drivers/local.driver.ts b/packages/twenty-server/src/engine/core-modules/serverless/drivers/local.driver.ts index 769c5c61524f..50943f428820 100644 --- a/packages/twenty-server/src/engine/core-modules/serverless/drivers/local.driver.ts +++ b/packages/twenty-server/src/engine/core-modules/serverless/drivers/local.driver.ts @@ -94,9 +94,9 @@ export class LocalDriver implements ServerlessDriver { process.env = ${JSON.stringify(envVariables)} process.on('message', async (message) => { - const { event, context } = message; + const { params } = message; try { - const result = await index_1.handler(event, context); + const result = await index_1.main(params); process.send(result); } catch (error) { process.send({ @@ -245,7 +245,7 @@ export class LocalDriver implements ServerlessDriver { } }); - child.send({ event: payload }); + child.send({ params: payload }); }); } catch (error) { return { diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/function-parameter.dto.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/function-parameter.dto.ts deleted file mode 100644 index 4ad9c925ccb7..000000000000 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/function-parameter.dto.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Field, ObjectType } from '@nestjs/graphql'; - -import { IsString } from 'class-validator'; - -@ObjectType() -export class FunctionParameter { - @IsString() - @Field(() => String) - name: string; - - @IsString() - @Field(() => String) - type: string; -} diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto.ts index 26fe451e2fd1..d97a4993b64e 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 @@ -18,10 +18,11 @@ import { IsString, IsUUID, } from 'class-validator'; +import GraphQLJSON from 'graphql-type-json'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; -import { FunctionParameter } from 'src/engine/metadata-modules/serverless-function/dtos/function-parameter.dto'; import { ServerlessFunctionSyncStatus } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity'; +import { InputSchema } from 'src/modules/code-introspection/types/input-schema.type'; registerEnumType(ServerlessFunctionSyncStatus, { name: 'ServerlessFunctionSyncStatus', @@ -65,9 +66,8 @@ export class ServerlessFunctionDTO { @Field(() => [String], { nullable: false }) publishedVersions: string[]; - @IsArray() - @Field(() => [FunctionParameter], { nullable: true }) - latestVersionInputSchema: FunctionParameter[] | null; + @Field(() => GraphQLJSON, { nullable: true }) + latestVersionInputSchema: InputSchema; @IsEnum(ServerlessFunctionSyncStatus) @IsNotEmpty() diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/listeners/serverless-function-publication.listener.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/listeners/serverless-function-publication.listener.ts index af866b3f5d07..8eb1d8851779 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/listeners/serverless-function-publication.listener.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/listeners/serverless-function-publication.listener.ts @@ -29,7 +29,7 @@ export class ServerlessFunctionPublicationListener { serverlessFunctionVersion: string; }>, ): Promise { - payload.events.forEach(async (event) => { + for (const event of payload.events) { const sourceCode = await this.serverlessFunctionService.getServerlessFunctionSourceCode( payload.workspaceId, @@ -48,12 +48,12 @@ export class ServerlessFunctionPublicationListener { } const latestVersionInputSchema = - await this.codeIntrospectionService.getFunctionInputSchema(indexCode); + this.codeIntrospectionService.getFunctionInputSchema(indexCode); await this.serverlessFunctionRepository.update( { id: event.serverlessFunctionId }, { latestVersionInputSchema }, ); - }); + } } } 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 2bd890e6611f..c35dc4a2bbd0 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 @@ -6,7 +6,7 @@ import { UpdateDateColumn, } from 'typeorm'; -import { FunctionParameter } from 'src/engine/metadata-modules/serverless-function/dtos/function-parameter.dto'; +import { InputSchema } from 'src/modules/code-introspection/types/input-schema.type'; export enum ServerlessFunctionSyncStatus { NOT_READY = 'NOT_READY', @@ -35,7 +35,7 @@ export class ServerlessFunctionEntity { publishedVersions: string[]; @Column({ nullable: true, type: 'jsonb' }) - latestVersionInputSchema: FunctionParameter[]; + latestVersionInputSchema: InputSchema; @Column({ nullable: false, default: ServerlessFunctionRuntime.NODE18 }) runtime: ServerlessFunctionRuntime; diff --git a/packages/twenty-server/src/modules/code-introspection/__tests__/code-introspection.service.spec.ts b/packages/twenty-server/src/modules/code-introspection/__tests__/code-introspection.service.spec.ts index 1b1347b8780b..71d8e2a4838d 100644 --- a/packages/twenty-server/src/modules/code-introspection/__tests__/code-introspection.service.spec.ts +++ b/packages/twenty-server/src/modules/code-introspection/__tests__/code-introspection.service.spec.ts @@ -1,6 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { CodeIntrospectionException } from 'src/modules/code-introspection/code-introspection.exception'; import { CodeIntrospectionService } from 'src/modules/code-introspection/code-introspection.service'; describe('CodeIntrospectionService', () => { @@ -19,118 +18,121 @@ describe('CodeIntrospectionService', () => { }); describe('getFunctionInputSchema', () => { - it('should analyze a function declaration correctly', () => { + it('should analyze a simple function correctly', () => { const fileContent = ` function testFunction(param1: string, param2: number): void { - console.log(param1, param2); + return; } `; - const result = service.getFunctionInputSchema(fileContent); - expect(result).toEqual([ - { name: 'param1', type: 'string' }, - { name: 'param2', type: 'number' }, - ]); + expect(result).toEqual({ + param1: { type: 'string' }, + param2: { type: 'number' }, + }); }); - it('should analyze an arrow function correctly', () => { + it('should analyze a arrow function correctly', () => { const fileContent = ` - const testArrowFunction = (param1: string, param2: number): void => { - console.log(param1, param2); + export const main = async ( + param1: string, + param2: number, + ): Promise => { + return params; }; `; - - const result = service.getFunctionInputSchema(fileContent); - - expect(result).toEqual([ - { name: 'param1', type: 'string' }, - { name: 'param2', type: 'number' }, - ]); - }); - - it('should return an empty array for files without functions', () => { - const fileContent = ` - const x = 5; - console.log(x); - `; - const result = service.getFunctionInputSchema(fileContent); - expect(result).toEqual([]); - }); - - it('should throw an exception for multiple function declarations', () => { - const fileContent = ` - function func1(param1: string) {} - function func2(param2: number) {} - `; - - expect(() => service.getFunctionInputSchema(fileContent)).toThrow( - CodeIntrospectionException, - ); - expect(() => service.getFunctionInputSchema(fileContent)).toThrow( - 'Only one function is allowed', - ); + expect(result).toEqual({ + param1: { type: 'string' }, + param2: { type: 'number' }, + }); }); - it('should throw an exception for multiple arrow functions', () => { + it('should analyze a complex function correctly', () => { const fileContent = ` - const func1 = (param1: string) => {}; - const func2 = (param2: number) => {}; - `; - - expect(() => service.getFunctionInputSchema(fileContent)).toThrow( - CodeIntrospectionException, - ); - expect(() => service.getFunctionInputSchema(fileContent)).toThrow( - 'Only one arrow function is allowed', - ); - }); - - it('should correctly analyze complex types', () => { - const fileContent = ` - function complexFunction(param1: string[], param2: { key: number }): Promise { - return Promise.resolve(true); + function testFunction( + params: { + param1: string; + param2: number; + param3: boolean; + param4: object; + param5: { subParam1: string }; + param6: "my" | "enum"; + param7: string[]; + } + ): void { + return } `; - const result = service.getFunctionInputSchema(fileContent); - expect(result).toEqual([ - { name: 'param1', type: 'string[]' }, - { name: 'param2', type: '{ key: number; }' }, - ]); + expect(result).toEqual({ + params: { + type: 'object', + properties: { + param1: { type: 'string' }, + param2: { type: 'number' }, + param3: { type: 'boolean' }, + param4: { type: 'object' }, + param5: { + type: 'object', + properties: { + subParam1: { type: 'string' }, + }, + }, + param6: { type: 'string', enum: ['my', 'enum'] }, + param7: { type: 'array', items: { type: 'string' } }, + }, + }, + }); }); }); - describe('generateFakeDataForFunction', () => { - it('should generate fake data for function', () => { + describe('generateInputData', () => { + it('should generate fake data for simple function', () => { const fileContent = ` - const testArrowFunction = (param1: string, param2: number): void => { - console.log(param1, param2); - }; + function testFunction(param1: string, param2: number): void { + return; + } `; + const inputSchema = service.getFunctionInputSchema(fileContent); + const result = service.generateInputData(inputSchema); - const result = service.generateInputData(fileContent); - - expect(typeof result['param1']).toEqual('string'); - expect(typeof result['param2']).toEqual('number'); + expect(result).toEqual({ param1: 'generated-string-value', param2: 1 }); }); it('should generate fake data for complex function', () => { const fileContent = ` - const testArrowFunction = (param1: string[], param2: { key: number }): void => { - console.log(param1, param2); - }; + function testFunction( + params: { + param1: string; + param2: number; + param3: boolean; + param4: object; + param5: { subParam1: string }; + param6: "my" | "enum"; + param7: string[]; + } + ): void { + return + } `; - const result = service.generateInputData(fileContent); - - expect(Array.isArray(result['param1'])).toBeTruthy(); - expect(typeof result['param1'][0]).toEqual('string'); - expect(typeof result['param2']).toEqual('object'); - expect(typeof result['param2']['key']).toEqual('number'); + const inputSchema = service.getFunctionInputSchema(fileContent); + const result = service.generateInputData(inputSchema); + + expect(result).toEqual({ + params: { + param1: 'generated-string-value', + param2: 1, + param3: true, + param4: {}, + param5: { subParam1: 'generated-string-value' }, + param6: 'my', + param7: ['generated-string-value'], + }, + }); }); }); }); diff --git a/packages/twenty-server/src/modules/code-introspection/code-introspection.service.ts b/packages/twenty-server/src/modules/code-introspection/code-introspection.service.ts index 72a7bf6e254c..3b188cdab837 100644 --- a/packages/twenty-server/src/modules/code-introspection/code-introspection.service.ts +++ b/packages/twenty-server/src/modules/code-introspection/code-introspection.service.ts @@ -1,105 +1,157 @@ import { Injectable } from '@nestjs/common'; import { + ArrayTypeNode, + createSourceFile, + LiteralTypeNode, + PropertySignature, + ScriptTarget, + StringLiteral, + SyntaxKind, + TypeNode, + UnionTypeNode, + VariableStatement, ArrowFunction, FunctionDeclaration, - ParameterDeclaration, - Project, - SyntaxKind, -} from 'ts-morph'; +} from 'typescript'; -import { FunctionParameter } from 'src/engine/metadata-modules/serverless-function/dtos/function-parameter.dto'; import { generateFakeValue } from 'src/engine/utils/generate-fake-value'; +import { isDefined } from 'src/utils/is-defined'; import { - CodeIntrospectionException, - CodeIntrospectionExceptionCode, -} from 'src/modules/code-introspection/code-introspection.exception'; + InputSchema, + InputSchemaProperty, +} from 'src/modules/code-introspection/types/input-schema.type'; @Injectable() export class CodeIntrospectionService { - private project: Project; - - constructor() { - this.project = new Project(); - } + public generateInputData(inputSchema: InputSchema) { + return Object.entries(inputSchema).reduce((acc, [key, value]) => { + if (isDefined(value.enum)) { + acc[key] = value.enum?.[0]; + } else if (['string', 'number', 'boolean'].includes(value.type)) { + acc[key] = generateFakeValue(value.type); + } else if (value.type === 'object') { + acc[key] = isDefined(value.properties) + ? this.generateInputData(value.properties) + : {}; + } else if (value.type === 'array' && isDefined(value.items)) { + acc[key] = [generateFakeValue(value.items.type)]; + } - public generateInputData(fileContent: string, fileName = 'temp.ts') { - const parameters = this.getFunctionInputSchema(fileContent, fileName); - - return this.generateFakeDataFromParams(parameters); + return acc; + }, {}); } - public getFunctionInputSchema( - fileContent: string, - fileName = 'temp.ts', - ): FunctionParameter[] { - const sourceFile = this.project.createSourceFile(fileName, fileContent, { - overwrite: true, - }); - - const functionDeclarations = sourceFile.getFunctions(); - - if (functionDeclarations.length > 0) { - return this.getFunctionParameters(functionDeclarations); - } - - const arrowFunctions = sourceFile.getDescendantsOfKind( - SyntaxKind.ArrowFunction, + public getFunctionInputSchema(fileContent: string): InputSchema { + const sourceFile = createSourceFile( + 'temp.ts', + fileContent, + ScriptTarget.ESNext, + true, ); - if (arrowFunctions.length > 0) { - return this.getArrowFunctionParameters(arrowFunctions); - } - - return []; - } - - private getFunctionParameters( - functionDeclarations: FunctionDeclaration[], - ): FunctionParameter[] { - if (functionDeclarations.length > 1) { - throw new CodeIntrospectionException( - 'Only one function is allowed', - CodeIntrospectionExceptionCode.ONLY_ONE_FUNCTION_ALLOWED, - ); - } - - const functionDeclaration = functionDeclarations[0]; + const schema: InputSchema = {}; + + sourceFile.forEachChild((node) => { + if (node.kind === SyntaxKind.FunctionDeclaration) { + const funcNode = node as FunctionDeclaration; + const params = funcNode.parameters; + + params.forEach((param) => { + const paramName = param.name.getText(); + const typeNode = param.type; + + if (typeNode) { + schema[paramName] = this.getTypeString(typeNode); + } else { + schema[paramName] = { type: 'unknown' }; + } + }); + } else if (node.kind === SyntaxKind.VariableStatement) { + const varStatement = node as VariableStatement; + + varStatement.declarationList.declarations.forEach((declaration) => { + if ( + declaration.initializer && + declaration.initializer.kind === SyntaxKind.ArrowFunction + ) { + const arrowFunction = declaration.initializer as ArrowFunction; + const params = arrowFunction.parameters; + + params.forEach((param: any) => { + const paramName = param.name.text; + const typeNode = param.type; + + if (typeNode) { + schema[paramName] = this.getTypeString(typeNode); + } else { + schema[paramName] = { type: 'unknown' }; + } + }); + } + }); + } + }); - return functionDeclaration.getParameters().map(this.buildFunctionParameter); + return schema; } - private getArrowFunctionParameters( - arrowFunctions: ArrowFunction[], - ): FunctionParameter[] { - if (arrowFunctions.length > 1) { - throw new CodeIntrospectionException( - 'Only one arrow function is allowed', - CodeIntrospectionExceptionCode.ONLY_ONE_FUNCTION_ALLOWED, - ); + private getTypeString(typeNode: TypeNode): InputSchemaProperty { + switch (typeNode.kind) { + case SyntaxKind.NumberKeyword: + return { type: 'number' }; + case SyntaxKind.StringKeyword: + return { type: 'string' }; + case SyntaxKind.BooleanKeyword: + return { type: 'boolean' }; + case SyntaxKind.ArrayType: + return { + type: 'array', + items: this.getTypeString((typeNode as ArrayTypeNode).elementType), + }; + case SyntaxKind.ObjectKeyword: + return { type: 'object' }; + case SyntaxKind.TypeLiteral: { + const properties: InputSchema = {}; + + (typeNode as any).members.forEach((member: PropertySignature) => { + if (member.name && member.type) { + const memberName = (member.name as any).text; + + properties[memberName] = this.getTypeString(member.type); + } + }); + + return { type: 'object', properties }; + } + case SyntaxKind.UnionType: { + const unionNode = typeNode as UnionTypeNode; + const enumValues: string[] = []; + + let isEnum = true; + + unionNode.types.forEach((subType) => { + if (subType.kind === SyntaxKind.LiteralType) { + const literal = (subType as LiteralTypeNode).literal; + + if (literal.kind === SyntaxKind.StringLiteral) { + enumValues.push((literal as StringLiteral).text); + } else { + isEnum = false; + } + } else { + isEnum = false; + } + }); + + if (isEnum) { + return { type: 'string', enum: enumValues }; + } + + return { type: 'unknown' }; + } + default: + return { type: 'unknown' }; } - - const arrowFunction = arrowFunctions[0]; - - return arrowFunction.getParameters().map(this.buildFunctionParameter); - } - - private buildFunctionParameter( - parameter: ParameterDeclaration, - ): FunctionParameter { - return { - name: parameter.getName(), - type: parameter.getType().getText(), - }; - } - - private generateFakeDataFromParams( - params: FunctionParameter[], - ): Record { - return params.reduce((acc, param) => { - acc[param.name] = generateFakeValue(param.type); - - return acc; - }, {}); } } diff --git a/packages/twenty-server/src/modules/code-introspection/types/input-schema.type.ts b/packages/twenty-server/src/modules/code-introspection/types/input-schema.type.ts new file mode 100644 index 000000000000..312615cabded --- /dev/null +++ b/packages/twenty-server/src/modules/code-introspection/types/input-schema.type.ts @@ -0,0 +1,18 @@ +type InputSchemaPropertyType = + | 'string' + | 'number' + | 'boolean' + | 'object' + | 'array' + | 'unknown'; + +export type InputSchemaProperty = { + type: InputSchemaPropertyType; + enum?: string[]; + items?: InputSchemaProperty; // used to describe array type elements + properties?: InputSchema; // used to describe object type elements +}; + +export type InputSchema = { + [name: string]: InputSchemaProperty; +}; diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-builder.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-builder.workspace-service.ts index 655dd8938900..0b6ea72fe9e0 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-builder.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-builder.workspace-service.ts @@ -173,18 +173,16 @@ export class WorkflowBuilderWorkspaceService { return {}; } + const inputSchema = + codeIntrospectionService.getFunctionInputSchema(sourceCode); const fakeFunctionInput = - codeIntrospectionService.generateInputData(sourceCode); - - // handle the case when event parameter is destructured: - // (event: {param1: string; param2: number}) VS ({param1, param2}: {param1: string; param2: number}) - const formattedInput = Object.values(fakeFunctionInput)[0]; + codeIntrospectionService.generateInputData(inputSchema); const resultFromFakeInput = await serverlessFunctionService.executeOneServerlessFunction( serverlessFunctionId, workspaceId, - formattedInput, + fakeFunctionInput, serverlessFunctionVersion, ); diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/types/workflow-step-settings.type.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/types/workflow-step-settings.type.ts index e14ac1805792..4e812b436b20 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/types/workflow-step-settings.type.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/types/workflow-step-settings.type.ts @@ -1,5 +1,4 @@ export type OutputSchema = object; -export type InputSchema = object; type BaseWorkflowStepSettings = { input: object;