Add unique indexes and indexes for composite types (#7162)
Add support for indexes on composite fields and unicity constraint on indexes This pull request includes several changes across multiple files to improve error handling, enforce unique constraints, and update database migrations. The most important changes include updating error messages for snack bars, adding a new command to enforce unique constraints, and updating database migrations to include new fields and constraints. ### Error Handling Improvements: * [`packages/twenty-front/src/modules/error-handler/components/PromiseRejectionEffect.tsx`](diffhunk://#diff-e7dc05ced8e4730430f5c7fcd0c75b3aa723da438c26e0bef8130b614427dd9aL23-R23): Updated error messages in `enqueueSnackBar` to use `error.message` directly. * [`packages/twenty-front/src/modules/object-metadata/hooks/useFindManyObjectMetadataItems.ts`](diffhunk://#diff-74c126d6bc7a5ed6b63be994d298df6669058034bfbc367b11045f9f31a3abe6L44-R46): Simplified error messages in `enqueueSnackBar`. * [`packages/twenty-front/src/modules/object-record/hooks/useFindDuplicateRecords.ts`](diffhunk://#diff-af23a1d99639a66c251f87473e63e2b7bceaa4ee4f70fedfa0fcffe5c7d79181L56-R58): Simplified error messages in `enqueueSnackBar`. * [`packages/twenty-front/src/modules/object-record/hooks/useHandleFindManyRecordsError.ts`](diffhunk://#diff-da04296cbe280202a1eaf6b1244a30490d4f400411bee139651172c59719088eL22-R24): Simplified error messages in `enqueueSnackBar`. ### New Command for Unique Constraints: * [`packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-enforce-unique-constraints.command.ts`](diffhunk://#diff-8337096c8c80dd2619a5ba691ae5145101f8ae0368a75192a050047e8c6ab7cbR1-R159): Added a new command to enforce unique constraints on company domain names and person emails. * [`packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-upgrade-version.command.ts`](diffhunk://#diff-20215e9981a53c7566e9cbff96715685125878f5bcb84fe461a7440f2e68f6fcR13-R14): Integrated the new `EnforceUniqueConstraintsCommand` into the upgrade process. [[1]](diffhunk://#diff-20215e9981a53c7566e9cbff96715685125878f5bcb84fe461a7440f2e68f6fcR13-R14) [[2]](diffhunk://#diff-20215e9981a53c7566e9cbff96715685125878f5bcb84fe461a7440f2e68f6fcR31) [[3]](diffhunk://#diff-20215e9981a53c7566e9cbff96715685125878f5bcb84fe461a7440f2e68f6fcR64-R68) * [`packages/twenty-server/src/database/commands/upgrade-version/0-31/0-31-upgrade-version.module.ts`](diffhunk://#diff-da52814efc674c25ed55645f8ee2561013641a407f88423e705dd6c77b405527R7): Registered the new `EnforceUniqueConstraintsCommand` in the module. [[1]](diffhunk://#diff-da52814efc674c25ed55645f8ee2561013641a407f88423e705dd6c77b405527R7) [[2]](diffhunk://#diff-da52814efc674c25ed55645f8ee2561013641a407f88423e705dd6c77b405527R24) ### Database Migrations: * [`packages/twenty-server/src/database/typeorm/metadata/migrations/1726757368824-migrationDebt.ts`](diffhunk://#diff-c450aeae7bc0ef4416a0ade2dc613ca3f688629f35d2a32f90a09c3f494febdcR1-R53): Added a migration to update the `relationMetadata_ondeleteaction_enum` and set default values. * [`packages/twenty-server/src/database/typeorm/metadata/migrations/1726757368825-addIsUniqueToIndexMetadata.ts`](diffhunk://#diff-8f1e14bd7f6835ec2c3bb39bcc51e3c318a3008d576a981e682f4c985e746fbfR1-R19): Added a migration to include the `isUnique` field in `indexMetadata`. * [`packages/twenty-server/src/database/typeorm/metadata/migrations/1726762935841-addCompostiveColumnToIndexFieldMetadata.ts`](diffhunk://#diff-7c96b7276c7722d41ff31de23b2de4d6e09adfdc74815356ba63bc96a2669440R1-R19): Added a migration to include the `compositeColumn` field in `indexFieldMetadata`. * [`packages/twenty-server/src/database/typeorm/metadata/migrations/1726766871572-addWhereToIndexMetadata.ts`](diffhunk://#diff-26651295a975eb50e672dce0e4e274e861f66feb1b68105eee5a04df32796190R1-R14): Added a migration to include the `indexWhereClause` field in `indexMetadata`. ### GraphQL Exception Handling: * [`packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util.ts`](diffhunk://#diff-58445eb362dc89e31107777d39b592d7842d2ab09a223012ccd055da325270a8R1-R4): Enhanced exception handling for `QueryFailedError` to provide more specific error messages for unique constraint violations. [[1]](diffhunk://#diff-58445eb362dc89e31107777d39b592d7842d2ab09a223012ccd055da325270a8R1-R4) [[2]](diffhunk://#diff-58445eb362dc89e31107777d39b592d7842d2ab09a223012ccd055da325270a8R23-R59) * [`packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts`](diffhunk://#diff-233d58ab2333586dd45e46e33d4f07e04a4b8adde4a11a48e25d86985e5a7943L58-R58): Updated the `workspaceQueryRunnerGraphqlApiExceptionHandler` call to include context. * [`packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts`](diffhunk://#diff-68b803f0762c407f5d2d1f5f8d389655a60654a2dd2394a81318655dcd44dc43L58-R58): Updated the `workspaceQueryRunnerGraphqlApiExceptionHandler` call to include context. --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import { ObjectLiteral, WhereExpressionBuilder } from 'typeorm';
|
||||
import { WhereExpressionBuilder } from 'typeorm';
|
||||
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
@ -6,17 +6,13 @@ import {
|
||||
GraphqlQueryRunnerException,
|
||||
GraphqlQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||
import { computeWhereConditionParts } from 'src/engine/api/graphql/graphql-query-runner/utils/compute-where-condition-parts';
|
||||
import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { FieldMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
|
||||
import { CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||
import { capitalize } from 'src/utils/capitalize';
|
||||
|
||||
type WhereConditionParts = {
|
||||
sql: string;
|
||||
params: ObjectLiteral;
|
||||
};
|
||||
|
||||
export class GraphqlQueryFilterFieldParser {
|
||||
private fieldMetadataMap: FieldMetadataMap;
|
||||
|
||||
@ -57,7 +53,7 @@ export class GraphqlQueryFilterFieldParser {
|
||||
}
|
||||
}
|
||||
|
||||
const { sql, params } = this.computeWhereConditionParts(
|
||||
const { sql, params } = computeWhereConditionParts(
|
||||
operator,
|
||||
objectNameSingular,
|
||||
key,
|
||||
@ -71,83 +67,6 @@ export class GraphqlQueryFilterFieldParser {
|
||||
}
|
||||
}
|
||||
|
||||
private computeWhereConditionParts(
|
||||
operator: string,
|
||||
objectNameSingular: string,
|
||||
key: string,
|
||||
value: any,
|
||||
): WhereConditionParts {
|
||||
const uuid = Math.random().toString(36).slice(2, 7);
|
||||
|
||||
switch (operator) {
|
||||
case 'eq':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" = :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
case 'neq':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" != :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
case 'gt':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" > :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
case 'gte':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" >= :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
case 'lt':
|
||||
return {
|
||||
sql: `"${objectNameSingular}".${key} < :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
case 'lte':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" <= :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
case 'in':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" IN (:...${key}${uuid})`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
case 'is':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" IS ${value === 'NULL' ? 'NULL' : 'NOT NULL'}`,
|
||||
params: {},
|
||||
};
|
||||
case 'like':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" LIKE :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: `${value}` },
|
||||
};
|
||||
case 'ilike':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" ILIKE :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: `${value}` },
|
||||
};
|
||||
case 'startsWith':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" LIKE :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: `${value}` },
|
||||
};
|
||||
case 'endsWith':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" LIKE :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: `${value}` },
|
||||
};
|
||||
default:
|
||||
throw new GraphqlQueryRunnerException(
|
||||
`Operator "${operator}" is not supported`,
|
||||
GraphqlQueryRunnerExceptionCode.UNSUPPORTED_OPERATOR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private parseCompositeFieldForFilter(
|
||||
queryBuilder: WhereExpressionBuilder,
|
||||
fieldMetadata: FieldMetadataInterface,
|
||||
@ -182,7 +101,7 @@ export class GraphqlQueryFilterFieldParser {
|
||||
subFieldFilter as Record<string, any>,
|
||||
);
|
||||
|
||||
const { sql, params } = this.computeWhereConditionParts(
|
||||
const { sql, params } = computeWhereConditionParts(
|
||||
operator,
|
||||
objectNameSingular,
|
||||
fullFieldName,
|
||||
|
||||
@ -107,6 +107,7 @@ export class GraphqlQueryCreateManyResolverService
|
||||
options: WorkspaceQueryRunnerOptions,
|
||||
): Promise<void> {
|
||||
assertMutationNotOnRemoteObject(options.objectMetadataItem);
|
||||
|
||||
args.data.forEach((record) => {
|
||||
if (record?.id) {
|
||||
assertIsValidUuid(record.id);
|
||||
|
||||
@ -0,0 +1,88 @@
|
||||
import { ObjectLiteral } from 'typeorm';
|
||||
|
||||
import {
|
||||
GraphqlQueryRunnerException,
|
||||
GraphqlQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||
|
||||
type WhereConditionParts = {
|
||||
sql: string;
|
||||
params: ObjectLiteral;
|
||||
};
|
||||
|
||||
export const computeWhereConditionParts = (
|
||||
operator: string,
|
||||
objectNameSingular: string,
|
||||
key: string,
|
||||
value: any,
|
||||
): WhereConditionParts => {
|
||||
const uuid = Math.random().toString(36).slice(2, 7);
|
||||
|
||||
switch (operator) {
|
||||
case 'eq':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" = :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
case 'neq':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" != :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
case 'gt':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" > :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
case 'gte':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" >= :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
case 'lt':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" < :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
case 'lte':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" <= :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
case 'in':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" IN (:...${key}${uuid})`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
case 'is':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" IS ${value === 'NULL' ? 'NULL' : 'NOT NULL'}`,
|
||||
params: {},
|
||||
};
|
||||
case 'like':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" LIKE :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: `${value}` },
|
||||
};
|
||||
case 'ilike':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" ILIKE :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: `${value}` },
|
||||
};
|
||||
case 'startsWith':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" LIKE :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: `${value}` },
|
||||
};
|
||||
case 'endsWith':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" LIKE :${key}${uuid}`,
|
||||
params: { [`${key}${uuid}`]: `${value}` },
|
||||
};
|
||||
default:
|
||||
throw new GraphqlQueryRunnerException(
|
||||
`Operator "${operator}" is not supported`,
|
||||
GraphqlQueryRunnerExceptionCode.UNSUPPORTED_OPERATOR,
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -1,3 +1,7 @@
|
||||
import { QueryFailedError } from 'typeorm';
|
||||
|
||||
import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface';
|
||||
|
||||
import {
|
||||
GraphqlQueryRunnerException,
|
||||
GraphqlQueryRunnerExceptionCode,
|
||||
@ -16,7 +20,51 @@ import {
|
||||
|
||||
export const workspaceQueryRunnerGraphqlApiExceptionHandler = (
|
||||
error: Error,
|
||||
context: WorkspaceSchemaBuilderContext,
|
||||
) => {
|
||||
if (error instanceof QueryFailedError) {
|
||||
if (
|
||||
error.message.includes('duplicate key value violates unique constraint')
|
||||
) {
|
||||
const indexNameMatch = error.message.match(/"([^"]+)"/);
|
||||
|
||||
if (indexNameMatch) {
|
||||
const indexName = indexNameMatch[1];
|
||||
|
||||
const deletedAtFieldMetadata = context.objectMetadataItem.fields.find(
|
||||
(field) => field.name === 'deletedAt',
|
||||
);
|
||||
|
||||
const affectedColumns = context.objectMetadataItem.indexMetadatas
|
||||
.find((index) => index.name === indexName)
|
||||
?.indexFieldMetadatas?.filter(
|
||||
(field) => field.fieldMetadataId !== deletedAtFieldMetadata?.id,
|
||||
)
|
||||
.map((indexField) => {
|
||||
const fieldMetadata = context.objectMetadataItem.fields.find(
|
||||
(objectField) => indexField.fieldMetadataId === objectField.id,
|
||||
);
|
||||
|
||||
return fieldMetadata?.label;
|
||||
});
|
||||
|
||||
const columnNames = affectedColumns?.join(', ');
|
||||
|
||||
if (affectedColumns?.length === 1) {
|
||||
throw new UserInputError(
|
||||
`Duplicate ${columnNames}. Please set a unique one.`,
|
||||
);
|
||||
}
|
||||
|
||||
throw new UserInputError(
|
||||
`A duplicate entry was detected. The combination of ${columnNames} must be unique.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (error instanceof WorkspaceQueryRunnerException) {
|
||||
switch (error.code) {
|
||||
case WorkspaceQueryRunnerExceptionCode.DATA_NOT_FOUND:
|
||||
|
||||
@ -40,7 +40,7 @@ export class CreateManyResolverFactory
|
||||
|
||||
return await this.graphqlQueryRunnerService.createMany(args, options);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error, context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ export class CreateOneResolverFactory
|
||||
|
||||
return await this.graphqlQueryRunnerService.createOne(args, options);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ export class DeleteManyResolverFactory
|
||||
|
||||
return await this.graphqlQueryRunnerService.deleteMany(args, options);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ export class DeleteOneResolverFactory
|
||||
|
||||
return await this.graphqlQueryRunnerService.deleteOne(args, options);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ export class DestroyManyResolverFactory
|
||||
|
||||
return await this.graphqlQueryRunnerService.destroyMany(args, options);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ export class DestroyOneResolverFactory
|
||||
|
||||
return await this.graphQLQueryRunnerService.destroyOne(args, options);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ export class FindDuplicatesResolverFactory
|
||||
options,
|
||||
);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ export class FindManyResolverFactory
|
||||
|
||||
return await this.graphqlQueryRunnerService.findMany(args, options);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ export class FindOneResolverFactory
|
||||
|
||||
return await this.graphqlQueryRunnerService.findOne(args, options);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ export class RestoreManyResolverFactory
|
||||
|
||||
return await this.graphqlQueryRunnerService.restoreMany(args, options);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ export class SearchResolverFactory
|
||||
|
||||
return await this.graphqlQueryRunnerService.search(args, options);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ export class UpdateManyResolverFactory
|
||||
|
||||
return await this.graphqlQueryRunnerService.updateMany(args, options);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ export class UpdateOneResolverFactory
|
||||
|
||||
return await this.graphqlQueryRunnerService.updateOne(args, options);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -5,12 +5,12 @@ import { GraphQLOutputType } from 'graphql';
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { PageInfoType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/object';
|
||||
import {
|
||||
TypeMapperService,
|
||||
TypeOptions,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { TypeDefinitionsStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/type-definitions.storage';
|
||||
import { PageInfoType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/object';
|
||||
|
||||
import { ConnectionTypeDefinitionKind } from './connection-type-definition.factory';
|
||||
import { ObjectTypeDefinitionKind } from './object-type-definition.factory';
|
||||
@ -27,7 +27,7 @@ export class ConnectionTypeFactory {
|
||||
public create(
|
||||
objectMetadata: ObjectMetadataInterface,
|
||||
kind: ConnectionTypeDefinitionKind,
|
||||
buildOtions: WorkspaceBuildSchemaOptions,
|
||||
buildOptions: WorkspaceBuildSchemaOptions,
|
||||
typeOptions: TypeOptions,
|
||||
): GraphQLOutputType {
|
||||
if (kind === ConnectionTypeDefinitionKind.PageInfo) {
|
||||
@ -44,7 +44,7 @@ export class ConnectionTypeFactory {
|
||||
`Edge type for ${objectMetadata.nameSingular} was not found. Please, check if you have defined it.`,
|
||||
{
|
||||
objectMetadata,
|
||||
buildOtions,
|
||||
buildOptions,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@ -5,15 +5,15 @@ import { GraphQLOutputType } from 'graphql';
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { CursorScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import {
|
||||
TypeMapperService,
|
||||
TypeOptions,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { TypeDefinitionsStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/type-definitions.storage';
|
||||
import { CursorScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
import { ObjectTypeDefinitionKind } from './object-type-definition.factory';
|
||||
import { EdgeTypeDefinitionKind } from './edge-type-definition.factory';
|
||||
import { ObjectTypeDefinitionKind } from './object-type-definition.factory';
|
||||
|
||||
@Injectable()
|
||||
export class EdgeTypeFactory {
|
||||
@ -27,7 +27,7 @@ export class EdgeTypeFactory {
|
||||
public create(
|
||||
objectMetadata: ObjectMetadataInterface,
|
||||
kind: EdgeTypeDefinitionKind,
|
||||
buildOtions: WorkspaceBuildSchemaOptions,
|
||||
buildOptions: WorkspaceBuildSchemaOptions,
|
||||
typeOptions: TypeOptions,
|
||||
): GraphQLOutputType {
|
||||
if (kind === EdgeTypeDefinitionKind.Cursor) {
|
||||
@ -44,7 +44,7 @@ export class EdgeTypeFactory {
|
||||
`Node type for ${objectMetadata.nameSingular} was not found. Please, check if you have defined it.`,
|
||||
{
|
||||
objectMetadata,
|
||||
buildOtions,
|
||||
buildOptions,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ export class OutputTypeFactory {
|
||||
target: string,
|
||||
type: FieldMetadataType,
|
||||
kind: ObjectTypeDefinitionKind,
|
||||
buildOtions: WorkspaceBuildSchemaOptions,
|
||||
buildOptions: WorkspaceBuildSchemaOptions,
|
||||
typeOptions: TypeOptions,
|
||||
): GraphQLOutputType {
|
||||
let gqlType: GraphQLOutputType | undefined =
|
||||
@ -40,8 +40,9 @@ export class OutputTypeFactory {
|
||||
|
||||
if (!gqlType) {
|
||||
this.logger.error(`Could not find a GraphQL type for ${target}`, {
|
||||
kind,
|
||||
type,
|
||||
buildOtions,
|
||||
buildOptions,
|
||||
typeOptions,
|
||||
});
|
||||
|
||||
|
||||
@ -79,6 +79,7 @@ export class TypeMapperService {
|
||||
StringArrayScalarType as unknown as GraphQLScalarType,
|
||||
],
|
||||
[FieldMetadataType.RICH_TEXT, GraphQLString],
|
||||
[FieldMetadataType.TS_VECTOR, GraphQLString],
|
||||
]);
|
||||
|
||||
return typeScalarMapping.get(fieldMetadataType);
|
||||
@ -114,6 +115,7 @@ export class TypeMapperService {
|
||||
[FieldMetadataType.RAW_JSON, RawJsonFilterType],
|
||||
[FieldMetadataType.RICH_TEXT, StringFilterType],
|
||||
[FieldMetadataType.ARRAY, ArrayFilterType],
|
||||
[FieldMetadataType.TS_VECTOR, StringFilterType], // TODO: Add TSVectorFilterType
|
||||
]);
|
||||
|
||||
return typeFilterMapping.get(fieldMetadataType);
|
||||
@ -137,6 +139,7 @@ export class TypeMapperService {
|
||||
[FieldMetadataType.RAW_JSON, OrderByDirectionType],
|
||||
[FieldMetadataType.RICH_TEXT, OrderByDirectionType],
|
||||
[FieldMetadataType.ARRAY, OrderByDirectionType],
|
||||
[FieldMetadataType.TS_VECTOR, OrderByDirectionType], // TODO: Add TSVectorOrderByType
|
||||
]);
|
||||
|
||||
return typeOrderByMapping.get(fieldMetadataType);
|
||||
|
||||
@ -46,10 +46,7 @@ export const generateFields = <
|
||||
const fields = {};
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
if (
|
||||
isRelationFieldMetadataType(fieldMetadata.type) ||
|
||||
fieldMetadata.type === FieldMetadataType.TS_VECTOR
|
||||
) {
|
||||
if (isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@ -76,6 +76,7 @@ export class WorkspaceSchemaFactory {
|
||||
(objectMetadataItem) => ({
|
||||
...objectMetadataItem,
|
||||
fields: Object.values(objectMetadataItem.fields),
|
||||
indexes: objectMetadataItem.indexMetadatas,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@ -31,9 +31,6 @@ describe('mapFieldMetadataToGraphqlQuery', () => {
|
||||
});
|
||||
describe('should handle all field metadata types', () => {
|
||||
Object.values(FieldMetadataType).forEach((fieldMetadataType) => {
|
||||
if (fieldMetadataType === FieldMetadataType.TS_VECTOR) {
|
||||
return;
|
||||
}
|
||||
it(`with field type ${fieldMetadataType}`, () => {
|
||||
const field = {
|
||||
type: fieldMetadataType,
|
||||
|
||||
@ -30,6 +30,7 @@ export const mapFieldMetadataToGraphqlQuery = (
|
||||
FieldMetadataType.RAW_JSON,
|
||||
FieldMetadataType.RICH_TEXT,
|
||||
FieldMetadataType.ARRAY,
|
||||
FieldMetadataType.TS_VECTOR,
|
||||
].includes(fieldType);
|
||||
|
||||
if (fieldIsSimpleValue) {
|
||||
|
||||
Reference in New Issue
Block a user