Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move the workflow draft version overriding to the backend #9328

Merged
merged 13 commits into from
Jan 6, 2025

Conversation

Devessier
Copy link
Contributor

@Devessier Devessier commented Jan 2, 2025

  • In the formatFieldMetadataValue function, allow people to call TypeORM's save() method with unserialized JSON data.
  • Create an overrideWorkflowDraftVersion mutation that takes a workflow id and the id of the workflow version to use as the new draft
    • If no draft exists yet, create one
    • If a draft already exists, deactivate its serverless functions
    • Duplicate every step. For serverless function steps, it includes duplicating the functions
    • Save the data of the step in DB
  • Call the overrideWorkflowDraftVersion mutation in the old workflow header and in the new Cmd+K actions
    • I chose to not update the Apollo cache manually as the information of the new draft are going to be automatically fetched once the user lands on the workflow's show page. Note that we redirect the user to this page after overriding the draft version.

@Devessier Devessier force-pushed the move-workflow-draft-version-override-to-backend branch from 6694098 to 70261ad Compare January 2, 2025 18:06
@Devessier Devessier force-pushed the move-workflow-draft-version-override-to-backend branch from 70261ad to 9299f08 Compare January 3, 2025 12:04
@Weiko Weiko added the -PR: wip label Jan 3, 2025
Comment on lines +85 to 90
if (
fieldMetadata.type === FieldMetadataType.RAW_JSON &&
typeof value === 'string'
) {
return JSON.parse(value as string);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I discussed this change with @Weiko

@Devessier
Copy link
Contributor Author

Hi @martmull! I would be glad if you could look at my PR when you return to work. It's related to serverless functions, and I want to confirm everything is okay for you. Thanks!

@Devessier Devessier marked this pull request as ready for review January 3, 2025 17:14
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

This PR moves workflow draft version overriding functionality from frontend to backend, improving architecture and data handling.

  • Added new overrideWorkflowDraftVersion mutation in /packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver.ts to handle draft version management
  • Added copyOneServerlessFunction method in serverless-function.service.ts to properly duplicate serverless functions when copying workflow steps
  • Modified formatFieldMetadataValue in /packages/twenty-server/src/engine/twenty-orm/utils/format-data.util.ts to handle unserialized JSON data for TypeORM's save() method
  • Added type safety with assertWorkflowVersionHasSteps utility in /packages/twenty-server/src/modules/workflow/common/utils/assert-workflow-version-has-steps.ts
  • Updated frontend components to use new backend mutation instead of handling draft version creation client-side

13 file(s) reviewed, 18 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines 97 to 100
await overrideWorkflowDraftVersion({
workflowId: workflowVersion.workflow.id,
name: `v${workflowVersion.workflow.versions.length + 1}`,
status: 'DRAFT',
trigger: workflowVersion.trigger,
steps: workflowVersion.steps,
workflowVersionIdToCopy: workflowVersion.id,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: No error handling for failed override operation. Consider wrapping in try/catch.

mutation OverrideWorkflowDraftVersion(
$input: OverrideWorkflowDraftVersionInput!
) {
overrideWorkflowDraftVersion(input: $input)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: mutation response type is not specified - should define exact return type for better type safety

@@ -9,7 +9,7 @@ export const getServerlessFolder = ({
version,
}: {
serverlessFunction: ServerlessFunctionEntity;
version?: string;
version?: 'draft' | 'latest' | (string & NonNullable<unknown>);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: The type string & NonNullable<unknown> is redundant since string is already non-nullable. Consider using just string or creating a specific type/enum for versions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure this change helps a lot

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can use constants instead of hard-coded strings?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can't because version can be '1', '2', etc...

description: 'Workflow version ID',
nullable: false,
})
workflowVersionIdToCopy: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: field name 'workflowVersionIdToCopy' could be more concise, e.g. 'sourceVersionId'

Comment on lines +10 to +12
): asserts workflowVersion is WorkflowVersionWorkspaceEntity & {
steps: WorkflowAction[];
} {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: assertion type could be more specific by using Required<Pick<WorkflowVersionWorkspaceEntity, 'steps'>> to ensure steps property exists

Comment on lines +476 to +485
if (Array.isArray(draftWorkflowVersion.steps)) {
await Promise.all(
draftWorkflowVersion.steps.map((step) =>
this.runWorkflowVersionStepDeletionSideEffects({
step,
workspaceId,
}),
),
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: potential race condition if multiple deletion operations fail - consider using Promise.allSettled and handling partial failures

return stepToDelete;
}

async overrideWorkflowDraftVersion({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: no return type specified for overrideWorkflowDraftVersion method

Comment on lines +490 to +497
for (const step of workflowVersionToCopy.steps) {
const duplicatedStep = await this.duplicateStep({
step,
workspaceId,
});

newWorkflowVersionSteps.push(duplicatedStep);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: duplicating steps sequentially could be slow for large workflows - consider using Promise.all for parallel execution

Copy link
Contributor

@martmull martmull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice code, I left some minor comments but the overall logic looks good to me

@@ -9,7 +9,7 @@ export const getServerlessFolder = ({
version,
}: {
serverlessFunction: ServerlessFunctionEntity;
version?: string;
version?: 'draft' | 'latest' | (string & NonNullable<unknown>);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure this change helps a lot

thomtrp

This comment was marked as duplicate.

@Devessier
Copy link
Contributor Author

We want to rename the overrideWorkflowDraftVersion to createDraftFromWorkflowVersion.

Copy link
Contributor

@thomtrp thomtrp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work 🔥

});
}

assertWorkflowVersionIsDraft(draftWorkflowVersion);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be in else {}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's safer to run the assertion for both cases. I might have forgotten to create a draft version above. That way, we are sure we will deal with a draft version.

CleanShot 2025-01-06 at 14 33 43@2x

@Devessier Devessier merged commit 17bf2b6 into main Jan 6, 2025
23 checks passed
@Devessier Devessier deleted the move-workflow-draft-version-override-to-backend branch January 6, 2025 13:56
Copy link

github-actions bot commented Jan 6, 2025

Fails
🚫

node failed.

Log

�[31mError: �[39m SyntaxError: Unexpected token C in JSON at position 0
    at JSON.parse (<anonymous>)
�[90m    at parseJSONFromBytes (node:internal/deps/undici/undici:5584:19)�[39m
�[90m    at successSteps (node:internal/deps/undici/undici:5555:27)�[39m
�[90m    at fullyReadBody (node:internal/deps/undici/undici:1665:9)�[39m
�[90m    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)�[39m
�[90m    at async specConsumeBody (node:internal/deps/undici/undici:5564:7)�[39m
danger-results://tmp/danger-results-7fc25f99.json

Generated by 🚫 dangerJS against 5ba02b6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants