[GQL_VIEW_FILTER_API_BREAKING_CHANGE][WHEN_RELEASED_REQUIRES_CACHE_FLUSH] ViewFilter migration to workspace migration v2 (#15010)

# Introduction
Migrating `viewFilter` to v2 in order to migrate later the field update
side effect on view to v2 too

## What's done
- Created flat-view-filter
- flat view filter runner
- flat view filter builder
- create view filter service v2 and input transpilers
- refactor the existing view filter resolver to fix standard (
BREAKING_CHANGE on graphql api update especially ) REST stays the same
- refactored the front to consume the mutations autogenerated

## New generic tools
### Compare two flat entity
Introducing a new util to compare two flat entity, it's strictly typed
and will be added to the generic builder in a following PR
This will ease flat entity addition as won't required to create a
specific abstraction for comparison
Generic builder will expect specific constant: properties to compare and
properties to stringify

### Transform flat entity for comparison
Forked and refactor the initial existing method for flat entity business
scope and type safety

## Coverage
Migrated existing integration tests to fit new contract API
This PR does not add strong coverage on validation exceptions
Deadlines are too short

close https://github.com/twentyhq/core-team-issues/issues/1666
This commit is contained in:
Paul Rastoin
2025-10-10 15:02:14 +02:00
committed by GitHub
parent 37473175a0
commit 651ab184a7
97 changed files with 3012 additions and 620 deletions
@@ -3,13 +3,14 @@ import { randomUUID } from 'crypto';
import { PERSON_GQL_FIELDS } from 'test/integration/constants/person-gql-fields.constants';
import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util';
import { createViewFilterGroupOperationFactory } from 'test/integration/graphql/utils/create-view-filter-group-operation-factory.util';
import { createViewFilterOperationFactory } from 'test/integration/graphql/utils/create-view-filter-operation-factory.util';
import { createViewOperationFactory } from 'test/integration/graphql/utils/create-view-operation-factory.util';
import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util';
import { groupByOperationFactory } from 'test/integration/graphql/utils/group-by-operation-factory.util';
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
import { findManyObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata.util';
import { ViewFilterOperand } from 'twenty-shared/types';
import { createOneCoreViewFilter } from 'test/integration/metadata/suites/view-filter/utils/create-one-core-view-filter.util';
import { jestExpectToBeDefined } from 'test/utils/expect-to-be-defined.util.test';
import { type FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
import { type ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
@@ -443,18 +444,18 @@ describe('group-by resolvers (integration)', () => {
const viewFilterGroupId = viewFilterGroupResponse.body.data
.createCoreViewFilterGroup.id as string;
await makeGraphqlAPIRequest(
createViewFilterOperationFactory({
data: {
viewId,
viewFilterGroupId,
fieldMetadataId: cityFieldMetadataId,
operand: ViewFilterOperand.CONTAINS,
value: cityToKeep,
positionInViewFilterGroup: 0,
},
}),
);
jestExpectToBeDefined(cityFieldMetadataId);
await createOneCoreViewFilter({
input: {
viewId,
viewFilterGroupId,
fieldMetadataId: cityFieldMetadataId,
operand: ViewFilterOperand.CONTAINS,
value: cityToKeep,
positionInViewFilterGroup: 0,
},
expectToFail: false,
});
const response = await makeGraphqlAPIRequest(
groupByOperationFactory({
@@ -1,10 +1,10 @@
import { print } from 'graphql';
import request from 'supertest';
import { deleteOneRoleOperationFactory } from 'test/integration/graphql/utils/delete-one-role-operation-factory.util';
import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util';
import { updateWorkspaceMemberRole } from 'test/integration/graphql/utils/update-workspace-member-role.util';
import { createOneObjectMetadataQueryFactory } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata-query-factory.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 { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
import { ErrorCode } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
@@ -16,6 +16,7 @@ const client = request(`http://localhost:${APP_PORT}`);
describe('Granular settings permissions', () => {
let customRoleId: string;
let originalMemberRoleId: string;
const createdObjectMetadataIds: string[] = [];
beforeAll(async () => {
// Get the original Member role ID for restoration later
@@ -123,54 +124,50 @@ describe('Granular settings permissions', () => {
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send(deleteRoleQuery);
for (const objectMetadataId of createdObjectMetadataIds) {
await updateOneObjectMetadata({
expectToFail: false,
input: {
idToUpdate: objectMetadataId,
updatePayload: {
isActive: false,
},
},
});
await deleteOneObjectMetadata({
input: {
idToDelete: objectMetadataId,
},
expectToFail: false,
});
}
});
describe('Data Model Permissions', () => {
it('should allow access to data model operations when user has DATA_MODEL setting permission', async () => {
// Test creating an object metadata (requires DATA_MODEL permission)
const { query: createObjectQuery, variables } =
createOneObjectMetadataQueryFactory({
input: {
labelSingular: 'House',
labelPlural: 'Houses',
nameSingular: 'house',
namePlural: 'houses',
description: 'a house',
icon: 'IconHome',
},
gqlFields: `
const { data, errors } = await createOneObjectMetadata({
input: {
labelSingular: 'House',
labelPlural: 'Houses',
nameSingular: 'house',
namePlural: 'houses',
description: 'a house',
icon: 'IconHome',
},
gqlFields: `
id
labelSingular
labelPlural
`,
});
expectToFail: false,
});
const response = await client
.post('/metadata')
.set('Authorization', `Bearer ${APPLE_JONY_MEMBER_ACCESS_TOKEN}`)
.send({ query: print(createObjectQuery), variables });
expect(response.status).toBe(200);
expect(response.body.errors).toBeUndefined();
expect(response.body.data.createOneObject).toBeDefined();
expect(response.body.data.createOneObject.labelSingular).toBe('House');
// Clean up - delete the created object
const { query: deleteObjectQuery, variables: deleteObjectVariables } =
deleteOneObjectMetadataQueryFactory({
input: {
idToDelete: response.body.data.createOneObject.id,
},
gqlFields: 'id',
});
await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: print(deleteObjectQuery),
variables: deleteObjectVariables,
});
createdObjectMetadataIds.push(data.createOneObject.id);
expect(errors).toBeUndefined();
expect(data.createOneObject).toBeDefined();
expect(data.createOneObject.labelSingular).toBe('House');
});
});
@@ -0,0 +1,34 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`View Filter Resolver deleteCoreViewFilter should throw an error when deleting non-existent view filter 1`] = `
{
"extensions": {
"code": "NOT_FOUND",
"userFriendlyMessage": "An error occurred.",
},
"message": "View filter to delete not found",
"name": "NotFoundError",
}
`;
exports[`View Filter Resolver destroyCoreViewFilter should throw an error when destroying non-existent view filter 1`] = `
{
"extensions": {
"code": "NOT_FOUND",
"userFriendlyMessage": "An error occurred.",
},
"message": "View filter to destroy not found",
"name": "NotFoundError",
}
`;
exports[`View Filter Resolver updateCoreViewFilter should throw an error when updating non-existent view filter 1`] = `
{
"extensions": {
"code": "NOT_FOUND",
"userFriendlyMessage": "An error occurred.",
},
"message": "View filter to update not found",
"name": "NotFoundError",
}
`;
@@ -31,6 +31,7 @@ exports[`View Field Resolver - Failing Create Operation - v2 Should fail to crea
"type": "create_view_field",
},
],
"viewFilter": [],
},
"message": "Validation failed for 0 object(s) and 0 field(s)",
"summary": {
@@ -43,6 +44,7 @@ exports[`View Field Resolver - Failing Create Operation - v2 Should fail to crea
"invalidServerlessFunction": 0,
"invalidView": 0,
"invalidViewField": 0,
"invalidViewFilter": 0,
"totalErrors": 0,
},
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
@@ -83,6 +85,7 @@ exports[`View Field Resolver - Failing Create Operation - v2 should fail to crea
"type": "create_view_field",
},
],
"viewFilter": [],
},
"message": "Validation failed for 0 object(s) and 0 field(s)",
"summary": {
@@ -95,6 +98,7 @@ exports[`View Field Resolver - Failing Create Operation - v2 should fail to crea
"invalidServerlessFunction": 0,
"invalidView": 0,
"invalidViewField": 0,
"invalidViewFilter": 0,
"totalErrors": 0,
},
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
@@ -135,6 +139,7 @@ exports[`View Field Resolver - Failing Create Operation - v2 should fail to crea
"type": "create_view_field",
},
],
"viewFilter": [],
},
"message": "Validation failed for 0 object(s) and 0 field(s)",
"summary": {
@@ -147,6 +152,7 @@ exports[`View Field Resolver - Failing Create Operation - v2 should fail to crea
"invalidServerlessFunction": 0,
"invalidView": 0,
"invalidViewField": 0,
"invalidViewFilter": 0,
"totalErrors": 0,
},
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
@@ -31,6 +31,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side
"type": "create_view_field",
},
],
"viewFilter": [],
},
"message": "Validation failed for 0 object(s) and 0 field(s)",
"summary": {
@@ -43,6 +44,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side
"invalidServerlessFunction": 0,
"invalidView": 0,
"invalidViewField": 0,
"invalidViewFilter": 0,
"totalErrors": 0,
},
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
@@ -83,6 +85,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side
"type": "update_view_field",
},
],
"viewFilter": [],
},
"message": "Validation failed for 0 object(s) and 0 field(s)",
"summary": {
@@ -95,6 +98,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side
"invalidServerlessFunction": 0,
"invalidView": 0,
"invalidViewField": 0,
"invalidViewFilter": 0,
"totalErrors": 0,
},
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
@@ -133,6 +137,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side
"type": "delete_view_field",
},
],
"viewFilter": [],
},
"message": "Validation failed for 0 object(s) and 0 field(s)",
"summary": {
@@ -145,6 +150,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side
"invalidServerlessFunction": 0,
"invalidView": 0,
"invalidViewField": 0,
"invalidViewFilter": 0,
"totalErrors": 0,
},
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
@@ -185,6 +191,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side
"type": "update_view_field",
},
],
"viewFilter": [],
},
"message": "Validation failed for 0 object(s) and 0 field(s)",
"summary": {
@@ -197,6 +204,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side
"invalidServerlessFunction": 0,
"invalidView": 0,
"invalidViewField": 0,
"invalidViewFilter": 0,
"totalErrors": 0,
},
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
@@ -237,6 +245,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side
"type": "update_view_field",
},
],
"viewFilter": [],
},
"message": "Validation failed for 0 object(s) and 0 field(s)",
"summary": {
@@ -249,6 +258,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side
"invalidServerlessFunction": 0,
"invalidView": 0,
"invalidViewField": 0,
"invalidViewFilter": 0,
"totalErrors": 0,
},
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
@@ -1,31 +1,18 @@
import { TEST_NOT_EXISTING_VIEW_FILTER_ID } from 'test/integration/constants/test-view-ids.constants';
import { createViewFilterOperationFactory } from 'test/integration/graphql/utils/create-view-filter-operation-factory.util';
import { deleteViewFilterOperationFactory } from 'test/integration/graphql/utils/delete-view-filter-operation-factory.util';
import { destroyViewFilterOperationFactory } from 'test/integration/graphql/utils/destroy-view-filter-operation-factory.util';
import { findViewFiltersOperationFactory } from 'test/integration/graphql/utils/find-view-filters-operation-factory.util';
import {
assertGraphQLErrorResponse,
assertGraphQLSuccessfulResponse,
} from 'test/integration/graphql/utils/graphql-test-assertions.util';
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
import { updateViewFilterOperationFactory } from 'test/integration/graphql/utils/update-view-filter-operation-factory.util';
import { createViewFilterData } from 'test/integration/graphql/utils/view-data-factory.util';
import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util';
import { createTestViewWithGraphQL } from 'test/integration/graphql/utils/view-graphql.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 { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
import {
assertViewFilterStructure,
cleanupViewRecords,
} from 'test/integration/utils/view-test.util';
import { createOneCoreViewFilter } from 'test/integration/metadata/suites/view-filter/utils/create-one-core-view-filter.util';
import { deleteOneCoreViewFilter } from 'test/integration/metadata/suites/view-filter/utils/delete-one-core-view-filter.util';
import { destroyOneCoreViewFilter } from 'test/integration/metadata/suites/view-filter/utils/destroy-one-core-view-filter.util';
import { findCoreViewFilters } from 'test/integration/metadata/suites/view-filter/utils/find-core-view-filters.util';
import { updateOneCoreViewFilter } from 'test/integration/metadata/suites/view-filter/utils/update-one-core-view-filter.util';
import { cleanupViewRecords } from 'test/integration/utils/view-test.util';
import { FieldMetadataType, ViewFilterOperand } from 'twenty-shared/types';
import { ErrorCode } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
import {
generateViewFilterExceptionMessage,
ViewFilterExceptionMessageKey,
} from 'src/engine/metadata-modules/view-filter/exceptions/view-filter.exception';
const TEST_NOT_EXISTING_VIEW_FILTER_ID = '20202020-52c5-4152-8c09-76a845fb8ece';
describe('View Filter Resolver', () => {
let testViewId: string;
@@ -98,33 +85,34 @@ describe('View Filter Resolver', () => {
describe('getCoreViewFilters', () => {
it('should return empty array when no view filters exist', async () => {
const operation = findViewFiltersOperationFactory({ viewId: testViewId });
const response = await makeGraphqlAPIRequest(operation);
const { data, errors } = await findCoreViewFilters({
viewId: testViewId,
expectToFail: false,
});
assertGraphQLSuccessfulResponse(response);
expect(response.body.data.getCoreViewFilters).toEqual([]);
expect(errors).toBeUndefined();
expect(data.getCoreViewFilters).toEqual([]);
});
it('should return view filters for a specific view', async () => {
const filterData = createViewFilterData(testViewId, {
operand: ViewFilterOperand.CONTAINS,
value: 'test',
fieldMetadataId: testFieldMetadataId,
});
const createOperation = createViewFilterOperationFactory({
data: filterData,
await createOneCoreViewFilter({
input: {
fieldMetadataId: testFieldMetadataId,
viewId: testViewId,
operand: ViewFilterOperand.CONTAINS,
value: 'test',
},
expectToFail: false,
});
await makeGraphqlAPIRequest(createOperation);
const getOperation = findViewFiltersOperationFactory({
const { data, errors } = await findCoreViewFilters({
viewId: testViewId,
expectToFail: false,
});
const response = await makeGraphqlAPIRequest(getOperation);
assertGraphQLSuccessfulResponse(response);
expect(response.body.data.getCoreViewFilters).toHaveLength(1);
assertViewFilterStructure(response.body.data.getCoreViewFilters[0], {
expect(errors).toBeUndefined();
expect(data.getCoreViewFilters).toHaveLength(1);
expect(data.getCoreViewFilters[0]).toMatchObject({
fieldMetadataId: testFieldMetadataId,
operand: ViewFilterOperand.CONTAINS,
value: 'test',
@@ -135,17 +123,18 @@ describe('View Filter Resolver', () => {
describe('createCoreViewFilter', () => {
it('should create a new view filter with string value', async () => {
const filterData = createViewFilterData(testViewId, {
operand: ViewFilterOperand.IS,
value: 'test value',
fieldMetadataId: testFieldMetadataId,
const { data, errors } = await createOneCoreViewFilter({
input: {
fieldMetadataId: testFieldMetadataId,
viewId: testViewId,
operand: ViewFilterOperand.IS,
value: 'test value',
},
expectToFail: false,
});
const operation = createViewFilterOperationFactory({ data: filterData });
const response = await makeGraphqlAPIRequest(operation);
assertGraphQLSuccessfulResponse(response);
assertViewFilterStructure(response.body.data.createCoreViewFilter, {
expect(errors).toBeUndefined();
expect(data.createCoreViewFilter).toMatchObject({
fieldMetadataId: testFieldMetadataId,
operand: ViewFilterOperand.IS,
value: 'test value',
@@ -154,17 +143,18 @@ describe('View Filter Resolver', () => {
});
it('should create a view filter with numeric value', async () => {
const filterData = createViewFilterData(testViewId, {
operand: ViewFilterOperand.GREATER_THAN_OR_EQUAL,
value: 100,
fieldMetadataId: testFieldMetadataId,
const { data, errors } = await createOneCoreViewFilter({
input: {
fieldMetadataId: testFieldMetadataId,
viewId: testViewId,
operand: ViewFilterOperand.GREATER_THAN_OR_EQUAL,
value: 100,
},
expectToFail: false,
});
const operation = createViewFilterOperationFactory({ data: filterData });
const response = await makeGraphqlAPIRequest(operation);
assertGraphQLSuccessfulResponse(response);
assertViewFilterStructure(response.body.data.createCoreViewFilter, {
expect(errors).toBeUndefined();
expect(data.createCoreViewFilter).toMatchObject({
fieldMetadataId: testFieldMetadataId,
operand: ViewFilterOperand.GREATER_THAN_OR_EQUAL,
value: 100,
@@ -173,18 +163,18 @@ describe('View Filter Resolver', () => {
});
it('should create a view filter with boolean value', async () => {
const operation = createViewFilterOperationFactory({
data: createViewFilterData(testViewId, {
const { data, errors } = await createOneCoreViewFilter({
input: {
fieldMetadataId: testFieldMetadataId,
viewId: testViewId,
operand: ViewFilterOperand.IS,
value: true,
fieldMetadataId: testFieldMetadataId,
}),
},
expectToFail: false,
});
const response = await makeGraphqlAPIRequest(operation);
assertGraphQLSuccessfulResponse(response);
assertViewFilterStructure(response.body.data.createCoreViewFilter, {
expect(errors).toBeUndefined();
expect(data.createCoreViewFilter).toMatchObject({
fieldMetadataId: testFieldMetadataId,
operand: ViewFilterOperand.IS,
value: true,
@@ -195,30 +185,31 @@ describe('View Filter Resolver', () => {
describe('updateCoreViewFilter', () => {
it('should update an existing view filter', async () => {
const createOperation = createViewFilterOperationFactory({
data: createViewFilterData(testViewId, {
const { data: createData } = await createOneCoreViewFilter({
input: {
fieldMetadataId: testFieldMetadataId,
viewId: testViewId,
operand: ViewFilterOperand.CONTAINS,
value: 'original',
fieldMetadataId: testFieldMetadataId,
}),
});
const createResponse = await makeGraphqlAPIRequest(createOperation);
const viewFilterId = createResponse.body.data.createCoreViewFilter.id;
const updateOperation = updateViewFilterOperationFactory({
viewFilterId: viewFilterId,
data: {
operand: ViewFilterOperand.DOES_NOT_CONTAIN,
value: 'updated',
},
expectToFail: false,
});
const response = await makeGraphqlAPIRequest(updateOperation);
const viewFilterId = createData.createCoreViewFilter.id;
assertGraphQLSuccessfulResponse(response);
assertViewFilterStructure(response.body.data.updateCoreViewFilter, {
const { data, errors } = await updateOneCoreViewFilter({
input: {
id: viewFilterId,
update: {
operand: ViewFilterOperand.DOES_NOT_CONTAIN,
value: 'updated',
},
},
expectToFail: false,
});
expect(errors).toBeUndefined();
expect(data.updateCoreViewFilter).toMatchObject({
id: viewFilterId,
operand: ViewFilterOperand.DOES_NOT_CONTAIN,
value: 'updated',
@@ -226,101 +217,92 @@ describe('View Filter Resolver', () => {
});
it('should throw an error when updating non-existent view filter', async () => {
const operation = updateViewFilterOperationFactory({
viewFilterId: TEST_NOT_EXISTING_VIEW_FILTER_ID,
const { errors } = await updateOneCoreViewFilter({
input: {
id: TEST_NOT_EXISTING_VIEW_FILTER_ID,
update: {},
},
expectToFail: true,
});
const response = await makeGraphqlAPIRequest(operation);
assertGraphQLErrorResponse(
response,
ErrorCode.NOT_FOUND,
generateViewFilterExceptionMessage(
ViewFilterExceptionMessageKey.VIEW_FILTER_NOT_FOUND,
TEST_NOT_EXISTING_VIEW_FILTER_ID,
),
);
expectOneNotInternalServerErrorSnapshot({ errors });
});
});
describe('deleteCoreViewFilter', () => {
it('should delete an existing view filter', async () => {
const createOperation = createViewFilterOperationFactory({
data: createViewFilterData(testViewId, {
const { data: createData } = await createOneCoreViewFilter({
input: {
fieldMetadataId: testFieldMetadataId,
viewId: testViewId,
operand: ViewFilterOperand.CONTAINS,
value: 'to delete',
fieldMetadataId: testFieldMetadataId,
}),
},
expectToFail: false,
});
const createResponse = await makeGraphqlAPIRequest(createOperation);
const viewFilterId = createData.createCoreViewFilter.id;
const viewFilterId = createResponse.body.data.createCoreViewFilter.id;
const deleteOperation = deleteViewFilterOperationFactory({
viewFilterId: viewFilterId,
const { data, errors } = await deleteOneCoreViewFilter({
input: { id: viewFilterId },
expectToFail: false,
});
const response = await makeGraphqlAPIRequest(deleteOperation);
assertGraphQLSuccessfulResponse(response);
expect(response.body.data.deleteCoreViewFilter).toBe(true);
expect(errors).toBeUndefined();
expect(data.deleteCoreViewFilter).toMatchObject({
id: viewFilterId,
});
expect(data.deleteCoreViewFilter.deletedAt).toBeDefined();
});
it('should throw an error when deleting non-existent view filter', async () => {
const operation = deleteViewFilterOperationFactory({
viewFilterId: TEST_NOT_EXISTING_VIEW_FILTER_ID,
const { errors } = await deleteOneCoreViewFilter({
input: { id: TEST_NOT_EXISTING_VIEW_FILTER_ID },
expectToFail: true,
});
const response = await makeGraphqlAPIRequest(operation);
assertGraphQLErrorResponse(
response,
ErrorCode.NOT_FOUND,
generateViewFilterExceptionMessage(
ViewFilterExceptionMessageKey.VIEW_FILTER_NOT_FOUND,
TEST_NOT_EXISTING_VIEW_FILTER_ID,
),
);
expectOneNotInternalServerErrorSnapshot({ errors });
});
});
describe('destroyCoreViewFilter', () => {
it('should destroy an existing view filter', async () => {
const createOperation = createViewFilterOperationFactory({
data: createViewFilterData(testViewId, {
const { data: createData } = await createOneCoreViewFilter({
input: {
fieldMetadataId: testFieldMetadataId,
viewId: testViewId,
operand: ViewFilterOperand.CONTAINS,
value: 'to destroy',
fieldMetadataId: testFieldMetadataId,
}),
},
expectToFail: false,
});
const createResponse = await makeGraphqlAPIRequest(createOperation);
const viewFilterId = createData.createCoreViewFilter.id;
const viewFilterId = createResponse.body.data.createCoreViewFilter.id;
const destroyOperation = destroyViewFilterOperationFactory({
viewFilterId: viewFilterId,
await deleteOneCoreViewFilter({
input: {
id: viewFilterId,
},
expectToFail: false,
});
const { data, errors } = await destroyOneCoreViewFilter({
input: { id: viewFilterId },
expectToFail: false,
});
const response = await makeGraphqlAPIRequest(destroyOperation);
assertGraphQLSuccessfulResponse(response);
expect(response.body.data.destroyCoreViewFilter).toBe(true);
expect(errors).toBeUndefined();
expect(data.destroyCoreViewFilter).toMatchObject({
id: viewFilterId,
});
});
it('should throw an error when destroying non-existent view filter', async () => {
const operation = destroyViewFilterOperationFactory({
viewFilterId: TEST_NOT_EXISTING_VIEW_FILTER_ID,
const { errors } = await destroyOneCoreViewFilter({
input: { id: TEST_NOT_EXISTING_VIEW_FILTER_ID },
expectToFail: true,
});
const response = await makeGraphqlAPIRequest(operation);
assertGraphQLErrorResponse(
response,
ErrorCode.NOT_FOUND,
generateViewFilterExceptionMessage(
ViewFilterExceptionMessageKey.VIEW_FILTER_NOT_FOUND,
TEST_NOT_EXISTING_VIEW_FILTER_ID,
),
);
expectOneNotInternalServerErrorSnapshot({ errors });
});
});
});
@@ -1,25 +0,0 @@
import gql from 'graphql-tag';
import { VIEW_FILTER_GQL_FIELDS } from 'test/integration/constants/view-gql-fields.constants';
import { type ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
type CreateViewFilterOperationFactoryParams = {
gqlFields?: string;
data?: Partial<ViewFilterEntity>;
};
export const createViewFilterOperationFactory = ({
gqlFields = VIEW_FILTER_GQL_FIELDS,
data = {},
}: CreateViewFilterOperationFactoryParams = {}) => ({
query: gql`
mutation CreateCoreViewFilter($input: CreateViewFilterInput!) {
createCoreViewFilter(input: $input) {
${gqlFields}
}
}
`,
variables: {
input: data,
},
});
@@ -1,16 +0,0 @@
import gql from 'graphql-tag';
export const deleteViewFilterOperationFactory = ({
viewFilterId,
}: {
viewFilterId: string;
}) => ({
query: gql`
mutation DeleteCoreViewFilter($id: String!) {
deleteCoreViewFilter(id: $id)
}
`,
variables: {
id: viewFilterId,
},
});
@@ -1,16 +0,0 @@
import gql from 'graphql-tag';
export const destroyViewFilterOperationFactory = ({
viewFilterId,
}: {
viewFilterId: string;
}) => ({
query: gql`
mutation DestroyCoreViewFilter($id: String!) {
destroyCoreViewFilter(id: $id)
}
`,
variables: {
id: viewFilterId,
},
});
@@ -1,21 +0,0 @@
import gql from 'graphql-tag';
import { VIEW_FILTER_GQL_FIELDS } from 'test/integration/constants/view-gql-fields.constants';
export const findViewFiltersOperationFactory = ({
gqlFields = VIEW_FILTER_GQL_FIELDS,
viewId,
}: {
gqlFields?: string;
viewId: string;
}) => ({
query: gql`
query GetCoreViewFilters($viewId: String!) {
getCoreViewFilters(viewId: $viewId) {
${gqlFields}
}
}
`,
variables: {
viewId,
},
});
@@ -1,24 +0,0 @@
import gql from 'graphql-tag';
import { VIEW_FILTER_GQL_FIELDS } from 'test/integration/constants/view-gql-fields.constants';
export const updateViewFilterOperationFactory = ({
gqlFields = VIEW_FILTER_GQL_FIELDS,
viewFilterId,
data = {},
}: {
gqlFields?: string;
viewFilterId: string;
data?: object;
}) => ({
query: gql`
mutation UpdateCoreViewFilter($id: String!, $input: UpdateViewFilterInput!) {
updateCoreViewFilter(id: $id, input: $input) {
${gqlFields}
}
}
`,
variables: {
id: viewFilterId,
input: data,
},
});