feat: Adding support for new FieldMetadataType with Postgres enums (#2674)
* feat: add enum type (RATING, SELECT, MULTI_SELECT) feat: wip enum type feat: try to alter enum feat: wip enum feat: wip enum feat: schema-builder can handle enum fix: return default value in field metadata response * fix: create fieldMedata with options * fix: lint issues * fix: rename abstract factory * feat: drop `PHONE` and `EMAIL` fieldMetadata types * feat: drop `VARCHAR` fieldMetadata type and rely on `TEXT` * Revert "feat: drop `PHONE` and `EMAIL` fieldMetadata types" This reverts commit 3857539f7d42f17c81f6ab92a6db950140b3c8e5.
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import { RelationMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/relation-metadata.interface';
|
||||
import { RelationMetadataInterface } from 'src/metadata/field-metadata/interfaces/relation-metadata.interface';
|
||||
|
||||
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||
import {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { RelationMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/relation-metadata.interface';
|
||||
import { RelationMetadataInterface } from 'src/metadata/field-metadata/interfaces/relation-metadata.interface';
|
||||
|
||||
export enum RelationDirection {
|
||||
FROM = 'from',
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { WorkspaceResolverBuilderMethodNames } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { camelCase } from 'src/utils/camel-case';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
|
||||
export const isCompositeFieldMetadataType = (type: FieldMetadataType) => {
|
||||
export const isRelationFieldMetadataType = (
|
||||
type: FieldMetadataType,
|
||||
): type is FieldMetadataType.RELATION => {
|
||||
return type === FieldMetadataType.RELATION;
|
||||
};
|
||||
@ -0,0 +1,183 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { WorkspaceMigrationColumnAlter } from 'src/metadata/workspace-migration/workspace-migration.entity';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationEnumService {
|
||||
async alterEnum(
|
||||
queryRunner: QueryRunner,
|
||||
schemaName: string,
|
||||
tableName: string,
|
||||
migrationColumn: WorkspaceMigrationColumnAlter,
|
||||
) {
|
||||
const oldEnumTypeName = `${tableName}_${migrationColumn.columnName}_enum`;
|
||||
const newEnumTypeName = `${tableName}_${migrationColumn.columnName}_enum_new`;
|
||||
const enumValues =
|
||||
migrationColumn.enum?.map((enumValue) => {
|
||||
if (typeof enumValue === 'string') {
|
||||
return enumValue;
|
||||
}
|
||||
|
||||
return enumValue.to;
|
||||
}) ?? [];
|
||||
|
||||
if (!migrationColumn.isNullable && !migrationColumn.defaultValue) {
|
||||
migrationColumn.defaultValue = migrationColumn.enum?.[0];
|
||||
}
|
||||
|
||||
// Create new enum type with new values
|
||||
await this.createNewEnumType(
|
||||
newEnumTypeName,
|
||||
queryRunner,
|
||||
schemaName,
|
||||
enumValues,
|
||||
);
|
||||
|
||||
// Temporarily change column type to text
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "${schemaName}"."${tableName}"
|
||||
ALTER COLUMN "${migrationColumn.columnName}" TYPE TEXT
|
||||
`);
|
||||
|
||||
// Migrate existing values to new values
|
||||
await this.migrateEnumValues(
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
migrationColumn,
|
||||
);
|
||||
|
||||
// Update existing rows to handle missing values
|
||||
await this.handleMissingEnumValues(
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
migrationColumn,
|
||||
enumValues,
|
||||
);
|
||||
|
||||
// Alter column type to new enum
|
||||
await this.updateColumnToNewEnum(
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
migrationColumn.columnName,
|
||||
newEnumTypeName,
|
||||
);
|
||||
|
||||
// Drop old enum type
|
||||
await this.dropOldEnumType(queryRunner, schemaName, oldEnumTypeName);
|
||||
|
||||
// Rename new enum type to old enum type name
|
||||
await this.renameEnumType(
|
||||
queryRunner,
|
||||
schemaName,
|
||||
oldEnumTypeName,
|
||||
newEnumTypeName,
|
||||
);
|
||||
}
|
||||
|
||||
private async createNewEnumType(
|
||||
name: string,
|
||||
queryRunner: QueryRunner,
|
||||
schemaName: string,
|
||||
newValues: string[],
|
||||
) {
|
||||
const enumValues = newValues
|
||||
.map((value) => `'${value.replace(/'/g, "''")}'`)
|
||||
.join(', ');
|
||||
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "${schemaName}"."${name}" AS ENUM (${enumValues})`,
|
||||
);
|
||||
}
|
||||
|
||||
private async migrateEnumValues(
|
||||
queryRunner: QueryRunner,
|
||||
schemaName: string,
|
||||
tableName: string,
|
||||
migrationColumn: WorkspaceMigrationColumnAlter,
|
||||
) {
|
||||
if (!migrationColumn.enum) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const enumValue of migrationColumn.enum) {
|
||||
// Skip string values
|
||||
if (typeof enumValue === 'string') {
|
||||
continue;
|
||||
}
|
||||
|
||||
await queryRunner.query(`
|
||||
UPDATE "${schemaName}"."${tableName}"
|
||||
SET "${migrationColumn.columnName}" = '${enumValue.to}'
|
||||
WHERE "${migrationColumn.columnName}" = '${enumValue.from}'
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleMissingEnumValues(
|
||||
queryRunner: QueryRunner,
|
||||
schemaName: string,
|
||||
tableName: string,
|
||||
migrationColumn: WorkspaceMigrationColumnAlter,
|
||||
enumValues: string[],
|
||||
) {
|
||||
// Set missing values to null or default value
|
||||
let defaultValue = 'NULL';
|
||||
|
||||
if (migrationColumn.defaultValue) {
|
||||
if (Array.isArray(migrationColumn.defaultValue)) {
|
||||
defaultValue = `ARRAY[${migrationColumn.defaultValue
|
||||
.map((e) => `'${e}'`)
|
||||
.join(', ')}]`;
|
||||
} else {
|
||||
defaultValue = `'${migrationColumn.defaultValue}'`;
|
||||
}
|
||||
}
|
||||
|
||||
await queryRunner.query(`
|
||||
UPDATE "${schemaName}"."${tableName}"
|
||||
SET "${migrationColumn.columnName}" = ${defaultValue}
|
||||
WHERE "${migrationColumn.columnName}" NOT IN (${enumValues
|
||||
.map((e) => `'${e}'`)
|
||||
.join(', ')})
|
||||
`);
|
||||
}
|
||||
|
||||
private async updateColumnToNewEnum(
|
||||
queryRunner: QueryRunner,
|
||||
schemaName: string,
|
||||
tableName: string,
|
||||
columnName: string,
|
||||
newEnumTypeName: string,
|
||||
) {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "${schemaName}"."${tableName}" ALTER COLUMN "${columnName}" TYPE "${schemaName}"."${newEnumTypeName}" USING ("${columnName}"::text::"${schemaName}"."${newEnumTypeName}")`,
|
||||
);
|
||||
}
|
||||
|
||||
private async dropOldEnumType(
|
||||
queryRunner: QueryRunner,
|
||||
schemaName: string,
|
||||
oldEnumTypeName: string,
|
||||
) {
|
||||
await queryRunner.query(
|
||||
`DROP TYPE IF EXISTS "${schemaName}"."${oldEnumTypeName}"`,
|
||||
);
|
||||
}
|
||||
|
||||
private async renameEnumType(
|
||||
queryRunner: QueryRunner,
|
||||
schemaName: string,
|
||||
oldEnumTypeName: string,
|
||||
newEnumTypeName: string,
|
||||
) {
|
||||
await queryRunner.query(`
|
||||
ALTER TYPE "${schemaName}"."${newEnumTypeName}"
|
||||
RENAME TO "${oldEnumTypeName}"
|
||||
`);
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@ import { Module } from '@nestjs/common';
|
||||
import { WorkspaceMigrationModule } from 'src/metadata/workspace-migration/workspace-migration.module';
|
||||
import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/workspace-datasource.module';
|
||||
import { WorkspaceCacheVersionModule } from 'src/metadata/workspace-cache-version/workspace-cache-version.module';
|
||||
import { WorkspaceMigrationEnumService } from 'src/workspace/workspace-migration-runner/services/workspace-migration-enum.service';
|
||||
|
||||
import { WorkspaceMigrationRunnerService } from './workspace-migration-runner.service';
|
||||
|
||||
@ -12,7 +13,7 @@ import { WorkspaceMigrationRunnerService } from './workspace-migration-runner.se
|
||||
WorkspaceMigrationModule,
|
||||
WorkspaceCacheVersionModule,
|
||||
],
|
||||
providers: [WorkspaceMigrationRunnerService, WorkspaceMigrationEnumService],
|
||||
exports: [WorkspaceMigrationRunnerService],
|
||||
providers: [WorkspaceMigrationRunnerService],
|
||||
})
|
||||
export class WorkspaceMigrationRunnerModule {}
|
||||
|
||||
@ -16,8 +16,10 @@ import {
|
||||
WorkspaceMigrationColumnActionType,
|
||||
WorkspaceMigrationColumnCreate,
|
||||
WorkspaceMigrationColumnRelation,
|
||||
WorkspaceMigrationColumnAlter,
|
||||
} from 'src/metadata/workspace-migration/workspace-migration.entity';
|
||||
import { WorkspaceCacheVersionService } from 'src/metadata/workspace-cache-version/workspace-cache-version.service';
|
||||
import { WorkspaceMigrationEnumService } from 'src/workspace/workspace-migration-runner/services/workspace-migration-enum.service';
|
||||
|
||||
import { customTableDefaultColumns } from './utils/custom-table-default-column.util';
|
||||
|
||||
@ -27,6 +29,7 @@ export class WorkspaceMigrationRunnerService {
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
||||
private readonly workspaceCacheVersionService: WorkspaceCacheVersionService,
|
||||
private readonly workspaceMigrationEnumService: WorkspaceMigrationEnumService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@ -168,6 +171,14 @@ export class WorkspaceMigrationRunnerService {
|
||||
columnMigration,
|
||||
);
|
||||
break;
|
||||
case WorkspaceMigrationColumnActionType.ALTER:
|
||||
await this.alterColumn(
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
columnMigration,
|
||||
);
|
||||
break;
|
||||
case WorkspaceMigrationColumnActionType.RELATION:
|
||||
await this.createForeignKey(
|
||||
queryRunner,
|
||||
@ -200,6 +211,7 @@ export class WorkspaceMigrationRunnerService {
|
||||
`${schemaName}.${tableName}`,
|
||||
migrationColumn.columnName,
|
||||
);
|
||||
|
||||
if (hasColumn) {
|
||||
return;
|
||||
}
|
||||
@ -210,11 +222,46 @@ export class WorkspaceMigrationRunnerService {
|
||||
name: migrationColumn.columnName,
|
||||
type: migrationColumn.columnType,
|
||||
default: migrationColumn.defaultValue,
|
||||
enum: migrationColumn.enum?.filter(
|
||||
(value): value is string => typeof value === 'string',
|
||||
),
|
||||
isArray: migrationColumn.isArray,
|
||||
isNullable: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private async alterColumn(
|
||||
queryRunner: QueryRunner,
|
||||
schemaName: string,
|
||||
tableName: string,
|
||||
migrationColumn: WorkspaceMigrationColumnAlter,
|
||||
) {
|
||||
const enumValues = migrationColumn.enum;
|
||||
|
||||
// TODO: Maybe we can do something better if we can recreate the old `TableColumn` object
|
||||
if (enumValues) {
|
||||
// This is returning the old enum values to avoid TypeORM droping the enum type
|
||||
await this.workspaceMigrationEnumService.alterEnum(
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
migrationColumn,
|
||||
);
|
||||
} else {
|
||||
await queryRunner.changeColumn(
|
||||
`${schemaName}.${tableName}`,
|
||||
migrationColumn.columnName,
|
||||
new TableColumn({
|
||||
name: migrationColumn.columnName,
|
||||
type: migrationColumn.columnType,
|
||||
default: migrationColumn.defaultValue,
|
||||
isNullable: migrationColumn.isNullable,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async createForeignKey(
|
||||
queryRunner: QueryRunner,
|
||||
schemaName: string,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { FieldMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/field-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
@Injectable()
|
||||
export class ArgsAliasFactory {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { FieldMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/field-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import { stringifyWithoutKeyQuote } from 'src/workspace/workspace-query-builder/utils/stringify-without-key-quote.util';
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ArgsAliasFactory } from './args-alias.factory';
|
||||
import { ArgsStringFactory } from './args-string.factory';
|
||||
import { CompositeFieldAliasFactory } from './composite-field-alias.factory';
|
||||
import { RelationFieldAliasFactory } from './relation-field-alias.factory';
|
||||
import { CreateManyQueryFactory } from './create-many-query.factory';
|
||||
import { DeleteOneQueryFactory } from './delete-one-query.factory';
|
||||
import { FieldAliasFacotry } from './field-alias.factory';
|
||||
@ -14,7 +14,7 @@ import { DeleteManyQueryFactory } from './delete-many-query.factory';
|
||||
export const workspaceQueryBuilderFactories = [
|
||||
ArgsAliasFactory,
|
||||
ArgsStringFactory,
|
||||
CompositeFieldAliasFactory,
|
||||
RelationFieldAliasFactory,
|
||||
CreateManyQueryFactory,
|
||||
DeleteOneQueryFactory,
|
||||
FieldAliasFacotry,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { FieldMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/field-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
@Injectable()
|
||||
export class FieldAliasFacotry {
|
||||
|
||||
@ -4,12 +4,12 @@ import { GraphQLResolveInfo } from 'graphql';
|
||||
import graphqlFields from 'graphql-fields';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
|
||||
import { FieldMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/field-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import { isCompositeFieldMetadataType } from 'src/workspace/utils/is-composite-field-metadata-type.util';
|
||||
import { isRelationFieldMetadataType } from 'src/workspace/utils/is-relation-field-metadata-type.util';
|
||||
|
||||
import { FieldAliasFacotry } from './field-alias.factory';
|
||||
import { CompositeFieldAliasFactory } from './composite-field-alias.factory';
|
||||
import { RelationFieldAliasFactory } from './relation-field-alias.factory';
|
||||
|
||||
@Injectable()
|
||||
export class FieldsStringFactory {
|
||||
@ -17,7 +17,7 @@ export class FieldsStringFactory {
|
||||
|
||||
constructor(
|
||||
private readonly fieldAliasFactory: FieldAliasFacotry,
|
||||
private readonly compositeFieldAliasFactory: CompositeFieldAliasFactory,
|
||||
private readonly relationFieldAliasFactory: RelationFieldAliasFactory,
|
||||
) {}
|
||||
|
||||
create(
|
||||
@ -52,9 +52,9 @@ export class FieldsStringFactory {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const fieldMetadata = fieldMetadataMap.get(fieldKey)!;
|
||||
|
||||
// If the field is a composite field, we need to create a special alias
|
||||
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
const alias = this.compositeFieldAliasFactory.create(
|
||||
// If the field is a relation field, we need to create a special alias
|
||||
if (isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||
const alias = this.relationFieldAliasFactory.create(
|
||||
fieldKey,
|
||||
fieldValue,
|
||||
fieldMetadata,
|
||||
|
||||
@ -2,10 +2,9 @@ import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { GraphQLResolveInfo } from 'graphql';
|
||||
|
||||
import { FieldMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/field-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFieldMetadataType } from 'src/workspace/utils/is-composite-field-metadata-type.util';
|
||||
import { isRelationFieldMetadataType } from 'src/workspace/utils/is-relation-field-metadata-type.util';
|
||||
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||
import {
|
||||
deduceRelationDirection,
|
||||
@ -17,8 +16,8 @@ import { FieldsStringFactory } from './fields-string.factory';
|
||||
import { ArgsStringFactory } from './args-string.factory';
|
||||
|
||||
@Injectable()
|
||||
export class CompositeFieldAliasFactory {
|
||||
private logger = new Logger(CompositeFieldAliasFactory.name);
|
||||
export class RelationFieldAliasFactory {
|
||||
private logger = new Logger(RelationFieldAliasFactory.name);
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => FieldsStringFactory))
|
||||
@ -32,21 +31,11 @@ export class CompositeFieldAliasFactory {
|
||||
fieldMetadata: FieldMetadataInterface,
|
||||
info: GraphQLResolveInfo,
|
||||
) {
|
||||
if (!isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
throw new Error(`Field ${fieldMetadata.name} is not a composite field`);
|
||||
if (!isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||
throw new Error(`Field ${fieldMetadata.name} is not a relation field`);
|
||||
}
|
||||
|
||||
switch (fieldMetadata.type) {
|
||||
case FieldMetadataType.RELATION:
|
||||
return this.createRelationAlias(
|
||||
fieldKey,
|
||||
fieldValue,
|
||||
fieldMetadata,
|
||||
info,
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
return this.createRelationAlias(fieldKey, fieldValue, fieldMetadata, info);
|
||||
}
|
||||
|
||||
private createRelationAlias(
|
||||
@ -1,6 +1,6 @@
|
||||
import { GraphQLResolveInfo } from 'graphql';
|
||||
|
||||
import { FieldMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/field-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
export interface WorkspaceQueryBuilderOptions {
|
||||
targetTableName: string;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { GraphQLResolveInfo } from 'graphql';
|
||||
|
||||
import { FieldMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/field-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
export interface WorkspaceQueryRunnerOptions {
|
||||
targetTableName: string;
|
||||
|
||||
@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { IResolvers } from '@graphql-tools/utils';
|
||||
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { getResolverName } from 'src/workspace/utils/get-resolver-name.util';
|
||||
import { UpdateManyResolverFactory } from 'src/workspace/workspace-resolver-builder/factories/update-many-resolver.factory';
|
||||
|
||||
@ -3,7 +3,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { GraphQLFieldConfigMap, GraphQLObjectType } from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { GraphQLOutputType } from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import {
|
||||
TypeMapperService,
|
||||
|
||||
@ -3,7 +3,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { GraphQLFieldConfigMap, GraphQLObjectType } from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { GraphQLOutputType } from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import {
|
||||
TypeMapperService,
|
||||
|
||||
@ -0,0 +1,85 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { GraphQLEnumType } from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
import {
|
||||
FieldMetadataComplexOptions,
|
||||
FieldMetadataDefaultOptions,
|
||||
} from 'src/metadata/field-metadata/dtos/options.input';
|
||||
import { isEnumFieldMetadataType } from 'src/metadata/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||
|
||||
export interface EnumTypeDefinition {
|
||||
target: string;
|
||||
type: GraphQLEnumType;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class EnumTypeDefinitionFactory {
|
||||
private readonly logger = new Logger(EnumTypeDefinitionFactory.name);
|
||||
|
||||
public create(
|
||||
objectMetadata: ObjectMetadataInterface,
|
||||
options: WorkspaceBuildSchemaOptions,
|
||||
): EnumTypeDefinition[] {
|
||||
const enumTypeDefinitions: EnumTypeDefinition[] = [];
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
if (!isEnumFieldMetadataType(fieldMetadata.type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
enumTypeDefinitions.push({
|
||||
target: fieldMetadata.id,
|
||||
type: this.generateEnum(
|
||||
objectMetadata.nameSingular,
|
||||
fieldMetadata,
|
||||
options,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return enumTypeDefinitions;
|
||||
}
|
||||
|
||||
private generateEnum(
|
||||
objectName: string,
|
||||
fieldMetadata: FieldMetadataInterface,
|
||||
options: WorkspaceBuildSchemaOptions,
|
||||
): GraphQLEnumType {
|
||||
// FixMe: It's a hack until Typescript get fixed on union types for reduce function
|
||||
// https://github.com/microsoft/TypeScript/issues/36390
|
||||
const enumOptions = fieldMetadata.options as Array<
|
||||
FieldMetadataDefaultOptions | FieldMetadataComplexOptions
|
||||
>;
|
||||
|
||||
if (!enumOptions) {
|
||||
this.logger.error(
|
||||
`Enum options are not defined for ${fieldMetadata.name}`,
|
||||
{
|
||||
fieldMetadata,
|
||||
options,
|
||||
},
|
||||
);
|
||||
|
||||
throw new Error(`Enum options are not defined for ${fieldMetadata.name}`);
|
||||
}
|
||||
|
||||
return new GraphQLEnumType({
|
||||
name: `${pascalCase(objectName)}${pascalCase(fieldMetadata.name)}Enum`,
|
||||
description: fieldMetadata.description,
|
||||
values: enumOptions.reduce((acc, enumOption) => {
|
||||
acc[enumOption.value] = {
|
||||
value: enumOption.value,
|
||||
description: enumOption.label,
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {} as { [key: string]: { value: string; description: string } }),
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -7,13 +7,13 @@ import {
|
||||
} from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
import { TypeDefinitionsStorage } from 'src/workspace/workspace-schema-builder/storages/type-definitions.storage';
|
||||
import { objectContainsCompositeField } from 'src/workspace/workspace-schema-builder/utils/object-contains-composite-field';
|
||||
import { objectContainsRelationField } from 'src/workspace/workspace-schema-builder/utils/object-contains-relation-field';
|
||||
import { getResolverArgs } from 'src/workspace/workspace-schema-builder/utils/get-resolver-args.util';
|
||||
import { isCompositeFieldMetadataType } from 'src/workspace/utils/is-composite-field-metadata-type.util';
|
||||
import { isRelationFieldMetadataType } from 'src/workspace/utils/is-relation-field-metadata-type.util';
|
||||
import {
|
||||
RelationDirection,
|
||||
deduceRelationDirection,
|
||||
@ -54,7 +54,7 @@ export class ExtendObjectTypeDefinitionFactory {
|
||||
objectMetadata.id,
|
||||
kind,
|
||||
);
|
||||
const containsCompositeField = objectContainsCompositeField(objectMetadata);
|
||||
const containsRelationField = objectContainsRelationField(objectMetadata);
|
||||
|
||||
if (!gqlType) {
|
||||
this.logger.error(
|
||||
@ -71,7 +71,7 @@ export class ExtendObjectTypeDefinitionFactory {
|
||||
}
|
||||
|
||||
// Security check to avoid extending an object that does not need to be extended
|
||||
if (!containsCompositeField) {
|
||||
if (!containsRelationField) {
|
||||
this.logger.error(
|
||||
`This object does not need to be extended: ${objectMetadata.id.toString()}`,
|
||||
{
|
||||
@ -109,8 +109,8 @@ export class ExtendObjectTypeDefinitionFactory {
|
||||
const fields: GraphQLFieldConfigMap<any, any> = {};
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
// Ignore non composite fields as they are already defined
|
||||
if (!isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
// Ignore relation fields as they are already defined
|
||||
if (!isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { EnumTypeDefinitionFactory } from 'src/workspace/workspace-schema-builder/factories/enum-type-definition.factory';
|
||||
|
||||
import { ArgsFactory } from './args.factory';
|
||||
import { InputTypeFactory } from './input-type.factory';
|
||||
import { InputTypeDefinitionFactory } from './input-type-definition.factory';
|
||||
@ -24,6 +26,7 @@ export const workspaceSchemaBuilderFactories = [
|
||||
InputTypeDefinitionFactory,
|
||||
OutputTypeFactory,
|
||||
ObjectTypeDefinitionFactory,
|
||||
EnumTypeDefinitionFactory,
|
||||
RelationTypeFactory,
|
||||
ExtendObjectTypeDefinitionFactory,
|
||||
FilterTypeFactory,
|
||||
|
||||
@ -3,11 +3,11 @@ import { Injectable } from '@nestjs/common';
|
||||
import { GraphQLInputFieldConfigMap, GraphQLInputObjectType } from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
import { TypeMapperService } from 'src/workspace/workspace-schema-builder/services/type-mapper.service';
|
||||
import { isCompositeFieldMetadataType } from 'src/workspace/utils/is-composite-field-metadata-type.util';
|
||||
import { isRelationFieldMetadataType } from 'src/workspace/utils/is-relation-field-metadata-type.util';
|
||||
|
||||
import { FilterTypeFactory } from './filter-type.factory';
|
||||
import {
|
||||
@ -68,8 +68,8 @@ export class FilterTypeDefinitionFactory {
|
||||
const fields: GraphQLInputFieldConfigMap = {};
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
// Composite field types are generated during extension of object type definition
|
||||
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
// Relation types are generated during extension of object type definition
|
||||
if (isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||
//continue;
|
||||
}
|
||||
|
||||
|
||||
@ -1,15 +1,23 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { GraphQLInputType } from 'graphql';
|
||||
import {
|
||||
GraphQLInputObjectType,
|
||||
GraphQLInputType,
|
||||
GraphQLList,
|
||||
GraphQLScalarType,
|
||||
} from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { FieldMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/field-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import {
|
||||
TypeMapperService,
|
||||
TypeOptions,
|
||||
} from 'src/workspace/workspace-schema-builder/services/type-mapper.service';
|
||||
import { TypeDefinitionsStorage } from 'src/workspace/workspace-schema-builder/storages/type-definitions.storage';
|
||||
import { isCompositeFieldMetadataType } from 'src/metadata/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { isEnumFieldMetadataType } from 'src/metadata/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||
import { FilterIs } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is.input-type';
|
||||
|
||||
import { InputTypeDefinitionKind } from './input-type-definition.factory';
|
||||
|
||||
@ -27,34 +35,68 @@ export class FilterTypeFactory {
|
||||
buildOtions: WorkspaceBuildSchemaOptions,
|
||||
typeOptions: TypeOptions,
|
||||
): GraphQLInputType {
|
||||
let filterType = this.typeMapperService.mapToFilterType(
|
||||
fieldMetadata.type,
|
||||
buildOtions.dateScalarMode,
|
||||
buildOtions.numberScalarMode,
|
||||
);
|
||||
const target = isCompositeFieldMetadataType(fieldMetadata.type)
|
||||
? fieldMetadata.type.toString()
|
||||
: fieldMetadata.id;
|
||||
let filterType: GraphQLInputObjectType | GraphQLScalarType | undefined =
|
||||
undefined;
|
||||
|
||||
if (!filterType) {
|
||||
filterType = this.typeDefinitionsStorage.getInputTypeByKey(
|
||||
fieldMetadata.type.toString(),
|
||||
InputTypeDefinitionKind.Filter,
|
||||
if (isEnumFieldMetadataType(fieldMetadata.type)) {
|
||||
filterType = this.createEnumFilterType(fieldMetadata);
|
||||
} else {
|
||||
filterType = this.typeMapperService.mapToFilterType(
|
||||
fieldMetadata.type,
|
||||
buildOtions.dateScalarMode,
|
||||
buildOtions.numberScalarMode,
|
||||
);
|
||||
|
||||
if (!filterType) {
|
||||
this.logger.error(
|
||||
`Could not find a GraphQL type for ${fieldMetadata.type.toString()}`,
|
||||
{
|
||||
fieldMetadata,
|
||||
buildOtions,
|
||||
typeOptions,
|
||||
},
|
||||
);
|
||||
filterType ??= this.typeDefinitionsStorage.getInputTypeByKey(
|
||||
target,
|
||||
InputTypeDefinitionKind.Filter,
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Could not find a GraphQL type for ${fieldMetadata.type.toString()}`,
|
||||
);
|
||||
}
|
||||
if (!filterType) {
|
||||
this.logger.error(`Could not find a GraphQL type for ${target}`, {
|
||||
fieldMetadata,
|
||||
buildOtions,
|
||||
typeOptions,
|
||||
});
|
||||
|
||||
throw new Error(`Could not find a GraphQL type for ${target}`);
|
||||
}
|
||||
|
||||
return this.typeMapperService.mapToGqlType(filterType, typeOptions);
|
||||
}
|
||||
|
||||
private createEnumFilterType(
|
||||
fieldMetadata: FieldMetadataInterface,
|
||||
): GraphQLInputObjectType {
|
||||
const enumType = this.typeDefinitionsStorage.getEnumTypeByKey(
|
||||
fieldMetadata.id,
|
||||
);
|
||||
|
||||
if (!enumType) {
|
||||
this.logger.error(
|
||||
`Could not find a GraphQL enum type for ${fieldMetadata.id}`,
|
||||
{
|
||||
fieldMetadata,
|
||||
},
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`Could not find a GraphQL enum type for ${fieldMetadata.id}`,
|
||||
);
|
||||
}
|
||||
|
||||
return new GraphQLInputObjectType({
|
||||
name: `${enumType.name}Filter`,
|
||||
fields: () => ({
|
||||
eq: { type: enumType },
|
||||
neq: { type: enumType },
|
||||
in: { type: new GraphQLList(enumType) },
|
||||
is: { type: FilterIs },
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,10 +3,11 @@ import { Injectable } from '@nestjs/common';
|
||||
import { GraphQLInputFieldConfigMap, GraphQLInputObjectType } from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
import { isCompositeFieldMetadataType } from 'src/workspace/utils/is-composite-field-metadata-type.util';
|
||||
import { isRelationFieldMetadataType } from 'src/workspace/utils/is-relation-field-metadata-type.util';
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
|
||||
import { InputTypeFactory } from './input-type.factory';
|
||||
|
||||
@ -53,14 +54,15 @@ export class InputTypeDefinitionFactory {
|
||||
const fields: GraphQLInputFieldConfigMap = {};
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
// Composite field types are generated during extension of object type definition
|
||||
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
// Relation field types are generated during extension of object type definition
|
||||
if (isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||
//continue;
|
||||
}
|
||||
|
||||
const type = this.inputTypeFactory.create(fieldMetadata, kind, options, {
|
||||
nullable: fieldMetadata.isNullable,
|
||||
defaultValue: fieldMetadata.defaultValue,
|
||||
isArray: fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
});
|
||||
|
||||
fields[fieldMetadata.name] = {
|
||||
|
||||
@ -3,13 +3,14 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { GraphQLInputType } from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { FieldMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/field-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import {
|
||||
TypeMapperService,
|
||||
TypeOptions,
|
||||
} from 'src/workspace/workspace-schema-builder/services/type-mapper.service';
|
||||
import { TypeDefinitionsStorage } from 'src/workspace/workspace-schema-builder/storages/type-definitions.storage';
|
||||
import { isCompositeFieldMetadataType } from 'src/metadata/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
|
||||
import { InputTypeDefinitionKind } from './input-type-definition.factory';
|
||||
|
||||
@ -28,6 +29,9 @@ export class InputTypeFactory {
|
||||
buildOtions: WorkspaceBuildSchemaOptions,
|
||||
typeOptions: TypeOptions,
|
||||
): GraphQLInputType {
|
||||
const target = isCompositeFieldMetadataType(fieldMetadata.type)
|
||||
? fieldMetadata.type.toString()
|
||||
: fieldMetadata.id;
|
||||
let inputType: GraphQLInputType | undefined =
|
||||
this.typeMapperService.mapToScalarType(
|
||||
fieldMetadata.type,
|
||||
@ -35,27 +39,19 @@ export class InputTypeFactory {
|
||||
buildOtions.numberScalarMode,
|
||||
);
|
||||
|
||||
inputType ??= this.typeDefinitionsStorage.getInputTypeByKey(target, kind);
|
||||
|
||||
inputType ??= this.typeDefinitionsStorage.getEnumTypeByKey(target);
|
||||
|
||||
if (!inputType) {
|
||||
inputType = this.typeDefinitionsStorage.getInputTypeByKey(
|
||||
fieldMetadata.type.toString(),
|
||||
this.logger.error(`Could not find a GraphQL type for ${target}`, {
|
||||
fieldMetadata,
|
||||
kind,
|
||||
);
|
||||
buildOtions,
|
||||
typeOptions,
|
||||
});
|
||||
|
||||
if (!inputType) {
|
||||
this.logger.error(
|
||||
`Could not find a GraphQL type for ${fieldMetadata.type.toString()}`,
|
||||
{
|
||||
fieldMetadata,
|
||||
kind,
|
||||
buildOtions,
|
||||
typeOptions,
|
||||
},
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`Could not find a GraphQL type for ${fieldMetadata.type.toString()}`,
|
||||
);
|
||||
}
|
||||
throw new Error(`Could not find a GraphQL type for ${target}`);
|
||||
}
|
||||
|
||||
return this.typeMapperService.mapToGqlType(inputType, typeOptions);
|
||||
|
||||
@ -4,7 +4,7 @@ import { GraphQLObjectType } from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { WorkspaceResolverBuilderMutationMethodNames } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { ObjectTypeName, RootTypeFactory } from './root-type.factory';
|
||||
|
||||
|
||||
@ -3,10 +3,11 @@ import { Injectable } from '@nestjs/common';
|
||||
import { GraphQLFieldConfigMap, GraphQLObjectType } from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
import { isCompositeFieldMetadataType } from 'src/workspace/utils/is-composite-field-metadata-type.util';
|
||||
import { isRelationFieldMetadataType } from 'src/workspace/utils/is-relation-field-metadata-type.util';
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
|
||||
import { OutputTypeFactory } from './output-type.factory';
|
||||
|
||||
@ -50,13 +51,14 @@ export class ObjectTypeDefinitionFactory {
|
||||
const fields: GraphQLFieldConfigMap<any, any> = {};
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
// Composite field types are generated during extension of object type definition
|
||||
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
// Relation field types are generated during extension of object type definition
|
||||
if (isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const type = this.outputTypeFactory.create(fieldMetadata, kind, options, {
|
||||
nullable: fieldMetadata.isNullable,
|
||||
isArray: fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
});
|
||||
|
||||
fields[fieldMetadata.name] = {
|
||||
|
||||
@ -3,10 +3,10 @@ import { Injectable } from '@nestjs/common';
|
||||
import { GraphQLInputFieldConfigMap, GraphQLInputObjectType } from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
import { isCompositeFieldMetadataType } from 'src/workspace/utils/is-composite-field-metadata-type.util';
|
||||
import { isRelationFieldMetadataType } from 'src/workspace/utils/is-relation-field-metadata-type.util';
|
||||
|
||||
import {
|
||||
InputTypeDefinition,
|
||||
@ -44,8 +44,8 @@ export class OrderByTypeDefinitionFactory {
|
||||
const fields: GraphQLInputFieldConfigMap = {};
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
// Composite field types are generated during extension of object type definition
|
||||
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
// Relation field types are generated during extension of object type definition
|
||||
if (isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@ -3,13 +3,14 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { GraphQLInputType } from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { FieldMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/field-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import {
|
||||
TypeMapperService,
|
||||
TypeOptions,
|
||||
} from 'src/workspace/workspace-schema-builder/services/type-mapper.service';
|
||||
import { TypeDefinitionsStorage } from 'src/workspace/workspace-schema-builder/storages/type-definitions.storage';
|
||||
import { isCompositeFieldMetadataType } from 'src/metadata/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
|
||||
import { InputTypeDefinitionKind } from './input-type-definition.factory';
|
||||
|
||||
@ -27,30 +28,26 @@ export class OrderByTypeFactory {
|
||||
buildOtions: WorkspaceBuildSchemaOptions,
|
||||
typeOptions: TypeOptions,
|
||||
): GraphQLInputType {
|
||||
const target = isCompositeFieldMetadataType(fieldMetadata.type)
|
||||
? fieldMetadata.type.toString()
|
||||
: fieldMetadata.id;
|
||||
let orderByType = this.typeMapperService.mapToOrderByType(
|
||||
fieldMetadata.type,
|
||||
);
|
||||
|
||||
orderByType ??= this.typeDefinitionsStorage.getInputTypeByKey(
|
||||
target,
|
||||
InputTypeDefinitionKind.OrderBy,
|
||||
);
|
||||
|
||||
if (!orderByType) {
|
||||
orderByType = this.typeDefinitionsStorage.getInputTypeByKey(
|
||||
fieldMetadata.type.toString(),
|
||||
InputTypeDefinitionKind.OrderBy,
|
||||
);
|
||||
this.logger.error(`Could not find a GraphQL type for ${target}`, {
|
||||
fieldMetadata,
|
||||
buildOtions,
|
||||
typeOptions,
|
||||
});
|
||||
|
||||
if (!orderByType) {
|
||||
this.logger.error(
|
||||
`Could not find a GraphQL type for ${fieldMetadata.type.toString()}`,
|
||||
{
|
||||
fieldMetadata,
|
||||
buildOtions,
|
||||
typeOptions,
|
||||
},
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`Could not find a GraphQL type for ${fieldMetadata.type.toString()}`,
|
||||
);
|
||||
}
|
||||
throw new Error(`Could not find a GraphQL type for ${target}`);
|
||||
}
|
||||
|
||||
return this.typeMapperService.mapToGqlType(orderByType, typeOptions);
|
||||
|
||||
@ -3,13 +3,14 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { GraphQLOutputType } from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { FieldMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/field-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import {
|
||||
TypeMapperService,
|
||||
TypeOptions,
|
||||
} from 'src/workspace/workspace-schema-builder/services/type-mapper.service';
|
||||
import { TypeDefinitionsStorage } from 'src/workspace/workspace-schema-builder/storages/type-definitions.storage';
|
||||
import { isCompositeFieldMetadataType } from 'src/metadata/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
|
||||
import { ObjectTypeDefinitionKind } from './object-type-definition.factory';
|
||||
|
||||
@ -28,6 +29,9 @@ export class OutputTypeFactory {
|
||||
buildOtions: WorkspaceBuildSchemaOptions,
|
||||
typeOptions: TypeOptions,
|
||||
): GraphQLOutputType {
|
||||
const target = isCompositeFieldMetadataType(fieldMetadata.type)
|
||||
? fieldMetadata.type.toString()
|
||||
: fieldMetadata.id;
|
||||
let gqlType: GraphQLOutputType | undefined =
|
||||
this.typeMapperService.mapToScalarType(
|
||||
fieldMetadata.type,
|
||||
@ -35,26 +39,18 @@ export class OutputTypeFactory {
|
||||
buildOtions.numberScalarMode,
|
||||
);
|
||||
|
||||
gqlType ??= this.typeDefinitionsStorage.getObjectTypeByKey(target, kind);
|
||||
|
||||
gqlType ??= this.typeDefinitionsStorage.getEnumTypeByKey(target);
|
||||
|
||||
if (!gqlType) {
|
||||
gqlType = this.typeDefinitionsStorage.getObjectTypeByKey(
|
||||
fieldMetadata.type.toString(),
|
||||
kind,
|
||||
);
|
||||
this.logger.error(`Could not find a GraphQL type for ${target}`, {
|
||||
fieldMetadata,
|
||||
buildOtions,
|
||||
typeOptions,
|
||||
});
|
||||
|
||||
if (!gqlType) {
|
||||
this.logger.error(
|
||||
`Could not find a GraphQL type for ${fieldMetadata.type.toString()}`,
|
||||
{
|
||||
fieldMetadata,
|
||||
buildOtions,
|
||||
typeOptions,
|
||||
},
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`Could not find a GraphQL type for ${fieldMetadata.type.toString()}`,
|
||||
);
|
||||
}
|
||||
throw new Error(`Could not find a GraphQL type for ${target}`);
|
||||
}
|
||||
|
||||
return this.typeMapperService.mapToGqlType(gqlType, typeOptions);
|
||||
|
||||
@ -4,7 +4,7 @@ import { GraphQLObjectType } from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { WorkspaceResolverBuilderQueryMethodNames } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { ObjectTypeName, RootTypeFactory } from './root-type.factory';
|
||||
|
||||
|
||||
@ -2,8 +2,8 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { GraphQLOutputType } from 'graphql';
|
||||
|
||||
import { FieldMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/field-metadata.interface';
|
||||
import { RelationMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/relation-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
import { RelationMetadataInterface } from 'src/metadata/field-metadata/interfaces/relation-metadata.interface';
|
||||
|
||||
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||
import { TypeDefinitionsStorage } from 'src/workspace/workspace-schema-builder/storages/type-definitions.storage';
|
||||
|
||||
@ -4,7 +4,7 @@ import { GraphQLFieldConfigMap, GraphQLObjectType } from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { WorkspaceResolverBuilderMethodNames } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { TypeDefinitionsStorage } from 'src/workspace/workspace-schema-builder/storages/type-definitions.storage';
|
||||
import { getResolverName } from 'src/workspace/utils/get-resolver-name.util';
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from 'graphql';
|
||||
|
||||
import { FilterIsNullable } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is-nullable.input-type';
|
||||
import { FilterIs } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is.input-type';
|
||||
import { BigFloatScalarType } from 'src/workspace/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
export const BigFloatFilterType = new GraphQLInputObjectType({
|
||||
@ -13,6 +13,6 @@ export const BigFloatFilterType = new GraphQLInputObjectType({
|
||||
lt: { type: BigFloatScalarType },
|
||||
lte: { type: BigFloatScalarType },
|
||||
neq: { type: BigFloatScalarType },
|
||||
is: { type: FilterIsNullable },
|
||||
is: { type: FilterIs },
|
||||
},
|
||||
});
|
||||
|
||||
@ -5,7 +5,7 @@ import {
|
||||
GraphQLInt,
|
||||
} from 'graphql';
|
||||
|
||||
import { FilterIsNullable } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is-nullable.input-type';
|
||||
import { FilterIs } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is.input-type';
|
||||
|
||||
export const BigIntFilterType = new GraphQLInputObjectType({
|
||||
name: 'BigIntFilter',
|
||||
@ -17,6 +17,6 @@ export const BigIntFilterType = new GraphQLInputObjectType({
|
||||
lt: { type: GraphQLInt },
|
||||
lte: { type: GraphQLInt },
|
||||
neq: { type: GraphQLInt },
|
||||
is: { type: FilterIsNullable },
|
||||
is: { type: FilterIs },
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { GraphQLBoolean, GraphQLInputObjectType } from 'graphql';
|
||||
|
||||
import { FilterIsNullable } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is-nullable.input-type';
|
||||
import { FilterIs } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is.input-type';
|
||||
|
||||
export const BooleanFilterType = new GraphQLInputObjectType({
|
||||
name: 'BooleanFilter',
|
||||
fields: {
|
||||
eq: { type: GraphQLBoolean },
|
||||
is: { type: FilterIsNullable },
|
||||
is: { type: FilterIs },
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from 'graphql';
|
||||
|
||||
import { FilterIsNullable } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is-nullable.input-type';
|
||||
import { FilterIs } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is.input-type';
|
||||
import { DateScalarType } from 'src/workspace/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
export const DateFilterType = new GraphQLInputObjectType({
|
||||
@ -13,6 +13,6 @@ export const DateFilterType = new GraphQLInputObjectType({
|
||||
lt: { type: DateScalarType },
|
||||
lte: { type: DateScalarType },
|
||||
neq: { type: DateScalarType },
|
||||
is: { type: FilterIsNullable },
|
||||
is: { type: FilterIs },
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from 'graphql';
|
||||
|
||||
import { FilterIsNullable } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is-nullable.input-type';
|
||||
import { FilterIs } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is.input-type';
|
||||
import { DateTimeScalarType } from 'src/workspace/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
export const DatetimeFilterType = new GraphQLInputObjectType({
|
||||
@ -13,6 +13,6 @@ export const DatetimeFilterType = new GraphQLInputObjectType({
|
||||
lt: { type: DateTimeScalarType },
|
||||
lte: { type: DateTimeScalarType },
|
||||
neq: { type: DateTimeScalarType },
|
||||
is: { type: FilterIsNullable },
|
||||
is: { type: FilterIs },
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { GraphQLEnumType } from 'graphql';
|
||||
|
||||
export const FilterIsNullable = new GraphQLEnumType({
|
||||
name: 'FilterIsNullable',
|
||||
export const FilterIs = new GraphQLEnumType({
|
||||
name: 'FilterIs',
|
||||
description: 'This enum to filter by nullability',
|
||||
values: {
|
||||
NULL: {
|
||||
@ -5,7 +5,7 @@ import {
|
||||
GraphQLNonNull,
|
||||
} from 'graphql';
|
||||
|
||||
import { FilterIsNullable } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is-nullable.input-type';
|
||||
import { FilterIs } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is.input-type';
|
||||
|
||||
export const FloatFilterType = new GraphQLInputObjectType({
|
||||
name: 'FloatFilter',
|
||||
@ -17,6 +17,6 @@ export const FloatFilterType = new GraphQLInputObjectType({
|
||||
lt: { type: GraphQLFloat },
|
||||
lte: { type: GraphQLFloat },
|
||||
neq: { type: GraphQLFloat },
|
||||
is: { type: FilterIsNullable },
|
||||
is: { type: FilterIs },
|
||||
},
|
||||
});
|
||||
|
||||
@ -5,7 +5,7 @@ import {
|
||||
GraphQLInt,
|
||||
} from 'graphql';
|
||||
|
||||
import { FilterIsNullable } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is-nullable.input-type';
|
||||
import { FilterIs } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is.input-type';
|
||||
|
||||
export const IntFilterType = new GraphQLInputObjectType({
|
||||
name: 'IntFilter',
|
||||
@ -17,6 +17,6 @@ export const IntFilterType = new GraphQLInputObjectType({
|
||||
lt: { type: GraphQLInt },
|
||||
lte: { type: GraphQLInt },
|
||||
neq: { type: GraphQLInt },
|
||||
is: { type: FilterIsNullable },
|
||||
is: { type: FilterIs },
|
||||
},
|
||||
});
|
||||
|
||||
@ -5,7 +5,7 @@ import {
|
||||
GraphQLString,
|
||||
} from 'graphql';
|
||||
|
||||
import { FilterIsNullable } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is-nullable.input-type';
|
||||
import { FilterIs } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is.input-type';
|
||||
|
||||
export const StringFilterType = new GraphQLInputObjectType({
|
||||
name: 'StringFilter',
|
||||
@ -22,6 +22,6 @@ export const StringFilterType = new GraphQLInputObjectType({
|
||||
ilike: { type: GraphQLString },
|
||||
regex: { type: GraphQLString },
|
||||
iregex: { type: GraphQLString },
|
||||
is: { type: FilterIsNullable },
|
||||
is: { type: FilterIs },
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from 'graphql';
|
||||
|
||||
import { FilterIsNullable } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is-nullable.input-type';
|
||||
import { FilterIs } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is.input-type';
|
||||
import { TimeScalarType } from 'src/workspace/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
export const TimeFilterType = new GraphQLInputObjectType({
|
||||
@ -13,6 +13,6 @@ export const TimeFilterType = new GraphQLInputObjectType({
|
||||
lt: { type: TimeScalarType },
|
||||
lte: { type: TimeScalarType },
|
||||
neq: { type: TimeScalarType },
|
||||
is: { type: FilterIsNullable },
|
||||
is: { type: FilterIs },
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { GraphQLInputObjectType, GraphQLList } from 'graphql';
|
||||
|
||||
import { FilterIsNullable } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is-nullable.input-type';
|
||||
import { FilterIs } from 'src/workspace/workspace-schema-builder/graphql-types/input/filter-is.input-type';
|
||||
import { UUIDScalarType } from 'src/workspace/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
export const UUIDFilterType = new GraphQLInputObjectType({
|
||||
@ -9,6 +9,6 @@ export const UUIDFilterType = new GraphQLInputObjectType({
|
||||
eq: { type: UUIDScalarType },
|
||||
in: { type: new GraphQLList(UUIDScalarType) },
|
||||
neq: { type: UUIDScalarType },
|
||||
is: { type: FilterIsNullable },
|
||||
is: { type: FilterIs },
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
import { FieldMetadataTargetColumnMap } from 'src/metadata/field-metadata/interfaces/field-metadata-target-column-map.interface';
|
||||
import { FieldMetadataDefaultValue } from 'src/metadata/field-metadata/interfaces/field-metadata-default-value.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
import { RelationMetadataEntity } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||
|
||||
export interface FieldMetadataInterface<
|
||||
T extends FieldMetadataType | 'default' = 'default',
|
||||
> {
|
||||
id: string;
|
||||
type: FieldMetadataType;
|
||||
name: string;
|
||||
label: string;
|
||||
targetColumnMap: FieldMetadataTargetColumnMap<T>;
|
||||
defaultValue?: FieldMetadataDefaultValue<T>;
|
||||
objectMetadataId: string;
|
||||
description?: string;
|
||||
isNullable?: boolean;
|
||||
fromRelationMetadata?: RelationMetadataEntity;
|
||||
toRelationMetadata?: RelationMetadataEntity;
|
||||
isCustom?: boolean;
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
import { FieldMetadataInterface } from './field-metadata.interface';
|
||||
import { RelationMetadataInterface } from './relation-metadata.interface';
|
||||
|
||||
export interface ObjectMetadataInterface {
|
||||
id: string;
|
||||
nameSingular: string;
|
||||
namePlural: string;
|
||||
labelSingular: string;
|
||||
labelPlural: string;
|
||||
description?: string;
|
||||
targetTableName: string;
|
||||
fromRelations: RelationMetadataInterface[];
|
||||
toRelations: RelationMetadataInterface[];
|
||||
fields: FieldMetadataInterface[];
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { InputTypeDefinitionKind } from 'src/workspace/workspace-schema-builder/factories/input-type-definition.factory';
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
|
||||
import { ObjectMetadataInterface } from './object-metadata.interface';
|
||||
|
||||
export interface ArgMetadata<T = any> {
|
||||
kind?: InputTypeDefinitionKind;
|
||||
type?: FieldMetadataType;
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||
|
||||
import { ObjectMetadataInterface } from './object-metadata.interface';
|
||||
import { FieldMetadataInterface } from './field-metadata.interface';
|
||||
|
||||
export interface RelationMetadataInterface {
|
||||
id: string;
|
||||
|
||||
relationType: RelationMetadataType;
|
||||
|
||||
fromObjectMetadataId: string;
|
||||
fromObjectMetadata: ObjectMetadataInterface;
|
||||
|
||||
toObjectMetadataId: string;
|
||||
toObjectMetadata: ObjectMetadataInterface;
|
||||
|
||||
fromFieldMetadataId: string;
|
||||
fromFieldMetadata: FieldMetadataInterface;
|
||||
|
||||
toFieldMetadataId: string;
|
||||
toFieldMetadata: FieldMetadataInterface;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import { FieldMetadataInterface } from './field-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
export interface WorkspaceSchemaBuilderContext {
|
||||
workspaceId: string;
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/field-metadata.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
|
||||
export const currencyObjectDefinition = {
|
||||
id: FieldMetadataType.CURRENCY.toString(),
|
||||
nameSingular: 'currency',
|
||||
namePlural: 'currency',
|
||||
labelSingular: 'Currency',
|
||||
labelPlural: 'Currency',
|
||||
targetTableName: '',
|
||||
fields: [
|
||||
{
|
||||
id: 'amountMicros',
|
||||
type: FieldMetadataType.NUMERIC,
|
||||
objectMetadataId: FieldMetadataType.CURRENCY.toString(),
|
||||
name: 'amountMicros',
|
||||
label: 'AmountMicros',
|
||||
targetColumnMap: { value: 'amountMicros' },
|
||||
isNullable: true,
|
||||
} satisfies FieldMetadataInterface,
|
||||
{
|
||||
id: 'currencyCode',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: FieldMetadataType.CURRENCY.toString(),
|
||||
name: 'currencyCode',
|
||||
label: 'Currency Code',
|
||||
targetColumnMap: { value: 'currencyCode' },
|
||||
isNullable: true,
|
||||
} satisfies FieldMetadataInterface,
|
||||
],
|
||||
fromRelations: [],
|
||||
toRelations: [],
|
||||
} satisfies ObjectMetadataInterface;
|
||||
@ -1,35 +0,0 @@
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/field-metadata.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
|
||||
export const fullNameObjectDefinition = {
|
||||
id: FieldMetadataType.FULL_NAME.toString(),
|
||||
nameSingular: 'fullName',
|
||||
namePlural: 'fullName',
|
||||
labelSingular: 'FullName',
|
||||
labelPlural: 'FullName',
|
||||
targetTableName: '',
|
||||
fields: [
|
||||
{
|
||||
id: 'firstName',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: FieldMetadataType.FULL_NAME.toString(),
|
||||
name: 'firstName',
|
||||
label: 'First Name',
|
||||
targetColumnMap: { value: 'firstName' },
|
||||
isNullable: true,
|
||||
} satisfies FieldMetadataInterface,
|
||||
{
|
||||
id: 'lastName',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: FieldMetadataType.FULL_NAME.toString(),
|
||||
name: 'lastName',
|
||||
label: 'Last Name',
|
||||
targetColumnMap: { value: 'lastName' },
|
||||
isNullable: true,
|
||||
} satisfies FieldMetadataInterface,
|
||||
],
|
||||
fromRelations: [],
|
||||
toRelations: [],
|
||||
} satisfies ObjectMetadataInterface;
|
||||
@ -1,35 +0,0 @@
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/field-metadata.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
|
||||
export const linkObjectDefinition = {
|
||||
id: FieldMetadataType.LINK.toString(),
|
||||
nameSingular: 'link',
|
||||
namePlural: 'link',
|
||||
labelSingular: 'Link',
|
||||
labelPlural: 'Link',
|
||||
targetTableName: '',
|
||||
fields: [
|
||||
{
|
||||
id: 'label',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: FieldMetadataType.LINK.toString(),
|
||||
name: 'label',
|
||||
label: 'Label',
|
||||
targetColumnMap: { value: 'label' },
|
||||
isNullable: true,
|
||||
} satisfies FieldMetadataInterface,
|
||||
{
|
||||
id: 'url',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: FieldMetadataType.LINK.toString(),
|
||||
name: 'url',
|
||||
label: 'Url',
|
||||
targetColumnMap: { value: 'url' },
|
||||
isNullable: true,
|
||||
} satisfies FieldMetadataInterface,
|
||||
],
|
||||
fromRelations: [],
|
||||
toRelations: [],
|
||||
} satisfies ObjectMetadataInterface;
|
||||
@ -75,7 +75,7 @@ export class TypeMapperService {
|
||||
fieldMetadataType: FieldMetadataType,
|
||||
dateScalarMode: DateScalarMode = 'isoDate',
|
||||
numberScalarMode: NumberScalarMode = 'float',
|
||||
): GraphQLInputObjectType | GraphQLScalarType<boolean, boolean> | undefined {
|
||||
): GraphQLInputObjectType | GraphQLScalarType | undefined {
|
||||
const dateFilter =
|
||||
dateScalarMode === 'timestamp' ? DatetimeFilterType : DateFilterType;
|
||||
const numberScalar =
|
||||
@ -84,7 +84,7 @@ export class TypeMapperService {
|
||||
// LINK and CURRENCY are handled in the factories because they are objects
|
||||
const typeFilterMapping = new Map<
|
||||
FieldMetadataType,
|
||||
GraphQLInputObjectType | GraphQLScalarType<boolean, boolean>
|
||||
GraphQLInputObjectType | GraphQLScalarType
|
||||
>([
|
||||
[FieldMetadataType.UUID, UUIDFilterType],
|
||||
[FieldMetadataType.TEXT, StringFilterType],
|
||||
@ -115,6 +115,9 @@ export class TypeMapperService {
|
||||
[FieldMetadataType.NUMBER, OrderByDirectionType],
|
||||
[FieldMetadataType.NUMERIC, OrderByDirectionType],
|
||||
[FieldMetadataType.PROBABILITY, OrderByDirectionType],
|
||||
[FieldMetadataType.RATING, OrderByDirectionType],
|
||||
[FieldMetadataType.SELECT, OrderByDirectionType],
|
||||
[FieldMetadataType.MULTI_SELECT, OrderByDirectionType],
|
||||
]);
|
||||
|
||||
return typeOrderByMapping.get(fieldMetadataType);
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
|
||||
import { GraphQLInputObjectType, GraphQLObjectType } from 'graphql';
|
||||
import {
|
||||
GraphQLEnumType,
|
||||
GraphQLInputObjectType,
|
||||
GraphQLObjectType,
|
||||
} from 'graphql';
|
||||
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
import { EnumTypeDefinition } from 'src/workspace/workspace-schema-builder/factories/enum-type-definition.factory';
|
||||
import {
|
||||
InputTypeDefinition,
|
||||
InputTypeDefinitionKind,
|
||||
@ -15,6 +20,7 @@ import {
|
||||
// Must be scoped on REQUEST level
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class TypeDefinitionsStorage {
|
||||
private readonly enumTypeDefinitions = new Map<string, EnumTypeDefinition>();
|
||||
private readonly objectTypeDefinitions = new Map<
|
||||
string,
|
||||
ObjectTypeDefinition
|
||||
@ -24,6 +30,10 @@ export class TypeDefinitionsStorage {
|
||||
InputTypeDefinition
|
||||
>();
|
||||
|
||||
addEnumTypes(enumDefs: EnumTypeDefinition[]) {
|
||||
enumDefs.forEach((item) => this.enumTypeDefinitions.set(item.target, item));
|
||||
}
|
||||
|
||||
addObjectTypes(objectDefs: ObjectTypeDefinition[]) {
|
||||
objectDefs.forEach((item) =>
|
||||
this.objectTypeDefinitions.set(
|
||||
@ -64,6 +74,10 @@ export class TypeDefinitionsStorage {
|
||||
)?.type;
|
||||
}
|
||||
|
||||
getEnumTypeByKey(target: string): GraphQLEnumType | undefined {
|
||||
return this.enumTypeDefinitions.get(target)?.type;
|
||||
}
|
||||
|
||||
getAllInputTypeDefinitions(): InputTypeDefinition[] {
|
||||
return Array.from(this.inputTypeDefinitions.values());
|
||||
}
|
||||
|
||||
@ -1,8 +1,14 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import { FieldMetadataEntity } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
import { customTableDefaultColumns } from 'src/workspace/workspace-migration-runner/utils/custom-table-default-column.util';
|
||||
import { fullNameObjectDefinition } from 'src/workspace/workspace-schema-builder/object-definitions/full-name.object-definition';
|
||||
import { fullNameObjectDefinition } from 'src/metadata/field-metadata/composite-types/full-name.composite-type';
|
||||
import { currencyObjectDefinition } from 'src/metadata/field-metadata/composite-types/currency.composite-type';
|
||||
import { linkObjectDefinition } from 'src/metadata/field-metadata/composite-types/link.composite-type';
|
||||
import { EnumTypeDefinitionFactory } from 'src/workspace/workspace-schema-builder/factories/enum-type-definition.factory';
|
||||
|
||||
import { TypeDefinitionsStorage } from './storages/type-definitions.storage';
|
||||
import {
|
||||
@ -15,16 +21,12 @@ import {
|
||||
} from './factories/input-type-definition.factory';
|
||||
import { getFieldMetadataType } from './utils/get-field-metadata-type.util';
|
||||
import { WorkspaceBuildSchemaOptions } from './interfaces/workspace-build-schema-optionts.interface';
|
||||
import { currencyObjectDefinition } from './object-definitions/currency.object-definition';
|
||||
import { linkObjectDefinition } from './object-definitions/link.object-definition';
|
||||
import { ObjectMetadataInterface } from './interfaces/object-metadata.interface';
|
||||
import { FieldMetadataInterface } from './interfaces/field-metadata.interface';
|
||||
import { FilterTypeDefinitionFactory } from './factories/filter-type-definition.factory';
|
||||
import { ConnectionTypeDefinitionFactory } from './factories/connection-type-definition.factory';
|
||||
import { EdgeTypeDefinitionFactory } from './factories/edge-type-definition.factory';
|
||||
import { OrderByTypeDefinitionFactory } from './factories/order-by-type-definition.factory';
|
||||
import { ExtendObjectTypeDefinitionFactory } from './factories/extend-object-type-definition.factory';
|
||||
import { objectContainsCompositeField } from './utils/object-contains-composite-field';
|
||||
import { objectContainsRelationField } from './utils/object-contains-relation-field';
|
||||
|
||||
// Create a default field for each custom table default column
|
||||
const defaultFields = customTableDefaultColumns.map((column) => {
|
||||
@ -42,6 +44,7 @@ export class TypeDefinitionsGenerator {
|
||||
constructor(
|
||||
private readonly typeDefinitionsStorage: TypeDefinitionsStorage,
|
||||
private readonly objectTypeDefinitionFactory: ObjectTypeDefinitionFactory,
|
||||
private readonly enumTypeDefinitionFactory: EnumTypeDefinitionFactory,
|
||||
private readonly inputTypeDefinitionFactory: InputTypeDefinitionFactory,
|
||||
private readonly filterTypeDefintionFactory: FilterTypeDefinitionFactory,
|
||||
private readonly orderByTypeDefinitionFactory: OrderByTypeDefinitionFactory,
|
||||
@ -74,6 +77,7 @@ export class TypeDefinitionsGenerator {
|
||||
);
|
||||
|
||||
// Generate static objects first because they can be used in dynamic objects
|
||||
this.generateEnumTypeDefs(staticObjectMetadataCollection, options);
|
||||
this.generateObjectTypeDefs(staticObjectMetadataCollection, options);
|
||||
this.generateInputTypeDefs(staticObjectMetadataCollection, options);
|
||||
}
|
||||
@ -89,6 +93,7 @@ export class TypeDefinitionsGenerator {
|
||||
);
|
||||
|
||||
// Generate dynamic objects
|
||||
this.generateEnumTypeDefs(dynamicObjectMetadataCollection, options);
|
||||
this.generateObjectTypeDefs(dynamicObjectMetadataCollection, options);
|
||||
this.generatePaginationTypeDefs(dynamicObjectMetadataCollection, options);
|
||||
this.generateInputTypeDefs(dynamicObjectMetadataCollection, options);
|
||||
@ -203,13 +208,26 @@ export class TypeDefinitionsGenerator {
|
||||
this.typeDefinitionsStorage.addInputTypes(inputTypeDefs);
|
||||
}
|
||||
|
||||
private generateEnumTypeDefs(
|
||||
objectMetadataCollection: ObjectMetadataInterface[],
|
||||
options: WorkspaceBuildSchemaOptions,
|
||||
) {
|
||||
const enumTypeDefs = objectMetadataCollection
|
||||
.map((objectMetadata) =>
|
||||
this.enumTypeDefinitionFactory.create(objectMetadata, options),
|
||||
)
|
||||
.flat();
|
||||
|
||||
this.typeDefinitionsStorage.addEnumTypes(enumTypeDefs);
|
||||
}
|
||||
|
||||
private generateExtendedObjectTypeDefs(
|
||||
objectMetadataCollection: ObjectMetadataInterface[],
|
||||
options: WorkspaceBuildSchemaOptions,
|
||||
) {
|
||||
// Generate extended object type defs only for objects that contain composite fields
|
||||
const objectMetadataCollectionWithCompositeFields =
|
||||
objectMetadataCollection.filter(objectContainsCompositeField);
|
||||
objectMetadataCollection.filter(objectContainsRelationField);
|
||||
const objectTypeDefs = objectMetadataCollectionWithCompositeFields.map(
|
||||
(objectMetadata) =>
|
||||
this.extendObjectTypeDefinitionFactory.create(objectMetadata, options),
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
||||
|
||||
import { isCompositeFieldMetadataType } from 'src/workspace/utils/is-composite-field-metadata-type.util';
|
||||
|
||||
export const objectContainsCompositeField = (
|
||||
objectMetadata: ObjectMetadataInterface,
|
||||
): boolean => {
|
||||
return objectMetadata.fields.some((field) =>
|
||||
isCompositeFieldMetadataType(field.type),
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,11 @@
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { isRelationFieldMetadataType } from 'src/workspace/utils/is-relation-field-metadata-type.util';
|
||||
|
||||
export const objectContainsRelationField = (
|
||||
objectMetadata: ObjectMetadataInterface,
|
||||
): boolean => {
|
||||
return objectMetadata.fields.some((field) =>
|
||||
isRelationFieldMetadataType(field.type),
|
||||
);
|
||||
};
|
||||
@ -3,13 +3,13 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { GraphQLSchema } from 'graphql';
|
||||
|
||||
import { WorkspaceResolverBuilderMethods } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { TypeDefinitionsGenerator } from './type-definitions.generator';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from './interfaces/workspace-build-schema-optionts.interface';
|
||||
import { QueryTypeFactory } from './factories/query-type.factory';
|
||||
import { MutationTypeFactory } from './factories/mutation-type.factory';
|
||||
import { ObjectMetadataInterface } from './interfaces/object-metadata.interface';
|
||||
import { OrphanedTypesFactory } from './factories/orphaned-types.factory';
|
||||
|
||||
@Injectable()
|
||||
|
||||
Reference in New Issue
Block a user