Files
twenty_crm/packages/twenty-front/src/modules/workflow/utils/getFunctionInputSchema.ts
martmull d0ff1ffd5f 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">
2024-12-03 08:41:13 +00:00

130 lines
3.5 KiB
TypeScript

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;
};