8723 workflow add editor in serverless function code step (#8805)
- create a serverless function when creating a new workflow code step - add code editor in workflow code step - move workflowVersion steps management from frontend to backend - add a custom resolver for workflow-version management - fix optimistic rendering on frontend - fix css - delete serverless function when deleting workflow code step TODO - Don't update serverlessFunction if no code change - Factorize what can be between crud trigger and crud step - Publish serverless version when activating workflow - delete serverless functions when deleting workflow or workflowVersion - fix optimistic rendering for code updates - Unify CRUD types <img width="1279" alt="image" src="https://github.com/user-attachments/assets/3d97ee9f-4b96-4abc-9d36-5c0280058be4">
This commit is contained in:
@ -69,7 +69,7 @@
|
|||||||
"test": {},
|
"test": {},
|
||||||
"storybook:build": {
|
"storybook:build": {
|
||||||
"options": {
|
"options": {
|
||||||
"env": { "NODE_OPTIONS": "--max_old_space_size=6500" }
|
"env": { "NODE_OPTIONS": "--max_old_space_size=7000" }
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"docs": { "env": { "STORYBOOK_SCOPE": "ui-docs" } },
|
"docs": { "env": { "STORYBOOK_SCOPE": "ui-docs" } },
|
||||||
|
|||||||
@ -39,7 +39,7 @@ const documents = {
|
|||||||
"\n mutation ExecuteOneServerlessFunction(\n $input: ExecuteServerlessFunctionInput!\n ) {\n executeOneServerlessFunction(input: $input) {\n data\n duration\n status\n error\n }\n }\n": types.ExecuteOneServerlessFunctionDocument,
|
"\n mutation ExecuteOneServerlessFunction(\n $input: ExecuteServerlessFunctionInput!\n ) {\n executeOneServerlessFunction(input: $input) {\n data\n duration\n status\n error\n }\n }\n": types.ExecuteOneServerlessFunctionDocument,
|
||||||
"\n \n mutation PublishOneServerlessFunction(\n $input: PublishServerlessFunctionInput!\n ) {\n publishServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.PublishOneServerlessFunctionDocument,
|
"\n \n mutation PublishOneServerlessFunction(\n $input: PublishServerlessFunctionInput!\n ) {\n publishServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.PublishOneServerlessFunctionDocument,
|
||||||
"\n \n mutation UpdateOneServerlessFunction($input: UpdateServerlessFunctionInput!) {\n updateOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.UpdateOneServerlessFunctionDocument,
|
"\n \n mutation UpdateOneServerlessFunction($input: UpdateServerlessFunctionInput!) {\n updateOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.UpdateOneServerlessFunctionDocument,
|
||||||
"\n query FindManyAvailablePackages {\n getAvailablePackages\n }\n": types.FindManyAvailablePackagesDocument,
|
"\n query FindManyAvailablePackages($input: ServerlessFunctionIdInput!) {\n getAvailablePackages(input: $input)\n }\n": types.FindManyAvailablePackagesDocument,
|
||||||
"\n \n query GetManyServerlessFunctions {\n findManyServerlessFunctions {\n ...ServerlessFunctionFields\n }\n }\n": types.GetManyServerlessFunctionsDocument,
|
"\n \n query GetManyServerlessFunctions {\n findManyServerlessFunctions {\n ...ServerlessFunctionFields\n }\n }\n": types.GetManyServerlessFunctionsDocument,
|
||||||
"\n \n query GetOneServerlessFunction($input: ServerlessFunctionIdInput!) {\n findOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.GetOneServerlessFunctionDocument,
|
"\n \n query GetOneServerlessFunction($input: ServerlessFunctionIdInput!) {\n findOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.GetOneServerlessFunctionDocument,
|
||||||
"\n query FindOneServerlessFunctionSourceCode(\n $input: GetServerlessFunctionSourceCodeInput!\n ) {\n getServerlessFunctionSourceCode(input: $input)\n }\n": types.FindOneServerlessFunctionSourceCodeDocument,
|
"\n query FindOneServerlessFunctionSourceCode(\n $input: GetServerlessFunctionSourceCodeInput!\n ) {\n getServerlessFunctionSourceCode(input: $input)\n }\n": types.FindOneServerlessFunctionSourceCodeDocument,
|
||||||
@ -166,7 +166,7 @@ export function graphql(source: "\n \n mutation UpdateOneServerlessFunction($i
|
|||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n query FindManyAvailablePackages {\n getAvailablePackages\n }\n"): (typeof documents)["\n query FindManyAvailablePackages {\n getAvailablePackages\n }\n"];
|
export function graphql(source: "\n query FindManyAvailablePackages($input: ServerlessFunctionIdInput!) {\n getAvailablePackages(input: $input)\n }\n"): (typeof documents)["\n query FindManyAvailablePackages($input: ServerlessFunctionIdInput!) {\n getAvailablePackages(input: $input)\n }\n"];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -263,6 +263,13 @@ export type CreateServerlessFunctionInput = {
|
|||||||
name: Scalars['String']['input'];
|
name: Scalars['String']['input'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CreateWorkflowVersionStepInput = {
|
||||||
|
/** New step type */
|
||||||
|
stepType: Scalars['String']['input'];
|
||||||
|
/** Workflow version ID */
|
||||||
|
workflowVersionId: Scalars['String']['input'];
|
||||||
|
};
|
||||||
|
|
||||||
export type CursorPaging = {
|
export type CursorPaging = {
|
||||||
/** Paginate after opaque cursor */
|
/** Paginate after opaque cursor */
|
||||||
after?: InputMaybe<Scalars['ConnectionCursor']['input']>;
|
after?: InputMaybe<Scalars['ConnectionCursor']['input']>;
|
||||||
@ -298,6 +305,13 @@ export type DeleteSsoOutput = {
|
|||||||
identityProviderId: Scalars['String']['output'];
|
identityProviderId: Scalars['String']['output'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DeleteWorkflowVersionStepInput = {
|
||||||
|
/** Step to delete ID */
|
||||||
|
stepId: Scalars['String']['input'];
|
||||||
|
/** Workflow version ID */
|
||||||
|
workflowVersionId: Scalars['String']['input'];
|
||||||
|
};
|
||||||
|
|
||||||
/** Schema update on a table */
|
/** Schema update on a table */
|
||||||
export enum DistantTableUpdate {
|
export enum DistantTableUpdate {
|
||||||
ColumnsAdded = 'COLUMNS_ADDED',
|
ColumnsAdded = 'COLUMNS_ADDED',
|
||||||
@ -553,6 +567,7 @@ export type Mutation = {
|
|||||||
createOneRemoteServer: RemoteServer;
|
createOneRemoteServer: RemoteServer;
|
||||||
createOneServerlessFunction: ServerlessFunction;
|
createOneServerlessFunction: ServerlessFunction;
|
||||||
createSAMLIdentityProvider: SetupSsoOutput;
|
createSAMLIdentityProvider: SetupSsoOutput;
|
||||||
|
createWorkflowVersionStep: WorkflowAction;
|
||||||
deactivateWorkflowVersion: Scalars['Boolean']['output'];
|
deactivateWorkflowVersion: Scalars['Boolean']['output'];
|
||||||
deleteCurrentWorkspace: Workspace;
|
deleteCurrentWorkspace: Workspace;
|
||||||
deleteOneField: Field;
|
deleteOneField: Field;
|
||||||
@ -562,6 +577,7 @@ export type Mutation = {
|
|||||||
deleteOneServerlessFunction: ServerlessFunction;
|
deleteOneServerlessFunction: ServerlessFunction;
|
||||||
deleteSSOIdentityProvider: DeleteSsoOutput;
|
deleteSSOIdentityProvider: DeleteSsoOutput;
|
||||||
deleteUser: User;
|
deleteUser: User;
|
||||||
|
deleteWorkflowVersionStep: Scalars['Boolean']['output'];
|
||||||
deleteWorkspaceInvitation: Scalars['String']['output'];
|
deleteWorkspaceInvitation: Scalars['String']['output'];
|
||||||
disablePostgresProxy: PostgresCredentials;
|
disablePostgresProxy: PostgresCredentials;
|
||||||
editSSOIdentityProvider: EditSsoOutput;
|
editSSOIdentityProvider: EditSsoOutput;
|
||||||
@ -592,6 +608,7 @@ export type Mutation = {
|
|||||||
updateOneRemoteServer: RemoteServer;
|
updateOneRemoteServer: RemoteServer;
|
||||||
updateOneServerlessFunction: ServerlessFunction;
|
updateOneServerlessFunction: ServerlessFunction;
|
||||||
updatePasswordViaResetToken: InvalidatePassword;
|
updatePasswordViaResetToken: InvalidatePassword;
|
||||||
|
updateWorkflowVersionStep: Scalars['Boolean']['output'];
|
||||||
updateWorkspace: Workspace;
|
updateWorkspace: Workspace;
|
||||||
uploadFile: Scalars['String']['output'];
|
uploadFile: Scalars['String']['output'];
|
||||||
uploadImage: Scalars['String']['output'];
|
uploadImage: Scalars['String']['output'];
|
||||||
@ -686,6 +703,11 @@ export type MutationCreateSamlIdentityProviderArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationCreateWorkflowVersionStepArgs = {
|
||||||
|
input: CreateWorkflowVersionStepInput;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationDeactivateWorkflowVersionArgs = {
|
export type MutationDeactivateWorkflowVersionArgs = {
|
||||||
workflowVersionId: Scalars['String']['input'];
|
workflowVersionId: Scalars['String']['input'];
|
||||||
};
|
};
|
||||||
@ -721,6 +743,11 @@ export type MutationDeleteSsoIdentityProviderArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationDeleteWorkflowVersionStepArgs = {
|
||||||
|
input: DeleteWorkflowVersionStepInput;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationDeleteWorkspaceInvitationArgs = {
|
export type MutationDeleteWorkspaceInvitationArgs = {
|
||||||
appTokenId: Scalars['String']['input'];
|
appTokenId: Scalars['String']['input'];
|
||||||
};
|
};
|
||||||
@ -855,6 +882,11 @@ export type MutationUpdatePasswordViaResetTokenArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationUpdateWorkflowVersionStepArgs = {
|
||||||
|
input: UpdateWorkflowVersionStepInput;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationUpdateWorkspaceArgs = {
|
export type MutationUpdateWorkspaceArgs = {
|
||||||
data: UpdateWorkspaceInput;
|
data: UpdateWorkspaceInput;
|
||||||
};
|
};
|
||||||
@ -1053,6 +1085,11 @@ export type QueryFindWorkspaceFromInviteHashArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type QueryGetAvailablePackagesArgs = {
|
||||||
|
input: ServerlessFunctionIdInput;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type QueryGetProductPricesArgs = {
|
export type QueryGetProductPricesArgs = {
|
||||||
product: Scalars['String']['input'];
|
product: Scalars['String']['input'];
|
||||||
};
|
};
|
||||||
@ -1490,6 +1527,13 @@ export type UpdateServerlessFunctionInput = {
|
|||||||
name: Scalars['String']['input'];
|
name: Scalars['String']['input'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UpdateWorkflowVersionStepInput = {
|
||||||
|
/** Step to update in JSON format */
|
||||||
|
step: Scalars['JSON']['input'];
|
||||||
|
/** Workflow version ID */
|
||||||
|
workflowVersionId: Scalars['String']['input'];
|
||||||
|
};
|
||||||
|
|
||||||
export type UpdateWorkspaceInput = {
|
export type UpdateWorkspaceInput = {
|
||||||
allowImpersonation?: InputMaybe<Scalars['Boolean']['input']>;
|
allowImpersonation?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
displayName?: InputMaybe<Scalars['String']['input']>;
|
displayName?: InputMaybe<Scalars['String']['input']>;
|
||||||
@ -1576,6 +1620,11 @@ export type Verify = {
|
|||||||
user: User;
|
user: User;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type WorkflowAction = {
|
||||||
|
__typename?: 'WorkflowAction';
|
||||||
|
id: Scalars['UUID']['output'];
|
||||||
|
};
|
||||||
|
|
||||||
export type WorkflowRun = {
|
export type WorkflowRun = {
|
||||||
__typename?: 'WorkflowRun';
|
__typename?: 'WorkflowRun';
|
||||||
workflowRunId: Scalars['UUID']['output'];
|
workflowRunId: Scalars['UUID']['output'];
|
||||||
@ -1585,6 +1634,7 @@ export type Workspace = {
|
|||||||
__typename?: 'Workspace';
|
__typename?: 'Workspace';
|
||||||
activationStatus: WorkspaceActivationStatus;
|
activationStatus: WorkspaceActivationStatus;
|
||||||
allowImpersonation: Scalars['Boolean']['output'];
|
allowImpersonation: Scalars['Boolean']['output'];
|
||||||
|
billingEntitlements?: Maybe<Array<BillingEntitlement>>;
|
||||||
billingSubscriptions?: Maybe<Array<BillingSubscription>>;
|
billingSubscriptions?: Maybe<Array<BillingSubscription>>;
|
||||||
createdAt: Scalars['DateTime']['output'];
|
createdAt: Scalars['DateTime']['output'];
|
||||||
currentBillingSubscription?: Maybe<BillingSubscription>;
|
currentBillingSubscription?: Maybe<BillingSubscription>;
|
||||||
@ -1605,6 +1655,12 @@ export type Workspace = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type WorkspaceBillingEntitlementsArgs = {
|
||||||
|
filter?: BillingEntitlementFilter;
|
||||||
|
sorting?: Array<BillingEntitlementSort>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type WorkspaceBillingSubscriptionsArgs = {
|
export type WorkspaceBillingSubscriptionsArgs = {
|
||||||
filter?: BillingSubscriptionFilter;
|
filter?: BillingSubscriptionFilter;
|
||||||
sorting?: Array<BillingSubscriptionSort>;
|
sorting?: Array<BillingSubscriptionSort>;
|
||||||
@ -1676,6 +1732,30 @@ export type WorkspaceNameAndId = {
|
|||||||
id: Scalars['String']['output'];
|
id: Scalars['String']['output'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type BillingEntitlement = {
|
||||||
|
__typename?: 'billingEntitlement';
|
||||||
|
id: Scalars['UUID']['output'];
|
||||||
|
key: Scalars['String']['output'];
|
||||||
|
value: Scalars['Boolean']['output'];
|
||||||
|
workspaceId: Scalars['String']['output'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BillingEntitlementFilter = {
|
||||||
|
and?: InputMaybe<Array<BillingEntitlementFilter>>;
|
||||||
|
id?: InputMaybe<UuidFilterComparison>;
|
||||||
|
or?: InputMaybe<Array<BillingEntitlementFilter>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BillingEntitlementSort = {
|
||||||
|
direction: SortDirection;
|
||||||
|
field: BillingEntitlementSortFields;
|
||||||
|
nulls?: InputMaybe<SortNulls>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum BillingEntitlementSortFields {
|
||||||
|
Id = 'id'
|
||||||
|
}
|
||||||
|
|
||||||
export type Field = {
|
export type Field = {
|
||||||
__typename?: 'field';
|
__typename?: 'field';
|
||||||
createdAt: Scalars['DateTime']['output'];
|
createdAt: Scalars['DateTime']['output'];
|
||||||
@ -2028,7 +2108,9 @@ export type UpdateOneServerlessFunctionMutationVariables = Exact<{
|
|||||||
|
|
||||||
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<string>, createdAt: any, updatedAt: any } };
|
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<string>, createdAt: any, updatedAt: any } };
|
||||||
|
|
||||||
export type FindManyAvailablePackagesQueryVariables = Exact<{ [key: string]: never; }>;
|
export type FindManyAvailablePackagesQueryVariables = Exact<{
|
||||||
|
input: ServerlessFunctionIdInput;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type FindManyAvailablePackagesQuery = { __typename?: 'Query', getAvailablePackages: any };
|
export type FindManyAvailablePackagesQuery = { __typename?: 'Query', getAvailablePackages: any };
|
||||||
@ -2078,7 +2160,7 @@ export const DeleteOneServerlessFunctionDocument = {"kind":"Document","definitio
|
|||||||
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<ExecuteOneServerlessFunctionMutation, ExecuteOneServerlessFunctionMutationVariables>;
|
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<ExecuteOneServerlessFunctionMutation, ExecuteOneServerlessFunctionMutationVariables>;
|
||||||
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<PublishOneServerlessFunctionMutation, PublishOneServerlessFunctionMutationVariables>;
|
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<PublishOneServerlessFunctionMutation, PublishOneServerlessFunctionMutationVariables>;
|
||||||
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<UpdateOneServerlessFunctionMutation, UpdateOneServerlessFunctionMutationVariables>;
|
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<UpdateOneServerlessFunctionMutation, UpdateOneServerlessFunctionMutationVariables>;
|
||||||
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<FindManyAvailablePackagesQuery, FindManyAvailablePackagesQueryVariables>;
|
export const FindManyAvailablePackagesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindManyAvailablePackages"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunctionIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getAvailablePackages"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<FindManyAvailablePackagesQuery, FindManyAvailablePackagesQueryVariables>;
|
||||||
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<GetManyServerlessFunctionsQuery, GetManyServerlessFunctionsQueryVariables>;
|
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<GetManyServerlessFunctionsQuery, GetManyServerlessFunctionsQueryVariables>;
|
||||||
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<GetOneServerlessFunctionQuery, GetOneServerlessFunctionQueryVariables>;
|
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<GetOneServerlessFunctionQuery, GetOneServerlessFunctionQueryVariables>;
|
||||||
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<FindOneServerlessFunctionSourceCodeQuery, FindOneServerlessFunctionSourceCodeQueryVariables>;
|
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<FindOneServerlessFunctionSourceCodeQuery, FindOneServerlessFunctionSourceCodeQueryVariables>;
|
||||||
@ -175,6 +175,13 @@ export type CreateServerlessFunctionInput = {
|
|||||||
name: Scalars['String'];
|
name: Scalars['String'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CreateWorkflowVersionStepInput = {
|
||||||
|
/** New step type */
|
||||||
|
stepType: Scalars['String'];
|
||||||
|
/** Workflow version ID */
|
||||||
|
workflowVersionId: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
export type CursorPaging = {
|
export type CursorPaging = {
|
||||||
/** Paginate after opaque cursor */
|
/** Paginate after opaque cursor */
|
||||||
after?: InputMaybe<Scalars['ConnectionCursor']>;
|
after?: InputMaybe<Scalars['ConnectionCursor']>;
|
||||||
@ -200,6 +207,13 @@ export type DeleteSsoOutput = {
|
|||||||
identityProviderId: Scalars['String'];
|
identityProviderId: Scalars['String'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DeleteWorkflowVersionStepInput = {
|
||||||
|
/** Step to delete ID */
|
||||||
|
stepId: Scalars['String'];
|
||||||
|
/** Workflow version ID */
|
||||||
|
workflowVersionId: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
/** Schema update on a table */
|
/** Schema update on a table */
|
||||||
export enum DistantTableUpdate {
|
export enum DistantTableUpdate {
|
||||||
ColumnsAdded = 'COLUMNS_ADDED',
|
ColumnsAdded = 'COLUMNS_ADDED',
|
||||||
@ -445,12 +459,14 @@ export type Mutation = {
|
|||||||
createOneObject: Object;
|
createOneObject: Object;
|
||||||
createOneServerlessFunction: ServerlessFunction;
|
createOneServerlessFunction: ServerlessFunction;
|
||||||
createSAMLIdentityProvider: SetupSsoOutput;
|
createSAMLIdentityProvider: SetupSsoOutput;
|
||||||
|
createWorkflowVersionStep: WorkflowAction;
|
||||||
deactivateWorkflowVersion: Scalars['Boolean'];
|
deactivateWorkflowVersion: Scalars['Boolean'];
|
||||||
deleteCurrentWorkspace: Workspace;
|
deleteCurrentWorkspace: Workspace;
|
||||||
deleteOneObject: Object;
|
deleteOneObject: Object;
|
||||||
deleteOneServerlessFunction: ServerlessFunction;
|
deleteOneServerlessFunction: ServerlessFunction;
|
||||||
deleteSSOIdentityProvider: DeleteSsoOutput;
|
deleteSSOIdentityProvider: DeleteSsoOutput;
|
||||||
deleteUser: User;
|
deleteUser: User;
|
||||||
|
deleteWorkflowVersionStep: WorkflowAction;
|
||||||
deleteWorkspaceInvitation: Scalars['String'];
|
deleteWorkspaceInvitation: Scalars['String'];
|
||||||
disablePostgresProxy: PostgresCredentials;
|
disablePostgresProxy: PostgresCredentials;
|
||||||
editSSOIdentityProvider: EditSsoOutput;
|
editSSOIdentityProvider: EditSsoOutput;
|
||||||
@ -476,6 +492,7 @@ export type Mutation = {
|
|||||||
updateOneObject: Object;
|
updateOneObject: Object;
|
||||||
updateOneServerlessFunction: ServerlessFunction;
|
updateOneServerlessFunction: ServerlessFunction;
|
||||||
updatePasswordViaResetToken: InvalidatePassword;
|
updatePasswordViaResetToken: InvalidatePassword;
|
||||||
|
updateWorkflowVersionStep: WorkflowAction;
|
||||||
updateWorkspace: Workspace;
|
updateWorkspace: Workspace;
|
||||||
updateWorkspaceFeatureFlag: Scalars['Boolean'];
|
updateWorkspaceFeatureFlag: Scalars['Boolean'];
|
||||||
uploadFile: Scalars['String'];
|
uploadFile: Scalars['String'];
|
||||||
@ -547,6 +564,11 @@ export type MutationCreateSamlIdentityProviderArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationCreateWorkflowVersionStepArgs = {
|
||||||
|
input: CreateWorkflowVersionStepInput;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationDeactivateWorkflowVersionArgs = {
|
export type MutationDeactivateWorkflowVersionArgs = {
|
||||||
workflowVersionId: Scalars['String'];
|
workflowVersionId: Scalars['String'];
|
||||||
};
|
};
|
||||||
@ -567,6 +589,11 @@ export type MutationDeleteSsoIdentityProviderArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationDeleteWorkflowVersionStepArgs = {
|
||||||
|
input: DeleteWorkflowVersionStepInput;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationDeleteWorkspaceInvitationArgs = {
|
export type MutationDeleteWorkspaceInvitationArgs = {
|
||||||
appTokenId: Scalars['String'];
|
appTokenId: Scalars['String'];
|
||||||
};
|
};
|
||||||
@ -676,6 +703,11 @@ export type MutationUpdatePasswordViaResetTokenArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationUpdateWorkflowVersionStepArgs = {
|
||||||
|
input: UpdateWorkflowVersionStepInput;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationUpdateWorkspaceArgs = {
|
export type MutationUpdateWorkspaceArgs = {
|
||||||
data: UpdateWorkspaceInput;
|
data: UpdateWorkspaceInput;
|
||||||
};
|
};
|
||||||
@ -853,6 +885,11 @@ export type QueryFindWorkspaceFromInviteHashArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type QueryGetAvailablePackagesArgs = {
|
||||||
|
input: ServerlessFunctionIdInput;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type QueryGetProductPricesArgs = {
|
export type QueryGetProductPricesArgs = {
|
||||||
product: Scalars['String'];
|
product: Scalars['String'];
|
||||||
};
|
};
|
||||||
@ -1214,6 +1251,13 @@ export type UpdateServerlessFunctionInput = {
|
|||||||
name: Scalars['String'];
|
name: Scalars['String'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UpdateWorkflowVersionStepInput = {
|
||||||
|
/** Step to update in JSON format */
|
||||||
|
step: Scalars['JSON'];
|
||||||
|
/** Workflow version ID */
|
||||||
|
workflowVersionId: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
export type UpdateWorkspaceInput = {
|
export type UpdateWorkspaceInput = {
|
||||||
allowImpersonation?: InputMaybe<Scalars['Boolean']>;
|
allowImpersonation?: InputMaybe<Scalars['Boolean']>;
|
||||||
displayName?: InputMaybe<Scalars['String']>;
|
displayName?: InputMaybe<Scalars['String']>;
|
||||||
@ -1304,6 +1348,15 @@ export type Verify = {
|
|||||||
user: User;
|
user: User;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type WorkflowAction = {
|
||||||
|
__typename?: 'WorkflowAction';
|
||||||
|
id: Scalars['UUID'];
|
||||||
|
name: Scalars['String'];
|
||||||
|
settings: Scalars['JSON'];
|
||||||
|
type: Scalars['String'];
|
||||||
|
valid: Scalars['Boolean'];
|
||||||
|
};
|
||||||
|
|
||||||
export type WorkflowRun = {
|
export type WorkflowRun = {
|
||||||
__typename?: 'WorkflowRun';
|
__typename?: 'WorkflowRun';
|
||||||
workflowRunId: Scalars['UUID'];
|
workflowRunId: Scalars['UUID'];
|
||||||
@ -1938,6 +1991,13 @@ export type ComputeStepOutputSchemaMutationVariables = Exact<{
|
|||||||
|
|
||||||
export type ComputeStepOutputSchemaMutation = { __typename?: 'Mutation', computeStepOutputSchema: any };
|
export type ComputeStepOutputSchemaMutation = { __typename?: 'Mutation', computeStepOutputSchema: any };
|
||||||
|
|
||||||
|
export type CreateWorkflowVersionStepMutationVariables = Exact<{
|
||||||
|
input: CreateWorkflowVersionStepInput;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type CreateWorkflowVersionStepMutation = { __typename?: 'Mutation', createWorkflowVersionStep: { __typename?: 'WorkflowAction', id: any, name: string, type: string, settings: any, valid: boolean } };
|
||||||
|
|
||||||
export type DeactivateWorkflowVersionMutationVariables = Exact<{
|
export type DeactivateWorkflowVersionMutationVariables = Exact<{
|
||||||
workflowVersionId: Scalars['String'];
|
workflowVersionId: Scalars['String'];
|
||||||
}>;
|
}>;
|
||||||
@ -1945,6 +2005,13 @@ export type DeactivateWorkflowVersionMutationVariables = Exact<{
|
|||||||
|
|
||||||
export type DeactivateWorkflowVersionMutation = { __typename?: 'Mutation', deactivateWorkflowVersion: boolean };
|
export type DeactivateWorkflowVersionMutation = { __typename?: 'Mutation', deactivateWorkflowVersion: boolean };
|
||||||
|
|
||||||
|
export type DeleteWorkflowVersionStepMutationVariables = Exact<{
|
||||||
|
input: DeleteWorkflowVersionStepInput;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type DeleteWorkflowVersionStepMutation = { __typename?: 'Mutation', deleteWorkflowVersionStep: { __typename?: 'WorkflowAction', id: any, name: string, type: string, settings: any, valid: boolean } };
|
||||||
|
|
||||||
export type RunWorkflowVersionMutationVariables = Exact<{
|
export type RunWorkflowVersionMutationVariables = Exact<{
|
||||||
input: RunWorkflowVersionInput;
|
input: RunWorkflowVersionInput;
|
||||||
}>;
|
}>;
|
||||||
@ -1952,6 +2019,13 @@ export type RunWorkflowVersionMutationVariables = Exact<{
|
|||||||
|
|
||||||
export type RunWorkflowVersionMutation = { __typename?: 'Mutation', runWorkflowVersion: { __typename?: 'WorkflowRun', workflowRunId: any } };
|
export type RunWorkflowVersionMutation = { __typename?: 'Mutation', runWorkflowVersion: { __typename?: 'WorkflowRun', workflowRunId: any } };
|
||||||
|
|
||||||
|
export type UpdateWorkflowVersionStepMutationVariables = Exact<{
|
||||||
|
input: UpdateWorkflowVersionStepInput;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type UpdateWorkflowVersionStepMutation = { __typename?: 'Mutation', updateWorkflowVersionStep: { __typename?: 'WorkflowAction', id: any, name: string, type: string, settings: any, valid: boolean } };
|
||||||
|
|
||||||
export type DeleteWorkspaceInvitationMutationVariables = Exact<{
|
export type DeleteWorkspaceInvitationMutationVariables = Exact<{
|
||||||
appTokenId: Scalars['String'];
|
appTokenId: Scalars['String'];
|
||||||
}>;
|
}>;
|
||||||
@ -3695,6 +3769,43 @@ export function useComputeStepOutputSchemaMutation(baseOptions?: Apollo.Mutation
|
|||||||
export type ComputeStepOutputSchemaMutationHookResult = ReturnType<typeof useComputeStepOutputSchemaMutation>;
|
export type ComputeStepOutputSchemaMutationHookResult = ReturnType<typeof useComputeStepOutputSchemaMutation>;
|
||||||
export type ComputeStepOutputSchemaMutationResult = Apollo.MutationResult<ComputeStepOutputSchemaMutation>;
|
export type ComputeStepOutputSchemaMutationResult = Apollo.MutationResult<ComputeStepOutputSchemaMutation>;
|
||||||
export type ComputeStepOutputSchemaMutationOptions = Apollo.BaseMutationOptions<ComputeStepOutputSchemaMutation, ComputeStepOutputSchemaMutationVariables>;
|
export type ComputeStepOutputSchemaMutationOptions = Apollo.BaseMutationOptions<ComputeStepOutputSchemaMutation, ComputeStepOutputSchemaMutationVariables>;
|
||||||
|
export const CreateWorkflowVersionStepDocument = gql`
|
||||||
|
mutation CreateWorkflowVersionStep($input: CreateWorkflowVersionStepInput!) {
|
||||||
|
createWorkflowVersionStep(input: $input) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
type
|
||||||
|
settings
|
||||||
|
valid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export type CreateWorkflowVersionStepMutationFn = Apollo.MutationFunction<CreateWorkflowVersionStepMutation, CreateWorkflowVersionStepMutationVariables>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useCreateWorkflowVersionStepMutation__
|
||||||
|
*
|
||||||
|
* To run a mutation, you first call `useCreateWorkflowVersionStepMutation` within a React component and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useCreateWorkflowVersionStepMutation` returns a tuple that includes:
|
||||||
|
* - A mutate function that you can call at any time to execute the mutation
|
||||||
|
* - An object with fields that represent the current status of the mutation's execution
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const [createWorkflowVersionStepMutation, { data, loading, error }] = useCreateWorkflowVersionStepMutation({
|
||||||
|
* variables: {
|
||||||
|
* input: // value for 'input'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useCreateWorkflowVersionStepMutation(baseOptions?: Apollo.MutationHookOptions<CreateWorkflowVersionStepMutation, CreateWorkflowVersionStepMutationVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useMutation<CreateWorkflowVersionStepMutation, CreateWorkflowVersionStepMutationVariables>(CreateWorkflowVersionStepDocument, options);
|
||||||
|
}
|
||||||
|
export type CreateWorkflowVersionStepMutationHookResult = ReturnType<typeof useCreateWorkflowVersionStepMutation>;
|
||||||
|
export type CreateWorkflowVersionStepMutationResult = Apollo.MutationResult<CreateWorkflowVersionStepMutation>;
|
||||||
|
export type CreateWorkflowVersionStepMutationOptions = Apollo.BaseMutationOptions<CreateWorkflowVersionStepMutation, CreateWorkflowVersionStepMutationVariables>;
|
||||||
export const DeactivateWorkflowVersionDocument = gql`
|
export const DeactivateWorkflowVersionDocument = gql`
|
||||||
mutation DeactivateWorkflowVersion($workflowVersionId: String!) {
|
mutation DeactivateWorkflowVersion($workflowVersionId: String!) {
|
||||||
deactivateWorkflowVersion(workflowVersionId: $workflowVersionId)
|
deactivateWorkflowVersion(workflowVersionId: $workflowVersionId)
|
||||||
@ -3726,6 +3837,43 @@ export function useDeactivateWorkflowVersionMutation(baseOptions?: Apollo.Mutati
|
|||||||
export type DeactivateWorkflowVersionMutationHookResult = ReturnType<typeof useDeactivateWorkflowVersionMutation>;
|
export type DeactivateWorkflowVersionMutationHookResult = ReturnType<typeof useDeactivateWorkflowVersionMutation>;
|
||||||
export type DeactivateWorkflowVersionMutationResult = Apollo.MutationResult<DeactivateWorkflowVersionMutation>;
|
export type DeactivateWorkflowVersionMutationResult = Apollo.MutationResult<DeactivateWorkflowVersionMutation>;
|
||||||
export type DeactivateWorkflowVersionMutationOptions = Apollo.BaseMutationOptions<DeactivateWorkflowVersionMutation, DeactivateWorkflowVersionMutationVariables>;
|
export type DeactivateWorkflowVersionMutationOptions = Apollo.BaseMutationOptions<DeactivateWorkflowVersionMutation, DeactivateWorkflowVersionMutationVariables>;
|
||||||
|
export const DeleteWorkflowVersionStepDocument = gql`
|
||||||
|
mutation DeleteWorkflowVersionStep($input: DeleteWorkflowVersionStepInput!) {
|
||||||
|
deleteWorkflowVersionStep(input: $input) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
type
|
||||||
|
settings
|
||||||
|
valid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export type DeleteWorkflowVersionStepMutationFn = Apollo.MutationFunction<DeleteWorkflowVersionStepMutation, DeleteWorkflowVersionStepMutationVariables>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useDeleteWorkflowVersionStepMutation__
|
||||||
|
*
|
||||||
|
* To run a mutation, you first call `useDeleteWorkflowVersionStepMutation` within a React component and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useDeleteWorkflowVersionStepMutation` returns a tuple that includes:
|
||||||
|
* - A mutate function that you can call at any time to execute the mutation
|
||||||
|
* - An object with fields that represent the current status of the mutation's execution
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const [deleteWorkflowVersionStepMutation, { data, loading, error }] = useDeleteWorkflowVersionStepMutation({
|
||||||
|
* variables: {
|
||||||
|
* input: // value for 'input'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useDeleteWorkflowVersionStepMutation(baseOptions?: Apollo.MutationHookOptions<DeleteWorkflowVersionStepMutation, DeleteWorkflowVersionStepMutationVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useMutation<DeleteWorkflowVersionStepMutation, DeleteWorkflowVersionStepMutationVariables>(DeleteWorkflowVersionStepDocument, options);
|
||||||
|
}
|
||||||
|
export type DeleteWorkflowVersionStepMutationHookResult = ReturnType<typeof useDeleteWorkflowVersionStepMutation>;
|
||||||
|
export type DeleteWorkflowVersionStepMutationResult = Apollo.MutationResult<DeleteWorkflowVersionStepMutation>;
|
||||||
|
export type DeleteWorkflowVersionStepMutationOptions = Apollo.BaseMutationOptions<DeleteWorkflowVersionStepMutation, DeleteWorkflowVersionStepMutationVariables>;
|
||||||
export const RunWorkflowVersionDocument = gql`
|
export const RunWorkflowVersionDocument = gql`
|
||||||
mutation RunWorkflowVersion($input: RunWorkflowVersionInput!) {
|
mutation RunWorkflowVersion($input: RunWorkflowVersionInput!) {
|
||||||
runWorkflowVersion(input: $input) {
|
runWorkflowVersion(input: $input) {
|
||||||
@ -3759,6 +3907,43 @@ export function useRunWorkflowVersionMutation(baseOptions?: Apollo.MutationHookO
|
|||||||
export type RunWorkflowVersionMutationHookResult = ReturnType<typeof useRunWorkflowVersionMutation>;
|
export type RunWorkflowVersionMutationHookResult = ReturnType<typeof useRunWorkflowVersionMutation>;
|
||||||
export type RunWorkflowVersionMutationResult = Apollo.MutationResult<RunWorkflowVersionMutation>;
|
export type RunWorkflowVersionMutationResult = Apollo.MutationResult<RunWorkflowVersionMutation>;
|
||||||
export type RunWorkflowVersionMutationOptions = Apollo.BaseMutationOptions<RunWorkflowVersionMutation, RunWorkflowVersionMutationVariables>;
|
export type RunWorkflowVersionMutationOptions = Apollo.BaseMutationOptions<RunWorkflowVersionMutation, RunWorkflowVersionMutationVariables>;
|
||||||
|
export const UpdateWorkflowVersionStepDocument = gql`
|
||||||
|
mutation UpdateWorkflowVersionStep($input: UpdateWorkflowVersionStepInput!) {
|
||||||
|
updateWorkflowVersionStep(input: $input) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
type
|
||||||
|
settings
|
||||||
|
valid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export type UpdateWorkflowVersionStepMutationFn = Apollo.MutationFunction<UpdateWorkflowVersionStepMutation, UpdateWorkflowVersionStepMutationVariables>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useUpdateWorkflowVersionStepMutation__
|
||||||
|
*
|
||||||
|
* To run a mutation, you first call `useUpdateWorkflowVersionStepMutation` within a React component and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useUpdateWorkflowVersionStepMutation` returns a tuple that includes:
|
||||||
|
* - A mutate function that you can call at any time to execute the mutation
|
||||||
|
* - An object with fields that represent the current status of the mutation's execution
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const [updateWorkflowVersionStepMutation, { data, loading, error }] = useUpdateWorkflowVersionStepMutation({
|
||||||
|
* variables: {
|
||||||
|
* input: // value for 'input'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useUpdateWorkflowVersionStepMutation(baseOptions?: Apollo.MutationHookOptions<UpdateWorkflowVersionStepMutation, UpdateWorkflowVersionStepMutationVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useMutation<UpdateWorkflowVersionStepMutation, UpdateWorkflowVersionStepMutationVariables>(UpdateWorkflowVersionStepDocument, options);
|
||||||
|
}
|
||||||
|
export type UpdateWorkflowVersionStepMutationHookResult = ReturnType<typeof useUpdateWorkflowVersionStepMutation>;
|
||||||
|
export type UpdateWorkflowVersionStepMutationResult = Apollo.MutationResult<UpdateWorkflowVersionStepMutation>;
|
||||||
|
export type UpdateWorkflowVersionStepMutationOptions = Apollo.BaseMutationOptions<UpdateWorkflowVersionStepMutation, UpdateWorkflowVersionStepMutationVariables>;
|
||||||
export const DeleteWorkspaceInvitationDocument = gql`
|
export const DeleteWorkspaceInvitationDocument = gql`
|
||||||
mutation DeleteWorkspaceInvitation($appTokenId: String!) {
|
mutation DeleteWorkspaceInvitation($appTokenId: String!) {
|
||||||
deleteWorkspaceInvitation(appTokenId: $appTokenId)
|
deleteWorkspaceInvitation(appTokenId: $appTokenId)
|
||||||
|
|||||||
@ -0,0 +1,31 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { BACKGROUND_LIGHT, GRAY_SCALE } from 'twenty-ui';
|
||||||
|
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
|
||||||
|
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
|
||||||
|
|
||||||
|
const StyledRightDrawerContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
padding: ${({ theme }) => theme.spacing(4)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledSkeletonLoader = () => {
|
||||||
|
return (
|
||||||
|
<SkeletonTheme
|
||||||
|
baseColor={GRAY_SCALE.gray15}
|
||||||
|
highlightColor={BACKGROUND_LIGHT.transparent.lighter}
|
||||||
|
borderRadius={4}
|
||||||
|
>
|
||||||
|
<Skeleton height={SKELETON_LOADER_HEIGHT_SIZES.standard.m} width={140} />
|
||||||
|
</SkeletonTheme>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RightDrawerSkeletonLoader = () => {
|
||||||
|
return (
|
||||||
|
<StyledRightDrawerContainer>
|
||||||
|
<StyledSkeletonLoader />
|
||||||
|
</StyledRightDrawerContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -57,11 +57,12 @@ export const useWorkflowRunRecordActions = ({
|
|||||||
index,
|
index,
|
||||||
activeWorkflowVersion,
|
activeWorkflowVersion,
|
||||||
] of activeWorkflowVersions.entries()) {
|
] of activeWorkflowVersions.entries()) {
|
||||||
|
const name = capitalize(activeWorkflowVersion.workflow.name);
|
||||||
addActionMenuEntry({
|
addActionMenuEntry({
|
||||||
type: ActionMenuEntryType.WorkflowRun,
|
type: ActionMenuEntryType.WorkflowRun,
|
||||||
key: `workflow-run-${activeWorkflowVersion.id}`,
|
key: `workflow-run-${activeWorkflowVersion.id}`,
|
||||||
scope: ActionMenuEntryScope.RecordSelection,
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
label: capitalize(activeWorkflowVersion.workflow.name),
|
label: name,
|
||||||
position: index,
|
position: index,
|
||||||
Icon: IconSettingsAutomation,
|
Icon: IconSettingsAutomation,
|
||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
@ -76,7 +77,7 @@ export const useWorkflowRunRecordActions = ({
|
|||||||
|
|
||||||
enqueueSnackBar('', {
|
enqueueSnackBar('', {
|
||||||
variant: SnackBarVariant.Success,
|
variant: SnackBarVariant.Success,
|
||||||
title: `${capitalize(activeWorkflowVersion.workflow.name)} starting...`,
|
title: `${name} starting...`,
|
||||||
icon: (
|
icon: (
|
||||||
<IconSettingsAutomation
|
<IconSettingsAutomation
|
||||||
size={16}
|
size={16}
|
||||||
|
|||||||
@ -37,11 +37,12 @@ export const useWorkflowRunActions = () => {
|
|||||||
index,
|
index,
|
||||||
activeWorkflowVersion,
|
activeWorkflowVersion,
|
||||||
] of activeWorkflowVersions.entries()) {
|
] of activeWorkflowVersions.entries()) {
|
||||||
|
const name = capitalize(activeWorkflowVersion.workflow.name);
|
||||||
addActionMenuEntry({
|
addActionMenuEntry({
|
||||||
type: ActionMenuEntryType.WorkflowRun,
|
type: ActionMenuEntryType.WorkflowRun,
|
||||||
key: `workflow-run-${activeWorkflowVersion.id}`,
|
key: `workflow-run-${activeWorkflowVersion.id}`,
|
||||||
scope: ActionMenuEntryScope.Global,
|
scope: ActionMenuEntryScope.Global,
|
||||||
label: capitalize(activeWorkflowVersion.workflow.name),
|
label: name,
|
||||||
position: index,
|
position: index,
|
||||||
Icon: IconSettingsAutomation,
|
Icon: IconSettingsAutomation,
|
||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
@ -51,7 +52,7 @@ export const useWorkflowRunActions = () => {
|
|||||||
|
|
||||||
enqueueSnackBar('', {
|
enqueueSnackBar('', {
|
||||||
variant: SnackBarVariant.Success,
|
variant: SnackBarVariant.Success,
|
||||||
title: `${capitalize(activeWorkflowVersion.workflow.name)} starting...`,
|
title: `${name} starting...`,
|
||||||
icon: (
|
icon: (
|
||||||
<IconSettingsAutomation
|
<IconSettingsAutomation
|
||||||
size={16}
|
size={16}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useLazyQuery } from '@apollo/client';
|
import { useLazyQuery, WatchQueryFetchPolicy } from '@apollo/client';
|
||||||
|
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||||
@ -10,6 +10,8 @@ import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
|||||||
|
|
||||||
type UseLazyFindOneRecordParams = ObjectMetadataItemIdentifier & {
|
type UseLazyFindOneRecordParams = ObjectMetadataItemIdentifier & {
|
||||||
recordGqlFields?: RecordGqlOperationGqlRecordFields;
|
recordGqlFields?: RecordGqlOperationGqlRecordFields;
|
||||||
|
withSoftDeleted?: boolean;
|
||||||
|
fetchPolicy?: WatchQueryFetchPolicy;
|
||||||
};
|
};
|
||||||
|
|
||||||
type FindOneRecordParams<T extends ObjectRecord> = {
|
type FindOneRecordParams<T extends ObjectRecord> = {
|
||||||
@ -20,6 +22,8 @@ type FindOneRecordParams<T extends ObjectRecord> = {
|
|||||||
export const useLazyFindOneRecord = <T extends ObjectRecord = ObjectRecord>({
|
export const useLazyFindOneRecord = <T extends ObjectRecord = ObjectRecord>({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
recordGqlFields,
|
recordGqlFields,
|
||||||
|
withSoftDeleted = false,
|
||||||
|
fetchPolicy = 'cache-first',
|
||||||
}: UseLazyFindOneRecordParams) => {
|
}: UseLazyFindOneRecordParams) => {
|
||||||
const { objectMetadataItem } = useObjectMetadataItem({
|
const { objectMetadataItem } = useObjectMetadataItem({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
@ -30,22 +34,28 @@ export const useLazyFindOneRecord = <T extends ObjectRecord = ObjectRecord>({
|
|||||||
recordGqlFields:
|
recordGqlFields:
|
||||||
recordGqlFields ??
|
recordGqlFields ??
|
||||||
generateDepthOneRecordGqlFields({ objectMetadataItem }),
|
generateDepthOneRecordGqlFields({ objectMetadataItem }),
|
||||||
|
withSoftDeleted,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [findOneRecord, { loading, error, data, called }] =
|
const [findOneRecord, { loading, error, data, called }] =
|
||||||
useLazyQuery(findOneRecordQuery);
|
useLazyQuery(findOneRecordQuery);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
findOneRecord: ({ objectRecordId, onCompleted }: FindOneRecordParams<T>) =>
|
findOneRecord: async ({
|
||||||
findOneRecord({
|
objectRecordId,
|
||||||
|
onCompleted,
|
||||||
|
}: FindOneRecordParams<T>) => {
|
||||||
|
await findOneRecord({
|
||||||
variables: { objectRecordId },
|
variables: { objectRecordId },
|
||||||
|
fetchPolicy,
|
||||||
onCompleted: (data) => {
|
onCompleted: (data) => {
|
||||||
const record = getRecordFromRecordNode<T>({
|
const record = getRecordFromRecordNode<T>({
|
||||||
recordNode: data[objectNameSingular],
|
recordNode: data[objectNameSingular],
|
||||||
});
|
});
|
||||||
onCompleted?.(record);
|
onCompleted?.(record);
|
||||||
},
|
},
|
||||||
}),
|
});
|
||||||
|
},
|
||||||
called,
|
called,
|
||||||
error,
|
error,
|
||||||
loading,
|
loading,
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { SettingsServerlessFunctionCodeEditorContainer } from '@/settings/serverless-functions/components/SettingsServerlessFunctionCodeEditorContainer';
|
|
||||||
import { useGetAvailablePackages } from '@/settings/serverless-functions/hooks/useGetAvailablePackages';
|
import { useGetAvailablePackages } from '@/settings/serverless-functions/hooks/useGetAvailablePackages';
|
||||||
import { EditorProps, Monaco } from '@monaco-editor/react';
|
import { EditorProps, Monaco } from '@monaco-editor/react';
|
||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
@ -6,6 +5,7 @@ import { editor, MarkerSeverity } from 'monaco-editor';
|
|||||||
import { AutoTypings } from 'monaco-editor-auto-typings';
|
import { AutoTypings } from 'monaco-editor-auto-typings';
|
||||||
import { CodeEditor } from 'twenty-ui';
|
import { CodeEditor } from 'twenty-ui';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
export type File = {
|
export type File = {
|
||||||
language: string;
|
language: string;
|
||||||
@ -31,7 +31,10 @@ export const SettingsServerlessFunctionCodeEditor = ({
|
|||||||
height = 450,
|
height = 450,
|
||||||
options = undefined,
|
options = undefined,
|
||||||
}: SettingsServerlessFunctionCodeEditorProps) => {
|
}: SettingsServerlessFunctionCodeEditorProps) => {
|
||||||
const { availablePackages } = useGetAvailablePackages();
|
const { serverlessFunctionId = '' } = useParams();
|
||||||
|
const { availablePackages } = useGetAvailablePackages({
|
||||||
|
id: serverlessFunctionId,
|
||||||
|
});
|
||||||
|
|
||||||
const currentFile = files.find((file) => file.path === currentFilePath);
|
const currentFile = files.find((file) => file.path === currentFilePath);
|
||||||
const environmentVariablesFile = files.find((file) => file.path === '.env');
|
const environmentVariablesFile = files.find((file) => file.path === '.env');
|
||||||
@ -116,17 +119,16 @@ export const SettingsServerlessFunctionCodeEditor = ({
|
|||||||
return (
|
return (
|
||||||
isDefined(currentFile) &&
|
isDefined(currentFile) &&
|
||||||
isDefined(availablePackages) && (
|
isDefined(availablePackages) && (
|
||||||
<SettingsServerlessFunctionCodeEditorContainer>
|
<CodeEditor
|
||||||
<CodeEditor
|
height={height}
|
||||||
height={height}
|
value={currentFile.content}
|
||||||
value={currentFile.content}
|
language={currentFile.language}
|
||||||
language={currentFile.language}
|
onMount={handleEditorDidMount}
|
||||||
onMount={handleEditorDidMount}
|
onChange={onChange}
|
||||||
onChange={onChange}
|
onValidate={handleEditorValidation}
|
||||||
onValidate={handleEditorValidation}
|
options={options}
|
||||||
options={options}
|
withHeader
|
||||||
/>
|
/>
|
||||||
</SettingsServerlessFunctionCodeEditorContainer>
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
|
|
||||||
const StyledEditorContainer = styled.div`
|
|
||||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
|
||||||
border-top: none;
|
|
||||||
border-radius: 0 0 ${({ theme }) => theme.border.radius.sm}
|
|
||||||
${({ theme }) => theme.border.radius.sm};
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const SettingsServerlessFunctionCodeEditorContainer =
|
|
||||||
StyledEditorContainer;
|
|
||||||
@ -8,7 +8,6 @@ import {
|
|||||||
} from 'twenty-ui';
|
} from 'twenty-ui';
|
||||||
|
|
||||||
import { LightCopyIconButton } from '@/object-record/record-field/components/LightCopyIconButton';
|
import { LightCopyIconButton } from '@/object-record/record-field/components/LightCopyIconButton';
|
||||||
import { SettingsServerlessFunctionCodeEditorContainer } from '@/settings/serverless-functions/components/SettingsServerlessFunctionCodeEditorContainer';
|
|
||||||
import { SettingsServerlessFunctionsOutputMetadataInfo } from '@/settings/serverless-functions/components/SettingsServerlessFunctionsOutputMetadataInfo';
|
import { SettingsServerlessFunctionsOutputMetadataInfo } from '@/settings/serverless-functions/components/SettingsServerlessFunctionsOutputMetadataInfo';
|
||||||
import { settingsServerlessFunctionCodeEditorOutputParamsState } from '@/settings/serverless-functions/states/settingsServerlessFunctionCodeEditorOutputParamsState';
|
import { settingsServerlessFunctionCodeEditorOutputParamsState } from '@/settings/serverless-functions/states/settingsServerlessFunctionCodeEditorOutputParamsState';
|
||||||
import { settingsServerlessFunctionInputState } from '@/settings/serverless-functions/states/settingsServerlessFunctionInputState';
|
import { settingsServerlessFunctionInputState } from '@/settings/serverless-functions/states/settingsServerlessFunctionInputState';
|
||||||
@ -82,30 +81,26 @@ export const SettingsServerlessFunctionTestTab = ({
|
|||||||
/>,
|
/>,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<SettingsServerlessFunctionCodeEditorContainer>
|
<CodeEditor
|
||||||
<CodeEditor
|
value={settingsServerlessFunctionInput}
|
||||||
value={settingsServerlessFunctionInput}
|
language="json"
|
||||||
language="json"
|
height={200}
|
||||||
height={200}
|
onChange={setSettingsServerlessFunctionInput}
|
||||||
onChange={setSettingsServerlessFunctionInput}
|
withHeader
|
||||||
/>
|
/>
|
||||||
</SettingsServerlessFunctionCodeEditorContainer>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<CoreEditorHeader
|
<CoreEditorHeader
|
||||||
leftNodes={[<SettingsServerlessFunctionsOutputMetadataInfo />]}
|
leftNodes={[<SettingsServerlessFunctionsOutputMetadataInfo />]}
|
||||||
rightNodes={[<LightCopyIconButton copyText={result} />]}
|
rightNodes={[<LightCopyIconButton copyText={result} />]}
|
||||||
/>
|
/>
|
||||||
<SettingsServerlessFunctionCodeEditorContainer>
|
<CodeEditor
|
||||||
<CodeEditor
|
value={result}
|
||||||
value={result}
|
language={settingsServerlessFunctionCodeEditorOutputParams.language}
|
||||||
language={
|
height={settingsServerlessFunctionCodeEditorOutputParams.height}
|
||||||
settingsServerlessFunctionCodeEditorOutputParams.language
|
options={{ readOnly: true, domReadOnly: true }}
|
||||||
}
|
withHeader
|
||||||
height={settingsServerlessFunctionCodeEditorOutputParams.height}
|
/>
|
||||||
options={{ readOnly: true, domReadOnly: true }}
|
|
||||||
/>
|
|
||||||
</SettingsServerlessFunctionCodeEditorContainer>
|
|
||||||
</div>
|
</div>
|
||||||
</StyledInputsContainer>
|
</StyledInputsContainer>
|
||||||
</Section>
|
</Section>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
export const FIND_MANY_AVAILABLE_PACKAGES = gql`
|
export const FIND_MANY_AVAILABLE_PACKAGES = gql`
|
||||||
query FindManyAvailablePackages {
|
query FindManyAvailablePackages($input: ServerlessFunctionIdInput!) {
|
||||||
getAvailablePackages
|
getAvailablePackages(input: $input)
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -4,15 +4,19 @@ import { FIND_MANY_AVAILABLE_PACKAGES } from '@/settings/serverless-functions/gr
|
|||||||
import {
|
import {
|
||||||
FindManyAvailablePackagesQuery,
|
FindManyAvailablePackagesQuery,
|
||||||
FindManyAvailablePackagesQueryVariables,
|
FindManyAvailablePackagesQueryVariables,
|
||||||
|
ServerlessFunctionIdInput,
|
||||||
} from '~/generated-metadata/graphql';
|
} from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
export const useGetAvailablePackages = () => {
|
export const useGetAvailablePackages = (input: ServerlessFunctionIdInput) => {
|
||||||
const apolloMetadataClient = useApolloMetadataClient();
|
const apolloMetadataClient = useApolloMetadataClient();
|
||||||
const { data } = useQuery<
|
const { data } = useQuery<
|
||||||
FindManyAvailablePackagesQuery,
|
FindManyAvailablePackagesQuery,
|
||||||
FindManyAvailablePackagesQueryVariables
|
FindManyAvailablePackagesQueryVariables
|
||||||
>(FIND_MANY_AVAILABLE_PACKAGES, {
|
>(FIND_MANY_AVAILABLE_PACKAGES, {
|
||||||
client: apolloMetadataClient ?? undefined,
|
client: apolloMetadataClient ?? undefined,
|
||||||
|
variables: {
|
||||||
|
input,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
availablePackages: data?.getAvailablePackages || null,
|
availablePackages: data?.getAvailablePackages || null,
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
|
|||||||
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||||
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
||||||
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
|
||||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||||
import { ViewField } from '@/views/types/ViewField';
|
import { ViewField } from '@/views/types/ViewField';
|
||||||
|
|
||||||
@ -94,7 +93,7 @@ export const usePersistViewFieldRecords = () => {
|
|||||||
update: (cache, { data }) => {
|
update: (cache, { data }) => {
|
||||||
const record = data?.['updateViewField'];
|
const record = data?.['updateViewField'];
|
||||||
if (!record) return;
|
if (!record) return;
|
||||||
const cachedRecord = getRecordFromCache<ObjectRecord>(record.id);
|
const cachedRecord = getRecordFromCache<ViewField>(record.id);
|
||||||
|
|
||||||
if (!cachedRecord) return;
|
if (!cachedRecord) return;
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordF
|
|||||||
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
||||||
import { useDestroyOneRecordMutation } from '@/object-record/hooks/useDestroyOneRecordMutation';
|
import { useDestroyOneRecordMutation } from '@/object-record/hooks/useDestroyOneRecordMutation';
|
||||||
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
|
||||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||||
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
|
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
@ -142,7 +141,9 @@ export const usePersistViewFilterGroupRecords = () => {
|
|||||||
update: (cache, { data }) => {
|
update: (cache, { data }) => {
|
||||||
const record = data?.updateViewFilterGroup;
|
const record = data?.updateViewFilterGroup;
|
||||||
if (!record) return;
|
if (!record) return;
|
||||||
const cachedRecord = getRecordFromCache<ObjectRecord>(record.id);
|
const cachedRecord = getRecordFromCache<ViewFilterGroup>(
|
||||||
|
record.id,
|
||||||
|
);
|
||||||
|
|
||||||
if (!cachedRecord) return;
|
if (!cachedRecord) return;
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordF
|
|||||||
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
||||||
import { useDestroyOneRecordMutation } from '@/object-record/hooks/useDestroyOneRecordMutation';
|
import { useDestroyOneRecordMutation } from '@/object-record/hooks/useDestroyOneRecordMutation';
|
||||||
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
|
||||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||||
|
|
||||||
@ -100,7 +99,7 @@ export const usePersistViewFilterRecords = () => {
|
|||||||
update: (cache, { data }) => {
|
update: (cache, { data }) => {
|
||||||
const record = data?.['updateViewFilter'];
|
const record = data?.['updateViewFilter'];
|
||||||
if (!record) return;
|
if (!record) return;
|
||||||
const cachedRecord = getRecordFromCache<ObjectRecord>(record.id);
|
const cachedRecord = getRecordFromCache<ViewFilter>(record.id);
|
||||||
|
|
||||||
if (!cachedRecord) return;
|
if (!cachedRecord) return;
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordF
|
|||||||
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
||||||
import { useDestroyOneRecordMutation } from '@/object-record/hooks/useDestroyOneRecordMutation';
|
import { useDestroyOneRecordMutation } from '@/object-record/hooks/useDestroyOneRecordMutation';
|
||||||
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
|
||||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||||
import { ViewSort } from '@/views/types/ViewSort';
|
import { ViewSort } from '@/views/types/ViewSort';
|
||||||
|
|
||||||
@ -93,7 +92,7 @@ export const usePersistViewSortRecords = () => {
|
|||||||
update: (cache, { data }) => {
|
update: (cache, { data }) => {
|
||||||
const record = data?.['updateViewSort'];
|
const record = data?.['updateViewSort'];
|
||||||
if (!record) return;
|
if (!record) return;
|
||||||
const cachedRecord = getRecordFromCache<ObjectRecord>(record.id);
|
const cachedRecord = getRecordFromCache<ViewSort>(record.id);
|
||||||
|
|
||||||
if (!cachedRecord) return;
|
if (!cachedRecord) return;
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { WorkflowStepDetail } from '@/workflow/components/WorkflowStepDetail';
|
import { WorkflowStepDetail } from '@/workflow/components/WorkflowStepDetail';
|
||||||
import { useUpdateWorkflowVersionStep } from '@/workflow/hooks/useUpdateWorkflowVersionStep';
|
import { useUpdateStep } from '@/workflow/hooks/useUpdateStep';
|
||||||
import { useUpdateWorkflowVersionTrigger } from '@/workflow/hooks/useUpdateWorkflowVersionTrigger';
|
import { useUpdateWorkflowVersionTrigger } from '@/workflow/hooks/useUpdateWorkflowVersionTrigger';
|
||||||
import { workflowSelectedNodeState } from '@/workflow/states/workflowSelectedNodeState';
|
import { workflowSelectedNodeState } from '@/workflow/states/workflowSelectedNodeState';
|
||||||
import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
|
import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
|
||||||
@ -19,9 +19,8 @@ export const RightDrawerWorkflowEditStepContent = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { updateTrigger } = useUpdateWorkflowVersionTrigger({ workflow });
|
const { updateTrigger } = useUpdateWorkflowVersionTrigger({ workflow });
|
||||||
const { updateStep } = useUpdateWorkflowVersionStep({
|
const { updateStep } = useUpdateStep({
|
||||||
workflow,
|
workflow,
|
||||||
stepId: workflowSelectedNode,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { WorkflowDiagramStepNodeBase } from '@/workflow/components/WorkflowDiagramStepNodeBase';
|
import { WorkflowDiagramStepNodeBase } from '@/workflow/components/WorkflowDiagramStepNodeBase';
|
||||||
import { useDeleteOneStep } from '@/workflow/hooks/useDeleteOneStep';
|
import { useDeleteStep } from '@/workflow/hooks/useDeleteStep';
|
||||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
||||||
import { WorkflowDiagramStepNodeData } from '@/workflow/types/WorkflowDiagram';
|
import { WorkflowDiagramStepNodeData } from '@/workflow/types/WorkflowDiagram';
|
||||||
@ -21,9 +21,8 @@ export const WorkflowDiagramStepNodeEditable = ({
|
|||||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
||||||
assertWorkflowWithCurrentVersionIsDefined(workflowWithCurrentVersion);
|
assertWorkflowWithCurrentVersionIsDefined(workflowWithCurrentVersion);
|
||||||
|
|
||||||
const { deleteOneStep } = useDeleteOneStep({
|
const { deleteStep } = useDeleteStep({
|
||||||
workflow: workflowWithCurrentVersion,
|
workflow: workflowWithCurrentVersion,
|
||||||
stepId: id,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -35,7 +34,7 @@ export const WorkflowDiagramStepNodeEditable = ({
|
|||||||
size="medium"
|
size="medium"
|
||||||
Icon={IconTrash}
|
Icon={IconTrash}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
return deleteOneStep();
|
deleteStep(id);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : undefined
|
) : undefined
|
||||||
|
|||||||
@ -1,256 +0,0 @@
|
|||||||
import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
|
|
||||||
import { useGetManyServerlessFunctions } from '@/settings/serverless-functions/hooks/useGetManyServerlessFunctions';
|
|
||||||
import { Select, SelectOption } from '@/ui/input/components/Select';
|
|
||||||
import { WorkflowEditGenericFormBase } from '@/workflow/components/WorkflowEditGenericFormBase';
|
|
||||||
import { WorkflowVariablePicker } from '@/workflow/components/WorkflowVariablePicker';
|
|
||||||
import { FunctionInput } from '@/workflow/types/FunctionInput';
|
|
||||||
import { WorkflowCodeAction } from '@/workflow/types/Workflow';
|
|
||||||
import { getDefaultFunctionInputFromInputSchema } from '@/workflow/utils/getDefaultFunctionInputFromInputSchema';
|
|
||||||
import { mergeDefaultFunctionInputAndFunctionInput } from '@/workflow/utils/mergeDefaultFunctionInputAndFunctionInput';
|
|
||||||
import { setNestedValue } from '@/workflow/utils/setNestedValue';
|
|
||||||
import { useTheme } from '@emotion/react';
|
|
||||||
import styled from '@emotion/styled';
|
|
||||||
import { Fragment, ReactNode, useState } from 'react';
|
|
||||||
import { HorizontalSeparator, IconCode, isDefined } from 'twenty-ui';
|
|
||||||
import { useDebouncedCallback } from 'use-debounce';
|
|
||||||
|
|
||||||
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(2)};
|
|
||||||
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledInputContainer = styled.div`
|
|
||||||
background: ${({ theme }) => theme.background.secondary};
|
|
||||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
|
||||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: ${({ theme }) => theme.spacing(2)};
|
|
||||||
padding: ${({ theme }) => theme.spacing(2)};
|
|
||||||
position: relative;
|
|
||||||
`;
|
|
||||||
|
|
||||||
type WorkflowEditActionFormServerlessFunctionInnerProps = {
|
|
||||||
action: WorkflowCodeAction;
|
|
||||||
actionOptions:
|
|
||||||
| {
|
|
||||||
readonly: true;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
readonly?: false;
|
|
||||||
onActionUpdate: (action: WorkflowCodeAction) => void;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type ServerlessFunctionInputFormData = {
|
|
||||||
[field: string]: string | ServerlessFunctionInputFormData;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const WorkflowEditActionFormServerlessFunctionInner = ({
|
|
||||||
action,
|
|
||||||
actionOptions,
|
|
||||||
}: WorkflowEditActionFormServerlessFunctionInnerProps) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const { serverlessFunctions } = useGetManyServerlessFunctions();
|
|
||||||
|
|
||||||
const getFunctionInput = (serverlessFunctionId: string) => {
|
|
||||||
if (!serverlessFunctionId) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const serverlessFunction = serverlessFunctions.find(
|
|
||||||
(f) => f.id === serverlessFunctionId,
|
|
||||||
);
|
|
||||||
const inputSchema = serverlessFunction?.latestVersionInputSchema;
|
|
||||||
const defaultFunctionInput =
|
|
||||||
getDefaultFunctionInputFromInputSchema(inputSchema);
|
|
||||||
|
|
||||||
return defaultFunctionInput;
|
|
||||||
};
|
|
||||||
|
|
||||||
const [selectedFunctionId, setSelectedFunctionId] = useState(
|
|
||||||
action.settings.input.serverlessFunctionId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [functionInput, setFunctionInput] =
|
|
||||||
useState<ServerlessFunctionInputFormData>(
|
|
||||||
mergeDefaultFunctionInputAndFunctionInput({
|
|
||||||
defaultFunctionInput: getFunctionInput(selectedFunctionId),
|
|
||||||
functionInput: action.settings.input.serverlessFunctionInput,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateFunctionInput = useDebouncedCallback(
|
|
||||||
async (newFunctionInput: object) => {
|
|
||||||
if (actionOptions.readonly === true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
actionOptions.onActionUpdate({
|
|
||||||
...action,
|
|
||||||
settings: {
|
|
||||||
...action.settings,
|
|
||||||
input: {
|
|
||||||
...action.settings.input,
|
|
||||||
serverlessFunctionInput: newFunctionInput,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
1_000,
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleInputChange = (value: any, path: string[]) => {
|
|
||||||
const updatedFunctionInput = setNestedValue(functionInput, path, value);
|
|
||||||
|
|
||||||
setFunctionInput(updatedFunctionInput);
|
|
||||||
|
|
||||||
updateFunctionInput(updatedFunctionInput);
|
|
||||||
};
|
|
||||||
|
|
||||||
const availableFunctions: Array<SelectOption<string>> = [
|
|
||||||
...serverlessFunctions
|
|
||||||
.filter((serverlessFunction) =>
|
|
||||||
isDefined(serverlessFunction.latestVersion),
|
|
||||||
)
|
|
||||||
.map((serverlessFunction) => ({
|
|
||||||
label: serverlessFunction.name,
|
|
||||||
value: serverlessFunction.id,
|
|
||||||
latestVersionInputSchema: serverlessFunction.latestVersionInputSchema,
|
|
||||||
})),
|
|
||||||
];
|
|
||||||
|
|
||||||
const handleFunctionChange = (newServerlessFunctionId: string) => {
|
|
||||||
if (actionOptions.readonly === true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFunctionInput.cancel();
|
|
||||||
|
|
||||||
setSelectedFunctionId(newServerlessFunctionId);
|
|
||||||
|
|
||||||
const serverlessFunction = serverlessFunctions.find(
|
|
||||||
(f) => f.id === newServerlessFunctionId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const newFunctionInput = getFunctionInput(newServerlessFunctionId);
|
|
||||||
|
|
||||||
const newProps = {
|
|
||||||
...action,
|
|
||||||
settings: {
|
|
||||||
...action.settings,
|
|
||||||
input: {
|
|
||||||
serverlessFunctionId: newServerlessFunctionId,
|
|
||||||
serverlessFunctionVersion:
|
|
||||||
serverlessFunction?.latestVersion || 'latest',
|
|
||||||
serverlessFunctionInput: newFunctionInput,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
setFunctionInput(newFunctionInput);
|
|
||||||
|
|
||||||
actionOptions.onActionUpdate(newProps);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderFields = (
|
|
||||||
functionInput: FunctionInput,
|
|
||||||
path: string[] = [],
|
|
||||||
isRoot = true,
|
|
||||||
): ReactNode[] => {
|
|
||||||
const displaySeparator = (functionInput: FunctionInput) => {
|
|
||||||
const keys = Object.keys(functionInput);
|
|
||||||
if (keys.length > 1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (keys.length === 1) {
|
|
||||||
const subKeys = Object.keys(functionInput[keys[0]]);
|
|
||||||
return subKeys.length > 0;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
return Object.entries(functionInput).map(([inputKey, inputValue]) => {
|
|
||||||
const currentPath = [...path, inputKey];
|
|
||||||
const pathKey = currentPath.join('.');
|
|
||||||
|
|
||||||
if (inputValue !== null && typeof inputValue === 'object') {
|
|
||||||
if (isRoot) {
|
|
||||||
return (
|
|
||||||
<Fragment key={pathKey}>
|
|
||||||
{displaySeparator(functionInput) && (
|
|
||||||
<HorizontalSeparator noMargin />
|
|
||||||
)}
|
|
||||||
{renderFields(inputValue, currentPath, false)}
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<StyledContainer key={pathKey}>
|
|
||||||
<StyledLabel>{inputKey}</StyledLabel>
|
|
||||||
<StyledInputContainer>
|
|
||||||
{renderFields(inputValue, currentPath, false)}
|
|
||||||
</StyledInputContainer>
|
|
||||||
</StyledContainer>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<FormTextFieldInput
|
|
||||||
key={pathKey}
|
|
||||||
label={inputKey}
|
|
||||||
placeholder="Enter value"
|
|
||||||
defaultValue={inputValue ? String(inputValue) : ''}
|
|
||||||
readonly={actionOptions.readonly}
|
|
||||||
onPersist={(value) => {
|
|
||||||
handleInputChange(value, currentPath);
|
|
||||||
}}
|
|
||||||
VariablePicker={WorkflowVariablePicker}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const headerTitle = isDefined(action.name)
|
|
||||||
? action.name
|
|
||||||
: 'Code - Serverless Function';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<WorkflowEditGenericFormBase
|
|
||||||
onTitleChange={(newName: string) => {
|
|
||||||
if (actionOptions.readonly === true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
actionOptions?.onActionUpdate({
|
|
||||||
...action,
|
|
||||||
name: newName,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
HeaderIcon={<IconCode color={theme.color.orange} />}
|
|
||||||
headerTitle={headerTitle}
|
|
||||||
headerType="Code"
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
dropdownId="select-serverless-function-id"
|
|
||||||
label="Function"
|
|
||||||
fullWidth
|
|
||||||
value={selectedFunctionId}
|
|
||||||
options={availableFunctions}
|
|
||||||
emptyOption={{ label: 'None', value: '' }}
|
|
||||||
disabled={actionOptions.readonly}
|
|
||||||
onChange={handleFunctionChange}
|
|
||||||
/>
|
|
||||||
{renderFields(functionInput)}
|
|
||||||
</WorkflowEditGenericFormBase>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,27 +1,36 @@
|
|||||||
import { TextInput } from '@/ui/field/input/components/TextInput';
|
import { TextInput } from '@/ui/field/input/components/TextInput';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useDebouncedCallback } from 'use-debounce';
|
import { useDebouncedCallback } from 'use-debounce';
|
||||||
|
import { IconComponent } from 'packages/twenty-ui';
|
||||||
|
import { useTheme } from '@emotion/react';
|
||||||
|
|
||||||
const StyledHeader = styled.div`
|
const StyledHeader = styled.div`
|
||||||
background-color: ${({ theme }) => theme.background.secondary};
|
background-color: ${({ theme }) => theme.background.secondary};
|
||||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.medium};
|
border-bottom: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
padding: ${({ theme }) => theme.spacing(6)};
|
padding: ${({ theme }) => theme.spacing(4)};
|
||||||
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledHeaderTitle = styled.p`
|
const StyledHeaderInfo = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledHeaderTitle = styled.div`
|
||||||
color: ${({ theme }) => theme.font.color.primary};
|
color: ${({ theme }) => theme.font.color.primary};
|
||||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||||
font-size: ${({ theme }) => theme.font.size.xl};
|
font-size: ${({ theme }) => theme.font.size.xl};
|
||||||
|
width: 420px;
|
||||||
margin: ${({ theme }) => theme.spacing(3)} 0;
|
overflow: hidden;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledHeaderType = styled.p`
|
const StyledHeaderType = styled.div`
|
||||||
color: ${({ theme }) => theme.font.color.tertiary};
|
color: ${({ theme }) => theme.font.color.tertiary};
|
||||||
margin: 0;
|
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledHeaderIconContainer = styled.div`
|
const StyledHeaderIconContainer = styled.div`
|
||||||
@ -30,8 +39,8 @@ const StyledHeaderIconContainer = styled.div`
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: ${({ theme }) => theme.background.transparent.light};
|
background-color: ${({ theme }) => theme.background.transparent.light};
|
||||||
border-radius: ${({ theme }) => theme.border.radius.xs};
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
padding: ${({ theme }) => theme.spacing(1)};
|
padding: ${({ theme }) => theme.spacing(2)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledContentContainer = styled.div`
|
const StyledContentContainer = styled.div`
|
||||||
@ -43,39 +52,54 @@ const StyledContentContainer = styled.div`
|
|||||||
|
|
||||||
export const WorkflowEditGenericFormBase = ({
|
export const WorkflowEditGenericFormBase = ({
|
||||||
onTitleChange,
|
onTitleChange,
|
||||||
HeaderIcon,
|
Icon,
|
||||||
headerTitle,
|
iconColor,
|
||||||
|
initialTitle,
|
||||||
headerType,
|
headerType,
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
onTitleChange: (newTitle: string) => void;
|
onTitleChange: (newTitle: string) => void;
|
||||||
HeaderIcon: React.ReactNode;
|
Icon: IconComponent;
|
||||||
headerTitle: string;
|
iconColor: string;
|
||||||
|
initialTitle: string;
|
||||||
headerType: string;
|
headerType: string;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) => {
|
}) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const [title, setTitle] = useState(initialTitle);
|
||||||
const debouncedOnTitleChange = useDebouncedCallback(onTitleChange, 100);
|
const debouncedOnTitleChange = useDebouncedCallback(onTitleChange, 100);
|
||||||
|
const handleChange = (newTitle: string) => {
|
||||||
|
setTitle(newTitle);
|
||||||
|
debouncedOnTitleChange(newTitle);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StyledHeader>
|
<StyledHeader>
|
||||||
<StyledHeaderIconContainer>{HeaderIcon}</StyledHeaderIconContainer>
|
<StyledHeaderIconContainer>
|
||||||
|
{
|
||||||
<StyledHeaderTitle>
|
<Icon
|
||||||
<TextInput
|
color={iconColor}
|
||||||
value={headerTitle}
|
stroke={theme.icon.stroke.sm}
|
||||||
copyButton={false}
|
size={theme.icon.size.lg}
|
||||||
hotkeyScope="workflow-step-title"
|
/>
|
||||||
onEnter={onTitleChange}
|
}
|
||||||
onEscape={onTitleChange}
|
</StyledHeaderIconContainer>
|
||||||
onChange={debouncedOnTitleChange}
|
<StyledHeaderInfo>
|
||||||
shouldTrim={false}
|
<StyledHeaderTitle>
|
||||||
/>
|
<TextInput
|
||||||
</StyledHeaderTitle>
|
value={title}
|
||||||
|
copyButton={false}
|
||||||
<StyledHeaderType>{headerType}</StyledHeaderType>
|
hotkeyScope="workflow-step-title"
|
||||||
|
onEnter={onTitleChange}
|
||||||
|
onEscape={onTitleChange}
|
||||||
|
onChange={handleChange}
|
||||||
|
shouldTrim={false}
|
||||||
|
/>
|
||||||
|
</StyledHeaderTitle>
|
||||||
|
<StyledHeaderType>{headerType}</StyledHeaderType>
|
||||||
|
</StyledHeaderInfo>
|
||||||
</StyledHeader>
|
</StyledHeader>
|
||||||
|
|
||||||
<StyledContentContainer>{children}</StyledContentContainer>
|
<StyledContentContainer>{children}</StyledContentContainer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -71,8 +71,9 @@ export const WorkflowEditTriggerDatabaseEventForm = ({
|
|||||||
name: newName,
|
name: newName,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
HeaderIcon={<IconPlaylistAdd color={theme.font.color.tertiary} />}
|
Icon={IconPlaylistAdd}
|
||||||
headerTitle={headerTitle}
|
iconColor={theme.font.color.tertiary}
|
||||||
|
initialTitle={headerTitle}
|
||||||
headerType={headerType}
|
headerType={headerType}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
@ -58,8 +58,9 @@ export const WorkflowEditTriggerManualForm = ({
|
|||||||
name: newName,
|
name: newName,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
HeaderIcon={<IconHandMove color={theme.font.color.tertiary} />}
|
Icon={IconHandMove}
|
||||||
headerTitle={headerTitle}
|
iconColor={theme.font.color.tertiary}
|
||||||
|
initialTitle={headerTitle}
|
||||||
headerType="Trigger · Manual"
|
headerType="Trigger · Manual"
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
@ -3,10 +3,7 @@ import styled from '@emotion/styled';
|
|||||||
import { CodeEditor, isDefined } from 'twenty-ui';
|
import { CodeEditor, isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
const StyledSourceCodeContainer = styled.div`
|
const StyledSourceCodeContainer = styled.div`
|
||||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
|
||||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
|
||||||
margin: ${({ theme }) => theme.spacing(4)};
|
margin: ${({ theme }) => theme.spacing(4)};
|
||||||
overflow: hidden;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const WorkflowRunOutputVisualizer = ({
|
export const WorkflowRunOutputVisualizer = ({
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { lazy, Suspense } from 'react';
|
||||||
import { WorkflowEditTriggerDatabaseEventForm } from '@/workflow/components/WorkflowEditTriggerDatabaseEventForm';
|
import { WorkflowEditTriggerDatabaseEventForm } from '@/workflow/components/WorkflowEditTriggerDatabaseEventForm';
|
||||||
import { WorkflowEditTriggerManualForm } from '@/workflow/components/WorkflowEditTriggerManualForm';
|
import { WorkflowEditTriggerManualForm } from '@/workflow/components/WorkflowEditTriggerManualForm';
|
||||||
import {
|
import {
|
||||||
@ -12,8 +13,16 @@ import { isWorkflowRecordUpdateAction } from '@/workflow/utils/isWorkflowRecordU
|
|||||||
import { WorkflowEditActionFormRecordCreate } from '@/workflow/workflow-actions/components/WorkflowEditActionFormRecordCreate';
|
import { WorkflowEditActionFormRecordCreate } from '@/workflow/workflow-actions/components/WorkflowEditActionFormRecordCreate';
|
||||||
import { WorkflowEditActionFormRecordUpdate } from '@/workflow/workflow-actions/components/WorkflowEditActionFormRecordUpdate';
|
import { WorkflowEditActionFormRecordUpdate } from '@/workflow/workflow-actions/components/WorkflowEditActionFormRecordUpdate';
|
||||||
import { WorkflowEditActionFormSendEmail } from '@/workflow/workflow-actions/components/WorkflowEditActionFormSendEmail';
|
import { WorkflowEditActionFormSendEmail } from '@/workflow/workflow-actions/components/WorkflowEditActionFormSendEmail';
|
||||||
import { WorkflowEditActionFormServerlessFunction } from '@/workflow/workflow-actions/components/WorkflowEditActionFormServerlessFunction';
|
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
import { RightDrawerSkeletonLoader } from '~/loading/components/RightDrawerSkeletonLoader';
|
||||||
|
|
||||||
|
const WorkflowEditActionFormServerlessFunction = lazy(() =>
|
||||||
|
import(
|
||||||
|
'@/workflow/workflow-actions/components/WorkflowEditActionFormServerlessFunction'
|
||||||
|
).then((module) => ({
|
||||||
|
default: module.WorkflowEditActionFormServerlessFunction,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
type WorkflowStepDetailProps =
|
type WorkflowStepDetailProps =
|
||||||
| {
|
| {
|
||||||
@ -80,10 +89,12 @@ export const WorkflowStepDetail = ({
|
|||||||
switch (stepDefinition.definition.type) {
|
switch (stepDefinition.definition.type) {
|
||||||
case 'CODE': {
|
case 'CODE': {
|
||||||
return (
|
return (
|
||||||
<WorkflowEditActionFormServerlessFunction
|
<Suspense fallback={<RightDrawerSkeletonLoader />}>
|
||||||
action={stepDefinition.definition}
|
<WorkflowEditActionFormServerlessFunction
|
||||||
actionOptions={props}
|
action={stepDefinition.definition}
|
||||||
/>
|
actionOptions={props}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case 'SEND_EMAIL': {
|
case 'SEND_EMAIL': {
|
||||||
|
|||||||
@ -33,36 +33,6 @@ export const BlankInitialVersion: Story = {
|
|||||||
parameters: {
|
parameters: {
|
||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
graphql.query('FindManyWorkflows', () => {
|
|
||||||
return HttpResponse.json({
|
|
||||||
data: {
|
|
||||||
workflows: {
|
|
||||||
__typename: 'WorkflowConnection',
|
|
||||||
totalCount: 1,
|
|
||||||
pageInfo: {
|
|
||||||
__typename: 'PageInfo',
|
|
||||||
hasNextPage: false,
|
|
||||||
hasPreviousPage: false,
|
|
||||||
startCursor:
|
|
||||||
'eyJpZCI6IjIwMGMxNTA4LWYxMDItNGJiOS1hZjMyLWVkYTU1MjM5YWU2MSJ9',
|
|
||||||
endCursor:
|
|
||||||
'eyJpZCI6IjIwMGMxNTA4LWYxMDItNGJiOS1hZjMyLWVkYTU1MjM5YWU2MSJ9',
|
|
||||||
},
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
__typename: 'WorkflowEdge',
|
|
||||||
cursor:
|
|
||||||
'eyJpZCI6IjIwMGMxNTA4LWYxMDItNGJiOS1hZjMyLWVkYTU1MjM5YWU2MSJ9',
|
|
||||||
node: {
|
|
||||||
__typename: 'Workflow',
|
|
||||||
id: blankInitialVersionWorkflowId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
graphql.query('FindOneWorkflow', () => {
|
graphql.query('FindOneWorkflow', () => {
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
data: {
|
data: {
|
||||||
@ -112,44 +82,6 @@ export const BlankInitialVersion: Story = {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
graphql.query('FindManyWorkflowVersions', () => {
|
|
||||||
return HttpResponse.json({
|
|
||||||
data: {
|
|
||||||
workflowVersions: {
|
|
||||||
__typename: 'WorkflowVersionConnection',
|
|
||||||
totalCount: 1,
|
|
||||||
pageInfo: {
|
|
||||||
__typename: 'PageInfo',
|
|
||||||
hasNextPage: false,
|
|
||||||
hasPreviousPage: false,
|
|
||||||
startCursor:
|
|
||||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTE5VDEwOjEwOjA0LjcyNVoiLCJpZCI6ImY2MTg4NDNhLTI2YmUtNGE1NC1hNjBmLWY0Y2U4OGE1OTRmMCJ9',
|
|
||||||
endCursor:
|
|
||||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTE5VDEwOjEwOjA0LjcyNVoiLCJpZCI6ImY2MTg4NDNhLTI2YmUtNGE1NC1hNjBmLWY0Y2U4OGE1OTRmMCJ9',
|
|
||||||
},
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
__typename: 'WorkflowVersionEdge',
|
|
||||||
cursor:
|
|
||||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTE5VDEwOjEwOjA0LjcyNVoiLCJpZCI6ImY2MTg4NDNhLTI2YmUtNGE1NC1hNjBmLWY0Y2U4OGE1OTRmMCJ9',
|
|
||||||
node: {
|
|
||||||
__typename: 'WorkflowVersion',
|
|
||||||
updatedAt: '2024-09-19T10:13:12.075Z',
|
|
||||||
steps: null,
|
|
||||||
createdAt: '2024-09-19T10:10:04.725Z',
|
|
||||||
status: 'DRAFT',
|
|
||||||
name: 'v1',
|
|
||||||
id: 'f618843a-26be-4a54-a60f-f4ce88a594f0',
|
|
||||||
trigger: null,
|
|
||||||
deletedAt: null,
|
|
||||||
workflowId: blankInitialVersionWorkflowId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
...graphqlMocks.handlers,
|
...graphqlMocks.handlers,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -172,53 +104,19 @@ export const ActiveVersion: Story = {
|
|||||||
parameters: {
|
parameters: {
|
||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
graphql.query('FindManyWorkflows', () => {
|
|
||||||
return HttpResponse.json({
|
|
||||||
data: {
|
|
||||||
workflows: {
|
|
||||||
__typename: 'WorkflowConnection',
|
|
||||||
totalCount: 1,
|
|
||||||
pageInfo: {
|
|
||||||
__typename: 'PageInfo',
|
|
||||||
hasNextPage: false,
|
|
||||||
hasPreviousPage: false,
|
|
||||||
startCursor:
|
|
||||||
'eyJwb3NpdGlvbiI6LTEsImlkIjoiN2JlM2E4MmMtNDRiNy00MTUwLWEyZTgtNDA4ODcxNDZmNGQ0In0=',
|
|
||||||
endCursor:
|
|
||||||
'eyJwb3NpdGlvbiI6LTEsImlkIjoiN2JlM2E4MmMtNDRiNy00MTUwLWEyZTgtNDA4ODcxNDZmNGQ0In0=',
|
|
||||||
},
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
__typename: 'WorkflowEdge',
|
|
||||||
cursor:
|
|
||||||
'eyJwb3NpdGlvbiI6LTEsImlkIjoiN2JlM2E4MmMtNDRiNy00MTUwLWEyZTgtNDA4ODcxNDZmNGQ0In0=',
|
|
||||||
node: {
|
|
||||||
__typename: 'Workflow',
|
|
||||||
id: activeVersionWorkflowId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
graphql.query('FindOneWorkflow', () => {
|
graphql.query('FindOneWorkflow', () => {
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
data: {
|
data: {
|
||||||
workflow: {
|
workflow: {
|
||||||
__typename: 'Workflow',
|
__typename: 'Workflow',
|
||||||
name: 'test qqqq',
|
id: blankInitialVersionWorkflowId,
|
||||||
lastPublishedVersionId: 'b57e577a-ae55-4de2-ba08-fe361dcc1a57',
|
name: '1231 qqerrt',
|
||||||
id: activeVersionWorkflowId,
|
|
||||||
deletedAt: null,
|
|
||||||
statuses: null,
|
statuses: null,
|
||||||
createdAt: '2024-09-20T10:18:59.977Z',
|
lastPublishedVersionId: '',
|
||||||
updatedAt: '2024-09-20T16:59:37.212Z',
|
deletedAt: null,
|
||||||
position: -1,
|
updatedAt: '2024-09-19T10:10:04.505Z',
|
||||||
runs: {
|
position: 0,
|
||||||
__typename: 'WorkflowRunConnection',
|
createdAt: '2024-09-19T10:10:04.505Z',
|
||||||
edges: [],
|
|
||||||
},
|
|
||||||
favorites: {
|
favorites: {
|
||||||
__typename: 'FavoriteConnection',
|
__typename: 'FavoriteConnection',
|
||||||
edges: [],
|
edges: [],
|
||||||
@ -227,6 +125,10 @@ export const ActiveVersion: Story = {
|
|||||||
__typename: 'WorkflowEventListenerConnection',
|
__typename: 'WorkflowEventListenerConnection',
|
||||||
edges: [],
|
edges: [],
|
||||||
},
|
},
|
||||||
|
runs: {
|
||||||
|
__typename: 'WorkflowRunConnection',
|
||||||
|
edges: [],
|
||||||
|
},
|
||||||
versions: {
|
versions: {
|
||||||
__typename: 'WorkflowVersionConnection',
|
__typename: 'WorkflowVersionConnection',
|
||||||
edges: [
|
edges: [
|
||||||
@ -234,165 +136,15 @@ export const ActiveVersion: Story = {
|
|||||||
__typename: 'WorkflowVersionEdge',
|
__typename: 'WorkflowVersionEdge',
|
||||||
node: {
|
node: {
|
||||||
__typename: 'WorkflowVersion',
|
__typename: 'WorkflowVersion',
|
||||||
updatedAt: '2024-09-20T16:59:37.212Z',
|
updatedAt: '2024-09-19T10:13:12.075Z',
|
||||||
status: 'ARCHIVED',
|
steps: null,
|
||||||
deletedAt: null,
|
createdAt: '2024-09-19T10:10:04.725Z',
|
||||||
steps: [
|
|
||||||
{
|
|
||||||
id: '93c41c1d-eff3-4c91-ac61-f56cc1a0df8a',
|
|
||||||
name: 'Code',
|
|
||||||
type: 'CODE',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
errorHandlingOptions: {
|
|
||||||
retryOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
continueOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
serverlessFunctionId: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
workflowId: activeVersionWorkflowId,
|
|
||||||
trigger: {
|
|
||||||
type: 'DATABASE_EVENT',
|
|
||||||
settings: {
|
|
||||||
eventName: 'note.created',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
name: 'v1',
|
|
||||||
id: '394cd0b5-bd48-41d7-a110-a92cafaf171d',
|
|
||||||
createdAt: '2024-09-20T10:19:00.141Z',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
__typename: 'WorkflowVersionEdge',
|
|
||||||
node: {
|
|
||||||
__typename: 'WorkflowVersion',
|
|
||||||
updatedAt: '2024-09-20T17:01:15.637Z',
|
|
||||||
status: 'DRAFT',
|
|
||||||
deletedAt: null,
|
|
||||||
steps: [
|
|
||||||
{
|
|
||||||
id: '93c41c1d-eff3-4c91-ac61-f56cc1a0df8a',
|
|
||||||
name: 'Code',
|
|
||||||
type: 'CODE',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
errorHandlingOptions: {
|
|
||||||
retryOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
continueOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
serverlessFunctionId: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4177d57d-35dc-4eb1-a467-07e25cb31da0',
|
|
||||||
name: 'Code',
|
|
||||||
type: 'CODE',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
errorHandlingOptions: {
|
|
||||||
retryOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
continueOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
serverlessFunctionId: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '0cc392d9-5f28-4d92-90a0-08180f264e68',
|
|
||||||
name: 'Code',
|
|
||||||
type: 'CODE',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
errorHandlingOptions: {
|
|
||||||
retryOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
continueOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
serverlessFunctionId: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
workflowId: activeVersionWorkflowId,
|
|
||||||
trigger: {
|
|
||||||
type: 'DATABASE_EVENT',
|
|
||||||
settings: {
|
|
||||||
eventName: 'note.created',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
name: 'v3',
|
|
||||||
id: '5eae34ef-9d62-4a9e-b827-3eb927481728',
|
|
||||||
createdAt: '2024-09-20T17:01:15.637Z',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
__typename: 'WorkflowVersionEdge',
|
|
||||||
node: {
|
|
||||||
__typename: 'WorkflowVersion',
|
|
||||||
updatedAt: '2024-09-20T17:00:16.097Z',
|
|
||||||
status: 'ACTIVE',
|
status: 'ACTIVE',
|
||||||
|
name: 'v1',
|
||||||
|
id: 'f618843a-26be-4a54-a60f-f4ce88a594f0',
|
||||||
|
trigger: null,
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
steps: [
|
workflowId: blankInitialVersionWorkflowId,
|
||||||
{
|
|
||||||
id: '93c41c1d-eff3-4c91-ac61-f56cc1a0df8a',
|
|
||||||
name: 'Code',
|
|
||||||
type: 'CODE',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
errorHandlingOptions: {
|
|
||||||
retryOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
continueOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
serverlessFunctionId: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4177d57d-35dc-4eb1-a467-07e25cb31da0',
|
|
||||||
name: 'Code',
|
|
||||||
type: 'CODE',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
errorHandlingOptions: {
|
|
||||||
retryOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
continueOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
serverlessFunctionId: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
workflowId: activeVersionWorkflowId,
|
|
||||||
trigger: {
|
|
||||||
type: 'DATABASE_EVENT',
|
|
||||||
settings: {
|
|
||||||
eventName: 'note.created',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
name: 'v2',
|
|
||||||
id: 'b57e577a-ae55-4de2-ba08-fe361dcc1a57',
|
|
||||||
createdAt: '2024-09-20T16:59:35.755Z',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -401,101 +153,6 @@ export const ActiveVersion: Story = {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
graphql.query('FindManyWorkflowVersions', () => {
|
|
||||||
return HttpResponse.json({
|
|
||||||
data: {
|
|
||||||
workflowVersions: {
|
|
||||||
__typename: 'WorkflowVersionConnection',
|
|
||||||
totalCount: 3,
|
|
||||||
pageInfo: {
|
|
||||||
__typename: 'PageInfo',
|
|
||||||
hasNextPage: true,
|
|
||||||
hasPreviousPage: false,
|
|
||||||
startCursor:
|
|
||||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTIwVDE3OjAxOjE1LjYzN1oiLCJpZCI6IjVlYWUzNGVmLTlkNjItNGE5ZS1iODI3LTNlYjkyNzQ4MTcyOCJ9',
|
|
||||||
endCursor:
|
|
||||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTIwVDE3OjAxOjE1LjYzN1oiLCJpZCI6IjVlYWUzNGVmLTlkNjItNGE5ZS1iODI3LTNlYjkyNzQ4MTcyOCJ9',
|
|
||||||
},
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
__typename: 'WorkflowVersionEdge',
|
|
||||||
cursor:
|
|
||||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTIwVDE3OjAxOjE1LjYzN1oiLCJpZCI6IjVlYWUzNGVmLTlkNjItNGE5ZS1iODI3LTNlYjkyNzQ4MTcyOCJ9',
|
|
||||||
node: {
|
|
||||||
__typename: 'WorkflowVersion',
|
|
||||||
updatedAt: '2024-09-20T17:01:15.637Z',
|
|
||||||
status: 'ACTIVE',
|
|
||||||
deletedAt: null,
|
|
||||||
steps: [
|
|
||||||
{
|
|
||||||
id: '93c41c1d-eff3-4c91-ac61-f56cc1a0df8a',
|
|
||||||
name: 'Code',
|
|
||||||
type: 'CODE',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
errorHandlingOptions: {
|
|
||||||
retryOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
continueOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
serverlessFunctionId: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4177d57d-35dc-4eb1-a467-07e25cb31da0',
|
|
||||||
name: 'Code',
|
|
||||||
type: 'CODE',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
errorHandlingOptions: {
|
|
||||||
retryOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
continueOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
serverlessFunctionId: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '0cc392d9-5f28-4d92-90a0-08180f264e68',
|
|
||||||
name: 'Code',
|
|
||||||
type: 'CODE',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
errorHandlingOptions: {
|
|
||||||
retryOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
continueOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
serverlessFunctionId: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
workflowId: activeVersionWorkflowId,
|
|
||||||
trigger: {
|
|
||||||
type: 'DATABASE_EVENT',
|
|
||||||
settings: {
|
|
||||||
eventName: 'note.created',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
name: 'v3',
|
|
||||||
id: '5eae34ef-9d62-4a9e-b827-3eb927481728',
|
|
||||||
createdAt: '2024-09-20T17:01:15.637Z',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
...graphqlMocks.handlers,
|
...graphqlMocks.handlers,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -518,36 +175,6 @@ export const DraftVersionWithPreviousActiveVersion: Story = {
|
|||||||
parameters: {
|
parameters: {
|
||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
graphql.query('FindManyWorkflows', () => {
|
|
||||||
return HttpResponse.json({
|
|
||||||
data: {
|
|
||||||
workflows: {
|
|
||||||
__typename: 'WorkflowConnection',
|
|
||||||
totalCount: 1,
|
|
||||||
pageInfo: {
|
|
||||||
__typename: 'PageInfo',
|
|
||||||
hasNextPage: false,
|
|
||||||
hasPreviousPage: false,
|
|
||||||
startCursor:
|
|
||||||
'eyJpZCI6IjIwMGMxNTA4LWYxMDItNGJiOS1hZjMyLWVkYTU1MjM5YWU2MSJ9',
|
|
||||||
endCursor:
|
|
||||||
'eyJpZCI6IjIwMGMxNTA4LWYxMDItNGJiOS1hZjMyLWVkYTU1MjM5YWU2MSJ9',
|
|
||||||
},
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
__typename: 'WorkflowEdge',
|
|
||||||
cursor:
|
|
||||||
'eyJpZCI6IjIwMGMxNTA4LWYxMDItNGJiOS1hZjMyLWVkYTU1MjM5YWU2MSJ9',
|
|
||||||
node: {
|
|
||||||
__typename: 'Workflow',
|
|
||||||
id: draftVersionWithPreviousActiveVersionWorkflowId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
graphql.query('FindOneWorkflow', () => {
|
graphql.query('FindOneWorkflow', () => {
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
data: {
|
data: {
|
||||||
@ -614,45 +241,6 @@ export const DraftVersionWithPreviousActiveVersion: Story = {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
graphql.query('FindManyWorkflowVersions', () => {
|
|
||||||
return HttpResponse.json({
|
|
||||||
data: {
|
|
||||||
workflowVersions: {
|
|
||||||
__typename: 'WorkflowVersionConnection',
|
|
||||||
totalCount: 1,
|
|
||||||
pageInfo: {
|
|
||||||
__typename: 'PageInfo',
|
|
||||||
hasNextPage: false,
|
|
||||||
hasPreviousPage: false,
|
|
||||||
startCursor:
|
|
||||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTE5VDEwOjEwOjA0LjcyNVoiLCJpZCI6ImY2MTg4NDNhLTI2YmUtNGE1NC1hNjBmLWY0Y2U4OGE1OTRmMCJ9',
|
|
||||||
endCursor:
|
|
||||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTE5VDEwOjEwOjA0LjcyNVoiLCJpZCI6ImY2MTg4NDNhLTI2YmUtNGE1NC1hNjBmLWY0Y2U4OGE1OTRmMCJ9',
|
|
||||||
},
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
__typename: 'WorkflowVersionEdge',
|
|
||||||
cursor:
|
|
||||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTE5VDEwOjEwOjA0LjcyNVoiLCJpZCI6ImY2MTg4NDNhLTI2YmUtNGE1NC1hNjBmLWY0Y2U4OGE1OTRmMCJ9',
|
|
||||||
node: {
|
|
||||||
__typename: 'WorkflowVersion',
|
|
||||||
updatedAt: '2024-09-19T10:13:12.075Z',
|
|
||||||
steps: null,
|
|
||||||
createdAt: '2024-09-19T10:10:05.725Z',
|
|
||||||
status: 'DRAFT',
|
|
||||||
name: 'v2',
|
|
||||||
id: 'f618843a-26be-4a54-a60f-f4ce88a594f1',
|
|
||||||
trigger: null,
|
|
||||||
deletedAt: null,
|
|
||||||
workflowId:
|
|
||||||
draftVersionWithPreviousActiveVersionWorkflowId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
...graphqlMocks.handlers,
|
...graphqlMocks.handlers,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const CREATE_WORKFLOW_VERSION_STEP = gql`
|
||||||
|
mutation CreateWorkflowVersionStep($input: CreateWorkflowVersionStepInput!) {
|
||||||
|
createWorkflowVersionStep(input: $input) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
type
|
||||||
|
settings
|
||||||
|
valid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const DELETE_WORKFLOW_VERSION_STEP = gql`
|
||||||
|
mutation DeleteWorkflowVersionStep($input: DeleteWorkflowVersionStepInput!) {
|
||||||
|
deleteWorkflowVersionStep(input: $input) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
type
|
||||||
|
settings
|
||||||
|
valid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const UPDATE_WORKFLOW_VERSION_STEP = gql`
|
||||||
|
mutation UpdateWorkflowVersionStep($input: UpdateWorkflowVersionStepInput!) {
|
||||||
|
updateWorkflowVersionStep(input: $input) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
type
|
||||||
|
settings
|
||||||
|
valid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
@ -3,10 +3,9 @@ import { renderHook } from '@testing-library/react';
|
|||||||
import { useCreateStep } from '../useCreateStep';
|
import { useCreateStep } from '../useCreateStep';
|
||||||
|
|
||||||
const mockOpenRightDrawer = jest.fn();
|
const mockOpenRightDrawer = jest.fn();
|
||||||
const mockUpdateOneWorkflowVersion = jest.fn();
|
|
||||||
const mockCreateNewWorkflowVersion = jest.fn();
|
const mockCreateNewWorkflowVersion = jest.fn();
|
||||||
const mockComputeStepOutputSchema = jest.fn().mockResolvedValue({
|
const mockCreateWorkflowVersionStep = jest.fn().mockResolvedValue({
|
||||||
data: { computeStepOutputSchema: { type: 'object' } },
|
data: { createWorkflowVersionStep: { id: '1' } },
|
||||||
});
|
});
|
||||||
|
|
||||||
jest.mock('recoil', () => ({
|
jest.mock('recoil', () => ({
|
||||||
@ -15,19 +14,15 @@ jest.mock('recoil', () => ({
|
|||||||
atom: (params: any) => params,
|
atom: (params: any) => params,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('@/workflow/states/workflowCreateStepFromParentStepIdState', () => ({
|
|
||||||
workflowCreateStepFromParentStepIdState: 'mockAtomState',
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('@/ui/layout/right-drawer/hooks/useRightDrawer', () => ({
|
jest.mock('@/ui/layout/right-drawer/hooks/useRightDrawer', () => ({
|
||||||
useRightDrawer: () => ({
|
useRightDrawer: () => ({
|
||||||
openRightDrawer: mockOpenRightDrawer,
|
openRightDrawer: mockOpenRightDrawer,
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('@/object-record/hooks/useUpdateOneRecord', () => ({
|
jest.mock('@/workflow/hooks/useCreateWorkflowVersionStep', () => ({
|
||||||
useUpdateOneRecord: () => ({
|
useCreateWorkflowVersionStep: () => ({
|
||||||
updateOneRecord: mockUpdateOneWorkflowVersion,
|
createWorkflowVersionStep: mockCreateWorkflowVersionStep,
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -37,24 +32,6 @@ jest.mock('@/workflow/hooks/useCreateNewWorkflowVersion', () => ({
|
|||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('@/workflow/hooks/useComputeStepOutputSchema', () => ({
|
|
||||||
useComputeStepOutputSchema: () => ({
|
|
||||||
computeStepOutputSchema: mockComputeStepOutputSchema,
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('@/object-metadata/hooks/useFilteredObjectMetadataItems', () => ({
|
|
||||||
useFilteredObjectMetadataItems: () => ({
|
|
||||||
activeObjectMetadataItems: [],
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('@/workflow/utils/insertStep', () => ({
|
|
||||||
insertStep: jest
|
|
||||||
.fn()
|
|
||||||
.mockImplementation(({ steps, stepToAdd }) => [...steps, stepToAdd]),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('useCreateStep', () => {
|
describe('useCreateStep', () => {
|
||||||
const mockWorkflow = {
|
const mockWorkflow = {
|
||||||
id: '123',
|
id: '123',
|
||||||
@ -75,7 +52,7 @@ describe('useCreateStep', () => {
|
|||||||
);
|
);
|
||||||
await result.current.createStep('CODE');
|
await result.current.createStep('CODE');
|
||||||
|
|
||||||
expect(mockUpdateOneWorkflowVersion).toHaveBeenCalled();
|
expect(mockCreateWorkflowVersionStep).toHaveBeenCalled();
|
||||||
expect(mockOpenRightDrawer).toHaveBeenCalled();
|
expect(mockOpenRightDrawer).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,62 @@
|
|||||||
|
import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { useDeleteStep } from '@/workflow/hooks/useDeleteStep';
|
||||||
|
|
||||||
|
const mockCloseRightDrawer = jest.fn();
|
||||||
|
const mockCreateNewWorkflowVersion = jest.fn();
|
||||||
|
const mockDeleteWorkflowVersionStep = jest.fn();
|
||||||
|
const updateOneRecordMock = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('@/object-record/hooks/useUpdateOneRecord', () => ({
|
||||||
|
useUpdateOneRecord: () => ({
|
||||||
|
updateOneRecord: updateOneRecordMock,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('recoil', () => ({
|
||||||
|
useRecoilValue: () => 'parent-step-id',
|
||||||
|
useSetRecoilState: () => jest.fn(),
|
||||||
|
atom: (params: any) => params,
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('@/ui/layout/right-drawer/hooks/useRightDrawer', () => ({
|
||||||
|
useRightDrawer: () => ({
|
||||||
|
closeRightDrawer: mockCloseRightDrawer,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useDeleteWorkflowVersionStep', () => ({
|
||||||
|
useDeleteWorkflowVersionStep: () => ({
|
||||||
|
deleteWorkflowVersionStep: mockDeleteWorkflowVersionStep,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useCreateNewWorkflowVersion', () => ({
|
||||||
|
useCreateNewWorkflowVersion: () => ({
|
||||||
|
createNewWorkflowVersion: mockCreateNewWorkflowVersion,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('useDeleteStep', () => {
|
||||||
|
const mockWorkflow = {
|
||||||
|
id: '123',
|
||||||
|
currentVersion: {
|
||||||
|
id: '456',
|
||||||
|
status: 'DRAFT',
|
||||||
|
steps: [],
|
||||||
|
trigger: { type: 'manual' },
|
||||||
|
},
|
||||||
|
versions: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should delete step in draft version', async () => {
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDeleteStep({
|
||||||
|
workflow: mockWorkflow as unknown as WorkflowWithCurrentVersion,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
await result.current.deleteStep('1');
|
||||||
|
|
||||||
|
expect(mockDeleteWorkflowVersionStep).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion';
|
||||||
|
import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
|
||||||
|
|
||||||
|
const mockCreateNewWorkflowVersion = jest.fn().mockResolvedValue({
|
||||||
|
id: '457',
|
||||||
|
name: 'toto',
|
||||||
|
createdAt: '2024-07-03T20:03:35.064Z',
|
||||||
|
updatedAt: '2024-07-03T20:03:35.064Z',
|
||||||
|
workflowId: '123',
|
||||||
|
__typename: 'WorkflowVersion',
|
||||||
|
status: 'DRAFT',
|
||||||
|
steps: [],
|
||||||
|
trigger: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useCreateNewWorkflowVersion', () => ({
|
||||||
|
useCreateNewWorkflowVersion: () => ({
|
||||||
|
createNewWorkflowVersion: mockCreateNewWorkflowVersion,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('useGetUpdatableWorkflowVersion', () => {
|
||||||
|
const mockWorkflow = (status: 'ACTIVE' | 'DRAFT') =>
|
||||||
|
({
|
||||||
|
id: '123',
|
||||||
|
__typename: 'Workflow',
|
||||||
|
statuses: [],
|
||||||
|
lastPublishedVersionId: '1',
|
||||||
|
name: 'toto',
|
||||||
|
versions: [],
|
||||||
|
currentVersion: {
|
||||||
|
id: '456',
|
||||||
|
name: 'toto',
|
||||||
|
createdAt: '2024-07-03T20:03:35.064Z',
|
||||||
|
updatedAt: '2024-07-03T20:03:35.064Z',
|
||||||
|
workflowId: '123',
|
||||||
|
__typename: 'WorkflowVersion',
|
||||||
|
status,
|
||||||
|
steps: [],
|
||||||
|
trigger: null,
|
||||||
|
},
|
||||||
|
}) as WorkflowWithCurrentVersion;
|
||||||
|
|
||||||
|
it('should not create workflow version if draft version exists', async () => {
|
||||||
|
const { result } = renderHook(() => useGetUpdatableWorkflowVersion());
|
||||||
|
const workflowVersion = await result.current.getUpdatableWorkflowVersion(
|
||||||
|
mockWorkflow('DRAFT'),
|
||||||
|
);
|
||||||
|
expect(mockCreateNewWorkflowVersion).not.toHaveBeenCalled();
|
||||||
|
expect(workflowVersion.id === '456');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create workflow version if no draft version exists', async () => {
|
||||||
|
const { result } = renderHook(() => useGetUpdatableWorkflowVersion());
|
||||||
|
const workflowVersion = await result.current.getUpdatableWorkflowVersion(
|
||||||
|
mockWorkflow('ACTIVE'),
|
||||||
|
);
|
||||||
|
expect(mockCreateNewWorkflowVersion).toHaveBeenCalled();
|
||||||
|
expect(workflowVersion.id === '457');
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
import { renderHook, act } from '@testing-library/react';
|
||||||
|
import { useTriggerNodeSelection } from '@/workflow/hooks/useTriggerNodeSelection';
|
||||||
|
import { RecoilRoot, useRecoilState } from 'recoil';
|
||||||
|
import { useReactFlow } from '@xyflow/react';
|
||||||
|
import { workflowDiagramTriggerNodeSelectionState } from '@/workflow/states/workflowDiagramTriggerNodeSelectionState';
|
||||||
|
|
||||||
|
jest.mock('@xyflow/react', () => ({
|
||||||
|
useReactFlow: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||||
|
<RecoilRoot>{children}</RecoilRoot>
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('useTriggerNodeSelection', () => {
|
||||||
|
const mockUpdateNode = jest.fn();
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(useReactFlow as jest.Mock).mockReturnValue({
|
||||||
|
updateNode: mockUpdateNode,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should trigger node selection', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const [
|
||||||
|
workflowDiagramTriggerNodeSelection,
|
||||||
|
setWorkflowDiagramTriggerNodeSelection,
|
||||||
|
] = useRecoilState(workflowDiagramTriggerNodeSelectionState);
|
||||||
|
|
||||||
|
useTriggerNodeSelection();
|
||||||
|
|
||||||
|
return {
|
||||||
|
workflowDiagramTriggerNodeSelection,
|
||||||
|
setWorkflowDiagramTriggerNodeSelection,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const mockNodeId = 'test-node-id';
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.setWorkflowDiagramTriggerNodeSelection(mockNodeId);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockUpdateNode).toHaveBeenCalledWith(mockNodeId, { selected: true });
|
||||||
|
expect(result.current.workflowDiagramTriggerNodeSelection).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not trigger update if state is not defined', () => {
|
||||||
|
renderHook(() => useTriggerNodeSelection(), {
|
||||||
|
wrapper,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ensure updateNode is not called when state is undefined
|
||||||
|
expect(mockUpdateNode).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { useUpdateStep } from '@/workflow/hooks/useUpdateStep';
|
||||||
|
|
||||||
|
const mockCreateNewWorkflowVersion = jest.fn();
|
||||||
|
const mockUpdateWorkflowVersionStep = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('recoil', () => ({
|
||||||
|
useRecoilValue: () => 'parent-step-id',
|
||||||
|
useSetRecoilState: () => jest.fn(),
|
||||||
|
atom: (params: any) => params,
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useUpdateWorkflowVersionStep', () => ({
|
||||||
|
useUpdateWorkflowVersionStep: () => ({
|
||||||
|
updateWorkflowVersionStep: mockUpdateWorkflowVersionStep,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useCreateNewWorkflowVersion', () => ({
|
||||||
|
useCreateNewWorkflowVersion: () => ({
|
||||||
|
createNewWorkflowVersion: mockCreateNewWorkflowVersion,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('useUpdateStep', () => {
|
||||||
|
const mockWorkflow = {
|
||||||
|
id: '123',
|
||||||
|
currentVersion: {
|
||||||
|
id: '456',
|
||||||
|
status: 'DRAFT',
|
||||||
|
steps: [],
|
||||||
|
trigger: { type: 'manual' },
|
||||||
|
},
|
||||||
|
versions: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should update step in draft version', async () => {
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useUpdateStep({
|
||||||
|
workflow: mockWorkflow as unknown as WorkflowWithCurrentVersion,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
await result.current.updateStep({
|
||||||
|
id: '1',
|
||||||
|
name: 'name',
|
||||||
|
valid: true,
|
||||||
|
type: 'CODE',
|
||||||
|
settings: {
|
||||||
|
input: {
|
||||||
|
serverlessFunctionId: 'id',
|
||||||
|
serverlessFunctionVersion: '1',
|
||||||
|
serverlessFunctionInput: {},
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
errorHandlingOptions: {
|
||||||
|
retryOnFailure: {
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
continueOnFailure: {
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockUpdateWorkflowVersionStep).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -67,5 +67,5 @@ export const useAllActiveWorkflowVersions = ({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return { records };
|
return { records: records.filter((record) => isDefined(record.workflow)) };
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,13 +8,13 @@ export const useCreateNewWorkflowVersion = () => {
|
|||||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||||
});
|
});
|
||||||
|
|
||||||
const createNewWorkflowVersion = (
|
const createNewWorkflowVersion = async (
|
||||||
workflowVersionData: Pick<
|
workflowVersionData: Pick<
|
||||||
WorkflowVersion,
|
WorkflowVersion,
|
||||||
'workflowId' | 'name' | 'status' | 'trigger' | 'steps'
|
'workflowId' | 'name' | 'status' | 'trigger' | 'steps'
|
||||||
>,
|
>,
|
||||||
) => {
|
) => {
|
||||||
return createOneWorkflowVersion(workflowVersionData);
|
return await createOneWorkflowVersion(workflowVersionData);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,23 +1,16 @@
|
|||||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
|
||||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
|
||||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||||
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
|
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
|
||||||
import { useComputeStepOutputSchema } from '@/workflow/hooks/useComputeStepOutputSchema';
|
|
||||||
import { useCreateNewWorkflowVersion } from '@/workflow/hooks/useCreateNewWorkflowVersion';
|
|
||||||
import { workflowCreateStepFromParentStepIdState } from '@/workflow/states/workflowCreateStepFromParentStepIdState';
|
import { workflowCreateStepFromParentStepIdState } from '@/workflow/states/workflowCreateStepFromParentStepIdState';
|
||||||
import { workflowDiagramTriggerNodeSelectionState } from '@/workflow/states/workflowDiagramTriggerNodeSelectionState';
|
import { workflowDiagramTriggerNodeSelectionState } from '@/workflow/states/workflowDiagramTriggerNodeSelectionState';
|
||||||
import { workflowSelectedNodeState } from '@/workflow/states/workflowSelectedNodeState';
|
import { workflowSelectedNodeState } from '@/workflow/states/workflowSelectedNodeState';
|
||||||
import {
|
import {
|
||||||
WorkflowStep,
|
|
||||||
WorkflowStepType,
|
WorkflowStepType,
|
||||||
WorkflowVersion,
|
|
||||||
WorkflowWithCurrentVersion,
|
WorkflowWithCurrentVersion,
|
||||||
} from '@/workflow/types/Workflow';
|
} from '@/workflow/types/Workflow';
|
||||||
import { getStepDefaultDefinition } from '@/workflow/utils/getStepDefaultDefinition';
|
|
||||||
import { insertStep } from '@/workflow/utils/insertStep';
|
|
||||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
import { useCreateWorkflowVersionStep } from '@/workflow/hooks/useCreateWorkflowVersionStep';
|
||||||
|
import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion';
|
||||||
|
|
||||||
export const useCreateStep = ({
|
export const useCreateStep = ({
|
||||||
workflow,
|
workflow,
|
||||||
@ -25,6 +18,7 @@ export const useCreateStep = ({
|
|||||||
workflow: WorkflowWithCurrentVersion;
|
workflow: WorkflowWithCurrentVersion;
|
||||||
}) => {
|
}) => {
|
||||||
const { openRightDrawer } = useRightDrawer();
|
const { openRightDrawer } = useRightDrawer();
|
||||||
|
const { createWorkflowVersionStep } = useCreateWorkflowVersionStep();
|
||||||
const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState);
|
const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState);
|
||||||
|
|
||||||
const workflowCreateStepFromParentStepId = useRecoilValue(
|
const workflowCreateStepFromParentStepId = useRecoilValue(
|
||||||
@ -35,82 +29,27 @@ export const useCreateStep = ({
|
|||||||
workflowDiagramTriggerNodeSelectionState,
|
workflowDiagramTriggerNodeSelectionState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { updateOneRecord: updateOneWorkflowVersion } =
|
const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion();
|
||||||
useUpdateOneRecord<WorkflowVersion>({
|
|
||||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { createNewWorkflowVersion } = useCreateNewWorkflowVersion();
|
|
||||||
|
|
||||||
const { computeStepOutputSchema } = useComputeStepOutputSchema();
|
|
||||||
|
|
||||||
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
|
|
||||||
|
|
||||||
const insertNodeAndSave = async ({
|
|
||||||
parentNodeId,
|
|
||||||
nodeToAdd,
|
|
||||||
}: {
|
|
||||||
parentNodeId: string;
|
|
||||||
nodeToAdd: WorkflowStep;
|
|
||||||
}) => {
|
|
||||||
const currentVersion = workflow.currentVersion;
|
|
||||||
if (!isDefined(currentVersion)) {
|
|
||||||
throw new Error("Can't add a node when there is no current version.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedSteps = insertStep({
|
|
||||||
steps: currentVersion.steps ?? [],
|
|
||||||
parentStepId: parentNodeId,
|
|
||||||
stepToAdd: nodeToAdd,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (workflow.currentVersion.status === 'DRAFT') {
|
|
||||||
await updateOneWorkflowVersion({
|
|
||||||
idToUpdate: currentVersion.id,
|
|
||||||
updateOneRecordInput: {
|
|
||||||
steps: updatedSteps,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await createNewWorkflowVersion({
|
|
||||||
workflowId: workflow.id,
|
|
||||||
name: `v${workflow.versions.length + 1}`,
|
|
||||||
status: 'DRAFT',
|
|
||||||
trigger: workflow.currentVersion.trigger,
|
|
||||||
steps: updatedSteps,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const createStep = async (newStepType: WorkflowStepType) => {
|
const createStep = async (newStepType: WorkflowStepType) => {
|
||||||
if (!isDefined(workflowCreateStepFromParentStepId)) {
|
if (!isDefined(workflowCreateStepFromParentStepId)) {
|
||||||
throw new Error('Select a step to create a new step from first.');
|
throw new Error('Select a step to create a new step from first.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const newStep = getStepDefaultDefinition({
|
const workflowVersion = await getUpdatableWorkflowVersion(workflow);
|
||||||
type: newStepType,
|
|
||||||
activeObjectMetadataItems,
|
|
||||||
});
|
|
||||||
|
|
||||||
const outputSchema = (
|
const createdStep = (
|
||||||
await computeStepOutputSchema({
|
await createWorkflowVersionStep({
|
||||||
step: newStep,
|
workflowVersionId: workflowVersion.id,
|
||||||
|
stepType: newStepType,
|
||||||
})
|
})
|
||||||
)?.data?.computeStepOutputSchema;
|
)?.data?.createWorkflowVersionStep;
|
||||||
|
|
||||||
newStep.settings = {
|
if (!createdStep) {
|
||||||
...newStep.settings,
|
return;
|
||||||
outputSchema: outputSchema || {},
|
}
|
||||||
};
|
|
||||||
|
|
||||||
await insertNodeAndSave({
|
setWorkflowSelectedNode(createdStep.id);
|
||||||
parentNodeId: workflowCreateStepFromParentStepId,
|
|
||||||
nodeToAdd: newStep,
|
|
||||||
});
|
|
||||||
|
|
||||||
setWorkflowSelectedNode(newStep.id);
|
|
||||||
openRightDrawer(RightDrawerPages.WorkflowStepEdit);
|
openRightDrawer(RightDrawerPages.WorkflowStepEdit);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,7 +59,7 @@ export const useCreateStep = ({
|
|||||||
*
|
*
|
||||||
* Selecting the node will cause a right drawer to open in order to edit the step.
|
* Selecting the node will cause a right drawer to open in order to edit the step.
|
||||||
*/
|
*/
|
||||||
setWorkflowDiagramTriggerNodeSelection(newStep.id);
|
setWorkflowDiagramTriggerNodeSelection(createdStep.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -0,0 +1,64 @@
|
|||||||
|
import { useApolloClient, useMutation } from '@apollo/client';
|
||||||
|
import {
|
||||||
|
CreateWorkflowVersionStepMutation,
|
||||||
|
CreateWorkflowVersionStepMutationVariables,
|
||||||
|
CreateWorkflowVersionStepInput,
|
||||||
|
} from '~/generated/graphql';
|
||||||
|
import { CREATE_WORKFLOW_VERSION_STEP } from '@/workflow/graphql/mutations/createWorkflowVersionStep';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||||
|
import { WorkflowVersion } from '@/workflow/types/Workflow';
|
||||||
|
|
||||||
|
export const useCreateWorkflowVersionStep = () => {
|
||||||
|
const apolloClient = useApolloClient();
|
||||||
|
const { objectMetadataItems } = useObjectMetadataItems();
|
||||||
|
const { objectMetadataItem } = useObjectMetadataItem({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||||
|
});
|
||||||
|
const getRecordFromCache = useGetRecordFromCache({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||||
|
});
|
||||||
|
const [mutate] = useMutation<
|
||||||
|
CreateWorkflowVersionStepMutation,
|
||||||
|
CreateWorkflowVersionStepMutationVariables
|
||||||
|
>(CREATE_WORKFLOW_VERSION_STEP, {
|
||||||
|
client: apolloClient,
|
||||||
|
});
|
||||||
|
const createWorkflowVersionStep = async (
|
||||||
|
input: CreateWorkflowVersionStepInput,
|
||||||
|
) => {
|
||||||
|
const result = await mutate({
|
||||||
|
variables: { input },
|
||||||
|
});
|
||||||
|
const createdStep = result?.data?.createWorkflowVersionStep;
|
||||||
|
if (!isDefined(createdStep)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedRecord = getRecordFromCache<WorkflowVersion>(
|
||||||
|
input.workflowVersionId,
|
||||||
|
);
|
||||||
|
if (!cachedRecord) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newCachedRecord = {
|
||||||
|
...cachedRecord,
|
||||||
|
steps: [...(cachedRecord.steps || []), createdStep],
|
||||||
|
};
|
||||||
|
|
||||||
|
updateRecordFromCache({
|
||||||
|
objectMetadataItems,
|
||||||
|
objectMetadataItem,
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
record: newCachedRecord,
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
return { createWorkflowVersionStep };
|
||||||
|
};
|
||||||
@ -1,81 +0,0 @@
|
|||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
|
||||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
|
||||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
|
||||||
import { TRIGGER_STEP_ID } from '@/workflow/constants/TriggerStepId';
|
|
||||||
import { useCreateNewWorkflowVersion } from '@/workflow/hooks/useCreateNewWorkflowVersion';
|
|
||||||
import {
|
|
||||||
WorkflowVersion,
|
|
||||||
WorkflowWithCurrentVersion,
|
|
||||||
} from '@/workflow/types/Workflow';
|
|
||||||
import { removeStep } from '@/workflow/utils/removeStep';
|
|
||||||
|
|
||||||
export const useDeleteOneStep = ({
|
|
||||||
stepId,
|
|
||||||
workflow,
|
|
||||||
}: {
|
|
||||||
stepId: string;
|
|
||||||
workflow: WorkflowWithCurrentVersion;
|
|
||||||
}) => {
|
|
||||||
const { closeRightDrawer } = useRightDrawer();
|
|
||||||
|
|
||||||
const { updateOneRecord: updateOneWorkflowVersion } =
|
|
||||||
useUpdateOneRecord<WorkflowVersion>({
|
|
||||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { createNewWorkflowVersion } = useCreateNewWorkflowVersion();
|
|
||||||
|
|
||||||
const deleteOneStep = async () => {
|
|
||||||
closeRightDrawer();
|
|
||||||
|
|
||||||
if (workflow.currentVersion.status !== 'DRAFT') {
|
|
||||||
const newVersionName = `v${workflow.versions.length + 1}`;
|
|
||||||
|
|
||||||
if (stepId === TRIGGER_STEP_ID) {
|
|
||||||
await createNewWorkflowVersion({
|
|
||||||
workflowId: workflow.id,
|
|
||||||
name: newVersionName,
|
|
||||||
status: 'DRAFT',
|
|
||||||
trigger: null,
|
|
||||||
steps: workflow.currentVersion.steps,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await createNewWorkflowVersion({
|
|
||||||
workflowId: workflow.id,
|
|
||||||
name: newVersionName,
|
|
||||||
status: 'DRAFT',
|
|
||||||
trigger: workflow.currentVersion.trigger,
|
|
||||||
steps: removeStep({
|
|
||||||
steps: workflow.currentVersion.steps ?? [],
|
|
||||||
stepId,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stepId === TRIGGER_STEP_ID) {
|
|
||||||
await updateOneWorkflowVersion({
|
|
||||||
idToUpdate: workflow.currentVersion.id,
|
|
||||||
updateOneRecordInput: {
|
|
||||||
trigger: null,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await updateOneWorkflowVersion({
|
|
||||||
idToUpdate: workflow.currentVersion.id,
|
|
||||||
updateOneRecordInput: {
|
|
||||||
steps: removeStep({
|
|
||||||
steps: workflow.currentVersion.steps ?? [],
|
|
||||||
stepId,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
deleteOneStep,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
import {
|
||||||
|
WorkflowWithCurrentVersion,
|
||||||
|
WorkflowVersion,
|
||||||
|
} from '@/workflow/types/Workflow';
|
||||||
|
import { useDeleteWorkflowVersionStep } from '@/workflow/hooks/useDeleteWorkflowVersionStep';
|
||||||
|
import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion';
|
||||||
|
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||||
|
import { TRIGGER_STEP_ID } from '@/workflow/constants/TriggerStepId';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||||
|
|
||||||
|
export const useDeleteStep = ({
|
||||||
|
workflow,
|
||||||
|
}: {
|
||||||
|
workflow: WorkflowWithCurrentVersion;
|
||||||
|
}) => {
|
||||||
|
const { deleteWorkflowVersionStep } = useDeleteWorkflowVersionStep();
|
||||||
|
const { updateOneRecord: updateOneWorkflowVersion } =
|
||||||
|
useUpdateOneRecord<WorkflowVersion>({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion();
|
||||||
|
const { closeRightDrawer } = useRightDrawer();
|
||||||
|
|
||||||
|
const deleteStep = async (stepId: string) => {
|
||||||
|
closeRightDrawer();
|
||||||
|
const workflowVersion = await getUpdatableWorkflowVersion(workflow);
|
||||||
|
if (stepId === TRIGGER_STEP_ID) {
|
||||||
|
await updateOneWorkflowVersion({
|
||||||
|
idToUpdate: workflow.currentVersion.id,
|
||||||
|
updateOneRecordInput: {
|
||||||
|
trigger: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await deleteWorkflowVersionStep({
|
||||||
|
workflowVersionId: workflowVersion.id,
|
||||||
|
stepId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
deleteStep,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
import { useApolloClient, useMutation } from '@apollo/client';
|
||||||
|
import {
|
||||||
|
DeleteWorkflowVersionStepMutation,
|
||||||
|
DeleteWorkflowVersionStepMutationVariables,
|
||||||
|
DeleteWorkflowVersionStepInput,
|
||||||
|
WorkflowAction,
|
||||||
|
} from '~/generated/graphql';
|
||||||
|
import { DELETE_WORKFLOW_VERSION_STEP } from '@/workflow/graphql/mutations/deleteWorkflowVersionStep';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||||
|
import { WorkflowVersion } from '@/workflow/types/Workflow';
|
||||||
|
|
||||||
|
export const useDeleteWorkflowVersionStep = () => {
|
||||||
|
const apolloClient = useApolloClient();
|
||||||
|
const { objectMetadataItems } = useObjectMetadataItems();
|
||||||
|
const { objectMetadataItem } = useObjectMetadataItem({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||||
|
});
|
||||||
|
const getRecordFromCache = useGetRecordFromCache({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||||
|
});
|
||||||
|
const [mutate] = useMutation<
|
||||||
|
DeleteWorkflowVersionStepMutation,
|
||||||
|
DeleteWorkflowVersionStepMutationVariables
|
||||||
|
>(DELETE_WORKFLOW_VERSION_STEP, {
|
||||||
|
client: apolloClient,
|
||||||
|
});
|
||||||
|
const deleteWorkflowVersionStep = async (
|
||||||
|
input: DeleteWorkflowVersionStepInput,
|
||||||
|
) => {
|
||||||
|
const result = await mutate({ variables: { input } });
|
||||||
|
const deletedStep = result?.data?.deleteWorkflowVersionStep;
|
||||||
|
if (!isDefined(deletedStep)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedRecord = getRecordFromCache<WorkflowVersion>(
|
||||||
|
input.workflowVersionId,
|
||||||
|
);
|
||||||
|
if (!cachedRecord) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newCachedRecord = {
|
||||||
|
...cachedRecord,
|
||||||
|
steps: (cachedRecord.steps || []).filter(
|
||||||
|
(step: WorkflowAction) => step.id !== deletedStep.id,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
updateRecordFromCache({
|
||||||
|
objectMetadataItems,
|
||||||
|
objectMetadataItem,
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
record: newCachedRecord,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return { deleteWorkflowVersionStep };
|
||||||
|
};
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
|
||||||
|
import { useCreateNewWorkflowVersion } from '@/workflow/hooks/useCreateNewWorkflowVersion';
|
||||||
|
|
||||||
|
export const useGetUpdatableWorkflowVersion = () => {
|
||||||
|
const { createNewWorkflowVersion } = useCreateNewWorkflowVersion();
|
||||||
|
const getUpdatableWorkflowVersion = async (
|
||||||
|
workflow: WorkflowWithCurrentVersion,
|
||||||
|
) => {
|
||||||
|
if (workflow.currentVersion.status === 'DRAFT') {
|
||||||
|
return workflow.currentVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await createNewWorkflowVersion({
|
||||||
|
workflowId: workflow.id,
|
||||||
|
name: `v${workflow.versions.length + 1}`,
|
||||||
|
status: 'DRAFT',
|
||||||
|
trigger: workflow.currentVersion.trigger,
|
||||||
|
steps: workflow.currentVersion.steps,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return { getUpdatableWorkflowVersion };
|
||||||
|
};
|
||||||
@ -5,18 +5,16 @@ import {
|
|||||||
} from '@/workflow/types/WorkflowDiagram';
|
} from '@/workflow/types/WorkflowDiagram';
|
||||||
import { useReactFlow } from '@xyflow/react';
|
import { useReactFlow } from '@xyflow/react';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useTriggerNodeSelection = () => {
|
export const useTriggerNodeSelection = () => {
|
||||||
const reactflow = useReactFlow<WorkflowDiagramNode, WorkflowDiagramEdge>();
|
const reactflow = useReactFlow<WorkflowDiagramNode, WorkflowDiagramEdge>();
|
||||||
|
|
||||||
const workflowDiagramTriggerNodeSelection = useRecoilValue(
|
const [
|
||||||
workflowDiagramTriggerNodeSelectionState,
|
workflowDiagramTriggerNodeSelection,
|
||||||
);
|
setWorkflowDiagramTriggerNodeSelection,
|
||||||
const setWorkflowDiagramTriggerNodeSelection = useSetRecoilState(
|
] = useRecoilState(workflowDiagramTriggerNodeSelectionState);
|
||||||
workflowDiagramTriggerNodeSelectionState,
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isDefined(workflowDiagramTriggerNodeSelection)) {
|
if (!isDefined(workflowDiagramTriggerNodeSelection)) {
|
||||||
|
|||||||
@ -0,0 +1,32 @@
|
|||||||
|
import {
|
||||||
|
WorkflowStep,
|
||||||
|
WorkflowWithCurrentVersion,
|
||||||
|
} from '@/workflow/types/Workflow';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion';
|
||||||
|
import { useUpdateWorkflowVersionStep } from '@/workflow/hooks/useUpdateWorkflowVersionStep';
|
||||||
|
|
||||||
|
export const useUpdateStep = ({
|
||||||
|
workflow,
|
||||||
|
}: {
|
||||||
|
workflow: WorkflowWithCurrentVersion;
|
||||||
|
}) => {
|
||||||
|
const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion();
|
||||||
|
const { updateWorkflowVersionStep } = useUpdateWorkflowVersionStep();
|
||||||
|
|
||||||
|
const updateStep = async <T extends WorkflowStep>(updatedStep: T) => {
|
||||||
|
if (!isDefined(workflow.currentVersion)) {
|
||||||
|
throw new Error('Can not update an undefined workflow version.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowVersion = await getUpdatableWorkflowVersion(workflow);
|
||||||
|
await updateWorkflowVersionStep({
|
||||||
|
workflowVersionId: workflowVersion.id,
|
||||||
|
step: updatedStep,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
updateStep,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,73 +1,69 @@
|
|||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { useApolloClient, useMutation } from '@apollo/client';
|
||||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
|
||||||
import { useCreateNewWorkflowVersion } from '@/workflow/hooks/useCreateNewWorkflowVersion';
|
|
||||||
import {
|
import {
|
||||||
WorkflowStep,
|
UpdateWorkflowVersionStepInput,
|
||||||
WorkflowVersion,
|
UpdateWorkflowVersionStepMutation,
|
||||||
WorkflowWithCurrentVersion,
|
UpdateWorkflowVersionStepMutationVariables,
|
||||||
} from '@/workflow/types/Workflow';
|
WorkflowAction,
|
||||||
import { replaceStep } from '@/workflow/utils/replaceStep';
|
} from '~/generated/graphql';
|
||||||
|
import { UPDATE_WORKFLOW_VERSION_STEP } from '@/workflow/graphql/mutations/updateWorkflowVersionStep';
|
||||||
|
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||||
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
import { useComputeStepOutputSchema } from '@/workflow/hooks/useComputeStepOutputSchema';
|
import { WorkflowVersion } from '@/workflow/types/Workflow';
|
||||||
|
|
||||||
export const useUpdateWorkflowVersionStep = ({
|
export const useUpdateWorkflowVersionStep = () => {
|
||||||
workflow,
|
const apolloClient = useApolloClient();
|
||||||
stepId,
|
const { objectMetadataItems } = useObjectMetadataItems();
|
||||||
}: {
|
const { objectMetadataItem } = useObjectMetadataItem({
|
||||||
workflow: WorkflowWithCurrentVersion;
|
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||||
stepId: string;
|
});
|
||||||
}) => {
|
const getRecordFromCache = useGetRecordFromCache({
|
||||||
const { updateOneRecord: updateOneWorkflowVersion } =
|
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||||
useUpdateOneRecord<WorkflowVersion>({
|
});
|
||||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
const [mutate] = useMutation<
|
||||||
});
|
UpdateWorkflowVersionStepMutation,
|
||||||
|
UpdateWorkflowVersionStepMutationVariables
|
||||||
const { createNewWorkflowVersion } = useCreateNewWorkflowVersion();
|
>(UPDATE_WORKFLOW_VERSION_STEP, {
|
||||||
const { computeStepOutputSchema } = useComputeStepOutputSchema();
|
client: apolloClient,
|
||||||
|
});
|
||||||
const updateStep = async <T extends WorkflowStep>(updatedStep: T) => {
|
|
||||||
if (!isDefined(workflow.currentVersion)) {
|
|
||||||
throw new Error('Can not update an undefined workflow version.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const outputSchema = (
|
|
||||||
await computeStepOutputSchema({
|
|
||||||
step: updatedStep,
|
|
||||||
})
|
|
||||||
)?.data?.computeStepOutputSchema;
|
|
||||||
|
|
||||||
updatedStep.settings = {
|
|
||||||
...updatedStep.settings,
|
|
||||||
outputSchema: outputSchema || {},
|
|
||||||
};
|
|
||||||
|
|
||||||
const updatedSteps = replaceStep({
|
|
||||||
steps: workflow.currentVersion.steps ?? [],
|
|
||||||
stepId,
|
|
||||||
stepToReplace: updatedStep,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (workflow.currentVersion.status === 'DRAFT') {
|
|
||||||
await updateOneWorkflowVersion({
|
|
||||||
idToUpdate: workflow.currentVersion.id,
|
|
||||||
updateOneRecordInput: {
|
|
||||||
steps: updatedSteps,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
|
const updateWorkflowVersionStep = async (
|
||||||
|
input: UpdateWorkflowVersionStepInput,
|
||||||
|
) => {
|
||||||
|
const result = await mutate({ variables: { input } });
|
||||||
|
const updatedStep = result?.data?.updateWorkflowVersionStep;
|
||||||
|
if (!isDefined(updatedStep)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await createNewWorkflowVersion({
|
const cachedRecord = getRecordFromCache<WorkflowVersion>(
|
||||||
workflowId: workflow.id,
|
input.workflowVersionId,
|
||||||
name: `v${workflow.versions.length + 1}`,
|
);
|
||||||
status: 'DRAFT',
|
if (!cachedRecord) {
|
||||||
trigger: workflow.currentVersion.trigger,
|
return;
|
||||||
steps: updatedSteps,
|
}
|
||||||
|
|
||||||
|
const newCachedRecord = {
|
||||||
|
...cachedRecord,
|
||||||
|
steps: (cachedRecord.steps || []).map((step: WorkflowAction) => {
|
||||||
|
if (step.id === updatedStep.id) {
|
||||||
|
return updatedStep;
|
||||||
|
}
|
||||||
|
return step;
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
updateRecordFromCache({
|
||||||
|
objectMetadataItems,
|
||||||
|
objectMetadataItem,
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
record: newCachedRecord,
|
||||||
});
|
});
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return { updateWorkflowVersionStep };
|
||||||
updateStep,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||||
import { useCreateNewWorkflowVersion } from '@/workflow/hooks/useCreateNewWorkflowVersion';
|
|
||||||
import {
|
import {
|
||||||
WorkflowTrigger,
|
WorkflowTrigger,
|
||||||
WorkflowVersion,
|
WorkflowVersion,
|
||||||
@ -8,6 +7,7 @@ import {
|
|||||||
} from '@/workflow/types/Workflow';
|
} from '@/workflow/types/Workflow';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
import { useComputeStepOutputSchema } from '@/workflow/hooks/useComputeStepOutputSchema';
|
import { useComputeStepOutputSchema } from '@/workflow/hooks/useComputeStepOutputSchema';
|
||||||
|
import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion';
|
||||||
|
|
||||||
export const useUpdateWorkflowVersionTrigger = ({
|
export const useUpdateWorkflowVersionTrigger = ({
|
||||||
workflow,
|
workflow,
|
||||||
@ -19,7 +19,7 @@ export const useUpdateWorkflowVersionTrigger = ({
|
|||||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { createNewWorkflowVersion } = useCreateNewWorkflowVersion();
|
const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion();
|
||||||
|
|
||||||
const { computeStepOutputSchema } = useComputeStepOutputSchema();
|
const { computeStepOutputSchema } = useComputeStepOutputSchema();
|
||||||
|
|
||||||
@ -28,34 +28,24 @@ export const useUpdateWorkflowVersionTrigger = ({
|
|||||||
throw new Error('Can not update an undefined workflow version.');
|
throw new Error('Can not update an undefined workflow version.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (workflow.currentVersion.status === 'DRAFT') {
|
const workflowVersion = await getUpdatableWorkflowVersion(workflow);
|
||||||
const outputSchema = (
|
|
||||||
await computeStepOutputSchema({
|
|
||||||
step: updatedTrigger,
|
|
||||||
})
|
|
||||||
)?.data?.computeStepOutputSchema;
|
|
||||||
|
|
||||||
updatedTrigger.settings = {
|
const outputSchema = (
|
||||||
...updatedTrigger.settings,
|
await computeStepOutputSchema({
|
||||||
outputSchema: outputSchema || {},
|
step: updatedTrigger,
|
||||||
};
|
})
|
||||||
|
)?.data?.computeStepOutputSchema;
|
||||||
|
|
||||||
await updateOneWorkflowVersion({
|
updatedTrigger.settings = {
|
||||||
idToUpdate: workflow.currentVersion.id,
|
...updatedTrigger.settings,
|
||||||
updateOneRecordInput: {
|
outputSchema: outputSchema || {},
|
||||||
trigger: updatedTrigger,
|
};
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
await updateOneWorkflowVersion({
|
||||||
}
|
idToUpdate: workflowVersion.id,
|
||||||
|
updateOneRecordInput: {
|
||||||
await createNewWorkflowVersion({
|
trigger: updatedTrigger,
|
||||||
workflowId: workflow.id,
|
},
|
||||||
name: `v${workflow.versions.length + 1}`,
|
|
||||||
status: 'DRAFT',
|
|
||||||
trigger: updatedTrigger,
|
|
||||||
steps: workflow.currentVersion.steps,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
|
||||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||||
import {
|
import {
|
||||||
Workflow,
|
Workflow,
|
||||||
WorkflowVersion,
|
|
||||||
WorkflowWithCurrentVersion,
|
WorkflowWithCurrentVersion,
|
||||||
} from '@/workflow/types/Workflow';
|
} from '@/workflow/types/Workflow';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
@ -19,40 +17,25 @@ export const useWorkflowWithCurrentVersion = (
|
|||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
statuses: true,
|
statuses: true,
|
||||||
versions: {
|
versions: true,
|
||||||
totalCount: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
skip: !isDefined(workflowId),
|
skip: !isDefined(workflowId),
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
return useMemo(() => {
|
||||||
records: mostRecentWorkflowVersions,
|
if (!isDefined(workflow)) {
|
||||||
loading: loadingMostRecentWorkflowVersions,
|
|
||||||
} = useFindManyRecords<WorkflowVersion>({
|
|
||||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
|
||||||
filter: {
|
|
||||||
workflowId: {
|
|
||||||
eq: workflowId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
orderBy: [
|
|
||||||
{
|
|
||||||
createdAt: 'DescNullsLast',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
skip: !isDefined(workflowId),
|
|
||||||
});
|
|
||||||
|
|
||||||
const workflowWithCurrentVersion = useMemo(() => {
|
|
||||||
if (!isDefined(workflow) || loadingMostRecentWorkflowVersions) {
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const draftVersion = mostRecentWorkflowVersions.find(
|
const draftVersion = workflow.versions.find(
|
||||||
(workflowVersion) => workflowVersion.status === 'DRAFT',
|
(workflowVersion) => workflowVersion.status === 'DRAFT',
|
||||||
);
|
);
|
||||||
const latestVersion = mostRecentWorkflowVersions[0];
|
|
||||||
|
const workflowVersions = [...workflow.versions];
|
||||||
|
|
||||||
|
workflowVersions.sort((a, b) => (a.createdAt > b.createdAt ? -1 : 1));
|
||||||
|
|
||||||
|
const latestVersion = workflowVersions[0];
|
||||||
|
|
||||||
const currentVersion = draftVersion ?? latestVersion;
|
const currentVersion = draftVersion ?? latestVersion;
|
||||||
|
|
||||||
@ -64,7 +47,5 @@ export const useWorkflowWithCurrentVersion = (
|
|||||||
...workflow,
|
...workflow,
|
||||||
currentVersion,
|
currentVersion,
|
||||||
};
|
};
|
||||||
}, [loadingMostRecentWorkflowVersions, mostRecentWorkflowVersions, workflow]);
|
}, [workflow]);
|
||||||
|
|
||||||
return workflowWithCurrentVersion;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -9,7 +9,7 @@ export type InputSchemaPropertyType =
|
|||||||
| 'unknown'
|
| 'unknown'
|
||||||
| FieldMetadataType;
|
| FieldMetadataType;
|
||||||
|
|
||||||
type InputSchemaProperty = {
|
export type InputSchemaProperty = {
|
||||||
type: InputSchemaPropertyType;
|
type: InputSchemaPropertyType;
|
||||||
enum?: string[];
|
enum?: string[];
|
||||||
items?: InputSchemaProperty;
|
items?: InputSchemaProperty;
|
||||||
|
|||||||
@ -1,142 +0,0 @@
|
|||||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
|
||||||
import { getStepDefaultDefinition } from '../getStepDefaultDefinition';
|
|
||||||
|
|
||||||
it('returns a valid definition for CODE actions', () => {
|
|
||||||
expect(
|
|
||||||
getStepDefaultDefinition({
|
|
||||||
type: 'CODE',
|
|
||||||
activeObjectMetadataItems: generatedMockObjectMetadataItems,
|
|
||||||
}),
|
|
||||||
).toStrictEqual({
|
|
||||||
id: expect.any(String),
|
|
||||||
name: 'Code',
|
|
||||||
type: 'CODE',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
input: {
|
|
||||||
serverlessFunctionId: '',
|
|
||||||
serverlessFunctionVersion: '',
|
|
||||||
serverlessFunctionInput: {},
|
|
||||||
},
|
|
||||||
outputSchema: {},
|
|
||||||
errorHandlingOptions: {
|
|
||||||
continueOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
retryOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns a valid definition for SEND_EMAIL actions', () => {
|
|
||||||
expect(
|
|
||||||
getStepDefaultDefinition({
|
|
||||||
type: 'SEND_EMAIL',
|
|
||||||
activeObjectMetadataItems: generatedMockObjectMetadataItems,
|
|
||||||
}),
|
|
||||||
).toStrictEqual({
|
|
||||||
id: expect.any(String),
|
|
||||||
name: 'Send Email',
|
|
||||||
type: 'SEND_EMAIL',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
input: {
|
|
||||||
connectedAccountId: '',
|
|
||||||
email: '',
|
|
||||||
subject: '',
|
|
||||||
body: '',
|
|
||||||
},
|
|
||||||
outputSchema: {},
|
|
||||||
errorHandlingOptions: {
|
|
||||||
continueOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
retryOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns a valid definition for RECORD_CRUD.CREATE actions', () => {
|
|
||||||
expect(
|
|
||||||
getStepDefaultDefinition({
|
|
||||||
type: 'RECORD_CRUD.CREATE',
|
|
||||||
activeObjectMetadataItems: generatedMockObjectMetadataItems,
|
|
||||||
}),
|
|
||||||
).toStrictEqual({
|
|
||||||
id: expect.any(String),
|
|
||||||
name: 'Create Record',
|
|
||||||
type: 'RECORD_CRUD',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
input: {
|
|
||||||
type: 'CREATE',
|
|
||||||
objectName: generatedMockObjectMetadataItems[0].nameSingular,
|
|
||||||
objectRecord: {},
|
|
||||||
},
|
|
||||||
outputSchema: {},
|
|
||||||
errorHandlingOptions: {
|
|
||||||
continueOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
retryOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns a valid definition for RECORD_CRUD.UPDATE actions', () => {
|
|
||||||
expect(
|
|
||||||
getStepDefaultDefinition({
|
|
||||||
type: 'RECORD_CRUD.UPDATE',
|
|
||||||
activeObjectMetadataItems: generatedMockObjectMetadataItems,
|
|
||||||
}),
|
|
||||||
).toStrictEqual({
|
|
||||||
id: expect.any(String),
|
|
||||||
name: 'Update Record',
|
|
||||||
type: 'RECORD_CRUD',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
input: {
|
|
||||||
type: 'UPDATE',
|
|
||||||
objectName: generatedMockObjectMetadataItems[0].nameSingular,
|
|
||||||
objectRecord: {},
|
|
||||||
objectRecordId: '',
|
|
||||||
},
|
|
||||||
outputSchema: {},
|
|
||||||
errorHandlingOptions: {
|
|
||||||
continueOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
retryOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("throws for RECORD_CRUD.DELETE actions as it's not implemented yet", () => {
|
|
||||||
expect(() => {
|
|
||||||
getStepDefaultDefinition({
|
|
||||||
type: 'RECORD_CRUD.DELETE',
|
|
||||||
activeObjectMetadataItems: generatedMockObjectMetadataItems,
|
|
||||||
});
|
|
||||||
}).toThrow('Not implemented yet');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws when providing an unknown type', () => {
|
|
||||||
expect(() => {
|
|
||||||
getStepDefaultDefinition({
|
|
||||||
type: 'unknown' as any,
|
|
||||||
activeObjectMetadataItems: generatedMockObjectMetadataItems,
|
|
||||||
});
|
|
||||||
}).toThrow('Unknown type: unknown');
|
|
||||||
});
|
|
||||||
@ -0,0 +1,129 @@
|
|||||||
|
import {
|
||||||
|
ArrayTypeNode,
|
||||||
|
ArrowFunction,
|
||||||
|
createSourceFile,
|
||||||
|
FunctionDeclaration,
|
||||||
|
LiteralTypeNode,
|
||||||
|
PropertySignature,
|
||||||
|
ScriptTarget,
|
||||||
|
StringLiteral,
|
||||||
|
SyntaxKind,
|
||||||
|
TypeNode,
|
||||||
|
UnionTypeNode,
|
||||||
|
VariableStatement,
|
||||||
|
} from 'typescript';
|
||||||
|
import { InputSchema, InputSchemaProperty } from '@/workflow/types/InputSchema';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
const 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: 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 (isDefined(member.name) && isDefined(member.type)) {
|
||||||
|
const memberName = (member.name as any).text;
|
||||||
|
|
||||||
|
properties[memberName] = 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' };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFunctionInputSchema = (fileContent: string): InputSchema => {
|
||||||
|
const sourceFile = createSourceFile(
|
||||||
|
'temp.ts',
|
||||||
|
fileContent,
|
||||||
|
ScriptTarget.ESNext,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
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 (isDefined(typeNode)) {
|
||||||
|
schema[paramName] = getTypeString(typeNode);
|
||||||
|
} else {
|
||||||
|
schema[paramName] = { type: 'unknown' };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (node.kind === SyntaxKind.VariableStatement) {
|
||||||
|
const varStatement = node as VariableStatement;
|
||||||
|
|
||||||
|
varStatement.declarationList.declarations.forEach((declaration) => {
|
||||||
|
if (
|
||||||
|
isDefined(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 (isDefined(typeNode)) {
|
||||||
|
schema[paramName] = getTypeString(typeNode);
|
||||||
|
} else {
|
||||||
|
schema[paramName] = { type: 'unknown' };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return schema;
|
||||||
|
};
|
||||||
@ -1,100 +0,0 @@
|
|||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|
||||||
import { WorkflowStep, WorkflowStepType } from '@/workflow/types/Workflow';
|
|
||||||
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
|
|
||||||
import { v4 } from 'uuid';
|
|
||||||
|
|
||||||
const BASE_DEFAULT_STEP_SETTINGS = {
|
|
||||||
outputSchema: {},
|
|
||||||
errorHandlingOptions: {
|
|
||||||
continueOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
retryOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getStepDefaultDefinition = ({
|
|
||||||
type,
|
|
||||||
activeObjectMetadataItems,
|
|
||||||
}: {
|
|
||||||
type: WorkflowStepType;
|
|
||||||
activeObjectMetadataItems: ObjectMetadataItem[];
|
|
||||||
}): WorkflowStep => {
|
|
||||||
const newStepId = v4();
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 'CODE': {
|
|
||||||
return {
|
|
||||||
id: newStepId,
|
|
||||||
name: 'Code',
|
|
||||||
type: 'CODE',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
input: {
|
|
||||||
serverlessFunctionId: '',
|
|
||||||
serverlessFunctionVersion: '',
|
|
||||||
serverlessFunctionInput: {},
|
|
||||||
},
|
|
||||||
...BASE_DEFAULT_STEP_SETTINGS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
case 'SEND_EMAIL': {
|
|
||||||
return {
|
|
||||||
id: newStepId,
|
|
||||||
name: 'Send Email',
|
|
||||||
type: 'SEND_EMAIL',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
input: {
|
|
||||||
connectedAccountId: '',
|
|
||||||
email: '',
|
|
||||||
subject: '',
|
|
||||||
body: '',
|
|
||||||
},
|
|
||||||
...BASE_DEFAULT_STEP_SETTINGS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
case 'RECORD_CRUD.CREATE': {
|
|
||||||
return {
|
|
||||||
id: newStepId,
|
|
||||||
name: 'Create Record',
|
|
||||||
type: 'RECORD_CRUD',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
input: {
|
|
||||||
type: 'CREATE',
|
|
||||||
objectName: activeObjectMetadataItems[0].nameSingular,
|
|
||||||
objectRecord: {},
|
|
||||||
},
|
|
||||||
...BASE_DEFAULT_STEP_SETTINGS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
case 'RECORD_CRUD.UPDATE':
|
|
||||||
return {
|
|
||||||
id: newStepId,
|
|
||||||
name: 'Update Record',
|
|
||||||
type: 'RECORD_CRUD',
|
|
||||||
valid: false,
|
|
||||||
settings: {
|
|
||||||
input: {
|
|
||||||
type: 'UPDATE',
|
|
||||||
objectName: activeObjectMetadataItems[0].nameSingular,
|
|
||||||
objectRecordId: '',
|
|
||||||
objectRecord: {},
|
|
||||||
},
|
|
||||||
...BASE_DEFAULT_STEP_SETTINGS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
case 'RECORD_CRUD.DELETE': {
|
|
||||||
throw new Error('Not implemented yet');
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return assertUnreachable(type, `Unknown type: ${type}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -81,6 +81,7 @@ export const WorkflowEditActionFormRecordCreate = ({
|
|||||||
const selectedObjectMetadataItem = activeObjectMetadataItems.find(
|
const selectedObjectMetadataItem = activeObjectMetadataItems.find(
|
||||||
(item) => item.nameSingular === selectedObjectMetadataItemNameSingular,
|
(item) => item.nameSingular === selectedObjectMetadataItemNameSingular,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isDefined(selectedObjectMetadataItem)) {
|
if (!isDefined(selectedObjectMetadataItem)) {
|
||||||
throw new Error('Should have found the metadata item');
|
throw new Error('Should have found the metadata item');
|
||||||
}
|
}
|
||||||
@ -135,13 +136,9 @@ export const WorkflowEditActionFormRecordCreate = ({
|
|||||||
name: newName,
|
name: newName,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
HeaderIcon={
|
Icon={IconAddressBook}
|
||||||
<IconAddressBook
|
iconColor={theme.font.color.tertiary}
|
||||||
color={theme.font.color.tertiary}
|
initialTitle={headerTitle}
|
||||||
stroke={theme.icon.stroke.sm}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
headerTitle={headerTitle}
|
|
||||||
headerType="Action"
|
headerType="Action"
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
@ -135,13 +135,9 @@ export const WorkflowEditActionFormRecordUpdate = ({
|
|||||||
name: newName,
|
name: newName,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
HeaderIcon={
|
Icon={IconAddressBook}
|
||||||
<IconAddressBook
|
iconColor={theme.font.color.tertiary}
|
||||||
color={theme.font.color.tertiary}
|
initialTitle={headerTitle}
|
||||||
stroke={theme.icon.stroke.sm}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
headerTitle={headerTitle}
|
|
||||||
headerType="Action"
|
headerType="Action"
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
@ -182,8 +182,9 @@ export const WorkflowEditActionFormSendEmail = ({
|
|||||||
name: newName,
|
name: newName,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
HeaderIcon={<IconMail color={theme.color.blue} />}
|
Icon={IconMail}
|
||||||
headerTitle={headerTitle}
|
iconColor={theme.color.blue}
|
||||||
|
initialTitle={headerTitle}
|
||||||
headerType="Email"
|
headerType="Email"
|
||||||
>
|
>
|
||||||
<Controller
|
<Controller
|
||||||
|
|||||||
@ -1,6 +1,59 @@
|
|||||||
import { useGetManyServerlessFunctions } from '@/settings/serverless-functions/hooks/useGetManyServerlessFunctions';
|
import { WorkflowEditGenericFormBase } from '@/workflow/components/WorkflowEditGenericFormBase';
|
||||||
import { WorkflowEditActionFormServerlessFunctionInner } from '@/workflow/components/WorkflowEditActionFormServerlessFunctionInner';
|
import { FunctionInput } from '@/workflow/types/FunctionInput';
|
||||||
import { WorkflowCodeAction } from '@/workflow/types/Workflow';
|
import { WorkflowCodeAction } from '@/workflow/types/Workflow';
|
||||||
|
import { mergeDefaultFunctionInputAndFunctionInput } from '@/workflow/utils/mergeDefaultFunctionInputAndFunctionInput';
|
||||||
|
import { setNestedValue } from '@/workflow/utils/setNestedValue';
|
||||||
|
import { useTheme } from '@emotion/react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { Fragment, ReactNode, useState } from 'react';
|
||||||
|
import {
|
||||||
|
CodeEditor,
|
||||||
|
HorizontalSeparator,
|
||||||
|
IconCode,
|
||||||
|
isDefined,
|
||||||
|
} from 'twenty-ui';
|
||||||
|
import { useDebouncedCallback } from 'use-debounce';
|
||||||
|
import { useServerlessFunctionUpdateFormState } from '@/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState';
|
||||||
|
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||||
|
import { usePreventOverlapCallback } from '~/hooks/usePreventOverlapCallback';
|
||||||
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
|
import { useUpdateOneServerlessFunction } from '@/settings/serverless-functions/hooks/useUpdateOneServerlessFunction';
|
||||||
|
import { useGetAvailablePackages } from '@/settings/serverless-functions/hooks/useGetAvailablePackages';
|
||||||
|
import { AutoTypings } from 'monaco-editor-auto-typings';
|
||||||
|
import { editor } from 'monaco-editor';
|
||||||
|
import { Monaco } from '@monaco-editor/react';
|
||||||
|
import { WorkflowVariablePicker } from '@/workflow/components/WorkflowVariablePicker';
|
||||||
|
import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
|
||||||
|
import { getFunctionInputSchema } from '@/workflow/utils/getFunctionInputSchema';
|
||||||
|
import { getDefaultFunctionInputFromInputSchema } from '@/workflow/utils/getDefaultFunctionInputFromInputSchema';
|
||||||
|
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion';
|
||||||
|
|
||||||
|
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(2)};
|
||||||
|
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledInputContainer = styled.div`
|
||||||
|
background: ${({ theme }) => theme.background.secondary};
|
||||||
|
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||||
|
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
|
padding: ${({ theme }) => theme.spacing(2)};
|
||||||
|
position: relative;
|
||||||
|
`;
|
||||||
|
|
||||||
type WorkflowEditActionFormServerlessFunctionProps = {
|
type WorkflowEditActionFormServerlessFunctionProps = {
|
||||||
action: WorkflowCodeAction;
|
action: WorkflowCodeAction;
|
||||||
@ -14,21 +67,227 @@ type WorkflowEditActionFormServerlessFunctionProps = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ServerlessFunctionInputFormData = {
|
||||||
|
[field: string]: string | ServerlessFunctionInputFormData;
|
||||||
|
};
|
||||||
|
|
||||||
|
const INDEX_FILE_PATH = 'src/index.ts';
|
||||||
|
|
||||||
export const WorkflowEditActionFormServerlessFunction = ({
|
export const WorkflowEditActionFormServerlessFunction = ({
|
||||||
action,
|
action,
|
||||||
actionOptions,
|
actionOptions,
|
||||||
}: WorkflowEditActionFormServerlessFunctionProps) => {
|
}: WorkflowEditActionFormServerlessFunctionProps) => {
|
||||||
const { loading: isLoadingServerlessFunctions } =
|
const theme = useTheme();
|
||||||
useGetManyServerlessFunctions();
|
const { enqueueSnackBar } = useSnackBar();
|
||||||
|
const { updateOneServerlessFunction } = useUpdateOneServerlessFunction();
|
||||||
|
const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion();
|
||||||
|
const serverlessFunctionId = action.settings.input.serverlessFunctionId;
|
||||||
|
const workflowId = useRecoilValue(workflowIdState);
|
||||||
|
const workflow = useWorkflowWithCurrentVersion(workflowId);
|
||||||
|
const { availablePackages } = useGetAvailablePackages({
|
||||||
|
id: serverlessFunctionId,
|
||||||
|
});
|
||||||
|
|
||||||
if (isLoadingServerlessFunctions) {
|
const { formValues, setFormValues, loading } =
|
||||||
return null;
|
useServerlessFunctionUpdateFormState(serverlessFunctionId);
|
||||||
}
|
|
||||||
|
const save = async () => {
|
||||||
|
try {
|
||||||
|
await updateOneServerlessFunction({
|
||||||
|
id: serverlessFunctionId,
|
||||||
|
name: formValues.name,
|
||||||
|
description: formValues.description,
|
||||||
|
code: formValues.code,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
enqueueSnackBar(
|
||||||
|
(err as Error)?.message || 'An error occurred while updating function',
|
||||||
|
{
|
||||||
|
variant: SnackBarVariant.Error,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = usePreventOverlapCallback(save, 1000);
|
||||||
|
|
||||||
|
const onCodeChange = async (value: string) => {
|
||||||
|
setFormValues((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
code: { ...prevState.code, [INDEX_FILE_PATH]: value },
|
||||||
|
}));
|
||||||
|
await handleSave();
|
||||||
|
await handleUpdateFunctionInputSchema();
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateFunctionInputSchema = async () => {
|
||||||
|
const sourceCode = formValues.code?.[INDEX_FILE_PATH];
|
||||||
|
if (!isDefined(sourceCode)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const functionInputSchema = getFunctionInputSchema(sourceCode);
|
||||||
|
const newMergedInputSchema = mergeDefaultFunctionInputAndFunctionInput({
|
||||||
|
defaultFunctionInput:
|
||||||
|
getDefaultFunctionInputFromInputSchema(functionInputSchema),
|
||||||
|
functionInput: action.settings.input.serverlessFunctionInput,
|
||||||
|
});
|
||||||
|
|
||||||
|
setFunctionInput(newMergedInputSchema);
|
||||||
|
await updateFunctionInput(newMergedInputSchema);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdateFunctionInputSchema = useDebouncedCallback(
|
||||||
|
updateFunctionInputSchema,
|
||||||
|
100,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [functionInput, setFunctionInput] =
|
||||||
|
useState<ServerlessFunctionInputFormData>(
|
||||||
|
action.settings.input.serverlessFunctionInput,
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateFunctionInput = useDebouncedCallback(
|
||||||
|
async (newFunctionInput: object) => {
|
||||||
|
if (actionOptions.readonly === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
actionOptions.onActionUpdate({
|
||||||
|
...action,
|
||||||
|
settings: {
|
||||||
|
...action.settings,
|
||||||
|
input: {
|
||||||
|
...action.settings.input,
|
||||||
|
serverlessFunctionInput: newFunctionInput,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
1_000,
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleInputChange = async (value: any, path: string[]) => {
|
||||||
|
const updatedFunctionInput = setNestedValue(functionInput, path, value);
|
||||||
|
|
||||||
|
setFunctionInput(updatedFunctionInput);
|
||||||
|
|
||||||
|
await updateFunctionInput(updatedFunctionInput);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderFields = (
|
||||||
|
functionInput: FunctionInput,
|
||||||
|
path: string[] = [],
|
||||||
|
isRoot = true,
|
||||||
|
): ReactNode[] => {
|
||||||
|
const displaySeparator = (functionInput: FunctionInput) => {
|
||||||
|
const keys = Object.keys(functionInput);
|
||||||
|
if (keys.length > 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (keys.length === 1) {
|
||||||
|
const subKeys = Object.keys(functionInput[keys[0]]);
|
||||||
|
return subKeys.length > 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
return Object.entries(functionInput).map(([inputKey, inputValue]) => {
|
||||||
|
const currentPath = [...path, inputKey];
|
||||||
|
const pathKey = currentPath.join('.');
|
||||||
|
|
||||||
|
if (inputValue !== null && typeof inputValue === 'object') {
|
||||||
|
if (isRoot) {
|
||||||
|
return (
|
||||||
|
<Fragment key={pathKey}>
|
||||||
|
{displaySeparator(functionInput) && (
|
||||||
|
<HorizontalSeparator noMargin />
|
||||||
|
)}
|
||||||
|
{renderFields(inputValue, currentPath, false)}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<StyledContainer key={pathKey}>
|
||||||
|
<StyledLabel>{inputKey}</StyledLabel>
|
||||||
|
<StyledInputContainer>
|
||||||
|
{renderFields(inputValue, currentPath, false)}
|
||||||
|
</StyledInputContainer>
|
||||||
|
</StyledContainer>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<FormTextFieldInput
|
||||||
|
key={pathKey}
|
||||||
|
label={inputKey}
|
||||||
|
placeholder="Enter value"
|
||||||
|
defaultValue={inputValue ? `${inputValue}` : ''}
|
||||||
|
readonly={actionOptions.readonly}
|
||||||
|
onPersist={(value) => handleInputChange(value, currentPath)}
|
||||||
|
VariablePicker={WorkflowVariablePicker}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const headerTitle = isDefined(action.name)
|
||||||
|
? action.name
|
||||||
|
: 'Code - Serverless Function';
|
||||||
|
|
||||||
|
const handleEditorDidMount = async (
|
||||||
|
editor: editor.IStandaloneCodeEditor,
|
||||||
|
monaco: Monaco,
|
||||||
|
) => {
|
||||||
|
await AutoTypings.create(editor, {
|
||||||
|
monaco,
|
||||||
|
preloadPackages: true,
|
||||||
|
onlySpecifiedPackages: true,
|
||||||
|
versions: availablePackages,
|
||||||
|
debounceDuration: 0,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onActionUpdate = (actionUpdate: Partial<WorkflowCodeAction>) => {
|
||||||
|
if (actionOptions.readonly === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
actionOptions?.onActionUpdate({
|
||||||
|
...action,
|
||||||
|
...actionUpdate,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkWorkflowUpdatable = async () => {
|
||||||
|
if (actionOptions.readonly === true || !isDefined(workflow)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await getUpdatableWorkflowVersion(workflow);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WorkflowEditActionFormServerlessFunctionInner
|
!loading && (
|
||||||
action={action}
|
<WorkflowEditGenericFormBase
|
||||||
actionOptions={actionOptions}
|
onTitleChange={(newName: string) => {
|
||||||
/>
|
onActionUpdate({ name: newName });
|
||||||
|
}}
|
||||||
|
Icon={IconCode}
|
||||||
|
iconColor={theme.color.orange}
|
||||||
|
initialTitle={headerTitle}
|
||||||
|
headerType="Code"
|
||||||
|
>
|
||||||
|
<CodeEditor
|
||||||
|
height={340}
|
||||||
|
value={formValues.code?.[INDEX_FILE_PATH]}
|
||||||
|
language={'typescript'}
|
||||||
|
onChange={async (value) => {
|
||||||
|
await checkWorkflowUpdatable();
|
||||||
|
await onCodeChange(value);
|
||||||
|
}}
|
||||||
|
onMount={handleEditorDidMount}
|
||||||
|
/>
|
||||||
|
{renderFields(functionInput)}
|
||||||
|
</WorkflowEditGenericFormBase>
|
||||||
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -17,4 +17,5 @@ export enum MessageQueue {
|
|||||||
entityEventsToDbQueue = 'entity-events-to-db-queue',
|
entityEventsToDbQueue = 'entity-events-to-db-queue',
|
||||||
testQueue = 'test-queue',
|
testQueue = 'test-queue',
|
||||||
workflowQueue = 'workflow-queue',
|
workflowQueue = 'workflow-queue',
|
||||||
|
serverlessFunctionQueue = 'serverless-function-queue',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,17 +8,18 @@ export type LayerDependencies = {
|
|||||||
yarnLock: string;
|
yarnLock: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLastLayerDependencies =
|
export const getLayerDependencies = async (
|
||||||
async (): Promise<LayerDependencies> => {
|
layerVersion: number | 'latest',
|
||||||
const lastVersionLayerDirName = getLayerDependenciesDirName('latest');
|
): Promise<LayerDependencies> => {
|
||||||
const packageJson = await fs.readFile(
|
const lastVersionLayerDirName = getLayerDependenciesDirName(layerVersion);
|
||||||
join(lastVersionLayerDirName, 'package.json'),
|
const packageJson = await fs.readFile(
|
||||||
'utf8',
|
join(lastVersionLayerDirName, 'package.json'),
|
||||||
);
|
'utf8',
|
||||||
const yarnLock = await fs.readFile(
|
);
|
||||||
join(lastVersionLayerDirName, 'yarn.lock'),
|
const yarnLock = await fs.readFile(
|
||||||
'utf8',
|
join(lastVersionLayerDirName, 'yarn.lock'),
|
||||||
);
|
'utf8',
|
||||||
|
);
|
||||||
|
|
||||||
return { packageJson: JSON.parse(packageJson), yarnLock };
|
return { packageJson: JSON.parse(packageJson), yarnLock };
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { Field, InputType } from '@nestjs/graphql';
|
||||||
|
|
||||||
|
import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||||
|
|
||||||
|
@InputType()
|
||||||
|
export class CreateWorkflowVersionStepInput {
|
||||||
|
@Field(() => String, {
|
||||||
|
description: 'Workflow version ID',
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
workflowVersionId: string;
|
||||||
|
|
||||||
|
@Field(() => String, {
|
||||||
|
description: 'New step type',
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
stepType: WorkflowActionType;
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import { Field, InputType } from '@nestjs/graphql';
|
||||||
|
|
||||||
|
@InputType()
|
||||||
|
export class DeleteWorkflowVersionStepInput {
|
||||||
|
@Field(() => String, {
|
||||||
|
description: 'Workflow version ID',
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
workflowVersionId: string;
|
||||||
|
|
||||||
|
@Field(() => String, {
|
||||||
|
description: 'Step to delete ID',
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
stepId: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import { Field, InputType } from '@nestjs/graphql';
|
||||||
|
|
||||||
|
import graphqlTypeJson from 'graphql-type-json';
|
||||||
|
|
||||||
|
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||||
|
|
||||||
|
@InputType()
|
||||||
|
export class UpdateWorkflowVersionStepInput {
|
||||||
|
@Field(() => String, {
|
||||||
|
description: 'Workflow version ID',
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
workflowVersionId: string;
|
||||||
|
|
||||||
|
@Field(() => graphqlTypeJson, {
|
||||||
|
description: 'Step to update in JSON format',
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
step: WorkflowAction;
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
import { Field, ObjectType } from '@nestjs/graphql';
|
||||||
|
|
||||||
|
import graphqlTypeJson from 'graphql-type-json';
|
||||||
|
|
||||||
|
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||||
|
import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||||
|
|
||||||
|
@ObjectType('WorkflowAction')
|
||||||
|
export class WorkflowActionDTO {
|
||||||
|
@Field(() => UUIDScalarType)
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
type: WorkflowActionType;
|
||||||
|
|
||||||
|
@Field(() => graphqlTypeJson)
|
||||||
|
settings: object;
|
||||||
|
|
||||||
|
@Field(() => Boolean)
|
||||||
|
valid: boolean;
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
import { UseFilters, UseGuards } from '@nestjs/common';
|
||||||
|
import { Args, Mutation, Resolver } from '@nestjs/graphql';
|
||||||
|
|
||||||
|
import { WorkflowTriggerGraphqlApiExceptionFilter } from 'src/engine/core-modules/workflow/filters/workflow-trigger-graphql-api-exception.filter';
|
||||||
|
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
|
||||||
|
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||||
|
import { CreateWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/create-workflow-version-step-input.dto';
|
||||||
|
import { UpdateWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-version-step-input.dto';
|
||||||
|
import { DeleteWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/delete-workflow-version-step-input.dto';
|
||||||
|
import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-version-step.workspace-service';
|
||||||
|
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||||
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
|
import { WorkflowActionDTO } from 'src/engine/core-modules/workflow/dtos/workflow-step.dto';
|
||||||
|
|
||||||
|
@Resolver()
|
||||||
|
@UseGuards(WorkspaceAuthGuard, UserAuthGuard)
|
||||||
|
@UseFilters(WorkflowTriggerGraphqlApiExceptionFilter)
|
||||||
|
export class WorkflowVersionStepResolver {
|
||||||
|
constructor(
|
||||||
|
private readonly workflowVersionStepWorkspaceService: WorkflowVersionStepWorkspaceService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Mutation(() => WorkflowActionDTO)
|
||||||
|
async createWorkflowVersionStep(
|
||||||
|
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||||
|
@Args('input')
|
||||||
|
{ stepType, workflowVersionId }: CreateWorkflowVersionStepInput,
|
||||||
|
): Promise<WorkflowActionDTO> {
|
||||||
|
return this.workflowVersionStepWorkspaceService.createWorkflowVersionStep({
|
||||||
|
workspaceId,
|
||||||
|
workflowVersionId,
|
||||||
|
stepType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mutation(() => WorkflowActionDTO)
|
||||||
|
async updateWorkflowVersionStep(
|
||||||
|
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||||
|
@Args('input') { step, workflowVersionId }: UpdateWorkflowVersionStepInput,
|
||||||
|
): Promise<WorkflowActionDTO> {
|
||||||
|
return this.workflowVersionStepWorkspaceService.updateWorkflowVersionStep({
|
||||||
|
workspaceId,
|
||||||
|
workflowVersionId,
|
||||||
|
step,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mutation(() => WorkflowActionDTO)
|
||||||
|
async deleteWorkflowVersionStep(
|
||||||
|
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||||
|
@Args('input')
|
||||||
|
{ stepId, workflowVersionId }: DeleteWorkflowVersionStepInput,
|
||||||
|
): Promise<WorkflowActionDTO> {
|
||||||
|
return this.workflowVersionStepWorkspaceService.deleteWorkflowVersionStep({
|
||||||
|
workspaceId,
|
||||||
|
workflowVersionId,
|
||||||
|
stepId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,11 +2,17 @@ import { Module } from '@nestjs/common';
|
|||||||
|
|
||||||
import { WorkflowTriggerResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver';
|
import { WorkflowTriggerResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver';
|
||||||
import { WorkflowBuilderResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-builder.resolver';
|
import { WorkflowBuilderResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-builder.resolver';
|
||||||
import { WorkflowBuilderModule } from 'src/modules/workflow/workflow-builder/workflow-builder.module';
|
|
||||||
import { WorkflowTriggerModule } from 'src/modules/workflow/workflow-trigger/workflow-trigger.module';
|
import { WorkflowTriggerModule } from 'src/modules/workflow/workflow-trigger/workflow-trigger.module';
|
||||||
|
import { WorkflowVersionStepResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver';
|
||||||
|
import { WorkflowBuilderModule } from 'src/modules/workflow/workflow-builder/workflow-builder.module';
|
||||||
|
import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [WorkflowTriggerModule, WorkflowBuilderModule],
|
imports: [WorkflowTriggerModule, WorkflowBuilderModule, WorkflowCommonModule],
|
||||||
providers: [WorkflowTriggerResolver, WorkflowBuilderResolver],
|
providers: [
|
||||||
|
WorkflowTriggerResolver,
|
||||||
|
WorkflowBuilderResolver,
|
||||||
|
WorkflowVersionStepResolver,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class WorkflowApiModule {}
|
export class WorkflowApiModule {}
|
||||||
|
|||||||
@ -0,0 +1,33 @@
|
|||||||
|
import { Scope } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||||
|
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||||
|
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
|
||||||
|
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||||
|
|
||||||
|
export type DeleteServerlessFunctionBatchEvent = {
|
||||||
|
ids: string[];
|
||||||
|
workspaceId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
@Processor({
|
||||||
|
queueName: MessageQueue.serverlessFunctionQueue,
|
||||||
|
scope: Scope.REQUEST,
|
||||||
|
})
|
||||||
|
export class DeleteServerlessFunctionJob {
|
||||||
|
constructor(
|
||||||
|
private readonly serverlessFunctionService: ServerlessFunctionService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Process(DeleteServerlessFunctionJob.name)
|
||||||
|
async handle(batchEvent: DeleteServerlessFunctionBatchEvent): Promise<void> {
|
||||||
|
await Promise.all(
|
||||||
|
batchEvent.ids.map((id) =>
|
||||||
|
this.serverlessFunctionService.deleteOneServerlessFunction(
|
||||||
|
id,
|
||||||
|
batchEvent.workspaceId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,59 +0,0 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
|
||||||
|
|
||||||
import { join } from 'path';
|
|
||||||
|
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
|
|
||||||
import { INDEX_FILE_NAME } from 'src/engine/core-modules/serverless/drivers/constants/index-file-name';
|
|
||||||
import { SERVERLESS_FUNCTION_PUBLISHED } from 'src/engine/metadata-modules/serverless-function/constants/serverless-function-published';
|
|
||||||
import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
|
|
||||||
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
|
|
||||||
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type';
|
|
||||||
import { CodeIntrospectionService } from 'src/modules/code-introspection/code-introspection.service';
|
|
||||||
import { OnCustomBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-custom-batch-event.decorator';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ServerlessFunctionPublicationListener {
|
|
||||||
constructor(
|
|
||||||
private readonly serverlessFunctionService: ServerlessFunctionService,
|
|
||||||
private readonly codeIntrospectionService: CodeIntrospectionService,
|
|
||||||
@InjectRepository(ServerlessFunctionEntity, 'metadata')
|
|
||||||
private readonly serverlessFunctionRepository: Repository<ServerlessFunctionEntity>,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@OnCustomBatchEvent(SERVERLESS_FUNCTION_PUBLISHED)
|
|
||||||
async handle(
|
|
||||||
batchEvent: WorkspaceEventBatch<{
|
|
||||||
serverlessFunctionId: string;
|
|
||||||
serverlessFunctionVersion: string;
|
|
||||||
}>,
|
|
||||||
): Promise<void> {
|
|
||||||
for (const event of batchEvent.events) {
|
|
||||||
const sourceCode =
|
|
||||||
await this.serverlessFunctionService.getServerlessFunctionSourceCode(
|
|
||||||
batchEvent.workspaceId,
|
|
||||||
event.serverlessFunctionId,
|
|
||||||
event.serverlessFunctionVersion,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!sourceCode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const indexCode = sourceCode[join('src', INDEX_FILE_NAME)];
|
|
||||||
|
|
||||||
if (!indexCode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const latestVersionInputSchema =
|
|
||||||
this.codeIntrospectionService.getFunctionInputSchema(indexCode);
|
|
||||||
|
|
||||||
await this.serverlessFunctionRepository.update(
|
|
||||||
{ id: event.serverlessFunctionId },
|
|
||||||
{ latestVersionInputSchema },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -8,11 +8,9 @@ import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-
|
|||||||
import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module';
|
import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module';
|
||||||
import { FileModule } from 'src/engine/core-modules/file/file.module';
|
import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||||
import { ThrottlerModule } from 'src/engine/core-modules/throttler/throttler.module';
|
import { ThrottlerModule } from 'src/engine/core-modules/throttler/throttler.module';
|
||||||
import { ServerlessFunctionPublicationListener } from 'src/engine/metadata-modules/serverless-function/listeners/serverless-function-publication.listener';
|
|
||||||
import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
|
import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
|
||||||
import { ServerlessFunctionResolver } from 'src/engine/metadata-modules/serverless-function/serverless-function.resolver';
|
import { ServerlessFunctionResolver } from 'src/engine/metadata-modules/serverless-function/serverless-function.resolver';
|
||||||
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
|
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
|
||||||
import { CodeIntrospectionModule } from 'src/modules/code-introspection/code-introspection.module';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -22,13 +20,8 @@ import { CodeIntrospectionModule } from 'src/modules/code-introspection/code-int
|
|||||||
FileModule,
|
FileModule,
|
||||||
ThrottlerModule,
|
ThrottlerModule,
|
||||||
AnalyticsModule,
|
AnalyticsModule,
|
||||||
CodeIntrospectionModule,
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
ServerlessFunctionService,
|
|
||||||
ServerlessFunctionResolver,
|
|
||||||
ServerlessFunctionPublicationListener,
|
|
||||||
],
|
],
|
||||||
|
providers: [ServerlessFunctionService, ServerlessFunctionResolver],
|
||||||
exports: [ServerlessFunctionService],
|
exports: [ServerlessFunctionService],
|
||||||
})
|
})
|
||||||
export class ServerlessFunctionModule {}
|
export class ServerlessFunctionModule {}
|
||||||
|
|||||||
@ -84,11 +84,14 @@ export class ServerlessFunctionResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Query(() => graphqlTypeJson)
|
@Query(() => graphqlTypeJson)
|
||||||
async getAvailablePackages(@AuthWorkspace() { id: workspaceId }: Workspace) {
|
async getAvailablePackages(
|
||||||
|
@Args('input') { id }: ServerlessFunctionIdInput,
|
||||||
|
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
await this.checkFeatureFlag(workspaceId);
|
await this.checkFeatureFlag(workspaceId);
|
||||||
|
|
||||||
return await this.serverlessFunctionService.getAvailablePackages();
|
return await this.serverlessFunctionService.getAvailablePackages(id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
serverlessFunctionGraphQLApiExceptionHandler(error);
|
serverlessFunctionGraphQLApiExceptionHandler(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,11 +17,9 @@ import { ENV_FILE_NAME } from 'src/engine/core-modules/serverless/drivers/consta
|
|||||||
import { INDEX_FILE_NAME } from 'src/engine/core-modules/serverless/drivers/constants/index-file-name';
|
import { INDEX_FILE_NAME } from 'src/engine/core-modules/serverless/drivers/constants/index-file-name';
|
||||||
import { LAST_LAYER_VERSION } from 'src/engine/core-modules/serverless/drivers/layers/last-layer-version';
|
import { LAST_LAYER_VERSION } from 'src/engine/core-modules/serverless/drivers/layers/last-layer-version';
|
||||||
import { getBaseTypescriptProjectFiles } from 'src/engine/core-modules/serverless/drivers/utils/get-base-typescript-project-files';
|
import { getBaseTypescriptProjectFiles } from 'src/engine/core-modules/serverless/drivers/utils/get-base-typescript-project-files';
|
||||||
import { getLastLayerDependencies } from 'src/engine/core-modules/serverless/drivers/utils/get-last-layer-dependencies';
|
|
||||||
import { ServerlessService } from 'src/engine/core-modules/serverless/serverless.service';
|
import { ServerlessService } from 'src/engine/core-modules/serverless/serverless.service';
|
||||||
import { getServerlessFolder } from 'src/engine/core-modules/serverless/utils/serverless-get-folder.utils';
|
import { getServerlessFolder } from 'src/engine/core-modules/serverless/utils/serverless-get-folder.utils';
|
||||||
import { ThrottlerService } from 'src/engine/core-modules/throttler/throttler.service';
|
import { ThrottlerService } from 'src/engine/core-modules/throttler/throttler.service';
|
||||||
import { SERVERLESS_FUNCTION_PUBLISHED } from 'src/engine/metadata-modules/serverless-function/constants/serverless-function-published';
|
|
||||||
import { CreateServerlessFunctionInput } from 'src/engine/metadata-modules/serverless-function/dtos/create-serverless-function.input';
|
import { CreateServerlessFunctionInput } from 'src/engine/metadata-modules/serverless-function/dtos/create-serverless-function.input';
|
||||||
import { UpdateServerlessFunctionInput } from 'src/engine/metadata-modules/serverless-function/dtos/update-serverless-function.input';
|
import { UpdateServerlessFunctionInput } from 'src/engine/metadata-modules/serverless-function/dtos/update-serverless-function.input';
|
||||||
import {
|
import {
|
||||||
@ -32,8 +30,8 @@ import {
|
|||||||
ServerlessFunctionException,
|
ServerlessFunctionException,
|
||||||
ServerlessFunctionExceptionCode,
|
ServerlessFunctionExceptionCode,
|
||||||
} from 'src/engine/metadata-modules/serverless-function/serverless-function.exception';
|
} from 'src/engine/metadata-modules/serverless-function/serverless-function.exception';
|
||||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
|
||||||
import { isDefined } from 'src/utils/is-defined';
|
import { isDefined } from 'src/utils/is-defined';
|
||||||
|
import { getLayerDependencies } from 'src/engine/core-modules/serverless/drivers/utils/get-last-layer-dependencies';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ServerlessFunctionService {
|
export class ServerlessFunctionService {
|
||||||
@ -45,7 +43,6 @@ export class ServerlessFunctionService {
|
|||||||
private readonly throttlerService: ThrottlerService,
|
private readonly throttlerService: ThrottlerService,
|
||||||
private readonly environmentService: EnvironmentService,
|
private readonly environmentService: EnvironmentService,
|
||||||
private readonly analyticsService: AnalyticsService,
|
private readonly analyticsService: AnalyticsService,
|
||||||
private readonly workspaceEventEmitter: WorkspaceEventEmitter,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async findManyServerlessFunctions(where) {
|
async findManyServerlessFunctions(where) {
|
||||||
@ -194,17 +191,6 @@ export class ServerlessFunctionService {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
this.workspaceEventEmitter.emitCustomBatchEvent(
|
|
||||||
SERVERLESS_FUNCTION_PUBLISHED,
|
|
||||||
[
|
|
||||||
{
|
|
||||||
serverlessFunctionId: existingServerlessFunction.id,
|
|
||||||
serverlessFunctionVersion: newVersion,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
workspaceId,
|
|
||||||
);
|
|
||||||
|
|
||||||
return this.serverlessFunctionRepository.findOneBy({
|
return this.serverlessFunctionRepository.findOneBy({
|
||||||
id: existingServerlessFunction.id,
|
id: existingServerlessFunction.id,
|
||||||
});
|
});
|
||||||
@ -290,8 +276,14 @@ export class ServerlessFunctionService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAvailablePackages() {
|
async getAvailablePackages(serverlessFunctionId: string) {
|
||||||
const { packageJson, yarnLock } = await getLastLayerDependencies();
|
const serverlessFunction =
|
||||||
|
await this.serverlessFunctionRepository.findOneBy({
|
||||||
|
id: serverlessFunctionId,
|
||||||
|
});
|
||||||
|
const { packageJson, yarnLock } = await getLayerDependencies(
|
||||||
|
serverlessFunction?.layerVersion || 'latest',
|
||||||
|
);
|
||||||
|
|
||||||
const packageVersionRegex = /^"([^@]+)@.*?":\n\s+version: (.+)$/gm;
|
const packageVersionRegex = /^"([^@]+)@.*?":\n\s+version: (.+)$/gm;
|
||||||
const versions: Record<string, string> = {};
|
const versions: Record<string, string> = {};
|
||||||
|
|||||||
@ -24,15 +24,15 @@ import {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CodeIntrospectionService {
|
export class CodeIntrospectionService {
|
||||||
public generateInputData(inputSchema: InputSchema) {
|
public generateInputData(inputSchema: InputSchema, setNullValue = false) {
|
||||||
return Object.entries(inputSchema).reduce((acc, [key, value]) => {
|
return Object.entries(inputSchema).reduce((acc, [key, value]) => {
|
||||||
if (isDefined(value.enum)) {
|
if (isDefined(value.enum)) {
|
||||||
acc[key] = value.enum?.[0];
|
acc[key] = value.enum?.[0];
|
||||||
} else if (['string', 'number', 'boolean'].includes(value.type)) {
|
} else if (['string', 'number', 'boolean'].includes(value.type)) {
|
||||||
acc[key] = generateFakeValue(value.type);
|
acc[key] = setNullValue ? null : generateFakeValue(value.type);
|
||||||
} else if (value.type === 'object') {
|
} else if (value.type === 'object') {
|
||||||
acc[key] = isDefined(value.properties)
|
acc[key] = isDefined(value.properties)
|
||||||
? this.generateInputData(value.properties)
|
? this.generateInputData(value.properties, setNullValue)
|
||||||
: {};
|
: {};
|
||||||
} else if (value.type === 'array' && isDefined(value.items)) {
|
} else if (value.type === 'array' && isDefined(value.items)) {
|
||||||
acc[key] = [generateFakeValue(value.items.type)];
|
acc[key] = [generateFakeValue(value.items.type)];
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { CustomException } from 'src/utils/custom-exception';
|
||||||
|
|
||||||
|
export class WorkflowVersionStepException extends CustomException {
|
||||||
|
constructor(message: string, code: WorkflowVersionStepExceptionCode) {
|
||||||
|
super(message, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export enum WorkflowVersionStepExceptionCode {
|
||||||
|
UNKNOWN = 'UNKNOWN',
|
||||||
|
NOT_FOUND = 'NOT_FOUND',
|
||||||
|
UNDEFINED = 'UNDEFINED',
|
||||||
|
FAILURE = 'FAILURE',
|
||||||
|
}
|
||||||
@ -1,12 +1,32 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
||||||
|
|
||||||
import { WorkflowCommandModule } from 'src/modules/workflow/common/commands/workflow-command.module';
|
import { WorkflowCommandModule } from 'src/modules/workflow/common/commands/workflow-command.module';
|
||||||
import { WorkflowQueryHookModule } from 'src/modules/workflow/common/query-hooks/workflow-query-hook.module';
|
import { WorkflowQueryHookModule } from 'src/modules/workflow/common/query-hooks/workflow-query-hook.module';
|
||||||
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||||
|
import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-version-step.workspace-service';
|
||||||
|
import { WorkflowBuilderModule } from 'src/modules/workflow/workflow-builder/workflow-builder.module';
|
||||||
|
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||||
|
import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless-function/serverless-function.module';
|
||||||
|
import { CodeIntrospectionModule } from 'src/modules/code-introspection/code-introspection.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [WorkflowQueryHookModule, WorkflowCommandModule],
|
imports: [
|
||||||
providers: [WorkflowCommonWorkspaceService],
|
WorkflowQueryHookModule,
|
||||||
exports: [WorkflowCommonWorkspaceService],
|
WorkflowCommandModule,
|
||||||
|
WorkflowBuilderModule,
|
||||||
|
ServerlessFunctionModule,
|
||||||
|
CodeIntrospectionModule,
|
||||||
|
NestjsQueryTypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'),
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
WorkflowCommonWorkspaceService,
|
||||||
|
WorkflowVersionStepWorkspaceService,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
WorkflowCommonWorkspaceService,
|
||||||
|
WorkflowVersionStepWorkspaceService,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class WorkflowCommonModule {}
|
export class WorkflowCommonModule {}
|
||||||
|
|||||||
@ -0,0 +1,363 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
|
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||||
|
import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
||||||
|
import {
|
||||||
|
WorkflowAction,
|
||||||
|
WorkflowActionType,
|
||||||
|
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||||
|
import { isDefined } from 'src/utils/is-defined';
|
||||||
|
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||||
|
import { WorkflowBuilderWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-builder.workspace-service';
|
||||||
|
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
|
||||||
|
import { WorkflowRecordCRUDType } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
|
||||||
|
import { WorkflowActionDTO } from 'src/engine/core-modules/workflow/dtos/workflow-step.dto';
|
||||||
|
import { INDEX_FILE_NAME } from 'src/engine/core-modules/serverless/drivers/constants/index-file-name';
|
||||||
|
import { CodeIntrospectionService } from 'src/modules/code-introspection/code-introspection.service';
|
||||||
|
import {
|
||||||
|
WorkflowVersionStepException,
|
||||||
|
WorkflowVersionStepExceptionCode,
|
||||||
|
} from 'src/modules/workflow/common/exceptions/workflow-version-step.exception';
|
||||||
|
import { BaseWorkflowActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';
|
||||||
|
|
||||||
|
const TRIGGER_STEP_ID = 'trigger';
|
||||||
|
|
||||||
|
const BASE_STEP_DEFINITION: BaseWorkflowActionSettings = {
|
||||||
|
input: {},
|
||||||
|
outputSchema: {},
|
||||||
|
errorHandlingOptions: {
|
||||||
|
continueOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
retryOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WorkflowVersionStepWorkspaceService {
|
||||||
|
constructor(
|
||||||
|
private readonly twentyORMManager: TwentyORMManager,
|
||||||
|
private readonly workflowBuilderWorkspaceService: WorkflowBuilderWorkspaceService,
|
||||||
|
private readonly serverlessFunctionService: ServerlessFunctionService,
|
||||||
|
private readonly codeIntrospectionService: CodeIntrospectionService,
|
||||||
|
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||||
|
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private async getStepDefaultDefinition({
|
||||||
|
type,
|
||||||
|
workspaceId,
|
||||||
|
}: {
|
||||||
|
type: WorkflowActionType;
|
||||||
|
workspaceId: string;
|
||||||
|
}): Promise<WorkflowAction> {
|
||||||
|
const newStepId = v4();
|
||||||
|
|
||||||
|
switch (`${type}`) {
|
||||||
|
case WorkflowActionType.CODE: {
|
||||||
|
const newServerlessFunction =
|
||||||
|
await this.serverlessFunctionService.createOneServerlessFunction(
|
||||||
|
{
|
||||||
|
name: 'A Serverless Function Code Workflow Step',
|
||||||
|
description: '',
|
||||||
|
},
|
||||||
|
workspaceId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isDefined(newServerlessFunction)) {
|
||||||
|
throw new WorkflowVersionStepException(
|
||||||
|
'Fail to create Code Step',
|
||||||
|
WorkflowVersionStepExceptionCode.FAILURE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceCode = (
|
||||||
|
await this.serverlessFunctionService.getServerlessFunctionSourceCode(
|
||||||
|
workspaceId,
|
||||||
|
newServerlessFunction.id,
|
||||||
|
'draft',
|
||||||
|
)
|
||||||
|
)?.[join('src', INDEX_FILE_NAME)];
|
||||||
|
|
||||||
|
const inputSchema = isDefined(sourceCode)
|
||||||
|
? this.codeIntrospectionService.getFunctionInputSchema(sourceCode)
|
||||||
|
: {};
|
||||||
|
|
||||||
|
const serverlessFunctionInput =
|
||||||
|
this.codeIntrospectionService.generateInputData(inputSchema, true);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: newStepId,
|
||||||
|
name: 'Code - Serverless Function',
|
||||||
|
type: WorkflowActionType.CODE,
|
||||||
|
valid: false,
|
||||||
|
settings: {
|
||||||
|
...BASE_STEP_DEFINITION,
|
||||||
|
input: {
|
||||||
|
serverlessFunctionId: newServerlessFunction.id,
|
||||||
|
serverlessFunctionVersion: 'draft',
|
||||||
|
serverlessFunctionInput,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case WorkflowActionType.SEND_EMAIL: {
|
||||||
|
return {
|
||||||
|
id: newStepId,
|
||||||
|
name: 'Send Email',
|
||||||
|
type: WorkflowActionType.SEND_EMAIL,
|
||||||
|
valid: false,
|
||||||
|
settings: {
|
||||||
|
...BASE_STEP_DEFINITION,
|
||||||
|
input: {
|
||||||
|
connectedAccountId: '',
|
||||||
|
email: '',
|
||||||
|
subject: '',
|
||||||
|
body: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case `${WorkflowActionType.RECORD_CRUD}.${WorkflowRecordCRUDType.CREATE}`: {
|
||||||
|
const activeObjectMetadataItem =
|
||||||
|
await this.objectMetadataRepository.findOne({
|
||||||
|
where: { workspaceId, isActive: true, isSystem: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: newStepId,
|
||||||
|
name: 'Create Record',
|
||||||
|
type: WorkflowActionType.RECORD_CRUD,
|
||||||
|
valid: false,
|
||||||
|
settings: {
|
||||||
|
...BASE_STEP_DEFINITION,
|
||||||
|
input: {
|
||||||
|
type: WorkflowRecordCRUDType.CREATE,
|
||||||
|
objectName: activeObjectMetadataItem?.nameSingular || '',
|
||||||
|
objectRecord: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case `${WorkflowActionType.RECORD_CRUD}.${WorkflowRecordCRUDType.UPDATE}`: {
|
||||||
|
const activeObjectMetadataItem =
|
||||||
|
await this.objectMetadataRepository.findOne({
|
||||||
|
where: { workspaceId, isActive: true, isSystem: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: newStepId,
|
||||||
|
name: 'Update Record',
|
||||||
|
type: WorkflowActionType.RECORD_CRUD,
|
||||||
|
valid: false,
|
||||||
|
settings: {
|
||||||
|
...BASE_STEP_DEFINITION,
|
||||||
|
input: {
|
||||||
|
type: WorkflowRecordCRUDType.UPDATE,
|
||||||
|
objectName: activeObjectMetadataItem?.nameSingular || '',
|
||||||
|
objectRecord: {},
|
||||||
|
objectRecordId: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new WorkflowVersionStepException(
|
||||||
|
`WorkflowActionType '${type}' unknown`,
|
||||||
|
WorkflowVersionStepExceptionCode.UNKNOWN,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async enrichOutputSchema({
|
||||||
|
step,
|
||||||
|
workspaceId,
|
||||||
|
}: {
|
||||||
|
step: WorkflowAction;
|
||||||
|
workspaceId: string;
|
||||||
|
}): Promise<WorkflowAction> {
|
||||||
|
const result = { ...step };
|
||||||
|
const outputSchema =
|
||||||
|
await this.workflowBuilderWorkspaceService.computeStepOutputSchema({
|
||||||
|
step,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
result.settings = {
|
||||||
|
...result.settings,
|
||||||
|
outputSchema: outputSchema || {},
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createWorkflowVersionStep({
|
||||||
|
workspaceId,
|
||||||
|
workflowVersionId,
|
||||||
|
stepType,
|
||||||
|
}: {
|
||||||
|
workspaceId: string;
|
||||||
|
workflowVersionId: string;
|
||||||
|
stepType: WorkflowActionType;
|
||||||
|
}): Promise<WorkflowActionDTO> {
|
||||||
|
const newStep = await this.getStepDefaultDefinition({
|
||||||
|
type: stepType,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
const enrichedNewStep = await this.enrichOutputSchema({
|
||||||
|
step: newStep,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
const workflowVersionRepository =
|
||||||
|
await this.twentyORMManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||||
|
'workflowVersion',
|
||||||
|
);
|
||||||
|
|
||||||
|
const workflowVersion = await workflowVersionRepository.findOne({
|
||||||
|
where: {
|
||||||
|
id: workflowVersionId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isDefined(workflowVersion)) {
|
||||||
|
throw new WorkflowVersionStepException(
|
||||||
|
'WorkflowVersion not found',
|
||||||
|
WorkflowVersionStepExceptionCode.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await workflowVersionRepository.update(workflowVersion.id, {
|
||||||
|
steps: [...(workflowVersion.steps || []), enrichedNewStep],
|
||||||
|
});
|
||||||
|
|
||||||
|
return enrichedNewStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateWorkflowVersionStep({
|
||||||
|
workspaceId,
|
||||||
|
workflowVersionId,
|
||||||
|
step,
|
||||||
|
}: {
|
||||||
|
workspaceId: string;
|
||||||
|
workflowVersionId: string;
|
||||||
|
step: WorkflowAction;
|
||||||
|
}): Promise<WorkflowAction> {
|
||||||
|
const workflowVersionRepository =
|
||||||
|
await this.twentyORMManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||||
|
'workflowVersion',
|
||||||
|
);
|
||||||
|
|
||||||
|
const workflowVersion = await workflowVersionRepository.findOne({
|
||||||
|
where: {
|
||||||
|
id: workflowVersionId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isDefined(workflowVersion)) {
|
||||||
|
throw new WorkflowVersionStepException(
|
||||||
|
'WorkflowVersion not found',
|
||||||
|
WorkflowVersionStepExceptionCode.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isDefined(workflowVersion.steps)) {
|
||||||
|
throw new WorkflowVersionStepException(
|
||||||
|
"Can't update step from undefined steps",
|
||||||
|
WorkflowVersionStepExceptionCode.UNDEFINED,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const enrichedNewStep = await this.enrichOutputSchema({
|
||||||
|
step,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updatedSteps = workflowVersion.steps.map((existingStep) => {
|
||||||
|
if (existingStep.id === step.id) {
|
||||||
|
return enrichedNewStep;
|
||||||
|
} else {
|
||||||
|
return existingStep;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await workflowVersionRepository.update(workflowVersion.id, {
|
||||||
|
steps: updatedSteps,
|
||||||
|
});
|
||||||
|
|
||||||
|
return enrichedNewStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteWorkflowVersionStep({
|
||||||
|
workspaceId,
|
||||||
|
workflowVersionId,
|
||||||
|
stepId,
|
||||||
|
}: {
|
||||||
|
workspaceId: string;
|
||||||
|
workflowVersionId: string;
|
||||||
|
stepId: string;
|
||||||
|
}): Promise<WorkflowActionDTO> {
|
||||||
|
const workflowVersionRepository =
|
||||||
|
await this.twentyORMManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||||
|
'workflowVersion',
|
||||||
|
);
|
||||||
|
|
||||||
|
const workflowVersion = await workflowVersionRepository.findOne({
|
||||||
|
where: {
|
||||||
|
id: workflowVersionId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isDefined(workflowVersion)) {
|
||||||
|
throw new WorkflowVersionStepException(
|
||||||
|
'WorkflowVersion not found',
|
||||||
|
WorkflowVersionStepExceptionCode.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isDefined(workflowVersion.steps)) {
|
||||||
|
throw new WorkflowVersionStepException(
|
||||||
|
"Can't delete step from undefined steps",
|
||||||
|
WorkflowVersionStepExceptionCode.UNDEFINED,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const stepToDelete = workflowVersion.steps.filter(
|
||||||
|
(step) => step.id === stepId,
|
||||||
|
)?.[0];
|
||||||
|
|
||||||
|
if (!isDefined(stepToDelete)) {
|
||||||
|
throw new WorkflowVersionStepException(
|
||||||
|
"Can't delete not existing step",
|
||||||
|
WorkflowVersionStepExceptionCode.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowVersionUpdates =
|
||||||
|
stepId === TRIGGER_STEP_ID
|
||||||
|
? { trigger: null }
|
||||||
|
: { steps: workflowVersion.steps.filter((step) => step.id !== stepId) };
|
||||||
|
|
||||||
|
await workflowVersionRepository.update(
|
||||||
|
workflowVersion.id,
|
||||||
|
workflowVersionUpdates,
|
||||||
|
);
|
||||||
|
|
||||||
|
switch (stepToDelete.type) {
|
||||||
|
case WorkflowActionType.CODE:
|
||||||
|
await this.serverlessFunctionService.deleteOneServerlessFunction(
|
||||||
|
stepToDelete.settings.input.serverlessFunctionId,
|
||||||
|
workspaceId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stepToDelete;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -79,6 +79,14 @@ export class WorkflowVersionValidationWorkspaceService {
|
|||||||
WorkflowQueryValidationExceptionCode.FORBIDDEN,
|
WorkflowQueryValidationExceptionCode.FORBIDDEN,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (payload.data.steps) {
|
||||||
|
throw new WorkflowQueryValidationException(
|
||||||
|
'Updating workflowVersion steps directly is forbidden. ' +
|
||||||
|
'Use createWorkflowVersionStep, updateWorkflowVersionStep or deleteWorkflowVersionStep endpoint instead.',
|
||||||
|
WorkflowQueryValidationExceptionCode.FORBIDDEN,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async validateWorkflowVersionForDeleteOne(payload: DeleteOneResolverArgs) {
|
async validateWorkflowVersionForDeleteOne(payload: DeleteOneResolverArgs) {
|
||||||
|
|||||||
@ -1,16 +1,35 @@
|
|||||||
import { useTheme } from '@emotion/react';
|
import { useTheme, css } from '@emotion/react';
|
||||||
import Editor, { EditorProps } from '@monaco-editor/react';
|
import Editor, { EditorProps } from '@monaco-editor/react';
|
||||||
import { codeEditorTheme } from '@ui/input';
|
import { codeEditorTheme } from '@ui/input';
|
||||||
import { isDefined } from '@ui/utilities';
|
import { isDefined } from '@ui/utilities';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
export type CodeEditorPackage = {
|
|
||||||
[packageName: string]: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type CodeEditorProps = Omit<EditorProps, 'onChange'> & {
|
type CodeEditorProps = Omit<EditorProps, 'onChange'> & {
|
||||||
onChange?: (value: string) => void;
|
onChange?: (value: string) => void;
|
||||||
|
withHeader?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const StyledEditor = styled(Editor)<{ withHeader: boolean }>`
|
||||||
|
.monaco-editor {
|
||||||
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
|
outline-width: 0;
|
||||||
|
}
|
||||||
|
.overflow-guard {
|
||||||
|
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||||
|
box-sizing: border-box;
|
||||||
|
${({ withHeader, theme }) =>
|
||||||
|
withHeader
|
||||||
|
? css`
|
||||||
|
border-radius: 0 0 ${theme.border.radius.sm}
|
||||||
|
${theme.border.radius.sm};
|
||||||
|
border-top: none;
|
||||||
|
`
|
||||||
|
: css`
|
||||||
|
border-radius: ${theme.border.radius.sm};
|
||||||
|
`}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const CodeEditor = ({
|
export const CodeEditor = ({
|
||||||
value,
|
value,
|
||||||
language,
|
language,
|
||||||
@ -18,38 +37,42 @@ export const CodeEditor = ({
|
|||||||
onChange,
|
onChange,
|
||||||
onValidate,
|
onValidate,
|
||||||
height = 450,
|
height = 450,
|
||||||
|
withHeader = false,
|
||||||
options,
|
options,
|
||||||
}: CodeEditorProps) => {
|
}: CodeEditorProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Editor
|
<div>
|
||||||
height={height}
|
<StyledEditor
|
||||||
value={value}
|
height={height}
|
||||||
language={language}
|
withHeader={withHeader}
|
||||||
onMount={(editor, monaco) => {
|
value={value}
|
||||||
monaco.editor.defineTheme('codeEditorTheme', codeEditorTheme(theme));
|
language={language}
|
||||||
monaco.editor.setTheme('codeEditorTheme');
|
onMount={(editor, monaco) => {
|
||||||
|
monaco.editor.defineTheme('codeEditorTheme', codeEditorTheme(theme));
|
||||||
|
monaco.editor.setTheme('codeEditorTheme');
|
||||||
|
|
||||||
onMount?.(editor, monaco);
|
onMount?.(editor, monaco);
|
||||||
}}
|
}}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
if (isDefined(value)) {
|
if (isDefined(value)) {
|
||||||
onChange?.(value);
|
onChange?.(value);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onValidate={onValidate}
|
onValidate={onValidate}
|
||||||
options={{
|
options={{
|
||||||
overviewRulerLanes: 0,
|
overviewRulerLanes: 0,
|
||||||
scrollbar: {
|
scrollbar: {
|
||||||
vertical: 'hidden',
|
vertical: 'hidden',
|
||||||
horizontal: 'hidden',
|
horizontal: 'hidden',
|
||||||
},
|
},
|
||||||
minimap: {
|
minimap: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user