diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/__tests__/computeViewRecordGqlOperationFilter.test.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/__tests__/computeViewRecordGqlOperationFilter.test.ts index 9542d5e42..1c5c75d6a 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/utils/__tests__/computeViewRecordGqlOperationFilter.test.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/__tests__/computeViewRecordGqlOperationFilter.test.ts @@ -591,13 +591,17 @@ describe('should work as expected for the different field types', () => { ], }, { - not: { - phones: { - primaryPhoneNumber: { - ilike: '%1234567890%', + and: [ + { + not: { + phones: { + primaryPhoneNumber: { + ilike: '%1234567890%', + }, + }, }, }, - }, + ], }, { and: [ diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/computeViewRecordGqlOperationFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/computeViewRecordGqlOperationFilter.ts index c47c0f86f..3b2223a46 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/utils/computeViewRecordGqlOperationFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/computeViewRecordGqlOperationFilter.ts @@ -848,7 +848,7 @@ const computeFilterRecordGqlOperationFilter = ( ], }; case ViewFilterOperand.DoesNotContain: - return { + return { and: [ { not: { diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts index e11a8fab0..137a75267 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts @@ -323,9 +323,7 @@ export const isRecordMatchingFilter = ({ case FieldMetadataType.Phones: { const phonesFilter = filterValue as PhonesFilter; - const keys: (keyof PhonesFilter)[] = [ - 'primaryPhoneNumber' - ]; + const keys: (keyof PhonesFilter)[] = ['primaryPhoneNumber']; return keys.some((key) => { const value = phonesFilter[key]; diff --git a/packages/twenty-front/src/modules/serverless-functions/hooks/useTestServerlessFunction.ts b/packages/twenty-front/src/modules/serverless-functions/hooks/useTestServerlessFunction.ts index af5bd65db..5c40270c2 100644 --- a/packages/twenty-front/src/modules/serverless-functions/hooks/useTestServerlessFunction.ts +++ b/packages/twenty-front/src/modules/serverless-functions/hooks/useTestServerlessFunction.ts @@ -3,10 +3,15 @@ import { useRecoilState } from 'recoil'; import { serverlessFunctionTestDataFamilyState } from '@/workflow/states/serverlessFunctionTestDataFamilyState'; import { isDefined } from 'twenty-ui'; -export const useTestServerlessFunction = ( - serverlessFunctionId: string, - callback?: (testResult: object) => void, -) => { +export const useTestServerlessFunction = ({ + serverlessFunctionId, + serverlessFunctionVersion = 'draft', + callback, +}: { + serverlessFunctionId: string; + serverlessFunctionVersion?: string; + callback?: (testResult: object) => void; +}) => { const { executeOneServerlessFunction } = useExecuteOneServerlessFunction(); const [serverlessFunctionTestData, setServerlessFunctionTestData] = useRecoilState(serverlessFunctionTestDataFamilyState(serverlessFunctionId)); @@ -15,7 +20,7 @@ export const useTestServerlessFunction = ( const result = await executeOneServerlessFunction({ id: serverlessFunctionId, payload: serverlessFunctionTestData.input, - version: 'draft', + version: serverlessFunctionVersion, }); if (isDefined(result?.data?.executeOneServerlessFunction?.data)) { diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/hooks/__tests__/useServerlessFunctionUpdateFormState.test.ts b/packages/twenty-front/src/modules/settings/serverless-functions/hooks/__tests__/useServerlessFunctionUpdateFormState.test.ts index 36de55415..fe403dee3 100644 --- a/packages/twenty-front/src/modules/settings/serverless-functions/hooks/__tests__/useServerlessFunctionUpdateFormState.test.ts +++ b/packages/twenty-front/src/modules/settings/serverless-functions/hooks/__tests__/useServerlessFunctionUpdateFormState.test.ts @@ -36,7 +36,7 @@ describe('useServerlessFunctionUpdateFormState', () => { }, ); const { result } = renderHook( - () => useServerlessFunctionUpdateFormState(serverlessFunctionId), + () => useServerlessFunctionUpdateFormState({ serverlessFunctionId }), { wrapper: RecoilRoot, }, diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState.ts b/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState.ts index 4ff119ab3..dc84a7ec1 100644 --- a/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState.ts +++ b/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState.ts @@ -20,9 +20,13 @@ type SetServerlessFunctionFormValues = Dispatch< SetStateAction >; -export const useServerlessFunctionUpdateFormState = ( - serverlessFunctionId: string, -): { +export const useServerlessFunctionUpdateFormState = ({ + serverlessFunctionId, + serverlessFunctionVersion = 'draft', +}: { + serverlessFunctionId: string; + serverlessFunctionVersion?: string; +}): { formValues: ServerlessFunctionFormValues; setFormValues: SetServerlessFunctionFormValues; loading: boolean; @@ -43,7 +47,7 @@ export const useServerlessFunctionUpdateFormState = ( const { loading } = useGetOneServerlessFunctionSourceCode({ id: serverlessFunctionId, - version: 'draft', + version: serverlessFunctionVersion, onCompleted: (data: FindOneServerlessFunctionSourceCodeQuery) => { const newState = { code: data?.getServerlessFunctionSourceCode || undefined, diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormServerlessFunction.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormServerlessFunction.tsx index 41aee7197..46533fa4f 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormServerlessFunction.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormServerlessFunction.tsx @@ -77,6 +77,8 @@ export const WorkflowEditActionFormServerlessFunction = ({ actionOptions, }: WorkflowEditActionFormServerlessFunctionProps) => { const serverlessFunctionId = action.settings.input.serverlessFunctionId; + const serverlessFunctionVersion = + action.settings.input.serverlessFunctionVersion; const theme = useTheme(); const tabListId = `${WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID}_${serverlessFunctionId}`; const { activeTabId, setActiveTabId } = useTabList(tabListId); @@ -99,7 +101,10 @@ export const WorkflowEditActionFormServerlessFunction = ({ ); const { formValues, setFormValues, loading } = - useServerlessFunctionUpdateFormState(serverlessFunctionId); + useServerlessFunctionUpdateFormState({ + serverlessFunctionId, + serverlessFunctionVersion, + }); const updateOutputSchemaFromTestResult = async (testResult: object) => { if (actionOptions.readonly === true) { @@ -112,10 +117,11 @@ export const WorkflowEditActionFormServerlessFunction = ({ }); }; - const { testServerlessFunction } = useTestServerlessFunction( + const { testServerlessFunction } = useTestServerlessFunction({ serverlessFunctionId, - updateOutputSchemaFromTestResult, - ); + serverlessFunctionVersion, + callback: updateOutputSchemaFromTestResult, + }); const handleSave = useDebouncedCallback(async () => { await updateOneServerlessFunction({ @@ -314,7 +320,6 @@ export const WorkflowEditActionFormServerlessFunction = ({ Result diff --git a/packages/twenty-front/src/pages/settings/serverless-functions/SettingsServerlessFunctionDetail.tsx b/packages/twenty-front/src/pages/settings/serverless-functions/SettingsServerlessFunctionDetail.tsx index 09b9db7cc..fe0e6a343 100644 --- a/packages/twenty-front/src/pages/settings/serverless-functions/SettingsServerlessFunctionDetail.tsx +++ b/packages/twenty-front/src/pages/settings/serverless-functions/SettingsServerlessFunctionDetail.tsx @@ -37,9 +37,10 @@ export const SettingsServerlessFunctionDetail = () => { useUpdateOneServerlessFunction(serverlessFunctionId); const { publishOneServerlessFunction } = usePublishOneServerlessFunction(); const { formValues, setFormValues, loading } = - useServerlessFunctionUpdateFormState(serverlessFunctionId); - const { testServerlessFunction } = - useTestServerlessFunction(serverlessFunctionId); + useServerlessFunctionUpdateFormState({ serverlessFunctionId }); + const { testServerlessFunction } = useTestServerlessFunction({ + serverlessFunctionId, + }); const { code: latestVersionCode } = useGetOneServerlessFunctionSourceCode({ id: serverlessFunctionId, version: 'latest', diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts index 2b72ec795..468587231 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts @@ -4,7 +4,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { basename, dirname, join } from 'path'; import deepEqual from 'deep-equal'; -import { Repository } from 'typeorm'; +import { IsNull, Not, Repository } from 'typeorm'; import { FileStorageExceptionCode } from 'src/engine/core-modules/file-storage/interfaces/file-storage-exception'; import { ServerlessExecuteResult } from 'src/engine/core-modules/serverless/drivers/interfaces/serverless-driver.interface'; @@ -58,6 +58,15 @@ export class ServerlessFunctionService { return this.serverlessFunctionRepository.findBy(where); } + async hasServerlessFunctionPublishedVersion(serverlessFunctionId: string) { + return await this.serverlessFunctionRepository.exists({ + where: { + id: serverlessFunctionId, + latestVersion: Not(IsNull()), + }, + }); + } + async getServerlessFunctionSourceCode( workspaceId: string, id: string, diff --git a/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-version-step.workspace-service.ts b/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-version-step.workspace-service.ts index 544cb38d8..83679775a 100644 --- a/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-version-step.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-version-step.workspace-service.ts @@ -511,11 +511,16 @@ export class WorkflowVersionStepWorkspaceService { }) { switch (step.type) { case WorkflowActionType.CODE: { - await this.serverlessFunctionService.deleteOneServerlessFunction({ - id: step.settings.input.serverlessFunctionId, - workspaceId, - }); - + if ( + !(await this.serverlessFunctionService.hasServerlessFunctionPublishedVersion( + step.settings.input.serverlessFunctionId, + )) + ) { + await this.serverlessFunctionService.deleteOneServerlessFunction({ + id: step.settings.input.serverlessFunctionId, + workspaceId, + }); + } break; } }