Add relations in database event trigger output data (#11820)
## Done - add relations in dropdown variables - add relations in worklfow run inputs - use objectMetadataMaps in workflow folder ## To do - does not work with rest api calls, will be fixed after https://github.com/twentyhq/twenty/pull/11349 is merged - waiting for crud action relation fields https://github.com/twentyhq/core-team-issues/issues/509
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { mockObjectMetadataItemsWithFieldMaps } from 'src/engine/core-modules/search/__mocks__/mockObjectMetadataItemsWithFieldMaps';
|
||||
import { mockObjectMetadataItemsWithFieldMaps } from 'src/engine/core-modules/__mocks__/mockObjectMetadataItemsWithFieldMaps';
|
||||
import { generateFakeFormResponse } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-response';
|
||||
import { FormFieldMetadata } from 'src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-action-settings.type';
|
||||
|
||||
@ -9,15 +9,6 @@ const companyMockObjectMetadataItem = mockObjectMetadataItemsWithFieldMaps.find(
|
||||
)!;
|
||||
|
||||
describe('generateFakeFormResponse', () => {
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
let objectMetadataRepository;
|
||||
|
||||
beforeEach(() => {
|
||||
objectMetadataRepository = {
|
||||
findOneOrFail: jest.fn().mockResolvedValue(companyMockObjectMetadataItem),
|
||||
};
|
||||
});
|
||||
|
||||
it('should generate fake responses for a form schema', async () => {
|
||||
const schema: FormFieldMetadata[] = [
|
||||
{
|
||||
@ -50,11 +41,19 @@ describe('generateFakeFormResponse', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const mockObjectMetadataMaps = {
|
||||
byId: {
|
||||
[companyMockObjectMetadataItem.id]: companyMockObjectMetadataItem,
|
||||
},
|
||||
idByNameSingular: {
|
||||
[companyMockObjectMetadataItem.nameSingular]:
|
||||
companyMockObjectMetadataItem.id,
|
||||
},
|
||||
};
|
||||
|
||||
const result = await generateFakeFormResponse({
|
||||
formMetadata: schema,
|
||||
workspaceId: '1',
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
objectMetadataRepository,
|
||||
objectMetadataMaps: mockObjectMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@ -82,7 +81,7 @@ describe('generateFakeFormResponse', () => {
|
||||
isLeaf: true,
|
||||
label: 'Company',
|
||||
fieldIdName: 'id',
|
||||
icon: undefined,
|
||||
icon: 'test-company-icon',
|
||||
nameSingular: 'company',
|
||||
value: 'A company',
|
||||
},
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { generateFakeObjectRecordEvent } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record-event';
|
||||
import { generateObjectRecordFields } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields';
|
||||
import { mockObjectMetadataItemsWithFieldMaps } from 'src/engine/core-modules/__mocks__/mockObjectMetadataItemsWithFieldMaps';
|
||||
|
||||
jest.mock(
|
||||
'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields',
|
||||
@ -12,35 +12,48 @@ describe('generateFakeObjectRecordEvent', () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
const mockObjectMetadata = {
|
||||
icon: 'test-icon',
|
||||
labelSingular: 'Test Object',
|
||||
description: 'Test Description',
|
||||
nameSingular: 'testObject',
|
||||
} as ObjectMetadataEntity;
|
||||
|
||||
const mockFields = {
|
||||
field1: { type: 'TEXT', value: 'test' },
|
||||
field2: { type: 'NUMBER', value: 123 },
|
||||
};
|
||||
|
||||
const companyMockObjectMetadataItem =
|
||||
mockObjectMetadataItemsWithFieldMaps.find(
|
||||
(item) => item.nameSingular === 'company',
|
||||
)!;
|
||||
|
||||
const mockObjectMetadataMaps = {
|
||||
byId: {
|
||||
[companyMockObjectMetadataItem.id]: companyMockObjectMetadataItem,
|
||||
},
|
||||
idByNameSingular: {
|
||||
[companyMockObjectMetadataItem.nameSingular]:
|
||||
companyMockObjectMetadataItem.id,
|
||||
},
|
||||
};
|
||||
|
||||
const objectMetadataInfo = {
|
||||
objectMetadataMaps: mockObjectMetadataMaps,
|
||||
objectMetadataItemWithFieldsMaps: companyMockObjectMetadataItem,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
(generateObjectRecordFields as jest.Mock).mockReturnValue(mockFields);
|
||||
});
|
||||
|
||||
it('should generate record with "after" prefix for CREATED action', () => {
|
||||
const result = generateFakeObjectRecordEvent(
|
||||
mockObjectMetadata,
|
||||
objectMetadataInfo,
|
||||
DatabaseEventAction.CREATED,
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
object: {
|
||||
isLeaf: true,
|
||||
icon: 'test-icon',
|
||||
label: 'Test Object',
|
||||
value: 'Test Description',
|
||||
nameSingular: 'testObject',
|
||||
icon: 'test-company-icon',
|
||||
label: 'Company',
|
||||
value: 'A company',
|
||||
nameSingular: 'company',
|
||||
fieldIdName: 'properties.after.id',
|
||||
},
|
||||
fields: {
|
||||
@ -53,17 +66,17 @@ describe('generateFakeObjectRecordEvent', () => {
|
||||
|
||||
it('should generate record with "after" prefix for UPDATED action', () => {
|
||||
const result = generateFakeObjectRecordEvent(
|
||||
mockObjectMetadata,
|
||||
objectMetadataInfo,
|
||||
DatabaseEventAction.UPDATED,
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
object: {
|
||||
isLeaf: true,
|
||||
icon: 'test-icon',
|
||||
label: 'Test Object',
|
||||
value: 'Test Description',
|
||||
nameSingular: 'testObject',
|
||||
icon: 'test-company-icon',
|
||||
label: 'Company',
|
||||
value: 'A company',
|
||||
nameSingular: 'company',
|
||||
fieldIdName: 'properties.after.id',
|
||||
},
|
||||
fields: {
|
||||
@ -76,17 +89,17 @@ describe('generateFakeObjectRecordEvent', () => {
|
||||
|
||||
it('should generate record with "before" prefix for DELETED action', () => {
|
||||
const result = generateFakeObjectRecordEvent(
|
||||
mockObjectMetadata,
|
||||
objectMetadataInfo,
|
||||
DatabaseEventAction.DELETED,
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
object: {
|
||||
isLeaf: true,
|
||||
icon: 'test-icon',
|
||||
label: 'Test Object',
|
||||
value: 'Test Description',
|
||||
nameSingular: 'testObject',
|
||||
icon: 'test-company-icon',
|
||||
label: 'Company',
|
||||
value: 'A company',
|
||||
nameSingular: 'company',
|
||||
fieldIdName: 'properties.before.id',
|
||||
},
|
||||
fields: {
|
||||
@ -99,17 +112,17 @@ describe('generateFakeObjectRecordEvent', () => {
|
||||
|
||||
it('should generate record with "before" prefix for DESTROYED action', () => {
|
||||
const result = generateFakeObjectRecordEvent(
|
||||
mockObjectMetadata,
|
||||
objectMetadataInfo,
|
||||
DatabaseEventAction.DESTROYED,
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
object: {
|
||||
isLeaf: true,
|
||||
icon: 'test-icon',
|
||||
label: 'Test Object',
|
||||
value: 'Test Description',
|
||||
nameSingular: 'testObject',
|
||||
icon: 'test-company-icon',
|
||||
label: 'Company',
|
||||
value: 'A company',
|
||||
nameSingular: 'company',
|
||||
fieldIdName: 'properties.before.id',
|
||||
},
|
||||
fields: {
|
||||
@ -123,7 +136,7 @@ describe('generateFakeObjectRecordEvent', () => {
|
||||
it('should throw error for unknown action', () => {
|
||||
expect(() => {
|
||||
generateFakeObjectRecordEvent(
|
||||
mockObjectMetadata,
|
||||
objectMetadataInfo,
|
||||
'UNKNOWN' as DatabaseEventAction,
|
||||
);
|
||||
}).toThrow("Unknown action 'UNKNOWN'");
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record';
|
||||
import { generateObjectRecordFields } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields';
|
||||
import { mockObjectMetadataItemsWithFieldMaps } from 'src/engine/core-modules/__mocks__/mockObjectMetadataItemsWithFieldMaps';
|
||||
|
||||
jest.mock(
|
||||
'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields',
|
||||
@ -12,24 +12,36 @@ jest.mock(
|
||||
}),
|
||||
);
|
||||
|
||||
const companyMockObjectMetadataItem = mockObjectMetadataItemsWithFieldMaps.find(
|
||||
(item) => item.nameSingular === 'company',
|
||||
)!;
|
||||
|
||||
const mockObjectMetadataMaps = {
|
||||
byId: {
|
||||
[companyMockObjectMetadataItem.id]: companyMockObjectMetadataItem,
|
||||
},
|
||||
idByNameSingular: {
|
||||
[companyMockObjectMetadataItem.nameSingular]:
|
||||
companyMockObjectMetadataItem.id,
|
||||
},
|
||||
};
|
||||
|
||||
const objectMetadataInfo = {
|
||||
objectMetadataMaps: mockObjectMetadataMaps,
|
||||
objectMetadataItemWithFieldsMaps: companyMockObjectMetadataItem,
|
||||
};
|
||||
|
||||
describe('generateFakeObjectRecord', () => {
|
||||
it('should generate a record with correct object metadata', () => {
|
||||
const mockObjectMetadata = {
|
||||
icon: 'test-icon',
|
||||
labelSingular: 'Test Object',
|
||||
description: 'Test Description',
|
||||
nameSingular: 'testObject',
|
||||
} as ObjectMetadataEntity;
|
||||
|
||||
const result = generateFakeObjectRecord(mockObjectMetadata);
|
||||
const result = generateFakeObjectRecord(objectMetadataInfo);
|
||||
|
||||
expect(result).toEqual({
|
||||
object: {
|
||||
isLeaf: true,
|
||||
icon: 'test-icon',
|
||||
label: 'Test Object',
|
||||
value: 'Test Description',
|
||||
nameSingular: 'testObject',
|
||||
icon: 'test-company-icon',
|
||||
label: 'Company',
|
||||
value: 'A company',
|
||||
nameSingular: 'company',
|
||||
fieldIdName: 'id',
|
||||
},
|
||||
fields: {
|
||||
@ -41,15 +53,10 @@ describe('generateFakeObjectRecord', () => {
|
||||
});
|
||||
|
||||
it('should call generateObjectRecordFields with the object metadata', () => {
|
||||
const mockObjectMetadata = {
|
||||
icon: 'test-icon',
|
||||
labelSingular: 'Test Object',
|
||||
description: 'Test Description',
|
||||
nameSingular: 'testObject',
|
||||
} as ObjectMetadataEntity;
|
||||
generateFakeObjectRecord(objectMetadataInfo);
|
||||
|
||||
generateFakeObjectRecord(mockObjectMetadata);
|
||||
|
||||
expect(generateObjectRecordFields).toHaveBeenCalledWith(mockObjectMetadata);
|
||||
expect(generateObjectRecordFields).toHaveBeenCalledWith({
|
||||
objectMetadataInfo,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { generateFakeField } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field';
|
||||
import { generateObjectRecordFields } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields';
|
||||
import { shouldGenerateFieldFakeValue } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/should-generate-field-fake-value';
|
||||
import { mockObjectMetadataItemsWithFieldMaps } from 'src/engine/core-modules/__mocks__/mockObjectMetadataItemsWithFieldMaps';
|
||||
|
||||
jest.mock(
|
||||
'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field',
|
||||
@ -17,38 +17,27 @@ describe('generateObjectRecordFields', () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
const companyMockObjectMetadataItem =
|
||||
mockObjectMetadataItemsWithFieldMaps.find(
|
||||
(item) => item.nameSingular === 'company',
|
||||
)!;
|
||||
|
||||
const mockObjectMetadataMaps = {
|
||||
byId: {
|
||||
[companyMockObjectMetadataItem.id]: companyMockObjectMetadataItem,
|
||||
},
|
||||
idByNameSingular: {
|
||||
[companyMockObjectMetadataItem.nameSingular]:
|
||||
companyMockObjectMetadataItem.id,
|
||||
},
|
||||
};
|
||||
|
||||
const objectMetadataInfo = {
|
||||
objectMetadataMaps: mockObjectMetadataMaps,
|
||||
objectMetadataItemWithFieldsMaps: companyMockObjectMetadataItem,
|
||||
};
|
||||
|
||||
it('should generate fields for valid fields only', () => {
|
||||
const mockFields = [
|
||||
{
|
||||
name: 'field1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
label: 'Field 1',
|
||||
icon: 'icon1',
|
||||
isSystem: false,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'field2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
label: 'Field 2',
|
||||
icon: 'icon2',
|
||||
isSystem: false,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'field3',
|
||||
type: FieldMetadataType.NUMBER,
|
||||
label: 'Field 3',
|
||||
icon: 'icon3',
|
||||
isSystem: false,
|
||||
isActive: true,
|
||||
},
|
||||
];
|
||||
|
||||
const mockObjectMetadata = {
|
||||
fields: mockFields,
|
||||
} as ObjectMetadataEntity;
|
||||
|
||||
(shouldGenerateFieldFakeValue as jest.Mock).mockImplementation(
|
||||
(field) => field.type !== FieldMetadataType.RELATION,
|
||||
);
|
||||
@ -62,49 +51,34 @@ describe('generateObjectRecordFields', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const result = generateObjectRecordFields(mockObjectMetadata);
|
||||
const result = generateObjectRecordFields({ objectMetadataInfo });
|
||||
|
||||
expect(result).toEqual({
|
||||
field1: {
|
||||
type: FieldMetadataType.TEXT,
|
||||
label: 'Field 1',
|
||||
icon: 'icon1',
|
||||
value: 'mock-TEXT',
|
||||
domainName: {
|
||||
icon: 'test-field-icon',
|
||||
label: 'Domain Name',
|
||||
type: 'LINKS',
|
||||
value: 'mock-LINKS',
|
||||
},
|
||||
field3: {
|
||||
type: FieldMetadataType.NUMBER,
|
||||
label: 'Field 3',
|
||||
icon: 'icon3',
|
||||
value: 'mock-NUMBER',
|
||||
name: {
|
||||
icon: 'test-field-icon',
|
||||
label: 'Name',
|
||||
type: 'TEXT',
|
||||
value: 'mock-TEXT',
|
||||
},
|
||||
});
|
||||
|
||||
expect(shouldGenerateFieldFakeValue).toHaveBeenCalledTimes(3);
|
||||
expect(shouldGenerateFieldFakeValue).toHaveBeenCalledTimes(2);
|
||||
expect(generateFakeField).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should return empty object when no valid fields', () => {
|
||||
const mockFields = [
|
||||
{
|
||||
name: 'field1',
|
||||
type: FieldMetadataType.RELATION,
|
||||
label: 'Field 1',
|
||||
icon: 'icon1',
|
||||
isSystem: false,
|
||||
isActive: true,
|
||||
},
|
||||
];
|
||||
|
||||
const mockObjectMetadata = {
|
||||
fields: mockFields,
|
||||
} as ObjectMetadataEntity;
|
||||
|
||||
(shouldGenerateFieldFakeValue as jest.Mock).mockReturnValue(false);
|
||||
|
||||
const result = generateObjectRecordFields(mockObjectMetadata);
|
||||
const result = generateObjectRecordFields({ objectMetadataInfo });
|
||||
|
||||
expect(result).toEqual({});
|
||||
expect(shouldGenerateFieldFakeValue).toHaveBeenCalledTimes(1);
|
||||
expect(shouldGenerateFieldFakeValue).toHaveBeenCalledTimes(2);
|
||||
expect(generateFakeField).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@ -19,15 +19,7 @@ export const generateFakeField = ({
|
||||
}): Leaf | Node => {
|
||||
const compositeType = compositeTypeDefinitions.get(type);
|
||||
|
||||
if (!compositeType) {
|
||||
return {
|
||||
isLeaf: true,
|
||||
type: type,
|
||||
icon: icon,
|
||||
label: label,
|
||||
value: generateFakeValue(type, 'FieldMetadataType'),
|
||||
};
|
||||
} else {
|
||||
if (compositeType) {
|
||||
return {
|
||||
isLeaf: false,
|
||||
icon: icon,
|
||||
@ -45,4 +37,12 @@ export const generateFakeField = ({
|
||||
}, {}),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
isLeaf: true,
|
||||
type: type,
|
||||
icon: icon,
|
||||
label: label,
|
||||
value: generateFakeValue(type, 'FieldMetadataType'),
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import {
|
||||
Leaf,
|
||||
Node,
|
||||
@ -9,15 +7,15 @@ import {
|
||||
import { generateFakeField } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field';
|
||||
import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record';
|
||||
import { FormFieldMetadata } from 'src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-action-settings.type';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||
|
||||
export const generateFakeFormResponse = async ({
|
||||
formMetadata,
|
||||
workspaceId,
|
||||
objectMetadataRepository,
|
||||
objectMetadataMaps,
|
||||
}: {
|
||||
formMetadata: FormFieldMetadata[];
|
||||
workspaceId: string;
|
||||
objectMetadataRepository: Repository<ObjectMetadataEntity>;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
}): Promise<Record<string, Leaf | Node>> => {
|
||||
const result = await Promise.all(
|
||||
formMetadata.map(async (formFieldMetadata) => {
|
||||
@ -26,19 +24,25 @@ export const generateFakeFormResponse = async ({
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const objectMetadata = await objectMetadataRepository.findOneOrFail({
|
||||
where: {
|
||||
nameSingular: formFieldMetadata?.settings?.objectName,
|
||||
workspaceId,
|
||||
},
|
||||
relations: ['fields'],
|
||||
});
|
||||
const objectMetadataItemWithFieldsMaps =
|
||||
getObjectMetadataMapItemByNameSingular(
|
||||
objectMetadataMaps,
|
||||
formFieldMetadata?.settings?.objectName,
|
||||
);
|
||||
|
||||
if (!objectMetadataItemWithFieldsMaps)
|
||||
throw new Error(
|
||||
`Object metadata not found for object name ${formFieldMetadata?.settings?.objectName}`,
|
||||
);
|
||||
|
||||
return {
|
||||
[formFieldMetadata.name]: {
|
||||
isLeaf: false,
|
||||
label: formFieldMetadata.label,
|
||||
value: generateFakeObjectRecord(objectMetadata),
|
||||
value: generateFakeObjectRecord({
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldsMaps,
|
||||
}),
|
||||
},
|
||||
};
|
||||
} else {
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import {
|
||||
BaseOutputSchema,
|
||||
RecordOutputSchema,
|
||||
} from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type';
|
||||
import { ObjectMetadataInfo } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||
import { generateObjectRecordFields } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields';
|
||||
|
||||
const generateFakeObjectRecordEventWithPrefix = ({
|
||||
objectMetadataEntity,
|
||||
objectMetadataInfo,
|
||||
prefix,
|
||||
}: {
|
||||
objectMetadataEntity: ObjectMetadataEntity;
|
||||
objectMetadataInfo: ObjectMetadataInfo;
|
||||
prefix: string;
|
||||
}): RecordOutputSchema => {
|
||||
const recordFields = generateObjectRecordFields(objectMetadataEntity);
|
||||
const recordFields = generateObjectRecordFields({ objectMetadataInfo });
|
||||
const prefixedRecordFields = Object.entries(recordFields).reduce(
|
||||
(acc, [key, value]) => {
|
||||
acc[`${prefix}.${key}`] = value;
|
||||
@ -26,10 +26,11 @@ const generateFakeObjectRecordEventWithPrefix = ({
|
||||
return {
|
||||
object: {
|
||||
isLeaf: true,
|
||||
icon: objectMetadataEntity.icon,
|
||||
label: objectMetadataEntity.labelSingular,
|
||||
value: objectMetadataEntity.description,
|
||||
nameSingular: objectMetadataEntity.nameSingular,
|
||||
icon: objectMetadataInfo.objectMetadataItemWithFieldsMaps.icon,
|
||||
label: objectMetadataInfo.objectMetadataItemWithFieldsMaps.labelSingular,
|
||||
value: objectMetadataInfo.objectMetadataItemWithFieldsMaps.description,
|
||||
nameSingular:
|
||||
objectMetadataInfo.objectMetadataItemWithFieldsMaps.nameSingular,
|
||||
fieldIdName: `${prefix}.id`,
|
||||
},
|
||||
fields: prefixedRecordFields,
|
||||
@ -38,20 +39,20 @@ const generateFakeObjectRecordEventWithPrefix = ({
|
||||
};
|
||||
|
||||
export const generateFakeObjectRecordEvent = (
|
||||
objectMetadataEntity: ObjectMetadataEntity,
|
||||
objectMetadataInfo: ObjectMetadataInfo,
|
||||
action: DatabaseEventAction,
|
||||
): RecordOutputSchema => {
|
||||
switch (action) {
|
||||
case DatabaseEventAction.CREATED:
|
||||
case DatabaseEventAction.UPDATED:
|
||||
return generateFakeObjectRecordEventWithPrefix({
|
||||
objectMetadataEntity,
|
||||
objectMetadataInfo,
|
||||
prefix: 'properties.after',
|
||||
});
|
||||
case DatabaseEventAction.DELETED:
|
||||
case DatabaseEventAction.DESTROYED:
|
||||
return generateFakeObjectRecordEventWithPrefix({
|
||||
objectMetadataEntity,
|
||||
objectMetadataInfo,
|
||||
prefix: 'properties.before',
|
||||
});
|
||||
default:
|
||||
|
||||
@ -1,18 +1,21 @@
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { RecordOutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type';
|
||||
import { generateObjectRecordFields } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields';
|
||||
import { ObjectMetadataInfo } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||
|
||||
export const generateFakeObjectRecord = (
|
||||
objectMetadataEntity: ObjectMetadataEntity,
|
||||
): RecordOutputSchema => ({
|
||||
object: {
|
||||
isLeaf: true,
|
||||
icon: objectMetadataEntity.icon,
|
||||
label: objectMetadataEntity.labelSingular,
|
||||
value: objectMetadataEntity.description,
|
||||
nameSingular: objectMetadataEntity.nameSingular,
|
||||
fieldIdName: 'id',
|
||||
},
|
||||
fields: generateObjectRecordFields(objectMetadataEntity),
|
||||
_outputSchemaType: 'RECORD',
|
||||
});
|
||||
objectMetadataInfo: ObjectMetadataInfo,
|
||||
): RecordOutputSchema => {
|
||||
return {
|
||||
object: {
|
||||
isLeaf: true,
|
||||
icon: objectMetadataInfo.objectMetadataItemWithFieldsMaps.icon,
|
||||
label: objectMetadataInfo.objectMetadataItemWithFieldsMaps.labelSingular,
|
||||
value: objectMetadataInfo.objectMetadataItemWithFieldsMaps.description,
|
||||
nameSingular:
|
||||
objectMetadataInfo.objectMetadataItemWithFieldsMaps.nameSingular,
|
||||
fieldIdName: 'id',
|
||||
},
|
||||
fields: generateObjectRecordFields({ objectMetadataInfo }),
|
||||
_outputSchemaType: 'RECORD',
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,21 +1,60 @@
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { BaseOutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type';
|
||||
import { generateFakeField } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field';
|
||||
import { shouldGenerateFieldFakeValue } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/should-generate-field-fake-value';
|
||||
import { ObjectMetadataInfo } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||
|
||||
export const generateObjectRecordFields = (
|
||||
objectMetadataEntity: ObjectMetadataEntity,
|
||||
): BaseOutputSchema =>
|
||||
objectMetadataEntity.fields.reduce((acc: BaseOutputSchema, field) => {
|
||||
const MAXIMUM_DEPTH = 1;
|
||||
|
||||
export const generateObjectRecordFields = ({
|
||||
objectMetadataInfo,
|
||||
depth = 0,
|
||||
}: {
|
||||
objectMetadataInfo: ObjectMetadataInfo;
|
||||
depth?: number;
|
||||
}): BaseOutputSchema => {
|
||||
const objectMetadata = objectMetadataInfo.objectMetadataItemWithFieldsMaps;
|
||||
|
||||
return objectMetadata.fields.reduce((acc: BaseOutputSchema, field) => {
|
||||
if (!shouldGenerateFieldFakeValue(field)) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
acc[field.name] = generateFakeField({
|
||||
type: field.type,
|
||||
label: field.label,
|
||||
icon: field.icon,
|
||||
});
|
||||
if (field.type !== FieldMetadataType.RELATION) {
|
||||
acc[field.name] = generateFakeField({
|
||||
type: field.type,
|
||||
label: field.label,
|
||||
icon: field.icon,
|
||||
});
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (
|
||||
depth < MAXIMUM_DEPTH &&
|
||||
isDefined(field.relationTargetObjectMetadataId)
|
||||
) {
|
||||
const relationTargetObjectMetadata =
|
||||
objectMetadataInfo.objectMetadataMaps.byId[
|
||||
field.relationTargetObjectMetadataId
|
||||
];
|
||||
|
||||
acc[field.name] = {
|
||||
isLeaf: false,
|
||||
icon: field.icon,
|
||||
label: field.label,
|
||||
value: generateObjectRecordFields({
|
||||
objectMetadataInfo: {
|
||||
objectMetadataItemWithFieldsMaps: relationTargetObjectMetadata,
|
||||
objectMetadataMaps: objectMetadataInfo.objectMetadataMaps,
|
||||
},
|
||||
depth: depth + 1,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {} as BaseOutputSchema);
|
||||
};
|
||||
|
||||
@ -1,11 +1,18 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
|
||||
export const shouldGenerateFieldFakeValue = (field: FieldMetadataEntity) => {
|
||||
const isManyToOneRelationField = (field: FieldMetadataInterface) =>
|
||||
(field as FieldMetadataEntity<FieldMetadataType.RELATION>).settings
|
||||
?.relationType === 'MANY_TO_ONE';
|
||||
|
||||
export const shouldGenerateFieldFakeValue = (field: FieldMetadataInterface) => {
|
||||
return (
|
||||
(!field.isSystem || field.name === 'id') &&
|
||||
field.isActive &&
|
||||
field.type !== FieldMetadataType.RELATION
|
||||
(!field.isSystem || field.name === 'id' || field.name === 'userEmail') &&
|
||||
(field.type !== FieldMetadataType.RELATION ||
|
||||
isManyToOneRelationField(field))
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { WorkflowSchemaWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service';
|
||||
import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata')],
|
||||
imports: [WorkflowCommonModule],
|
||||
providers: [WorkflowSchemaWorkspaceService],
|
||||
exports: [WorkflowSchemaWorkspaceService],
|
||||
})
|
||||
|
||||
@ -1,12 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { checkStringIsDatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/utils/check-string-is-database-event-action';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { generateFakeValue } from 'src/engine/utils/generate-fake-value';
|
||||
import { OutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type';
|
||||
import { generateFakeFormResponse } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-response';
|
||||
@ -21,12 +16,12 @@ import {
|
||||
WorkflowTrigger,
|
||||
WorkflowTriggerType,
|
||||
} from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
|
||||
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||
|
||||
@Injectable()
|
||||
export class WorkflowSchemaWorkspaceService {
|
||||
constructor(
|
||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService,
|
||||
) {}
|
||||
|
||||
async computeStepOutputSchema({
|
||||
@ -43,7 +38,6 @@ export class WorkflowSchemaWorkspaceService {
|
||||
return this.computeDatabaseEventTriggerOutputSchema({
|
||||
eventName: step.settings.eventName,
|
||||
workspaceId,
|
||||
objectMetadataRepository: this.objectMetadataRepository,
|
||||
});
|
||||
}
|
||||
case WorkflowTriggerType.MANUAL: {
|
||||
@ -56,7 +50,6 @@ export class WorkflowSchemaWorkspaceService {
|
||||
return this.computeRecordOutputSchema({
|
||||
objectType,
|
||||
workspaceId,
|
||||
objectMetadataRepository: this.objectMetadataRepository,
|
||||
});
|
||||
}
|
||||
case WorkflowTriggerType.WEBHOOK:
|
||||
@ -72,19 +65,16 @@ export class WorkflowSchemaWorkspaceService {
|
||||
return this.computeRecordOutputSchema({
|
||||
objectType: step.settings.input.objectName,
|
||||
workspaceId,
|
||||
objectMetadataRepository: this.objectMetadataRepository,
|
||||
});
|
||||
case WorkflowActionType.FIND_RECORDS:
|
||||
return this.computeFindRecordsOutputSchema({
|
||||
objectType: step.settings.input.objectName,
|
||||
workspaceId,
|
||||
objectMetadataRepository: this.objectMetadataRepository,
|
||||
});
|
||||
case WorkflowActionType.FORM:
|
||||
return this.computeFormActionOutputSchema({
|
||||
formMetadata: step.settings.input,
|
||||
workspaceId,
|
||||
objectMetadataRepository: this.objectMetadataRepository,
|
||||
});
|
||||
case WorkflowActionType.CODE: // StepOutput schema is computed on serverlessFunction draft execution
|
||||
default:
|
||||
@ -95,11 +85,9 @@ export class WorkflowSchemaWorkspaceService {
|
||||
private async computeDatabaseEventTriggerOutputSchema({
|
||||
eventName,
|
||||
workspaceId,
|
||||
objectMetadataRepository,
|
||||
}: {
|
||||
eventName: string;
|
||||
workspaceId: string;
|
||||
objectMetadataRepository: Repository<ObjectMetadataEntity>;
|
||||
}): Promise<OutputSchema> {
|
||||
const [nameSingular, action] = eventName.split('.');
|
||||
|
||||
@ -107,20 +95,14 @@ export class WorkflowSchemaWorkspaceService {
|
||||
return {};
|
||||
}
|
||||
|
||||
const objectMetadata = await objectMetadataRepository.findOneOrFail({
|
||||
where: {
|
||||
const objectMetadataInfo =
|
||||
await this.workflowCommonWorkspaceService.getObjectMetadataItemWithFieldsMaps(
|
||||
nameSingular,
|
||||
workspaceId,
|
||||
},
|
||||
relations: ['fields'],
|
||||
});
|
||||
|
||||
if (!isDefined(objectMetadata)) {
|
||||
return {};
|
||||
}
|
||||
);
|
||||
|
||||
return generateFakeObjectRecordEvent(
|
||||
objectMetadata,
|
||||
objectMetadataInfo,
|
||||
action as DatabaseEventAction,
|
||||
);
|
||||
}
|
||||
@ -128,16 +110,13 @@ export class WorkflowSchemaWorkspaceService {
|
||||
private async computeFindRecordsOutputSchema({
|
||||
objectType,
|
||||
workspaceId,
|
||||
objectMetadataRepository,
|
||||
}: {
|
||||
objectType: string;
|
||||
workspaceId: string;
|
||||
objectMetadataRepository: Repository<ObjectMetadataEntity>;
|
||||
}): Promise<OutputSchema> {
|
||||
const recordOutputSchema = await this.computeRecordOutputSchema({
|
||||
objectType,
|
||||
workspaceId,
|
||||
objectMetadataRepository,
|
||||
});
|
||||
|
||||
return {
|
||||
@ -159,25 +138,17 @@ export class WorkflowSchemaWorkspaceService {
|
||||
private async computeRecordOutputSchema({
|
||||
objectType,
|
||||
workspaceId,
|
||||
objectMetadataRepository,
|
||||
}: {
|
||||
objectType: string;
|
||||
workspaceId: string;
|
||||
objectMetadataRepository: Repository<ObjectMetadataEntity>;
|
||||
}): Promise<OutputSchema> {
|
||||
const objectMetadata = await objectMetadataRepository.findOneOrFail({
|
||||
where: {
|
||||
nameSingular: objectType,
|
||||
const objectMetadataInfo =
|
||||
await this.workflowCommonWorkspaceService.getObjectMetadataItemWithFieldsMaps(
|
||||
objectType,
|
||||
workspaceId,
|
||||
},
|
||||
relations: ['fields'],
|
||||
});
|
||||
);
|
||||
|
||||
if (!isDefined(objectMetadata)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return generateFakeObjectRecord(objectMetadata);
|
||||
return generateFakeObjectRecord(objectMetadataInfo);
|
||||
}
|
||||
|
||||
private computeSendEmailActionOutputSchema(): OutputSchema {
|
||||
@ -187,16 +158,18 @@ export class WorkflowSchemaWorkspaceService {
|
||||
private async computeFormActionOutputSchema({
|
||||
formMetadata,
|
||||
workspaceId,
|
||||
objectMetadataRepository,
|
||||
}: {
|
||||
formMetadata: FormFieldMetadata[];
|
||||
workspaceId: string;
|
||||
objectMetadataRepository: Repository<ObjectMetadataEntity>;
|
||||
}): Promise<OutputSchema> {
|
||||
const objectMetadataMaps =
|
||||
await this.workflowCommonWorkspaceService.getObjectMetadataMaps(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return generateFakeFormResponse({
|
||||
formMetadata,
|
||||
workspaceId,
|
||||
objectMetadataRepository,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user