diff --git a/packages/twenty-front/src/modules/activities/inline-cell/hooks/useOpenActivityTargetInlineCellEditMode.ts b/packages/twenty-front/src/modules/activities/inline-cell/hooks/useOpenActivityTargetInlineCellEditMode.ts index 1146d77cc..729b201a2 100644 --- a/packages/twenty-front/src/modules/activities/inline-cell/hooks/useOpenActivityTargetInlineCellEditMode.ts +++ b/packages/twenty-front/src/modules/activities/inline-cell/hooks/useOpenActivityTargetInlineCellEditMode.ts @@ -28,6 +28,7 @@ export const useOpenActivityTargetInlineCellEditMode = () => { .filter( (objectMetadataItem) => objectMetadataItem.isSearchable && + objectMetadataItem.isActive && objectMetadataItem.nameSingular !== CoreObjectNameSingular.Task && objectMetadataItem.nameSingular !== CoreObjectNameSingular.Note && objectMetadataItem.nameSingular !== diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.exception.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.exception.ts index 15fd4eb1d..f0ac60c8c 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.exception.ts @@ -15,4 +15,5 @@ export enum FieldMetadataExceptionCode { INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR', FIELD_METADATA_RELATION_NOT_ENABLED = 'FIELD_METADATA_RELATION_NOT_ENABLED', FIELD_METADATA_RELATION_MALFORMED = 'FIELD_METADATA_RELATION_MALFORMED', + LABEL_IDENTIFIER_FIELD_METADATA_ID_NOT_FOUND = 'LABEL_IDENTIFIER_FIELD_METADATA_ID_NOT_FOUND', } diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.service.ts index 509d76b33..43082a238 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.service.ts @@ -30,6 +30,7 @@ import { } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception'; import { FieldMetadataRelatedRecordsService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata-related-records.service'; import { assertDoesNotNullifyDefaultValueForNonNullableField } from 'src/engine/metadata-modules/field-metadata/utils/assert-does-not-nullify-default-value-for-non-nullable-field.util'; +import { checkCanDeactivateFieldOrThrow } from 'src/engine/metadata-modules/field-metadata/utils/check-can-deactivate-field-or-throw'; import { computeColumnName, computeCompositeColumnName, @@ -154,6 +155,12 @@ export class FieldMetadataService extends TypeOrmQueryService[0]; + shouldNotThrow?: true; +}>; + +const checkCanDeactivateFieldOrThrowTestCases: CheckCanDeactivateFieldOrThrowTestContext[] = + [ + { + title: 'does not throw if nominal case', + context: { + input: { + labelIdentifierFieldMetadataId: 'fieldIdentifierId', + existingFieldMetadata: { + id: 'myFieldId', + isSystem: false, + name: 'myFieldName', + }, + }, + shouldNotThrow: true, + }, + }, + { + title: 'throws if trying to deactivate label identifier field', + context: { + input: { + labelIdentifierFieldMetadataId: 'fieldId', + existingFieldMetadata: { + id: 'fieldId', + isSystem: false, + name: 'name', + }, + }, + }, + }, + { + title: 'throws if trying to deactivate system field', + context: { + input: { + labelIdentifierFieldMetadataId: 'fieldIdentifierId', + existingFieldMetadata: { + id: 'systemFieldId', + isSystem: true, + name: 'systemField', + }, + }, + }, + }, + { + title: 'throws if trying to deactivate createdAt field', + context: { + input: { + labelIdentifierFieldMetadataId: 'fieldIdentifierId', + existingFieldMetadata: { + id: 'createdAtId', + isSystem: false, + name: 'createdAt', + }, + }, + }, + }, + { + title: 'throws if trying to deactivate updatedAt field', + context: { + input: { + labelIdentifierFieldMetadataId: 'fieldIdentifierId', + existingFieldMetadata: { + id: 'updatedAtId', + isSystem: false, + name: 'updatedAt', + }, + }, + }, + }, + { + title: 'throws if trying to deactivate deletedAt field', + context: { + input: { + labelIdentifierFieldMetadataId: 'fieldIdentifierId', + existingFieldMetadata: { + id: 'deletedAtId', + isSystem: false, + name: 'deletedAt', + }, + }, + }, + }, + ]; + +describe('checkCanDeactivateFieldOrThrow', () => { + it.each(checkCanDeactivateFieldOrThrowTestCases)( + '$title', + ({ context: { input, shouldNotThrow } }) => { + if (shouldNotThrow) { + expect(() => checkCanDeactivateFieldOrThrow(input)).not.toThrow(); + } else { + expect(() => + checkCanDeactivateFieldOrThrow(input), + ).toThrowErrorMatchingSnapshot(); + } + }, + ); +}); diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/check-can-deactivate-field-or-throw.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/check-can-deactivate-field-or-throw.ts new file mode 100644 index 000000000..620d4e57e --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/check-can-deactivate-field-or-throw.ts @@ -0,0 +1,42 @@ +import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; + +import { + FieldMetadataException, + FieldMetadataExceptionCode, +} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception'; + +type CheckCanDeactivateFieldOptions = { + labelIdentifierFieldMetadataId: string; + existingFieldMetadata: Pick< + FieldMetadataInterface, + 'id' | 'isSystem' | 'name' + >; +}; + +export const checkCanDeactivateFieldOrThrow = ({ + labelIdentifierFieldMetadataId, + existingFieldMetadata, +}: CheckCanDeactivateFieldOptions) => { + if (existingFieldMetadata.id === labelIdentifierFieldMetadataId) { + throw new FieldMetadataException( + 'Cannot deactivate label identifier field', + FieldMetadataExceptionCode.INVALID_FIELD_INPUT, + ); + } + + if (existingFieldMetadata.isSystem === true) { + throw new FieldMetadataException( + 'Cannot deactivate system field', + FieldMetadataExceptionCode.INVALID_FIELD_INPUT, + ); + } + + if ( + ['deletedAt', 'createdAt', 'updatedAt'].includes(existingFieldMetadata.name) + ) { + throw new FieldMetadataException( + 'Cannot deactivate createdAt, updatedAt or deletedAt field', + FieldMetadataExceptionCode.INVALID_FIELD_INPUT, + ); + } +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/field-metadata-graphql-api-exception-handler.util.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/field-metadata-graphql-api-exception-handler.util.ts index e1874ebb4..540d0cc62 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/field-metadata-graphql-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/field-metadata-graphql-api-exception-handler.util.ts @@ -30,6 +30,7 @@ export const fieldMetadataGraphqlApiExceptionHandler = (error: Error) => { case FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR: case FieldMetadataExceptionCode.FIELD_METADATA_RELATION_NOT_ENABLED: case FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED: + case FieldMetadataExceptionCode.LABEL_IDENTIFIER_FIELD_METADATA_ID_NOT_FOUND: default: throw new InternalServerError(error.message); } diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/__tests__/validate-object-metadata-input.util.spec.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/__tests__/validate-object-metadata-input.util.spec.ts index a9bdf1b96..f9d53d97f 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/__tests__/validate-object-metadata-input.util.spec.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/__tests__/validate-object-metadata-input.util.spec.ts @@ -1,4 +1,4 @@ -import { getMockCreateObjectInput } from 'test/integration/utils/object-metadata/generate-mock-create-object-metadata-input'; +import { getMockCreateObjectInput } from 'test/integration/metadata/suites/object-metadata/utils/generate-mock-create-object-metadata-input'; import { EachTestingContext } from 'twenty-shared/testing'; import { UpdateObjectPayload } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input'; diff --git a/packages/twenty-server/test/integration/graphql/suites/search/search-resolver.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/search/search-resolver.integration-spec.ts index e3fa26a52..456980518 100644 --- a/packages/twenty-server/test/integration/graphql/suites/search/search-resolver.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/search/search-resolver.integration-spec.ts @@ -14,9 +14,9 @@ import { LISTING_NAME_PLURAL, LISTING_NAME_SINGULAR, } from 'test/integration/metadata/suites/object-metadata/constants/test-object-names.constant'; -import { createListingCustomObject } from 'test/integration/metadata/suites/object-metadata/utils/create-test-object-metadata.util'; -import { deleteOneObjectMetadataItem } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; -import { findManyObjectsMetadataItems } from 'test/integration/metadata/suites/object-metadata/utils/find-many-objects-metadata-items.util'; +import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util'; +import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; +import { findManyObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata.util'; import { EachTestingContext } from 'twenty-shared/testing'; import { SearchRecordDTO } from 'src/engine/core-modules/search/dtos/search-record-dto'; @@ -46,7 +46,14 @@ describe('SearchResolver', () => { beforeAll(async () => { try { - const objectsMetadata = await findManyObjectsMetadataItems(); + const objectsMetadata = await findManyObjectMetadata({ + input: { + filter: {}, + paging: { + first: 1000, + }, + }, + }); const listingObjectMetadata = objectsMetadata.find( (object) => object.nameSingular === LISTING_NAME_SINGULAR, ); @@ -56,7 +63,19 @@ describe('SearchResolver', () => { objectMetadataId: listingObjectMetadata.id, }; } else { - listingObjectMetadataId = await createListingCustomObject(); + const { data } = await createOneObjectMetadata({ + input: { + labelSingular: LISTING_NAME_SINGULAR, + labelPlural: LISTING_NAME_PLURAL, + nameSingular: LISTING_NAME_SINGULAR, + namePlural: LISTING_NAME_PLURAL, + icon: 'IconBuildingSkyscraper', + }, + }); + + listingObjectMetadataId = { + objectMetadataId: data.createOneObject.id, + }; } await performCreateManyOperation( @@ -102,9 +121,9 @@ describe('SearchResolver', () => { console.log(error); }); - await deleteOneObjectMetadataItem( - listingObjectMetadataId.objectMetadataId, - ).catch((error) => { + await deleteOneObjectMetadata({ + input: { idToDelete: listingObjectMetadataId.objectMetadataId }, + }).catch((error) => { // eslint-disable-next-line no-console console.log(error); }); diff --git a/packages/twenty-server/test/integration/graphql/suites/settings-permissions/data-model.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/settings-permissions/data-model.integration-spec.ts index fc500ccca..7d7ee7c05 100644 --- a/packages/twenty-server/test/integration/graphql/suites/settings-permissions/data-model.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/settings-permissions/data-model.integration-spec.ts @@ -1,12 +1,12 @@ -import { createCustomTextFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-custom-text-field-metadata.util'; -import { createOneFieldMetadataFactory } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata-factory.util'; -import { deleteOneFieldMetadataItemFactory } from 'test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata-factory.util'; -import { updateOneFieldMetadataFactory } from 'test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata-factory.util'; -import { createOneObjectMetadataFactory } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata-factory.util'; -import { createListingCustomObject } from 'test/integration/metadata/suites/object-metadata/utils/create-test-object-metadata.util'; -import { deleteOneObjectMetadataItemFactory } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata-factory.util'; -import { deleteOneObjectMetadataItem } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; -import { updateOneObjectMetadataItemFactory } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata-factory.util'; +import { createOneFieldMetadataQueryFactory } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata-query-factory.util'; +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadataQueryFactory } from 'test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata-query-factory.util'; +import { updateOneFieldMetadataQueryFactory } from 'test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata-query-factory.util'; +import { createOneObjectMetadataQueryFactory } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata-query-factory.util'; +import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util'; +import { deleteOneObjectMetadataQueryFactory } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata-query-factory.util'; +import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; +import { updateOneObjectMetadataQueryFactory } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata-query-factory.util'; import { makeMetadataAPIRequestWithMemberRole } from 'test/integration/metadata/suites/utils/make-metadata-api-request-with-member-role.util'; import { FieldMetadataType } from 'twenty-shared/types'; @@ -21,18 +21,33 @@ describe('datamodel permissions', () => { let testFieldId = ''; beforeAll(async () => { - const { objectMetadataId: createdObjectId } = - await createListingCustomObject(); + const { data } = await createOneObjectMetadata({ + input: { + nameSingular: 'listing', + namePlural: 'listings', + labelSingular: 'Listing', + labelPlural: 'Listings', + icon: 'IconBuildingSkyscraper', + }, + }); - listingObjectId = createdObjectId; + listingObjectId = data.createOneObject.id; - const { fieldMetadataId: createdFieldMetadaId } = - await createCustomTextFieldMetadata(createdObjectId); + const { data: createdFieldData } = await createOneFieldMetadata({ + input: { + name: 'house', + type: FieldMetadataType.TEXT, + label: 'House', + objectMetadataId: listingObjectId, + }, + }); - testFieldId = createdFieldMetadaId; + testFieldId = createdFieldData.createOneField.id; }); afterAll(async () => { - await deleteOneObjectMetadataItem(listingObjectId); + await deleteOneObjectMetadata({ + input: { idToDelete: listingObjectId }, + }); }); describe('createOne', () => { it('should throw a permission error when user does not have permission (member role)', async () => { @@ -46,8 +61,8 @@ describe('datamodel permissions', () => { }; // Act - const graphqlOperation = createOneFieldMetadataFactory({ - input: { field: createFieldInput }, + const graphqlOperation = createOneFieldMetadataQueryFactory({ + input: createFieldInput, gqlFields: ` id name @@ -77,8 +92,8 @@ describe('datamodel permissions', () => { label: 'Updated Name', }; - const graphqlOperation = updateOneFieldMetadataFactory({ - input: { id: testFieldId, update: updateFieldInput }, + const graphqlOperation = updateOneFieldMetadataQueryFactory({ + input: { idToUpdate: testFieldId, updatePayload: updateFieldInput }, gqlFields: ` id name @@ -103,8 +118,8 @@ describe('datamodel permissions', () => { describe('deleteOne', () => { it('should throw a permission error when user does not have permission (member role)', async () => { // Arrange - const graphqlOperation = deleteOneFieldMetadataItemFactory({ - idToDelete: testFieldId, + const graphqlOperation = deleteOneFieldMetadataQueryFactory({ + input: { idToDelete: testFieldId }, }); const response = @@ -127,17 +142,15 @@ describe('datamodel permissions', () => { describe('createOne', () => { it('should throw a permission error when user does not have permission (member role)', async () => { // Arrange - const graphqlOperation = createOneObjectMetadataFactory({ + const graphqlOperation = createOneObjectMetadataQueryFactory({ gqlFields: ` id `, input: { - object: { - labelPlural: 'Test Objects', - labelSingular: 'Test Object', - namePlural: 'testObjects', - nameSingular: 'testObject', - }, + labelPlural: 'Test Objects', + labelSingular: 'Test Object', + namePlural: 'testObjects', + nameSingular: 'testObject', }, }); @@ -160,18 +173,27 @@ describe('datamodel permissions', () => { let listingObjectId = ''; beforeAll(async () => { - const { objectMetadataId: createdObjectId } = - await createListingCustomObject(); + const { data } = await createOneObjectMetadata({ + input: { + labelPlural: 'Listings', + labelSingular: 'Listing', + namePlural: 'listings', + nameSingular: 'listing', + icon: 'IconBuildingSkyscraper', + }, + }); - listingObjectId = createdObjectId; + listingObjectId = data.createOneObject.id; }); afterAll(async () => { - await deleteOneObjectMetadataItem(listingObjectId); + await deleteOneObjectMetadata({ + input: { idToDelete: listingObjectId }, + }); }); describe('updateOne', () => { it('should throw a permission error when user does not have permission (member role)', async () => { // Arrange - const graphqlOperation = updateOneObjectMetadataItemFactory({ + const graphqlOperation = updateOneObjectMetadataQueryFactory({ gqlFields: ` id `, @@ -201,8 +223,8 @@ describe('datamodel permissions', () => { describe('deleteOne', () => { it('should throw a permission error when user does not have permission (member role)', async () => { // Arrange - const graphqlOperation = deleteOneObjectMetadataItemFactory({ - idToDelete: listingObjectId, + const graphqlOperation = deleteOneObjectMetadataQueryFactory({ + input: { idToDelete: listingObjectId }, }); const response = diff --git a/packages/twenty-server/test/integration/graphql/suites/settings-permissions/roles.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/settings-permissions/roles.integration-spec.ts index e6f0a301f..4b655562f 100644 --- a/packages/twenty-server/test/integration/graphql/suites/settings-permissions/roles.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/settings-permissions/roles.integration-spec.ts @@ -2,8 +2,8 @@ import request from 'supertest'; import { deleteOneRoleOperationFactory } from 'test/integration/graphql/utils/delete-one-role-operation-factory.util'; import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; import { updateFeatureFlagFactory } from 'test/integration/graphql/utils/update-feature-flag-factory.util'; -import { createListingCustomObject } from 'test/integration/metadata/suites/object-metadata/utils/create-test-object-metadata.util'; -import { deleteOneObjectMetadataItem } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; +import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util'; +import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; import { SEED_APPLE_WORKSPACE_ID } from 'src/database/typeorm-seeds/core/workspaces'; import { DEV_SEED_WORKSPACE_MEMBER_IDS } from 'src/database/typeorm-seeds/workspace/workspace-members'; @@ -38,19 +38,12 @@ describe('roles permissions', () => { let guestRoleId: string; beforeAll(async () => { - const enablePermissionsQuery = updateFeatureFlagFactory( - SEED_APPLE_WORKSPACE_ID, - 'IsPermissionsEnabled', - true, - ); - const enablePermissionsV2Query = updateFeatureFlagFactory( SEED_APPLE_WORKSPACE_ID, 'IsPermissionsV2Enabled', true, ); - await makeGraphqlAPIRequest(enablePermissionsQuery); await makeGraphqlAPIRequest(enablePermissionsV2Query); const query = { @@ -460,14 +453,23 @@ describe('roles permissions', () => { let listingObjectId = ''; beforeAll(async () => { - const { objectMetadataId: createdObjectId } = - await createListingCustomObject(); + const { data } = await createOneObjectMetadata({ + input: { + nameSingular: 'house', + namePlural: 'houses', + labelSingular: 'House', + labelPlural: 'Houses', + icon: 'IconBuildingSkyscraper', + }, + }); - listingObjectId = createdObjectId; + listingObjectId = data.createOneObject.id; }); afterAll(async () => { - await deleteOneObjectMetadataItem(listingObjectId); + await deleteOneObjectMetadata({ + input: { idToDelete: listingObjectId }, + }); }); const upsertObjectPermissionMutation = ({ diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/create-one-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/create-one-field-metadata.integration-spec.ts index d82f138ac..30ba4f926 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/create-one-field-metadata.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/create-one-field-metadata.integration-spec.ts @@ -1,21 +1,33 @@ -import { createOneFieldMetadataFactory } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata-factory.util'; -import { createListingCustomObject } from 'test/integration/metadata/suites/object-metadata/utils/create-test-object-metadata.util'; -import { deleteOneObjectMetadataItem } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; -import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util'; +import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; import { FieldMetadataType } from 'twenty-shared/types'; describe('createOne', () => { describe('FieldMetadataService name/label sync', () => { - let listingObjectId = ''; + let createdObjectMetadataId = ''; beforeEach(async () => { - const { objectMetadataId: createdObjectId } = - await createListingCustomObject(); + const { + data: { + createOneObject: { id: objectMetadataId }, + }, + } = await createOneObjectMetadata({ + input: { + nameSingular: 'myTestObject', + namePlural: 'myTestObjects', + labelSingular: 'My Test Object', + labelPlural: 'My Test Objects', + icon: 'Icon123', + }, + }); - listingObjectId = createdObjectId; + createdObjectMetadataId = objectMetadataId; }); afterEach(async () => { - await deleteOneObjectMetadataItem(listingObjectId); + await deleteOneObjectMetadata({ + input: { idToDelete: createdObjectMetadataId }, + }); }); it('should create a field when name and label are synced correctly', async () => { // Arrange @@ -24,13 +36,13 @@ describe('createOne', () => { name: FIELD_NAME, label: 'Test Field', type: FieldMetadataType.TEXT, - objectMetadataId: listingObjectId, + objectMetadataId: createdObjectMetadataId, isLabelSyncedWithName: true, }; // Act - const graphqlOperation = createOneFieldMetadataFactory({ - input: { field: createFieldInput }, + const { data } = await createOneFieldMetadata({ + input: createFieldInput, gqlFields: ` id name @@ -39,10 +51,8 @@ describe('createOne', () => { `, }); - const response = await makeMetadataAPIRequest(graphqlOperation); - // Assert - expect(response.body.data.createOneField.name).toBe(FIELD_NAME); + expect(data.createOneField.name).toBe(FIELD_NAME); }); it('should set isLabelSyncWithName to false if not in input', async () => { @@ -51,26 +61,22 @@ describe('createOne', () => { name: 'testField', label: 'Test Field', type: FieldMetadataType.TEXT, - objectMetadataId: listingObjectId, + objectMetadataId: createdObjectMetadataId, }; // Act - const graphqlOperation = createOneFieldMetadataFactory({ - input: { field: createFieldInput }, + const { data } = await createOneFieldMetadata({ + input: createFieldInput, gqlFields: ` - id - name - label - isLabelSyncedWithName - `, + id + name + label + isLabelSyncedWithName + `, }); - const response = await makeMetadataAPIRequest(graphqlOperation); - // Assert - expect(response.body.data.createOneField.isLabelSyncedWithName).toBe( - false, - ); + expect(data.createOneField.isLabelSyncedWithName).toBe(false); }); it('should return an error when name and label are not synced but isLabelSyncedWithName is true', async () => { @@ -79,25 +85,24 @@ describe('createOne', () => { name: 'testField', label: 'Different Label', type: FieldMetadataType.TEXT, - objectMetadataId: listingObjectId, + objectMetadataId: createdObjectMetadataId, isLabelSyncedWithName: true, }; - const graphqlOperation = createOneFieldMetadataFactory({ - input: { field: createFieldInput }, + // Act + const { errors } = await createOneFieldMetadata({ + input: createFieldInput, gqlFields: ` id name label isLabelSyncedWithName - `, + `, + expectToFail: true, }); - // Act - const response = await makeMetadataAPIRequest(graphqlOperation); - // Assert - expect(response.body.errors[0].message).toBe( + expect(errors[0].message).toBe( 'Name is not synced with label. Expected name: "differentLabel", got testField', ); }); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/delete-one-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/delete-one-field-metadata.integration-spec.ts index a6ef9219d..2f5154ada 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/delete-one-field-metadata.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/delete-one-field-metadata.integration-spec.ts @@ -1,12 +1,16 @@ import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util'; import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { createCustomTextFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-custom-text-field-metadata.util'; -import { deleteOneFieldMetadataItemFactory } from 'test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata-factory.util'; -import { updateOneFieldMetadataFactory } from 'test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata-factory.util'; -import { createListingCustomObject } from 'test/integration/metadata/suites/object-metadata/utils/create-test-object-metadata.util'; -import { deleteOneObjectMetadataItem } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; -import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata.util'; +import { updateOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata.util'; +import { + LISTING_NAME_PLURAL, + LISTING_NAME_SINGULAR, +} from 'test/integration/metadata/suites/object-metadata/constants/test-object-names.constant'; +import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util'; +import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; +import { FieldMetadataType } from 'twenty-shared/types'; describe('deleteOne', () => { describe('Kanban aggregate operation', () => { @@ -15,14 +19,27 @@ describe('deleteOne', () => { let viewId = ''; beforeEach(async () => { - const { objectMetadataId: createdObjectId } = - await createListingCustomObject(); + const { data } = await createOneObjectMetadata({ + input: { + nameSingular: LISTING_NAME_SINGULAR, + namePlural: LISTING_NAME_PLURAL, + labelSingular: 'Listing', + labelPlural: 'Listings', + icon: 'IconBuildingSkyscraper', + }, + }); - listingObjectId = createdObjectId; - const { fieldMetadataId: createdFieldMetadaId } = - await createCustomTextFieldMetadata(createdObjectId); + listingObjectId = data.createOneObject.id; + const { data: createdFieldData } = await createOneFieldMetadata({ + input: { + name: 'house', + type: FieldMetadataType.TEXT, + label: 'House', + objectMetadataId: listingObjectId, + }, + }); - testFieldId = createdFieldMetadaId; + testFieldId = createdFieldData.createOneField.id; // create view const graphqlOperation = createOneOperationFactory({ @@ -49,7 +66,9 @@ describe('deleteOne', () => { viewId = createdView.id; }); afterEach(async () => { - await deleteOneObjectMetadataItem(listingObjectId); + await deleteOneObjectMetadata({ + input: { idToDelete: listingObjectId }, + }); }); it('should reset kanban aggregate operation when deleting a field used as kanbanAggregateOperationFieldMetadataId', async () => { // Arrange @@ -76,25 +95,25 @@ describe('deleteOne', () => { expect(viewResponse.body.data.view.kanbanAggregateOperation).toBe('MAX'); // Deactivate field to be able to delete it after - const deactivateFieldOperation = updateOneFieldMetadataFactory({ - input: { id: testFieldId, update: { isActive: false } }, + await updateOneFieldMetadata({ + input: { + idToUpdate: testFieldId, + updatePayload: { isActive: false }, + }, gqlFields: ` id isActive `, }); - await makeMetadataAPIRequest(deactivateFieldOperation); - // Act - const graphqlOperation = deleteOneFieldMetadataItemFactory({ - idToDelete: testFieldId, + const { data } = await deleteOneFieldMetadata({ + input: { idToDelete: testFieldId }, }); - const response = await makeMetadataAPIRequest(graphqlOperation); // Assert // 1. Field is deleted - expect(response.body.data.deleteOneField.id).toBe(testFieldId); + expect(data.deleteOneField.id).toBe(testFieldId); // 2. Kanban aggregate operation has been reset on view using this field as kanbanAggregateOperationFieldMetadataId const updatedViewResponse = diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/update-one-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/update-one-field-metadata.integration-spec.ts index ba3df6c96..34ae3b08d 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/update-one-field-metadata.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/update-one-field-metadata.integration-spec.ts @@ -1,8 +1,12 @@ -import { createCustomTextFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-custom-text-field-metadata.util'; -import { updateOneFieldMetadataFactory } from 'test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata-factory.util'; -import { createListingCustomObject } from 'test/integration/metadata/suites/object-metadata/utils/create-test-object-metadata.util'; -import { deleteOneObjectMetadataItem } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; -import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { updateOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata.util'; +import { + LISTING_NAME_PLURAL, + LISTING_NAME_SINGULAR, +} from 'test/integration/metadata/suites/object-metadata/constants/test-object-names.constant'; +import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util'; +import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; +import { FieldMetadataType } from 'twenty-shared/types'; describe('updateOne', () => { describe('FieldMetadataService name/label sync', () => { @@ -10,18 +14,35 @@ describe('updateOne', () => { let testFieldId = ''; beforeEach(async () => { - const { objectMetadataId: createdObjectId } = - await createListingCustomObject(); + const { data } = await createOneObjectMetadata({ + input: { + labelSingular: LISTING_NAME_SINGULAR, + labelPlural: LISTING_NAME_PLURAL, + nameSingular: LISTING_NAME_SINGULAR, + namePlural: LISTING_NAME_PLURAL, + icon: 'IconBuildingSkyscraper', + isLabelSyncedWithName: true, + }, + }); - listingObjectId = createdObjectId; + listingObjectId = data.createOneObject.id; - const { fieldMetadataId: createdFieldMetadaId } = - await createCustomTextFieldMetadata(createdObjectId); + const { data: createdFieldMetadata } = await createOneFieldMetadata({ + input: { + objectMetadataId: listingObjectId, + type: FieldMetadataType.TEXT, + name: 'testName', + label: 'Test name', + isLabelSyncedWithName: true, + }, + }); - testFieldId = createdFieldMetadaId; + testFieldId = createdFieldMetadata.createOneField.id; }); afterEach(async () => { - await deleteOneObjectMetadataItem(listingObjectId); + await deleteOneObjectMetadata({ + input: { idToDelete: listingObjectId }, + }); }); it('should update a field name and label when they are synced correctly', async () => { @@ -29,11 +50,12 @@ describe('updateOne', () => { const updateFieldInput = { name: 'newName', label: 'New name', + isLabelSyncedWithName: true, }; // Act - const graphqlOperation = updateOneFieldMetadataFactory({ - input: { id: testFieldId, update: updateFieldInput }, + const { data } = await updateOneFieldMetadata({ + input: { idToUpdate: testFieldId, updatePayload: updateFieldInput }, gqlFields: ` id name @@ -42,10 +64,8 @@ describe('updateOne', () => { `, }); - const response = await makeMetadataAPIRequest(graphqlOperation); - // Assert - expect(response.body.data.updateOneField.name).toBe('newName'); + expect(data.updateOneField.name).toBe('newName'); }); it('should update a field name and label when they are not synced correctly and labelSync is false', async () => { @@ -57,8 +77,8 @@ describe('updateOne', () => { }; // Act - const graphqlOperation = updateOneFieldMetadataFactory({ - input: { id: testFieldId, update: updateFieldInput }, + const { data } = await updateOneFieldMetadata({ + input: { idToUpdate: testFieldId, updatePayload: updateFieldInput }, gqlFields: ` id name @@ -67,33 +87,31 @@ describe('updateOne', () => { `, }); - const response = await makeMetadataAPIRequest(graphqlOperation); - // Assert - expect(response.body.data.updateOneField.name).toBe('differentName'); + expect(data.updateOneField.name).toBe('differentName'); }); it('should not update a field name if it is not synced correctly with label and labelSync is true', async () => { // Arrange const updateFieldInput = { name: 'newName', + isLabelSyncedWithName: true, }; // Act - const graphqlOperation = updateOneFieldMetadataFactory({ - input: { id: testFieldId, update: updateFieldInput }, + const { errors } = await updateOneFieldMetadata({ + input: { idToUpdate: testFieldId, updatePayload: updateFieldInput }, gqlFields: ` id name label isLabelSyncedWithName `, + expectToFail: true, }); - const response = await makeMetadataAPIRequest(graphqlOperation); - // Assert - expect(response.body.errors[0].message).toBe( + expect(errors[0].message).toBe( 'Name is not synced with label. Expected name: "testName", got newName', ); }); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/create-custom-text-field-metadata.util.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/create-custom-text-field-metadata.util.ts deleted file mode 100644 index 10dec075d..000000000 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/create-custom-text-field-metadata.util.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { createOneFieldMetadataFactory } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata-factory.util'; -import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; -import { FieldMetadataType } from 'twenty-shared/types'; - -const FIELD_NAME = 'testName'; - -export const createCustomTextFieldMetadata = async ( - objectMetadataItemId: string, -) => { - const createFieldInput = { - name: FIELD_NAME, - label: 'Test name', - type: FieldMetadataType.TEXT, - objectMetadataId: objectMetadataItemId, - isLabelSyncedWithName: true, - }; - const graphqlOperation = createOneFieldMetadataFactory({ - input: { field: createFieldInput }, - gqlFields: ` - id - name - label - isLabelSyncedWithName - `, - }); - - const response = await makeMetadataAPIRequest(graphqlOperation); - - return { fieldMetadataId: response.body.data.createOneField.id }; -}; diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata-factory.util.ts deleted file mode 100644 index 04dbc7e50..000000000 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata-factory.util.ts +++ /dev/null @@ -1,24 +0,0 @@ -import gql from 'graphql-tag'; - -import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; - -type CreateOneFieldFactoryParams = { - gqlFields: string; - input?: { field: Omit }; -}; - -export const createOneFieldMetadataFactory = ({ - gqlFields, - input, -}: CreateOneFieldFactoryParams) => ({ - query: gql` - mutation CreateOneFieldMetadataItem($input: CreateOneFieldMetadataInput!) { - createOneField(input: $input) { - ${gqlFields} - } - } - `, - variables: { - input, - }, -}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata-query-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata-query-factory.util.ts new file mode 100644 index 000000000..f114c74fd --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata-query-factory.util.ts @@ -0,0 +1,25 @@ +import gql from 'graphql-tag'; +import { PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; + +import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; + +export type CreateOneFieldFactoryInput = Omit< + CreateFieldInput, + 'workspaceId' | 'dataSourceId' +>; + +export const createOneFieldMetadataQueryFactory = ({ + input, + gqlFields = 'id', +}: PerformMetadataQueryParams) => ({ + query: gql` + mutation CreateOneFieldMetadataItem($input: CreateOneFieldMetadataInput!) { + createOneField(input: $input) { + ${gqlFields} + } + } + `, + variables: { + input: { field: input }, + }, +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util.ts new file mode 100644 index 000000000..4196347fe --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util.ts @@ -0,0 +1,29 @@ +import { + CreateOneFieldFactoryInput, + createOneFieldMetadataQueryFactory, +} from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata-query-factory.util'; +import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; +import { PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; +import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util'; + +export const createOneFieldMetadata = async ({ + input, + gqlFields, + expectToFail = false, +}: PerformMetadataQueryParams) => { + const graphqlOperation = createOneFieldMetadataQueryFactory({ + input, + gqlFields, + }); + + const response = await makeMetadataAPIRequest(graphqlOperation); + + if (expectToFail) { + warnIfNoErrorButExpectedToFail({ + response, + errorMessage: 'Field Metadata creation should have failed but did not', + }); + } + + return { data: response.body.data, errors: response.body.errors }; +}; diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata-factory.util.ts deleted file mode 100644 index b1f75592b..000000000 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata-factory.util.ts +++ /dev/null @@ -1,20 +0,0 @@ -import gql from 'graphql-tag'; - -type DeleteOneFieldFactoryParams = { - idToDelete: string; -}; - -export const deleteOneFieldMetadataItemFactory = ({ - idToDelete, -}: DeleteOneFieldFactoryParams) => ({ - query: gql` - mutation DeleteOneFieldMetadataItem($idToDelete: UUID!) { - deleteOneField(input: { id: $idToDelete }) { - id - } - } - `, - variables: { - idToDelete, - }, -}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata-query-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata-query-factory.util.ts new file mode 100644 index 000000000..75ba24d8a --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata-query-factory.util.ts @@ -0,0 +1,22 @@ +import gql from 'graphql-tag'; +import { PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; + +export type DeleteOneFieldFactoryInput = { + idToDelete: string; +}; + +export const deleteOneFieldMetadataQueryFactory = ({ + input, + gqlFields = 'id', +}: PerformMetadataQueryParams) => ({ + query: gql` + mutation DeleteOneFieldMetadataItem($idToDelete: UUID!) { + deleteOneField(input: { id: $idToDelete }) { + ${gqlFields} + } + } + `, + variables: { + idToDelete: input.idToDelete, + }, +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata.util.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata.util.ts index 991c7cf43..d8e8ab785 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata.util.ts +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata.util.ts @@ -1,10 +1,29 @@ import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { deleteOneFieldMetadataItemFactory } from 'test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata-factory.util'; +import { + DeleteOneFieldFactoryInput, + deleteOneFieldMetadataQueryFactory, +} from 'test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata-query-factory.util'; +import { PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; +import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util'; -export const deleteFieldMetadata = async (fieldMetadataId: string) => { - const graphqlOperation = deleteOneFieldMetadataItemFactory({ - idToDelete: fieldMetadataId, +export const deleteOneFieldMetadata = async ({ + input, + gqlFields, + expectToFail = false, +}: PerformMetadataQueryParams) => { + const graphqlOperation = deleteOneFieldMetadataQueryFactory({ + input, + gqlFields, }); - await makeGraphqlAPIRequest(graphqlOperation); + const response = await makeGraphqlAPIRequest(graphqlOperation); + + if (expectToFail) { + warnIfNoErrorButExpectedToFail({ + response, + errorMessage: 'Field Metadata deletion should have failed but did not', + }); + } + + return { data: response.body.data, errors: response.body.errors }; }; diff --git a/packages/twenty-server/test/integration/metadata/suites/utils/fields-metadata-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/find-many-fields-metadata-query-factory.util.ts similarity index 52% rename from packages/twenty-server/test/integration/metadata/suites/utils/fields-metadata-factory.util.ts rename to packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/find-many-fields-metadata-query-factory.util.ts index b8157d248..e2df4c921 100644 --- a/packages/twenty-server/test/integration/metadata/suites/utils/fields-metadata-factory.util.ts +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/find-many-fields-metadata-query-factory.util.ts @@ -1,17 +1,15 @@ import gql from 'graphql-tag'; +import { PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; -type FieldsFactoryParams = { - gqlFields: string; - input: { - filter: object; - paging: object; - }; +export type FindManyFieldsMetadataFactoryInput = { + filter: object; + paging: object; }; -export const fieldsMetadataFactory = ({ - gqlFields, +export const findManyFieldsMetadataQueryFactory = ({ + gqlFields = 'id', input, -}: FieldsFactoryParams) => ({ +}: PerformMetadataQueryParams) => ({ query: gql` query FieldsMetadata($filter: FieldFilter!, $paging: CursorPaging!) { fields(filter: $filter, paging: $paging) { diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/find-many-fields-metadata.util.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/find-many-fields-metadata.util.ts new file mode 100644 index 000000000..af373704c --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/find-many-fields-metadata.util.ts @@ -0,0 +1,29 @@ +import { + FindManyFieldsMetadataFactoryInput, + findManyFieldsMetadataQueryFactory, +} from 'test/integration/metadata/suites/field-metadata/utils/find-many-fields-metadata-query-factory.util'; +import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; +import { PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; +import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util'; + +export const findManyFieldsMetadata = async ({ + input, + gqlFields, + expectToFail = false, +}: PerformMetadataQueryParams) => { + const graphqlOperation = findManyFieldsMetadataQueryFactory({ + input, + gqlFields, + }); + + const response = await makeMetadataAPIRequest(graphqlOperation); + + if (expectToFail) { + warnIfNoErrorButExpectedToFail({ + response, + errorMessage: 'Field Metadata retrieval should have failed but did not', + }); + } + + return response.body.data.fields.edges.map((edge) => edge.node); +}; diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata-factory.util.ts deleted file mode 100644 index ddbd843d2..000000000 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata-factory.util.ts +++ /dev/null @@ -1,25 +0,0 @@ -import gql from 'graphql-tag'; - -import { UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input'; - -type UpdateOneFieldFactoryParams = { - gqlFields: string; - input: { id: string; update: Omit }; -}; - -export const updateOneFieldMetadataFactory = ({ - gqlFields, - input, -}: UpdateOneFieldFactoryParams) => ({ - query: gql` - mutation UpdateOneFieldMetadataItem($idToUpdate: UUID!, $updatePayload: UpdateFieldInput!) { - updateOneField(input: {id: $idToUpdate, update: $updatePayload}) { - ${gqlFields} - } - } - `, - variables: { - idToUpdate: input.id, - updatePayload: input.update, - }, -}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata-query-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata-query-factory.util.ts new file mode 100644 index 000000000..c40cb356c --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata-query-factory.util.ts @@ -0,0 +1,26 @@ +import gql from 'graphql-tag'; +import { PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; + +import { UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input'; + +export type UpdateOneFieldFactoryInput = { + idToUpdate: string; + updatePayload: Omit; +}; + +export const updateOneFieldMetadataQueryFactory = ({ + gqlFields = 'id', + input, +}: PerformMetadataQueryParams) => ({ + query: gql` + mutation UpdateOneFieldMetadataItem($idToUpdate: UUID!, $updatePayload: UpdateFieldInput!) { + updateOneField(input: {id: $idToUpdate, update: $updatePayload}) { + ${gqlFields} + } + } + `, + variables: { + idToUpdate: input.idToUpdate, + updatePayload: input.updatePayload, + }, +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata.util.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata.util.ts new file mode 100644 index 000000000..08e90d53a --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata.util.ts @@ -0,0 +1,29 @@ +import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; +import { + UpdateOneFieldFactoryInput, + updateOneFieldMetadataQueryFactory, +} from 'test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata-query-factory.util'; +import { PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; +import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util'; + +export const updateOneFieldMetadata = async ({ + input, + gqlFields, + expectToFail = false, +}: PerformMetadataQueryParams) => { + const graphqlOperation = updateOneFieldMetadataQueryFactory({ + input, + gqlFields, + }); + + const response = await makeGraphqlAPIRequest(graphqlOperation); + + if (expectToFail) { + warnIfNoErrorButExpectedToFail({ + response, + errorMessage: 'Field Metadata update should have failed but did not', + }); + } + + return { data: response.body.data, errors: response.body.errors }; +}; diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/constants/test-object-names.constant.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/constants/test-object-names.constant.ts index aa1a53407..47fc3ba27 100644 --- a/packages/twenty-server/test/integration/metadata/suites/object-metadata/constants/test-object-names.constant.ts +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/constants/test-object-names.constant.ts @@ -1,2 +1,2 @@ -export const LISTING_NAME_SINGULAR = 'listing'; -export const LISTING_NAME_PLURAL = 'listings'; +export const LISTING_NAME_SINGULAR = 'listinga'; +export const LISTING_NAME_PLURAL = 'listingas'; diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/failing-create-one-object-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/failing-create-one-object-metadata.integration-spec.ts index 2ef814969..007f1b437 100644 --- a/packages/twenty-server/test/integration/metadata/suites/object-metadata/failing-create-one-object-metadata.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/failing-create-one-object-metadata.integration-spec.ts @@ -1,5 +1,5 @@ -import { getMockCreateObjectInput } from 'test/integration/utils/object-metadata/generate-mock-create-object-metadata-input'; -import { performFailingObjectMetadataCreation } from 'test/integration/utils/object-metadata/perform-failing-object-metadata-creation'; +import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util'; +import { getMockCreateObjectInput } from 'test/integration/metadata/suites/object-metadata/utils/generate-mock-create-object-metadata-input'; import { EachTestingContext } from 'twenty-shared/testing'; import { ErrorCode } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; @@ -124,9 +124,10 @@ const allTestsUseCases = [ describe('Object metadata creation should fail', () => { it.each(allTestsUseCases)('$title', async ({ context }) => { - const errors = await performFailingObjectMetadataCreation( - getMockCreateObjectInput(context), - ); + const { errors } = await createOneObjectMetadata({ + input: getMockCreateObjectInput(context), + expectToFail: true, + }); expect(errors.length).toBe(1); const firstError = errors[0]; diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/rename-custom-object.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/rename-custom-object.integration-spec.ts index e713a423e..0a3460340 100644 --- a/packages/twenty-server/test/integration/metadata/suites/object-metadata/rename-custom-object.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/rename-custom-object.integration-spec.ts @@ -1,11 +1,10 @@ -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { createOneObjectMetadataFactory } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata-factory.util'; -import { deleteOneObjectMetadataItemFactory } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata-factory.util'; -import { objectsMetadataFactory } from 'test/integration/metadata/suites/object-metadata/utils/objects-metadata-factory.util'; -import { updateOneObjectMetadataItemFactory } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata-factory.util'; +import { findManyFieldsMetadataQueryFactory } from 'test/integration/metadata/suites/field-metadata/utils/find-many-fields-metadata-query-factory.util'; +import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util'; +import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; +import { findManyObjectMetadataQueryFactory } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata-query-factory.util'; +import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util'; import { createOneRelationMetadataFactory } from 'test/integration/metadata/suites/utils/create-one-relation-metadata-factory.util'; import { deleteOneRelationMetadataItemFactory } from 'test/integration/metadata/suites/utils/delete-one-relation-metadata-factory.util'; -import { fieldsMetadataFactory } from 'test/integration/metadata/suites/utils/fields-metadata-factory.util'; import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; import { FieldMetadataType } from 'twenty-shared/types'; @@ -37,7 +36,7 @@ describe('Custom object renaming', () => { {}, ); - const standardObjectsGraphqlOperation = objectsMetadataFactory({ + const standardObjectsGraphqlOperation = findManyObjectMetadataQueryFactory({ gqlFields: ` id nameSingular @@ -50,7 +49,7 @@ describe('Custom object renaming', () => { }, }); - const fieldsGraphqlOperation = fieldsMetadataFactory({ + const fieldsGraphqlOperation = findManyFieldsMetadataQueryFactory({ gqlFields: ` id name @@ -94,22 +93,18 @@ describe('Custom object renaming', () => { }; // Act - const graphqlOperation = createOneObjectMetadataFactory({ - input: { object: LISTING_OBJECT }, + const { data } = await createOneObjectMetadata({ + input: LISTING_OBJECT, gqlFields: ` id nameSingular `, }); - const response = await makeMetadataAPIRequest(graphqlOperation); - // Assert - expect(response.body.data.createOneObject.nameSingular).toBe( - LISTING_NAME_SINGULAR, - ); + expect(data.createOneObject.nameSingular).toBe(LISTING_NAME_SINGULAR); - listingObjectId = response.body.data.createOneObject.id; + listingObjectId = data.createOneObject.id; const fields = await makeMetadataAPIRequest(fieldsGraphqlOperation); @@ -206,43 +201,31 @@ describe('Custom object renaming', () => { const HOUSE_NAME_PLURAL = 'houses'; const HOUSE_LABEL_SINGULAR = 'House'; const HOUSE_LABEL_PLURAL = 'Houses'; - const updateListingNameGraphqlOperation = - updateOneObjectMetadataItemFactory({ - gqlFields: ` + + // Act + const { data } = await updateOneObjectMetadata({ + gqlFields: ` nameSingular labelSingular namePlural labelPlural `, - input: { - idToUpdate: listingObjectId, - updatePayload: { - nameSingular: HOUSE_NAME_SINGULAR, - namePlural: HOUSE_NAME_PLURAL, - labelSingular: HOUSE_LABEL_SINGULAR, - labelPlural: HOUSE_LABEL_PLURAL, - }, + input: { + idToUpdate: listingObjectId, + updatePayload: { + nameSingular: HOUSE_NAME_SINGULAR, + namePlural: HOUSE_NAME_PLURAL, + labelSingular: HOUSE_LABEL_SINGULAR, + labelPlural: HOUSE_LABEL_PLURAL, }, - }); - - // Act - const updateListingNameResponse = await makeMetadataAPIRequest( - updateListingNameGraphqlOperation, - ); + }, + }); // Assert - expect( - updateListingNameResponse.body.data.updateOneObject.nameSingular, - ).toBe(HOUSE_NAME_SINGULAR); - expect(updateListingNameResponse.body.data.updateOneObject.namePlural).toBe( - HOUSE_NAME_PLURAL, - ); - expect( - updateListingNameResponse.body.data.updateOneObject.labelSingular, - ).toBe(HOUSE_LABEL_SINGULAR); - expect( - updateListingNameResponse.body.data.updateOneObject.labelPlural, - ).toBe(HOUSE_LABEL_PLURAL); + expect(data.updateOneObject.nameSingular).toBe(HOUSE_NAME_SINGULAR); + expect(data.updateOneObject.namePlural).toBe(HOUSE_NAME_PLURAL); + expect(data.updateOneObject.labelSingular).toBe(HOUSE_LABEL_SINGULAR); + expect(data.updateOneObject.labelPlural).toBe(HOUSE_LABEL_PLURAL); const fieldsResponse = await makeMetadataAPIRequest(fieldsGraphqlOperation); @@ -306,14 +289,12 @@ describe('Custom object renaming', () => { }); it('5. should delete custom object', async () => { - const graphqlOperation = deleteOneObjectMetadataItemFactory({ - idToDelete: listingObjectId, + const { data } = await deleteOneObjectMetadata({ + input: { + idToDelete: listingObjectId, + }, }); - const response = await makeGraphqlAPIRequest(graphqlOperation); - - const deleteListingResponse = response.body.data.deleteOneObject; - - expect(deleteListingResponse.id).toBe(listingObjectId); + expect(data.deleteOneObject.id).toBe(listingObjectId); }); }); diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/successful-create-one-object-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/successful-create-one-object-metadata.integration-spec.ts index ed51055eb..9e6023693 100644 --- a/packages/twenty-server/test/integration/metadata/suites/object-metadata/successful-create-one-object-metadata.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/successful-create-one-object-metadata.integration-spec.ts @@ -1,6 +1,6 @@ -import { deleteOneObjectMetadataItem } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; -import { getMockCreateObjectInput } from 'test/integration/utils/object-metadata/generate-mock-create-object-metadata-input'; -import { performObjectMetadataCreation } from 'test/integration/utils/object-metadata/perform-object-metadata-creation'; +import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util'; +import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; +import { getMockCreateObjectInput } from 'test/integration/metadata/suites/object-metadata/utils/generate-mock-create-object-metadata-input'; import { EachTestingContext } from 'twenty-shared/testing'; import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input'; @@ -26,13 +26,13 @@ const allTestsUseCases = [...successfulObjectMetadataItemCreateOneUseCase]; describe('Object metadata creation should succeed', () => { it.each(allTestsUseCases)('$title', async ({ context }) => { - const response = await performObjectMetadataCreation( - getMockCreateObjectInput(context), - ); + const { data } = await createOneObjectMetadata({ + input: getMockCreateObjectInput(context), + }); - expect(response.body.data.createOneObject.id).toBeDefined(); - await deleteOneObjectMetadataItem( - response.body.data.createOneObject.id, - ).catch(); + expect(data.createOneObject.id).toBeDefined(); + await deleteOneObjectMetadata({ + input: { idToDelete: data.createOneObject.id }, + }).catch(); }); }); diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata-factory.util.ts deleted file mode 100644 index 473e3bd95..000000000 --- a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata-factory.util.ts +++ /dev/null @@ -1,24 +0,0 @@ -import gql from 'graphql-tag'; - -import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input'; - -type CreateOneObjectFactoryParams = { - gqlFields: string; - input?: { object: Omit }; -}; - -export const createOneObjectMetadataFactory = ({ - gqlFields, - input, -}: CreateOneObjectFactoryParams) => ({ - query: gql` - mutation CreateOneObjectMetadataItem($input: CreateOneObjectInput!) { - createOneObject(input: $input) { - ${gqlFields} - } - } - `, - variables: { - input, - }, -}); diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata-query-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata-query-factory.util.ts new file mode 100644 index 000000000..aa0b5e5b3 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata-query-factory.util.ts @@ -0,0 +1,25 @@ +import gql from 'graphql-tag'; +import { PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; + +import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input'; + +export type CreateOneObjectFactoryInput = Omit< + CreateObjectInput, + 'workspaceId' | 'dataSourceId' +>; + +export const createOneObjectMetadataQueryFactory = ({ + input, + gqlFields = 'id', +}: PerformMetadataQueryParams) => ({ + query: gql` + mutation CreateOneObjectMetadataItem($input: CreateOneObjectInput!) { + createOneObject(input: $input) { + ${gqlFields} + } + } + `, + variables: { + input: { object: input }, + }, +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util.ts new file mode 100644 index 000000000..07a0ffb9e --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util.ts @@ -0,0 +1,29 @@ +import { + CreateOneObjectFactoryInput, + createOneObjectMetadataQueryFactory, +} from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata-query-factory.util'; +import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; +import { PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; +import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util'; + +export const createOneObjectMetadata = async ({ + input, + gqlFields, + expectToFail = false, +}: PerformMetadataQueryParams) => { + const graphqlOperation = createOneObjectMetadataQueryFactory({ + input, + gqlFields, + }); + + const response = await makeMetadataAPIRequest(graphqlOperation); + + if (expectToFail) { + warnIfNoErrorButExpectedToFail({ + response, + errorMessage: 'Object Metadata creation should have failed but did not', + }); + } + + return { data: response.body.data, errors: response.body.errors }; +}; diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/create-test-object-metadata.util.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/create-test-object-metadata.util.ts deleted file mode 100644 index b1e5b147d..000000000 --- a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/create-test-object-metadata.util.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { - LISTING_NAME_PLURAL, - LISTING_NAME_SINGULAR, -} from 'test/integration/metadata/suites/object-metadata/constants/test-object-names.constant'; -import { createOneObjectMetadataFactory } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata-factory.util'; -import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; - -const LISTING_OBJECT = { - namePlural: LISTING_NAME_PLURAL, - nameSingular: LISTING_NAME_SINGULAR, - labelPlural: 'Listings', - labelSingular: 'Listing', - description: 'Listing object', - icon: 'IconListNumbers', - isLabelSyncedWithName: false, -}; - -export const createListingCustomObject = async () => { - const createObjectOperation = createOneObjectMetadataFactory({ - input: { object: LISTING_OBJECT }, - gqlFields: ` - id - nameSingular - `, - }); - - const response = await makeMetadataAPIRequest(createObjectOperation); - - return { - objectMetadataId: response.body.data.createOneObject.id, - }; -}; diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata-factory.util.ts deleted file mode 100644 index 439df3bc3..000000000 --- a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata-factory.util.ts +++ /dev/null @@ -1,20 +0,0 @@ -import gql from 'graphql-tag'; - -type DeleteOneObjectFactoryParams = { - idToDelete: string; -}; - -export const deleteOneObjectMetadataItemFactory = ({ - idToDelete, -}: DeleteOneObjectFactoryParams) => ({ - query: gql` - mutation DeleteOneObjectMetadataItem($idToDelete: UUID!) { - deleteOneObject(input: { id: $idToDelete }) { - id - } - } - `, - variables: { - idToDelete, - }, -}); diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata-query-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata-query-factory.util.ts new file mode 100644 index 000000000..3b87cc7f2 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata-query-factory.util.ts @@ -0,0 +1,22 @@ +import gql from 'graphql-tag'; +import { PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; + +export type DeleteOneObjectFactoryInput = { + idToDelete: string; +}; + +export const deleteOneObjectMetadataQueryFactory = ({ + input, + gqlFields = 'id', +}: PerformMetadataQueryParams) => ({ + query: gql` + mutation DeleteOneObjectMetadataItem($idToDelete: UUID!) { + deleteOneObject(input: { id: $idToDelete }) { + ${gqlFields} + } + } + `, + variables: { + idToDelete: input.idToDelete, + }, +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util.ts index 8b0b561f8..006e009f3 100644 --- a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util.ts +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util.ts @@ -1,12 +1,29 @@ import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { deleteOneObjectMetadataItemFactory } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata-factory.util'; +import { + DeleteOneObjectFactoryInput, + deleteOneObjectMetadataQueryFactory, +} from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata-query-factory.util'; +import { PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; +import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util'; -export const deleteOneObjectMetadataItem = async ( - objectMetadataItemId: string, -) => { - const graphqlOperation = deleteOneObjectMetadataItemFactory({ - idToDelete: objectMetadataItemId, +export const deleteOneObjectMetadata = async ({ + input, + gqlFields, + expectToFail = false, +}: PerformMetadataQueryParams) => { + const graphqlOperation = deleteOneObjectMetadataQueryFactory({ + input, + gqlFields, }); - await makeGraphqlAPIRequest(graphqlOperation); + const response = await makeGraphqlAPIRequest(graphqlOperation); + + if (expectToFail) { + warnIfNoErrorButExpectedToFail({ + response, + errorMessage: 'Object Metadata deletion should have failed but did not', + }); + } + + return { data: response.body.data, errors: response.body.errors }; }; diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/objects-metadata-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata-query-factory.util.ts similarity index 52% rename from packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/objects-metadata-factory.util.ts rename to packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata-query-factory.util.ts index d76d76100..d31cc99ac 100644 --- a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/objects-metadata-factory.util.ts +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata-query-factory.util.ts @@ -1,17 +1,15 @@ import gql from 'graphql-tag'; +import { PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; -type ObjectsFactoryParams = { - gqlFields: string; - input: { - filter: object; - paging: object; - }; +export type FindManyObjectMetadataFactoryInput = { + filter: object; + paging: object; }; -export const objectsMetadataFactory = ({ - gqlFields, +export const findManyObjectMetadataQueryFactory = ({ + gqlFields = 'id', input, -}: ObjectsFactoryParams) => ({ +}: PerformMetadataQueryParams) => ({ query: gql` query ObjectsMetadata($filter: ObjectFilter!, $paging: CursorPaging!) { objects(filter: $filter, paging: $paging) { diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata.util.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata.util.ts new file mode 100644 index 000000000..1440e682d --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata.util.ts @@ -0,0 +1,29 @@ +import { + FindManyObjectMetadataFactoryInput, + findManyObjectMetadataQueryFactory, +} from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata-query-factory.util'; +import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; +import { PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; +import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util'; + +export const findManyObjectMetadata = async ({ + input, + gqlFields, + expectToFail = false, +}: PerformMetadataQueryParams) => { + const graphqlOperation = findManyObjectMetadataQueryFactory({ + input, + gqlFields, + }); + + const response = await makeMetadataAPIRequest(graphqlOperation); + + if (expectToFail) { + warnIfNoErrorButExpectedToFail({ + response, + errorMessage: 'Object Metadata retrieval should have failed but did not', + }); + } + + return response.body.data.objects.edges.map((edge) => edge.node); +}; diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/find-many-objects-metadata-items.util.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/find-many-objects-metadata-items.util.ts deleted file mode 100644 index ae9896dbb..000000000 --- a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/find-many-objects-metadata-items.util.ts +++ /dev/null @@ -1,28 +0,0 @@ -import gql from 'graphql-tag'; -import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; - -export const findManyObjectsMetadataItems = async () => { - const query = { - query: gql` - query ObjectMetadataItems { - objects(paging: { first: 1000 }) { - edges { - node { - id - nameSingular - namePlural - } - } - } - } - `, - }; - - const response = await makeMetadataAPIRequest(query); - - return response.body.data.objects.edges.map((edge) => edge.node) as { - id: string; - nameSingular: string; - namePlural: string; - }[]; -}; diff --git a/packages/twenty-server/test/integration/utils/object-metadata/generate-mock-create-object-metadata-input.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/generate-mock-create-object-metadata-input.ts similarity index 88% rename from packages/twenty-server/test/integration/utils/object-metadata/generate-mock-create-object-metadata-input.ts rename to packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/generate-mock-create-object-metadata-input.ts index 50fc02817..5a8ba9c3b 100644 --- a/packages/twenty-server/test/integration/utils/object-metadata/generate-mock-create-object-metadata-input.ts +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/generate-mock-create-object-metadata-input.ts @@ -4,8 +4,8 @@ import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/d export const getMockCreateObjectInput = ( overrides?: Partial>, ) => ({ - namePlural: 'listings', - nameSingular: 'listing', + namePlural: 'listingas', + nameSingular: 'listinga', labelPlural: 'Listings', labelSingular: 'Listing', description: 'Listing object', diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata-factory.util.ts deleted file mode 100644 index 28ac92420..000000000 --- a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata-factory.util.ts +++ /dev/null @@ -1,28 +0,0 @@ -import gql from 'graphql-tag'; - -import { UpdateObjectPayload } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input'; - -type UpdateOneObjectFactoryParams = { - gqlFields: string; - input: { - idToUpdate: string; - updatePayload: UpdateObjectPayload; - }; -}; - -export const updateOneObjectMetadataItemFactory = ({ - gqlFields, - input, -}: UpdateOneObjectFactoryParams) => ({ - query: gql` - mutation UpdateOneObjectMetadataItem($idToUpdate: UUID!, $updatePayload: UpdateObjectPayload!) { - updateOneObject(input: {id: $idToUpdate, update: $updatePayload}) { - ${gqlFields} - } - } - `, - variables: { - idToUpdate: input.idToUpdate, - updatePayload: input.updatePayload, - }, -}); diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata-query-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata-query-factory.util.ts new file mode 100644 index 000000000..642e31c16 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata-query-factory.util.ts @@ -0,0 +1,26 @@ +import gql from 'graphql-tag'; +import { PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; + +import { UpdateObjectPayload } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input'; + +export type UpdateOneObjectFactoryInput = { + idToUpdate: string; + updatePayload: UpdateObjectPayload; +}; + +export const updateOneObjectMetadataQueryFactory = ({ + gqlFields = 'id', + input, +}: PerformMetadataQueryParams) => ({ + query: gql` + mutation UpdateOneObjectMetadataItem($idToUpdate: UUID!, $updatePayload: UpdateObjectPayload!) { + updateOneObject(input: {id: $idToUpdate, update: $updatePayload}) { + ${gqlFields} + } + } + `, + variables: { + idToUpdate: input.idToUpdate, + updatePayload: input.updatePayload, + }, +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util.ts new file mode 100644 index 000000000..12beb98eb --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util.ts @@ -0,0 +1,29 @@ +import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; +import { + UpdateOneObjectFactoryInput, + updateOneObjectMetadataQueryFactory, +} from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata-query-factory.util'; +import { PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; +import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util'; + +export const updateOneObjectMetadata = async ({ + input, + gqlFields, + expectToFail = false, +}: PerformMetadataQueryParams) => { + const graphqlOperation = updateOneObjectMetadataQueryFactory({ + input, + gqlFields, + }); + + const response = await makeGraphqlAPIRequest(graphqlOperation); + + if (expectToFail) { + warnIfNoErrorButExpectedToFail({ + response, + errorMessage: 'Object Metadata update should have failed but did not', + }); + } + + return { data: response.body.data, errors: response.body.errors }; +}; diff --git a/packages/twenty-server/test/integration/metadata/types/perform-metadata-query.type.ts b/packages/twenty-server/test/integration/metadata/types/perform-metadata-query.type.ts new file mode 100644 index 000000000..b446c1782 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/types/perform-metadata-query.type.ts @@ -0,0 +1,5 @@ +export type PerformMetadataQueryParams = { + input: T; + gqlFields?: string; + expectToFail?: boolean; +}; diff --git a/packages/twenty-server/test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util.ts b/packages/twenty-server/test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util.ts new file mode 100644 index 000000000..36b7b75da --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util.ts @@ -0,0 +1,17 @@ +import { isDefined } from 'class-validator'; +import { Response } from 'supertest'; + +type WarnIfNoErrorButExpectedToFailInput = { + response: Response; + errorMessage: string; +}; + +export const warnIfNoErrorButExpectedToFail = ({ + response, + errorMessage, +}: WarnIfNoErrorButExpectedToFailInput) => { + if (isDefined(response.body.data)) { + expect(false).toEqual(errorMessage); + } + expect(response.body.errors.length).toBeGreaterThan(0); +}; diff --git a/packages/twenty-server/test/integration/utils/object-metadata/perform-failing-object-metadata-creation.ts b/packages/twenty-server/test/integration/utils/object-metadata/perform-failing-object-metadata-creation.ts deleted file mode 100644 index 6239edfe9..000000000 --- a/packages/twenty-server/test/integration/utils/object-metadata/perform-failing-object-metadata-creation.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { deleteOneObjectMetadataItem } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; -import { performObjectMetadataCreation } from 'test/integration/utils/object-metadata/perform-object-metadata-creation'; -import { isDefined } from 'twenty-shared/utils'; - -import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input'; - -export const performFailingObjectMetadataCreation = async ( - objectInput: Omit, -) => { - const response = await performObjectMetadataCreation(objectInput); - - if (isDefined(response.body.data)) { - try { - const createdId = response.body.data.createOneObject.id; - - await deleteOneObjectMetadataItem(createdId); - } catch (e) { - // eslint-disable-next-line no-console - console.error(e); - } - expect(false).toEqual( - 'Object Metadata Item should have failed but did not', - ); - } - expect(response.body.errors.length).toBeGreaterThan(0); - - return response.body.errors; -}; diff --git a/packages/twenty-server/test/integration/utils/object-metadata/perform-object-metadata-creation.ts b/packages/twenty-server/test/integration/utils/object-metadata/perform-object-metadata-creation.ts deleted file mode 100644 index d62d12a1d..000000000 --- a/packages/twenty-server/test/integration/utils/object-metadata/perform-object-metadata-creation.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { createOneObjectMetadataFactory } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata-factory.util'; -import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; - -import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input'; - -export const performObjectMetadataCreation = async ( - args: Omit, -) => { - const graphqlOperation = createOneObjectMetadataFactory({ - input: { object: args }, - gqlFields: ` - id - nameSingular - `, - }); - - return await makeMetadataAPIRequest(graphqlOperation); -};