Remove serverless functions on version archivation (#9535)
Fixes https://github.com/twentyhq/core-team-issues/issues/52 - contrary to title, we do not remove serverless functions on workflow version archivation because serverless fucntion might be used in another workflow version - we fix the serverless funciton version displayed in the code step - we allow test function version in step display right drawer - we delete serverless function only when serverless function has no published version
This commit is contained in:
@ -591,13 +591,17 @@ describe('should work as expected for the different field types', () => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
not: {
|
and: [
|
||||||
phones: {
|
{
|
||||||
primaryPhoneNumber: {
|
not: {
|
||||||
ilike: '%1234567890%',
|
phones: {
|
||||||
|
primaryPhoneNumber: {
|
||||||
|
ilike: '%1234567890%',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
and: [
|
and: [
|
||||||
|
|||||||
@ -848,7 +848,7 @@ const computeFilterRecordGqlOperationFilter = (
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
case ViewFilterOperand.DoesNotContain:
|
case ViewFilterOperand.DoesNotContain:
|
||||||
return {
|
return {
|
||||||
and: [
|
and: [
|
||||||
{
|
{
|
||||||
not: {
|
not: {
|
||||||
|
|||||||
@ -323,9 +323,7 @@ export const isRecordMatchingFilter = ({
|
|||||||
case FieldMetadataType.Phones: {
|
case FieldMetadataType.Phones: {
|
||||||
const phonesFilter = filterValue as PhonesFilter;
|
const phonesFilter = filterValue as PhonesFilter;
|
||||||
|
|
||||||
const keys: (keyof PhonesFilter)[] = [
|
const keys: (keyof PhonesFilter)[] = ['primaryPhoneNumber'];
|
||||||
'primaryPhoneNumber'
|
|
||||||
];
|
|
||||||
|
|
||||||
return keys.some((key) => {
|
return keys.some((key) => {
|
||||||
const value = phonesFilter[key];
|
const value = phonesFilter[key];
|
||||||
|
|||||||
@ -3,10 +3,15 @@ import { useRecoilState } from 'recoil';
|
|||||||
import { serverlessFunctionTestDataFamilyState } from '@/workflow/states/serverlessFunctionTestDataFamilyState';
|
import { serverlessFunctionTestDataFamilyState } from '@/workflow/states/serverlessFunctionTestDataFamilyState';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useTestServerlessFunction = (
|
export const useTestServerlessFunction = ({
|
||||||
serverlessFunctionId: string,
|
serverlessFunctionId,
|
||||||
callback?: (testResult: object) => void,
|
serverlessFunctionVersion = 'draft',
|
||||||
) => {
|
callback,
|
||||||
|
}: {
|
||||||
|
serverlessFunctionId: string;
|
||||||
|
serverlessFunctionVersion?: string;
|
||||||
|
callback?: (testResult: object) => void;
|
||||||
|
}) => {
|
||||||
const { executeOneServerlessFunction } = useExecuteOneServerlessFunction();
|
const { executeOneServerlessFunction } = useExecuteOneServerlessFunction();
|
||||||
const [serverlessFunctionTestData, setServerlessFunctionTestData] =
|
const [serverlessFunctionTestData, setServerlessFunctionTestData] =
|
||||||
useRecoilState(serverlessFunctionTestDataFamilyState(serverlessFunctionId));
|
useRecoilState(serverlessFunctionTestDataFamilyState(serverlessFunctionId));
|
||||||
@ -15,7 +20,7 @@ export const useTestServerlessFunction = (
|
|||||||
const result = await executeOneServerlessFunction({
|
const result = await executeOneServerlessFunction({
|
||||||
id: serverlessFunctionId,
|
id: serverlessFunctionId,
|
||||||
payload: serverlessFunctionTestData.input,
|
payload: serverlessFunctionTestData.input,
|
||||||
version: 'draft',
|
version: serverlessFunctionVersion,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isDefined(result?.data?.executeOneServerlessFunction?.data)) {
|
if (isDefined(result?.data?.executeOneServerlessFunction?.data)) {
|
||||||
|
|||||||
@ -36,7 +36,7 @@ describe('useServerlessFunctionUpdateFormState', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() => useServerlessFunctionUpdateFormState(serverlessFunctionId),
|
() => useServerlessFunctionUpdateFormState({ serverlessFunctionId }),
|
||||||
{
|
{
|
||||||
wrapper: RecoilRoot,
|
wrapper: RecoilRoot,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -20,9 +20,13 @@ type SetServerlessFunctionFormValues = Dispatch<
|
|||||||
SetStateAction<ServerlessFunctionFormValues>
|
SetStateAction<ServerlessFunctionFormValues>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export const useServerlessFunctionUpdateFormState = (
|
export const useServerlessFunctionUpdateFormState = ({
|
||||||
serverlessFunctionId: string,
|
serverlessFunctionId,
|
||||||
): {
|
serverlessFunctionVersion = 'draft',
|
||||||
|
}: {
|
||||||
|
serverlessFunctionId: string;
|
||||||
|
serverlessFunctionVersion?: string;
|
||||||
|
}): {
|
||||||
formValues: ServerlessFunctionFormValues;
|
formValues: ServerlessFunctionFormValues;
|
||||||
setFormValues: SetServerlessFunctionFormValues;
|
setFormValues: SetServerlessFunctionFormValues;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
@ -43,7 +47,7 @@ export const useServerlessFunctionUpdateFormState = (
|
|||||||
|
|
||||||
const { loading } = useGetOneServerlessFunctionSourceCode({
|
const { loading } = useGetOneServerlessFunctionSourceCode({
|
||||||
id: serverlessFunctionId,
|
id: serverlessFunctionId,
|
||||||
version: 'draft',
|
version: serverlessFunctionVersion,
|
||||||
onCompleted: (data: FindOneServerlessFunctionSourceCodeQuery) => {
|
onCompleted: (data: FindOneServerlessFunctionSourceCodeQuery) => {
|
||||||
const newState = {
|
const newState = {
|
||||||
code: data?.getServerlessFunctionSourceCode || undefined,
|
code: data?.getServerlessFunctionSourceCode || undefined,
|
||||||
|
|||||||
@ -77,6 +77,8 @@ export const WorkflowEditActionFormServerlessFunction = ({
|
|||||||
actionOptions,
|
actionOptions,
|
||||||
}: WorkflowEditActionFormServerlessFunctionProps) => {
|
}: WorkflowEditActionFormServerlessFunctionProps) => {
|
||||||
const serverlessFunctionId = action.settings.input.serverlessFunctionId;
|
const serverlessFunctionId = action.settings.input.serverlessFunctionId;
|
||||||
|
const serverlessFunctionVersion =
|
||||||
|
action.settings.input.serverlessFunctionVersion;
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const tabListId = `${WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID}_${serverlessFunctionId}`;
|
const tabListId = `${WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID}_${serverlessFunctionId}`;
|
||||||
const { activeTabId, setActiveTabId } = useTabList(tabListId);
|
const { activeTabId, setActiveTabId } = useTabList(tabListId);
|
||||||
@ -99,7 +101,10 @@ export const WorkflowEditActionFormServerlessFunction = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { formValues, setFormValues, loading } =
|
const { formValues, setFormValues, loading } =
|
||||||
useServerlessFunctionUpdateFormState(serverlessFunctionId);
|
useServerlessFunctionUpdateFormState({
|
||||||
|
serverlessFunctionId,
|
||||||
|
serverlessFunctionVersion,
|
||||||
|
});
|
||||||
|
|
||||||
const updateOutputSchemaFromTestResult = async (testResult: object) => {
|
const updateOutputSchemaFromTestResult = async (testResult: object) => {
|
||||||
if (actionOptions.readonly === true) {
|
if (actionOptions.readonly === true) {
|
||||||
@ -112,10 +117,11 @@ export const WorkflowEditActionFormServerlessFunction = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const { testServerlessFunction } = useTestServerlessFunction(
|
const { testServerlessFunction } = useTestServerlessFunction({
|
||||||
serverlessFunctionId,
|
serverlessFunctionId,
|
||||||
updateOutputSchemaFromTestResult,
|
serverlessFunctionVersion,
|
||||||
);
|
callback: updateOutputSchemaFromTestResult,
|
||||||
|
});
|
||||||
|
|
||||||
const handleSave = useDebouncedCallback(async () => {
|
const handleSave = useDebouncedCallback(async () => {
|
||||||
await updateOneServerlessFunction({
|
await updateOneServerlessFunction({
|
||||||
@ -314,7 +320,6 @@ export const WorkflowEditActionFormServerlessFunction = ({
|
|||||||
<WorkflowEditActionFormServerlessFunctionFields
|
<WorkflowEditActionFormServerlessFunctionFields
|
||||||
functionInput={serverlessFunctionTestData.input}
|
functionInput={serverlessFunctionTestData.input}
|
||||||
onInputChange={handleTestInputChange}
|
onInputChange={handleTestInputChange}
|
||||||
readonly={actionOptions.readonly}
|
|
||||||
/>
|
/>
|
||||||
<StyledCodeEditorContainer>
|
<StyledCodeEditorContainer>
|
||||||
<InputLabel>Result</InputLabel>
|
<InputLabel>Result</InputLabel>
|
||||||
|
|||||||
@ -37,9 +37,10 @@ export const SettingsServerlessFunctionDetail = () => {
|
|||||||
useUpdateOneServerlessFunction(serverlessFunctionId);
|
useUpdateOneServerlessFunction(serverlessFunctionId);
|
||||||
const { publishOneServerlessFunction } = usePublishOneServerlessFunction();
|
const { publishOneServerlessFunction } = usePublishOneServerlessFunction();
|
||||||
const { formValues, setFormValues, loading } =
|
const { formValues, setFormValues, loading } =
|
||||||
useServerlessFunctionUpdateFormState(serverlessFunctionId);
|
useServerlessFunctionUpdateFormState({ serverlessFunctionId });
|
||||||
const { testServerlessFunction } =
|
const { testServerlessFunction } = useTestServerlessFunction({
|
||||||
useTestServerlessFunction(serverlessFunctionId);
|
serverlessFunctionId,
|
||||||
|
});
|
||||||
const { code: latestVersionCode } = useGetOneServerlessFunctionSourceCode({
|
const { code: latestVersionCode } = useGetOneServerlessFunctionSourceCode({
|
||||||
id: serverlessFunctionId,
|
id: serverlessFunctionId,
|
||||||
version: 'latest',
|
version: 'latest',
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
|||||||
import { basename, dirname, join } from 'path';
|
import { basename, dirname, join } from 'path';
|
||||||
|
|
||||||
import deepEqual from 'deep-equal';
|
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 { 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';
|
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);
|
return this.serverlessFunctionRepository.findBy(where);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async hasServerlessFunctionPublishedVersion(serverlessFunctionId: string) {
|
||||||
|
return await this.serverlessFunctionRepository.exists({
|
||||||
|
where: {
|
||||||
|
id: serverlessFunctionId,
|
||||||
|
latestVersion: Not(IsNull()),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async getServerlessFunctionSourceCode(
|
async getServerlessFunctionSourceCode(
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
id: string,
|
id: string,
|
||||||
|
|||||||
@ -511,11 +511,16 @@ export class WorkflowVersionStepWorkspaceService {
|
|||||||
}) {
|
}) {
|
||||||
switch (step.type) {
|
switch (step.type) {
|
||||||
case WorkflowActionType.CODE: {
|
case WorkflowActionType.CODE: {
|
||||||
await this.serverlessFunctionService.deleteOneServerlessFunction({
|
if (
|
||||||
id: step.settings.input.serverlessFunctionId,
|
!(await this.serverlessFunctionService.hasServerlessFunctionPublishedVersion(
|
||||||
workspaceId,
|
step.settings.input.serverlessFunctionId,
|
||||||
});
|
))
|
||||||
|
) {
|
||||||
|
await this.serverlessFunctionService.deleteOneServerlessFunction({
|
||||||
|
id: step.settings.input.serverlessFunctionId,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user