Add integration tests for /metadata + fix relation deletion (#8706)
In this PR 1. Add integration tests for /metadata (master issue: https://github.com/twentyhq/twenty/issues/8719) 2. Fix relation deletion: index on "from" object was not deleted, impeding creation of a new relation between the same two objects A and B after relation between A and B was deleted
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { isDefined } from 'class-validator';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
@ -19,6 +19,7 @@ import {
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
import { isDefined } from 'src/utils/is-defined';
|
||||
|
||||
@Injectable()
|
||||
export class IndexMetadataService {
|
||||
@ -43,6 +44,10 @@ export class IndexMetadataService {
|
||||
(fieldMetadata) => fieldMetadata.name as string,
|
||||
);
|
||||
|
||||
if (isEmpty(columnNames)) {
|
||||
throw new Error('Column names must not be empty');
|
||||
}
|
||||
|
||||
const indexName = `IDX_${generateDeterministicIndexName([tableName, ...columnNames])}`;
|
||||
|
||||
let result: IndexMetadataEntity;
|
||||
@ -98,6 +103,44 @@ export class IndexMetadataService {
|
||||
);
|
||||
}
|
||||
|
||||
async deleteIndexMetadata(
|
||||
workspaceId: string,
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
fieldMetadataToIndex: Partial<FieldMetadataEntity>[],
|
||||
) {
|
||||
const tableName = computeObjectTargetTable(objectMetadata);
|
||||
|
||||
const columnNames: string[] = fieldMetadataToIndex.map(
|
||||
(fieldMetadata) => fieldMetadata.name as string,
|
||||
);
|
||||
|
||||
if (isEmpty(columnNames)) {
|
||||
throw new Error('Column names must not be empty');
|
||||
}
|
||||
|
||||
const indexName = `IDX_${generateDeterministicIndexName([tableName, ...columnNames])}`;
|
||||
|
||||
const indexMetadata = await this.indexMetadataRepository.findOne({
|
||||
where: {
|
||||
name: indexName,
|
||||
objectMetadataId: objectMetadata.id,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!indexMetadata) {
|
||||
throw new Error(`Index metadata with name ${indexName} not found`);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.indexMetadataRepository.delete(indexMetadata.id);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to delete index metadata with name ${indexName} (error: ${error.message})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async createIndexCreationMigration(
|
||||
workspaceId: string,
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
|
||||
@ -36,6 +36,7 @@ import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
|
||||
import { BASE_OBJECT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { isDefined } from 'src/utils/is-defined';
|
||||
|
||||
import {
|
||||
RelationMetadataEntity,
|
||||
@ -137,22 +138,20 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
);
|
||||
}
|
||||
|
||||
const deletedFieldMetadata = toObjectMetadata.fields.find(
|
||||
const deletedAtFieldMetadata = toObjectMetadata.fields.find(
|
||||
(fieldMetadata) =>
|
||||
fieldMetadata.standardId === BASE_OBJECT_STANDARD_FIELD_IDS.deletedAt,
|
||||
);
|
||||
|
||||
if (!deletedFieldMetadata) {
|
||||
throw new RelationMetadataException(
|
||||
`Deleted field metadata not found`,
|
||||
RelationMetadataExceptionCode.RELATION_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
this.throwIfDeletedAtFieldMetadataNotFound(deletedAtFieldMetadata);
|
||||
|
||||
await this.indexMetadataService.createIndexMetadata(
|
||||
relationMetadataInput.workspaceId,
|
||||
toObjectMetadata,
|
||||
[foreignKeyFieldMetadata, deletedFieldMetadata],
|
||||
[
|
||||
foreignKeyFieldMetadata,
|
||||
deletedAtFieldMetadata as FieldMetadataEntity<'default'>,
|
||||
],
|
||||
false,
|
||||
false,
|
||||
);
|
||||
@ -441,6 +440,24 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
columnName,
|
||||
);
|
||||
|
||||
const deletedAtFieldMetadata = await this.fieldMetadataRepository.findOneBy(
|
||||
{
|
||||
objectMetadataId: relationMetadata.toObjectMetadataId,
|
||||
name: 'deletedAt',
|
||||
},
|
||||
);
|
||||
|
||||
this.throwIfDeletedAtFieldMetadataNotFound(deletedAtFieldMetadata);
|
||||
|
||||
await this.indexMetadataService.deleteIndexMetadata(
|
||||
workspaceId,
|
||||
relationMetadata.toObjectMetadata,
|
||||
[
|
||||
foreignKeyFieldMetadata,
|
||||
deletedAtFieldMetadata as FieldMetadataEntity<'default'>,
|
||||
],
|
||||
);
|
||||
|
||||
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
||||
relationMetadata.workspaceId,
|
||||
);
|
||||
@ -554,4 +571,15 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
private throwIfDeletedAtFieldMetadataNotFound(
|
||||
deletedAtFieldMetadata?: FieldMetadataEntity<'default'> | null,
|
||||
) {
|
||||
if (!isDefined(deletedAtFieldMetadata)) {
|
||||
throw new RelationMetadataException(
|
||||
`Deleted field metadata not found`,
|
||||
RelationMetadataExceptionCode.RELATION_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,318 @@
|
||||
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
|
||||
import { createOneObjectMetadataFactory } from 'test/integration/metadata/suites/utils/create-one-object-metadata-factory.util';
|
||||
import { createOneRelationMetadataFactory } from 'test/integration/metadata/suites/utils/create-one-relation-metadata-factory.util';
|
||||
import { deleteOneObjectMetadataItemFactory } from 'test/integration/metadata/suites/utils/delete-one-object-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 { objectsMetadataFactory } from 'test/integration/metadata/suites/utils/objects-metadata-factory.util';
|
||||
import { updateOneObjectMetadataItemFactory } from 'test/integration/metadata/suites/utils/update-one-object-metadata-factory.util';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
const LISTING_NAME_SINGULAR = 'listing';
|
||||
|
||||
describe('Custom object renaming', () => {
|
||||
let listingObjectId = '';
|
||||
let customRelationId = '';
|
||||
|
||||
const STANDARD_OBJECT_RELATIONS = [
|
||||
'noteTarget',
|
||||
'attachment',
|
||||
'favorite',
|
||||
'taskTarget',
|
||||
'timelineActivity',
|
||||
];
|
||||
|
||||
const standardObjectRelationsMap = STANDARD_OBJECT_RELATIONS.reduce(
|
||||
(acc, relation) => ({
|
||||
...acc,
|
||||
[relation]: {
|
||||
objectMetadataId: '',
|
||||
foreignKeyFieldMetadataId: '',
|
||||
relationFieldMetadataId: '',
|
||||
},
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
const standardObjectsGraphqlOperation = objectsMetadataFactory({
|
||||
gqlFields: `
|
||||
id
|
||||
nameSingular
|
||||
`,
|
||||
input: {
|
||||
filter: {
|
||||
isCustom: { isNot: true },
|
||||
},
|
||||
paging: { first: 1000 },
|
||||
},
|
||||
});
|
||||
|
||||
const fieldsGraphqlOperation = fieldsMetadataFactory({
|
||||
gqlFields: `
|
||||
id
|
||||
name
|
||||
label
|
||||
type
|
||||
object {
|
||||
id
|
||||
}
|
||||
`,
|
||||
input: {
|
||||
filter: {},
|
||||
paging: { first: 1000 },
|
||||
},
|
||||
});
|
||||
|
||||
const fillStandardObjectRelationsMapObjectMetadataId = (standardObjects) => {
|
||||
STANDARD_OBJECT_RELATIONS.forEach((relation) => {
|
||||
standardObjectRelationsMap[relation].objectMetadataId =
|
||||
standardObjects.body.data.objects.edges.find(
|
||||
(object) => object.node.nameSingular === relation,
|
||||
).node.id;
|
||||
});
|
||||
};
|
||||
|
||||
it('1. should create one custom object with standard relations', async () => {
|
||||
// Arrange
|
||||
const standardObjects = await makeMetadataAPIRequest(
|
||||
standardObjectsGraphqlOperation,
|
||||
);
|
||||
|
||||
fillStandardObjectRelationsMapObjectMetadataId(standardObjects);
|
||||
|
||||
const LISTING_OBJECT = {
|
||||
namePlural: 'listings',
|
||||
nameSingular: LISTING_NAME_SINGULAR,
|
||||
labelPlural: 'Listings',
|
||||
labelSingular: 'Listing',
|
||||
description: 'Listing object',
|
||||
icon: 'IconListNumbers',
|
||||
isLabelSyncedWithName: false,
|
||||
};
|
||||
|
||||
// Act
|
||||
const graphqlOperation = createOneObjectMetadataFactory({
|
||||
input: { object: LISTING_OBJECT },
|
||||
gqlFields: `
|
||||
id
|
||||
nameSingular
|
||||
`,
|
||||
});
|
||||
|
||||
const response = await makeMetadataAPIRequest(graphqlOperation);
|
||||
|
||||
// Assert
|
||||
expect(response.body.data.createOneObject.nameSingular).toBe(
|
||||
LISTING_NAME_SINGULAR,
|
||||
);
|
||||
|
||||
listingObjectId = response.body.data.createOneObject.id;
|
||||
|
||||
const fields = await makeMetadataAPIRequest(fieldsGraphqlOperation);
|
||||
|
||||
const foreignKeyFieldsMetadataForListing = fields.body.data.fields.edges
|
||||
.filter((field) => field.node.name === `${LISTING_NAME_SINGULAR}Id`)
|
||||
.map((field) => field.node);
|
||||
|
||||
const relationFieldsMetadataForListing = fields.body.data.fields.edges
|
||||
.filter(
|
||||
(field) =>
|
||||
field.node.name === `${LISTING_NAME_SINGULAR}` &&
|
||||
field.node.type === FieldMetadataType.RELATION,
|
||||
)
|
||||
.map((field) => field.node);
|
||||
|
||||
expect(foreignKeyFieldsMetadataForListing.length).toBe(5);
|
||||
|
||||
STANDARD_OBJECT_RELATIONS.forEach((relation) => {
|
||||
// foreignKey field
|
||||
const foreignKeyFieldMetadataId = foreignKeyFieldsMetadataForListing.find(
|
||||
(field) =>
|
||||
field.object.id ===
|
||||
standardObjectRelationsMap[relation].objectMetadataId,
|
||||
).id;
|
||||
|
||||
expect(foreignKeyFieldMetadataId).not.toBeUndefined();
|
||||
|
||||
standardObjectRelationsMap[relation].foreignKeyFieldMetadataId =
|
||||
foreignKeyFieldMetadataId;
|
||||
|
||||
// relation field
|
||||
const relationFieldMetadataId = relationFieldsMetadataForListing.find(
|
||||
(field) =>
|
||||
field.object.id ===
|
||||
standardObjectRelationsMap[relation].objectMetadataId,
|
||||
).id;
|
||||
|
||||
expect(relationFieldMetadataId).not.toBeUndefined();
|
||||
|
||||
standardObjectRelationsMap[relation].relationFieldMetadataId =
|
||||
relationFieldMetadataId;
|
||||
});
|
||||
});
|
||||
|
||||
let relationFieldMetadataOnPersonId = '';
|
||||
const RELATION_FROM_NAME = 'guest';
|
||||
|
||||
it('2. should create a custom relation with the custom object', async () => {
|
||||
// Arrange
|
||||
const standardObjects = await makeMetadataAPIRequest(
|
||||
standardObjectsGraphqlOperation,
|
||||
);
|
||||
const personObjectId = standardObjects.body.data.objects.edges.find(
|
||||
(object) => object.node.nameSingular === 'person',
|
||||
).node.id;
|
||||
|
||||
// Act
|
||||
const createRelationGraphqlOperation = createOneRelationMetadataFactory({
|
||||
input: {
|
||||
relation: {
|
||||
fromDescription: '',
|
||||
fromIcon: 'IconRelationOneToMany',
|
||||
fromLabel: 'Guest',
|
||||
fromName: RELATION_FROM_NAME,
|
||||
fromObjectMetadataId: listingObjectId,
|
||||
relationType: RelationMetadataType.ONE_TO_MANY,
|
||||
toDescription: undefined,
|
||||
toIcon: 'IconListNumbers',
|
||||
toLabel: 'Property',
|
||||
toName: 'property',
|
||||
toObjectMetadataId: personObjectId,
|
||||
},
|
||||
},
|
||||
gqlFields: `
|
||||
id
|
||||
fromFieldMetadataId
|
||||
`,
|
||||
});
|
||||
|
||||
const relationResponse = await makeMetadataAPIRequest(
|
||||
createRelationGraphqlOperation,
|
||||
);
|
||||
|
||||
// Assert
|
||||
customRelationId = relationResponse.body.data.createOneRelation.id;
|
||||
|
||||
relationFieldMetadataOnPersonId =
|
||||
relationResponse.body.data.createOneRelation.fromFieldMetadataId;
|
||||
});
|
||||
|
||||
it('3. should rename custom object', async () => {
|
||||
// Arrange
|
||||
const HOUSE_NAME_SINGULAR = 'house';
|
||||
const HOUSE_NAME_PLURAL = 'houses';
|
||||
const HOUSE_LABEL_SINGULAR = 'House';
|
||||
const HOUSE_LABEL_PLURAL = 'Houses';
|
||||
const updateListingNameGraphqlOperation =
|
||||
updateOneObjectMetadataItemFactory({
|
||||
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,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 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);
|
||||
|
||||
const fieldsResponse = await makeMetadataAPIRequest(fieldsGraphqlOperation);
|
||||
|
||||
const fieldsMetadata = fieldsResponse.body.data.fields.edges.map(
|
||||
(field) => field.node,
|
||||
);
|
||||
|
||||
expect(
|
||||
fieldsMetadata.find(
|
||||
(field) => field.name === `${LISTING_NAME_SINGULAR}Id`,
|
||||
),
|
||||
).toBeUndefined();
|
||||
|
||||
// standard relations have been updated
|
||||
STANDARD_OBJECT_RELATIONS.forEach((relation) => {
|
||||
// foreignKey field
|
||||
const foreignKeyFieldMetadataId =
|
||||
standardObjectRelationsMap[relation].foreignKeyFieldMetadataId;
|
||||
|
||||
const updatedForeignKeyFieldMetadata = fieldsMetadata.find(
|
||||
(field) => field.id === foreignKeyFieldMetadataId,
|
||||
);
|
||||
|
||||
expect(updatedForeignKeyFieldMetadata.name).toBe(
|
||||
`${HOUSE_NAME_SINGULAR}Id`,
|
||||
);
|
||||
expect(updatedForeignKeyFieldMetadata.label).toBe(
|
||||
'House ID (foreign key)',
|
||||
);
|
||||
|
||||
// relation field
|
||||
const relationFieldMetadataId =
|
||||
standardObjectRelationsMap[relation].relationFieldMetadataId;
|
||||
|
||||
const updatedRelationFieldMetadataId = fieldsMetadata.find(
|
||||
(field) => field.id === relationFieldMetadataId,
|
||||
);
|
||||
|
||||
expect(updatedRelationFieldMetadataId.name).toBe(HOUSE_NAME_SINGULAR);
|
||||
expect(updatedRelationFieldMetadataId.label).toBe(HOUSE_LABEL_SINGULAR);
|
||||
});
|
||||
|
||||
// custom relation are unchanged
|
||||
const updatedRelationFieldMetadata = fieldsMetadata.find(
|
||||
(field) => field.id === relationFieldMetadataOnPersonId,
|
||||
);
|
||||
|
||||
expect(updatedRelationFieldMetadata.name).toBe(RELATION_FROM_NAME);
|
||||
});
|
||||
|
||||
it('4. should delete custom relation', async () => {
|
||||
const graphqlOperation = deleteOneRelationMetadataItemFactory({
|
||||
idToDelete: customRelationId,
|
||||
});
|
||||
|
||||
const response = await makeMetadataAPIRequest(graphqlOperation);
|
||||
|
||||
const deleteRelationResponse = response.body.data.deleteOneRelation;
|
||||
|
||||
expect(deleteRelationResponse.id).toBe(customRelationId);
|
||||
});
|
||||
|
||||
it('5. should delete custom object', async () => {
|
||||
const graphqlOperation = deleteOneObjectMetadataItemFactory({
|
||||
idToDelete: listingObjectId,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const deleteListingResponse = response.body.data.deleteOneObject;
|
||||
|
||||
expect(deleteListingResponse.id).toBe(listingObjectId);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,24 @@
|
||||
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<CreateObjectInput, 'workspaceId' | 'dataSourceId'> };
|
||||
};
|
||||
|
||||
export const createOneObjectMetadataFactory = ({
|
||||
gqlFields,
|
||||
input,
|
||||
}: CreateOneObjectFactoryParams) => ({
|
||||
query: gql`
|
||||
mutation CreateOneObjectMetadataItem($input: CreateOneObjectInput!) {
|
||||
createOneObject(input: $input) {
|
||||
${gqlFields}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
input,
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,26 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { CreateRelationInput } from 'src/engine/metadata-modules/relation-metadata/dtos/create-relation.input';
|
||||
|
||||
type CreateOneRelationFactoryParams = {
|
||||
gqlFields: string;
|
||||
input?: {
|
||||
relation: Omit<CreateRelationInput, 'workspaceId'>;
|
||||
};
|
||||
};
|
||||
|
||||
export const createOneRelationMetadataFactory = ({
|
||||
gqlFields,
|
||||
input,
|
||||
}: CreateOneRelationFactoryParams) => ({
|
||||
query: gql`
|
||||
mutation CreateOneRelationMetadata($input: CreateOneRelationInput!) {
|
||||
createOneRelation(input: $input) {
|
||||
${gqlFields}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
input,
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,20 @@
|
||||
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,
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,22 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
type DeleteOneRelationFactoryParams = {
|
||||
idToDelete: string;
|
||||
};
|
||||
|
||||
export const deleteOneRelationMetadataItemFactory = ({
|
||||
idToDelete,
|
||||
}: DeleteOneRelationFactoryParams) => ({
|
||||
query: gql`
|
||||
mutation DeleteOneRelation($input: DeleteOneRelationInput!) {
|
||||
deleteOneRelation(input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
input: {
|
||||
id: idToDelete,
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,30 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
type FieldsFactoryParams = {
|
||||
gqlFields: string;
|
||||
input: {
|
||||
filter: object;
|
||||
paging: object;
|
||||
};
|
||||
};
|
||||
|
||||
export const fieldsMetadataFactory = ({
|
||||
gqlFields,
|
||||
input,
|
||||
}: FieldsFactoryParams) => ({
|
||||
query: gql`
|
||||
query FieldsMetadata($filter: fieldFilter!, $paging: CursorPaging!) {
|
||||
fields(filter: $filter, paging: $paging) {
|
||||
edges {
|
||||
node {
|
||||
${gqlFields}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
filter: input.filter,
|
||||
paging: input.paging,
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,19 @@
|
||||
import { ASTNode, print } from 'graphql';
|
||||
import request from 'supertest';
|
||||
|
||||
type GraphqlOperation = {
|
||||
query: ASTNode;
|
||||
variables?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export const makeMetadataAPIRequest = (graphqlOperation: GraphqlOperation) => {
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
return client
|
||||
.post('/metadata')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.send({
|
||||
query: print(graphqlOperation.query),
|
||||
variables: graphqlOperation.variables || {},
|
||||
});
|
||||
};
|
||||
@ -0,0 +1,30 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
type ObjectsFactoryParams = {
|
||||
gqlFields: string;
|
||||
input: {
|
||||
filter: object;
|
||||
paging: object;
|
||||
};
|
||||
};
|
||||
|
||||
export const objectsMetadataFactory = ({
|
||||
gqlFields,
|
||||
input,
|
||||
}: ObjectsFactoryParams) => ({
|
||||
query: gql`
|
||||
query ObjectsMetadata($filter: objectFilter!, $paging: CursorPaging!) {
|
||||
objects(filter: $filter, paging: $paging) {
|
||||
edges {
|
||||
node {
|
||||
${gqlFields}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
filter: input.filter,
|
||||
paging: input.paging,
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,28 @@
|
||||
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,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user