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:
@ -3,7 +3,7 @@ ARG PG_MAIN_VERSION=14
|
|||||||
FROM postgres:${PG_MAIN_VERSION} as postgres
|
FROM postgres:${PG_MAIN_VERSION} as postgres
|
||||||
|
|
||||||
ARG PG_MAIN_VERSION
|
ARG PG_MAIN_VERSION
|
||||||
ARG PG_GRAPHQL_VERSION=1.3.0
|
ARG PG_GRAPHQL_VERSION=1.4.2
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
|
|
||||||
RUN set -eux; \
|
RUN set -eux; \
|
||||||
|
|||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class AddEnumOptions1700663879152 implements MigrationInterface {
|
||||||
|
name = 'AddEnumOptions1700663879152'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "metadata"."fieldMetadata" RENAME COLUMN "enums" TO "options"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "metadata"."fieldMetadata" DROP COLUMN "options"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "metadata"."fieldMetadata" ADD "options" jsonb`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "metadata"."fieldMetadata" DROP COLUMN "options"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "metadata"."fieldMetadata" ADD "options" text array`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "metadata"."fieldMetadata" RENAME COLUMN "options" TO "enums"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -35,7 +35,12 @@ export class LocalMemoryDriver<T> implements MemoryStorageDriver<T> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = this.storage.get(compositeKey)!;
|
const data = this.storage.get(compositeKey);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const deserializeData = this.serializer.deserialize(data);
|
const deserializeData = this.serializer.deserialize(data);
|
||||||
|
|
||||||
return deserializeData;
|
return deserializeData;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.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 { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.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 { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.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 { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
|
||||||
@ -2,21 +2,26 @@ import { Field, HideField, InputType } from '@nestjs/graphql';
|
|||||||
|
|
||||||
import { BeforeCreateOne } from '@ptc-org/nestjs-query-graphql';
|
import { BeforeCreateOne } from '@ptc-org/nestjs-query-graphql';
|
||||||
import {
|
import {
|
||||||
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
IsEnum,
|
IsEnum,
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
IsOptional,
|
IsOptional,
|
||||||
IsString,
|
IsString,
|
||||||
IsUUID,
|
IsUUID,
|
||||||
|
ValidateNested,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import graphqlTypeJson from 'graphql-type-json';
|
import GraphQLJSON from 'graphql-type-json';
|
||||||
|
import { Type } from 'class-transformer';
|
||||||
|
|
||||||
import { FieldMetadataTargetColumnMap } from 'src/metadata/field-metadata/interfaces/field-metadata-target-column-map.interface';
|
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 { FieldMetadataDefaultValue } from 'src/metadata/field-metadata/interfaces/field-metadata-default-value.interface';
|
||||||
|
import { FieldMetadataOptions } from 'src/metadata/field-metadata/interfaces/field-metadata-options.interface';
|
||||||
|
|
||||||
import { BeforeCreateOneField } from 'src/metadata/field-metadata/hooks/before-create-one-field.hook';
|
import { BeforeCreateOneField } from 'src/metadata/field-metadata/hooks/before-create-one-field.hook';
|
||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
import { IsDefaultValue } from 'src/metadata/field-metadata/validators/is-default-value.validator';
|
import { IsDefaultValue } from 'src/metadata/field-metadata/validators/is-default-value.validator';
|
||||||
|
import { FieldMetadataComplexOptions } from 'src/metadata/field-metadata/dtos/options.input';
|
||||||
|
|
||||||
@InputType()
|
@InputType()
|
||||||
@BeforeCreateOne(BeforeCreateOneField)
|
@BeforeCreateOne(BeforeCreateOneField)
|
||||||
@ -53,12 +58,19 @@ export class CreateFieldInput {
|
|||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@Field({ nullable: true })
|
@Field({ nullable: true })
|
||||||
isNullable: boolean;
|
isNullable?: boolean;
|
||||||
|
|
||||||
@IsDefaultValue({ message: 'Invalid default value for the specified type' })
|
@IsDefaultValue({ message: 'Invalid default value for the specified type' })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@Field(() => graphqlTypeJson, { nullable: true })
|
@Field(() => GraphQLJSON, { nullable: true })
|
||||||
defaultValue: FieldMetadataDefaultValue;
|
defaultValue?: FieldMetadataDefaultValue;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsArray()
|
||||||
|
@ValidateNested({ each: true })
|
||||||
|
@Type(() => FieldMetadataComplexOptions)
|
||||||
|
@Field(() => GraphQLJSON, { nullable: true })
|
||||||
|
options?: FieldMetadataOptions;
|
||||||
|
|
||||||
@HideField()
|
@HideField()
|
||||||
targetColumnMap: FieldMetadataTargetColumnMap;
|
targetColumnMap: FieldMetadataTargetColumnMap;
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import {
|
|||||||
registerEnumType,
|
registerEnumType,
|
||||||
} from '@nestjs/graphql';
|
} from '@nestjs/graphql';
|
||||||
|
|
||||||
|
import { GraphQLJSON } from 'graphql-type-json';
|
||||||
import {
|
import {
|
||||||
Authorize,
|
Authorize,
|
||||||
BeforeDeleteOne,
|
BeforeDeleteOne,
|
||||||
@ -15,6 +16,9 @@ import {
|
|||||||
Relation,
|
Relation,
|
||||||
} from '@ptc-org/nestjs-query-graphql';
|
} from '@ptc-org/nestjs-query-graphql';
|
||||||
|
|
||||||
|
import { FieldMetadataOptions } from 'src/metadata/field-metadata/interfaces/field-metadata-options.interface';
|
||||||
|
import { FieldMetadataDefaultValue } from 'src/metadata/field-metadata/interfaces/field-metadata-default-value.interface';
|
||||||
|
|
||||||
import { RelationMetadataDTO } from 'src/metadata/relation-metadata/dtos/relation-metadata.dto';
|
import { RelationMetadataDTO } from 'src/metadata/relation-metadata/dtos/relation-metadata.dto';
|
||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
import { BeforeDeleteOneField } from 'src/metadata/field-metadata/hooks/before-delete-one-field.hook';
|
import { BeforeDeleteOneField } from 'src/metadata/field-metadata/hooks/before-delete-one-field.hook';
|
||||||
@ -76,6 +80,12 @@ export class FieldMetadataDTO {
|
|||||||
@Field()
|
@Field()
|
||||||
isNullable: boolean;
|
isNullable: boolean;
|
||||||
|
|
||||||
|
@Field(() => GraphQLJSON, { nullable: true })
|
||||||
|
defaultValue?: FieldMetadataDefaultValue;
|
||||||
|
|
||||||
|
@Field(() => GraphQLJSON, { nullable: true })
|
||||||
|
options?: FieldMetadataOptions;
|
||||||
|
|
||||||
@HideField()
|
@HideField()
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
|
|
||||||
|
|||||||
22
server/src/metadata/field-metadata/dtos/options.input.ts
Normal file
22
server/src/metadata/field-metadata/dtos/options.input.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { IsString, IsNumber, IsOptional } from 'class-validator';
|
||||||
|
|
||||||
|
export class FieldMetadataDefaultOptions {
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
id?: string;
|
||||||
|
|
||||||
|
@IsNumber()
|
||||||
|
position: number;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
label: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FieldMetadataComplexOptions extends FieldMetadataDefaultOptions {
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
@ -1,9 +1,21 @@
|
|||||||
import { Field, InputType } from '@nestjs/graphql';
|
import { Field, HideField, InputType } from '@nestjs/graphql';
|
||||||
|
|
||||||
import { BeforeUpdateOne } from '@ptc-org/nestjs-query-graphql';
|
import { BeforeUpdateOne } from '@ptc-org/nestjs-query-graphql';
|
||||||
import { IsBoolean, IsOptional, IsString } from 'class-validator';
|
import {
|
||||||
|
IsArray,
|
||||||
|
IsBoolean,
|
||||||
|
IsOptional,
|
||||||
|
IsString,
|
||||||
|
ValidateNested,
|
||||||
|
} from 'class-validator';
|
||||||
|
import GraphQLJSON from 'graphql-type-json';
|
||||||
|
import { Type } from 'class-transformer';
|
||||||
|
|
||||||
|
import { FieldMetadataDefaultValue } from 'src/metadata/field-metadata/interfaces/field-metadata-default-value.interface';
|
||||||
|
import { FieldMetadataOptions } from 'src/metadata/field-metadata/interfaces/field-metadata-options.interface';
|
||||||
|
|
||||||
import { BeforeUpdateOneField } from 'src/metadata/field-metadata/hooks/before-update-one-field.hook';
|
import { BeforeUpdateOneField } from 'src/metadata/field-metadata/hooks/before-update-one-field.hook';
|
||||||
|
import { FieldMetadataComplexOptions } from 'src/metadata/field-metadata/dtos/options.input';
|
||||||
|
|
||||||
@InputType()
|
@InputType()
|
||||||
@BeforeUpdateOne(BeforeUpdateOneField)
|
@BeforeUpdateOne(BeforeUpdateOneField)
|
||||||
@ -32,4 +44,19 @@ export class UpdateFieldInput {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
@Field({ nullable: true })
|
@Field({ nullable: true })
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
|
|
||||||
|
// TODO: Add validation for this but we don't have the type actually
|
||||||
|
@IsOptional()
|
||||||
|
@Field(() => GraphQLJSON, { nullable: true })
|
||||||
|
defaultValue?: FieldMetadataDefaultValue;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsArray()
|
||||||
|
@ValidateNested({ each: true })
|
||||||
|
@Type(() => FieldMetadataComplexOptions)
|
||||||
|
@Field(() => GraphQLJSON, { nullable: true })
|
||||||
|
options?: FieldMetadataOptions;
|
||||||
|
|
||||||
|
@HideField()
|
||||||
|
workspaceId: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,9 +10,10 @@ import {
|
|||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
|
|
||||||
import { FieldMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/field-metadata.interface';
|
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||||
import { FieldMetadataTargetColumnMap } from 'src/metadata/field-metadata/interfaces/field-metadata-target-column-map.interface';
|
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 { FieldMetadataDefaultValue } from 'src/metadata/field-metadata/interfaces/field-metadata-default-value.interface';
|
||||||
|
import { FieldMetadataOptions } from 'src/metadata/field-metadata/interfaces/field-metadata-options.interface';
|
||||||
|
|
||||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||||
import { RelationMetadataEntity } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
import { RelationMetadataEntity } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
@ -27,11 +28,13 @@ export enum FieldMetadataType {
|
|||||||
NUMBER = 'NUMBER',
|
NUMBER = 'NUMBER',
|
||||||
NUMERIC = 'NUMERIC',
|
NUMERIC = 'NUMERIC',
|
||||||
PROBABILITY = 'PROBABILITY',
|
PROBABILITY = 'PROBABILITY',
|
||||||
ENUM = 'ENUM',
|
|
||||||
LINK = 'LINK',
|
LINK = 'LINK',
|
||||||
CURRENCY = 'CURRENCY',
|
CURRENCY = 'CURRENCY',
|
||||||
RELATION = 'RELATION',
|
|
||||||
FULL_NAME = 'FULL_NAME',
|
FULL_NAME = 'FULL_NAME',
|
||||||
|
RATING = 'RATING',
|
||||||
|
SELECT = 'SELECT',
|
||||||
|
MULTI_SELECT = 'MULTI_SELECT',
|
||||||
|
RELATION = 'RELATION',
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity('fieldMetadata')
|
@Entity('fieldMetadata')
|
||||||
@ -40,7 +43,10 @@ export enum FieldMetadataType {
|
|||||||
'objectMetadataId',
|
'objectMetadataId',
|
||||||
'workspaceId',
|
'workspaceId',
|
||||||
])
|
])
|
||||||
export class FieldMetadataEntity implements FieldMetadataInterface {
|
export class FieldMetadataEntity<
|
||||||
|
T extends FieldMetadataType | 'default' = 'default',
|
||||||
|
> implements FieldMetadataInterface<T>
|
||||||
|
{
|
||||||
@PrimaryGeneratedColumn('uuid')
|
@PrimaryGeneratedColumn('uuid')
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
@ -63,10 +69,10 @@ export class FieldMetadataEntity implements FieldMetadataInterface {
|
|||||||
label: string;
|
label: string;
|
||||||
|
|
||||||
@Column({ nullable: false, type: 'jsonb' })
|
@Column({ nullable: false, type: 'jsonb' })
|
||||||
targetColumnMap: FieldMetadataTargetColumnMap;
|
targetColumnMap: FieldMetadataTargetColumnMap<T>;
|
||||||
|
|
||||||
@Column({ nullable: true, type: 'jsonb' })
|
@Column({ nullable: true, type: 'jsonb' })
|
||||||
defaultValue: FieldMetadataDefaultValue;
|
defaultValue: FieldMetadataDefaultValue<T>;
|
||||||
|
|
||||||
@Column({ nullable: true, type: 'text' })
|
@Column({ nullable: true, type: 'text' })
|
||||||
description: string;
|
description: string;
|
||||||
@ -74,8 +80,8 @@ export class FieldMetadataEntity implements FieldMetadataInterface {
|
|||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
icon: string;
|
icon: string;
|
||||||
|
|
||||||
@Column('text', { nullable: true, array: true })
|
@Column('jsonb', { nullable: true })
|
||||||
enums: string[];
|
options: FieldMetadataOptions<T>;
|
||||||
|
|
||||||
@Column({ default: false })
|
@Column({ default: false })
|
||||||
isCustom: boolean;
|
isCustom: boolean;
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
|
BadRequestException,
|
||||||
ConflictException,
|
ConflictException,
|
||||||
Injectable,
|
Injectable,
|
||||||
NotFoundException,
|
NotFoundException,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { v4 as uuidV4 } from 'uuid';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||||
|
|
||||||
@ -12,11 +14,15 @@ import { WorkspaceMigrationRunnerService } from 'src/workspace/workspace-migrati
|
|||||||
import { WorkspaceMigrationService } from 'src/metadata/workspace-migration/workspace-migration.service';
|
import { WorkspaceMigrationService } from 'src/metadata/workspace-migration/workspace-migration.service';
|
||||||
import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service';
|
import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service';
|
||||||
import { CreateFieldInput } from 'src/metadata/field-metadata/dtos/create-field.input';
|
import { CreateFieldInput } from 'src/metadata/field-metadata/dtos/create-field.input';
|
||||||
import { WorkspaceMigrationTableAction } from 'src/metadata/workspace-migration/workspace-migration.entity';
|
import {
|
||||||
|
WorkspaceMigrationColumnActionType,
|
||||||
|
WorkspaceMigrationTableAction,
|
||||||
|
} from 'src/metadata/workspace-migration/workspace-migration.entity';
|
||||||
import { generateTargetColumnMap } from 'src/metadata/field-metadata/utils/generate-target-column-map.util';
|
import { generateTargetColumnMap } from 'src/metadata/field-metadata/utils/generate-target-column-map.util';
|
||||||
import { convertFieldMetadataToColumnActions } from 'src/metadata/field-metadata/utils/convert-field-metadata-to-column-action.util';
|
|
||||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||||
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
||||||
|
import { UpdateFieldInput } from 'src/metadata/field-metadata/dtos/update-field.input';
|
||||||
|
import { WorkspaceMigrationFactory } from 'src/metadata/workspace-migration/workspace-migration.factory';
|
||||||
|
|
||||||
import { FieldMetadataEntity } from './field-metadata.entity';
|
import { FieldMetadataEntity } from './field-metadata.entity';
|
||||||
|
|
||||||
@ -27,6 +33,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
|||||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||||
|
|
||||||
private readonly objectMetadataService: ObjectMetadataService,
|
private readonly objectMetadataService: ObjectMetadataService,
|
||||||
|
private readonly workspaceMigrationFactory: WorkspaceMigrationFactory,
|
||||||
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
||||||
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
|
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
|
||||||
private readonly dataSourceService: DataSourceService,
|
private readonly dataSourceService: DataSourceService,
|
||||||
@ -63,6 +70,12 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
|||||||
const createdFieldMetadata = await super.createOne({
|
const createdFieldMetadata = await super.createOne({
|
||||||
...record,
|
...record,
|
||||||
targetColumnMap: generateTargetColumnMap(record.type, true, record.name),
|
targetColumnMap: generateTargetColumnMap(record.type, true, record.name),
|
||||||
|
options: record.options
|
||||||
|
? record.options.map((option) => ({
|
||||||
|
...option,
|
||||||
|
id: uuidV4(),
|
||||||
|
}))
|
||||||
|
: undefined,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
isCustom: true,
|
isCustom: true,
|
||||||
});
|
});
|
||||||
@ -73,7 +86,10 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
|||||||
{
|
{
|
||||||
name: objectMetadata.targetTableName,
|
name: objectMetadata.targetTableName,
|
||||||
action: 'alter',
|
action: 'alter',
|
||||||
columns: convertFieldMetadataToColumnActions(createdFieldMetadata),
|
columns: this.workspaceMigrationFactory.createColumnActions(
|
||||||
|
WorkspaceMigrationColumnActionType.CREATE,
|
||||||
|
createdFieldMetadata,
|
||||||
|
),
|
||||||
} satisfies WorkspaceMigrationTableAction,
|
} satisfies WorkspaceMigrationTableAction,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -123,6 +139,66 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
|||||||
return createdFieldMetadata;
|
return createdFieldMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override async updateOne(
|
||||||
|
id: string,
|
||||||
|
record: UpdateFieldInput,
|
||||||
|
): Promise<FieldMetadataEntity> {
|
||||||
|
const existingFieldMetadata = await this.fieldMetadataRepository.findOne({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
workspaceId: record.workspaceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existingFieldMetadata) {
|
||||||
|
throw new NotFoundException('Field does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
const objectMetadata =
|
||||||
|
await this.objectMetadataService.findOneWithinWorkspace(
|
||||||
|
existingFieldMetadata?.objectMetadataId,
|
||||||
|
record.workspaceId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!objectMetadata) {
|
||||||
|
throw new NotFoundException('Object does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the id of the options has been provided
|
||||||
|
if (record.options) {
|
||||||
|
for (const option of record.options) {
|
||||||
|
if (!option.id) {
|
||||||
|
throw new BadRequestException('Option id is required');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedFieldMetadata = await super.updateOne(id, record);
|
||||||
|
|
||||||
|
if (record.options || record.defaultValue) {
|
||||||
|
await this.workspaceMigrationService.createCustomMigration(
|
||||||
|
existingFieldMetadata.workspaceId,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: objectMetadata.targetTableName,
|
||||||
|
action: 'alter',
|
||||||
|
columns: this.workspaceMigrationFactory.createColumnActions(
|
||||||
|
WorkspaceMigrationColumnActionType.ALTER,
|
||||||
|
existingFieldMetadata,
|
||||||
|
updatedFieldMetadata,
|
||||||
|
),
|
||||||
|
} satisfies WorkspaceMigrationTableAction,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
||||||
|
updatedFieldMetadata.workspaceId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedFieldMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
public async findOneWithinWorkspace(
|
public async findOneWithinWorkspace(
|
||||||
fieldMetadataId: string,
|
fieldMetadataId: string,
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
|
|||||||
@ -61,6 +61,8 @@ export class BeforeUpdateOneField<T extends UpdateFieldInput>
|
|||||||
|
|
||||||
this.checkIfFieldIsEditable(instance.update, fieldMetadata);
|
this.checkIfFieldIsEditable(instance.update, fieldMetadata);
|
||||||
|
|
||||||
|
instance.update.workspaceId = workspaceId;
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,11 @@ export interface FieldMetadataDefaultValueNumber {
|
|||||||
export interface FieldMetadataDefaultValueBoolean {
|
export interface FieldMetadataDefaultValueBoolean {
|
||||||
value: boolean;
|
value: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FieldMetadataDefaultValueStringArray {
|
||||||
|
value: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface FieldMetadataDefaultValueDateTime {
|
export interface FieldMetadataDefaultValueDateTime {
|
||||||
value: Date;
|
value: Date;
|
||||||
}
|
}
|
||||||
@ -55,10 +60,12 @@ type FieldMetadataDefaultValueMapping = {
|
|||||||
[FieldMetadataType.NUMBER]: FieldMetadataDefaultValueNumber;
|
[FieldMetadataType.NUMBER]: FieldMetadataDefaultValueNumber;
|
||||||
[FieldMetadataType.NUMERIC]: FieldMetadataDefaultValueString;
|
[FieldMetadataType.NUMERIC]: FieldMetadataDefaultValueString;
|
||||||
[FieldMetadataType.PROBABILITY]: FieldMetadataDefaultValueNumber;
|
[FieldMetadataType.PROBABILITY]: FieldMetadataDefaultValueNumber;
|
||||||
[FieldMetadataType.ENUM]: FieldMetadataDefaultValueString;
|
|
||||||
[FieldMetadataType.LINK]: FieldMetadataDefaultValueLink;
|
[FieldMetadataType.LINK]: FieldMetadataDefaultValueLink;
|
||||||
[FieldMetadataType.CURRENCY]: FieldMetadataDefaultValueCurrency;
|
[FieldMetadataType.CURRENCY]: FieldMetadataDefaultValueCurrency;
|
||||||
[FieldMetadataType.FULL_NAME]: FieldMetadataDefaultValueFullName;
|
[FieldMetadataType.FULL_NAME]: FieldMetadataDefaultValueFullName;
|
||||||
|
[FieldMetadataType.RATING]: FieldMetadataDefaultValueString;
|
||||||
|
[FieldMetadataType.SELECT]: FieldMetadataDefaultValueString;
|
||||||
|
[FieldMetadataType.MULTI_SELECT]: FieldMetadataDefaultValueStringArray;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DefaultValueByFieldMetadata<T extends FieldMetadataType | 'default'> = [
|
type DefaultValueByFieldMetadata<T extends FieldMetadataType | 'default'> = [
|
||||||
@ -78,7 +85,9 @@ type FieldMetadataDefaultValueExtractNestedType<T> = T extends {
|
|||||||
}
|
}
|
||||||
? U
|
? U
|
||||||
: T extends object
|
: T extends object
|
||||||
? T[keyof T]
|
? { [K in keyof T]: T[K] } extends { value: infer V }
|
||||||
|
? V
|
||||||
|
: T[keyof T]
|
||||||
: never;
|
: never;
|
||||||
|
|
||||||
type FieldMetadataDefaultValueExtractedTypes = {
|
type FieldMetadataDefaultValueExtractedTypes = {
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
import {
|
||||||
|
FieldMetadataComplexOptions,
|
||||||
|
FieldMetadataDefaultOptions,
|
||||||
|
} from 'src/metadata/field-metadata/dtos/options.input';
|
||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
|
||||||
|
type FieldMetadataOptionsMapping = {
|
||||||
|
[FieldMetadataType.RATING]: FieldMetadataDefaultOptions[];
|
||||||
|
[FieldMetadataType.SELECT]: FieldMetadataComplexOptions[];
|
||||||
|
[FieldMetadataType.MULTI_SELECT]: FieldMetadataComplexOptions[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type OptionsByFieldMetadata<T extends FieldMetadataType | 'default'> =
|
||||||
|
T extends keyof FieldMetadataOptionsMapping
|
||||||
|
? FieldMetadataOptionsMapping[T]
|
||||||
|
: T extends 'default'
|
||||||
|
? FieldMetadataDefaultOptions[] | FieldMetadataComplexOptions[]
|
||||||
|
: never;
|
||||||
|
|
||||||
|
export type FieldMetadataOptions<
|
||||||
|
T extends FieldMetadataType | 'default' = 'default',
|
||||||
|
> = OptionsByFieldMetadata<T>;
|
||||||
@ -29,12 +29,13 @@ type FieldMetadataTypeMapping = {
|
|||||||
[FieldMetadataType.FULL_NAME]: FieldMetadataTargetColumnMapFullName;
|
[FieldMetadataType.FULL_NAME]: FieldMetadataTargetColumnMapFullName;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TypeByFieldMetadata<T extends FieldMetadataType | 'default'> =
|
type TypeByFieldMetadata<T extends FieldMetadataType | 'default'> = [
|
||||||
T extends keyof FieldMetadataTypeMapping
|
T,
|
||||||
? FieldMetadataTypeMapping[T]
|
] extends [keyof FieldMetadataTypeMapping]
|
||||||
: T extends 'default'
|
? FieldMetadataTypeMapping[T]
|
||||||
? AllFieldMetadataTypes
|
: T extends 'default'
|
||||||
: FieldMetadataTargetColumnMapValue;
|
? AllFieldMetadataTypes
|
||||||
|
: FieldMetadataTargetColumnMapValue;
|
||||||
|
|
||||||
export type FieldMetadataTargetColumnMap<
|
export type FieldMetadataTargetColumnMap<
|
||||||
T extends FieldMetadataType | 'default' = 'default',
|
T extends FieldMetadataType | 'default' = 'default',
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { FieldMetadataTargetColumnMap } from 'src/metadata/field-metadata/interfaces/field-metadata-target-column-map.interface';
|
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 { FieldMetadataDefaultValue } from 'src/metadata/field-metadata/interfaces/field-metadata-default-value.interface';
|
||||||
|
import { FieldMetadataOptions } from 'src/metadata/field-metadata/interfaces/field-metadata-options.interface';
|
||||||
|
|
||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
import { RelationMetadataEntity } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
import { RelationMetadataEntity } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
@ -13,6 +14,7 @@ export interface FieldMetadataInterface<
|
|||||||
label: string;
|
label: string;
|
||||||
targetColumnMap: FieldMetadataTargetColumnMap<T>;
|
targetColumnMap: FieldMetadataTargetColumnMap<T>;
|
||||||
defaultValue?: FieldMetadataDefaultValue<T>;
|
defaultValue?: FieldMetadataDefaultValue<T>;
|
||||||
|
options?: FieldMetadataOptions<T>;
|
||||||
objectMetadataId: string;
|
objectMetadataId: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
isNullable?: boolean;
|
isNullable?: boolean;
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { FieldMetadataInterface } from './field-metadata.interface';
|
|
||||||
import { RelationMetadataInterface } from './relation-metadata.interface';
|
import { RelationMetadataInterface } from './relation-metadata.interface';
|
||||||
|
import { FieldMetadataInterface } from './field-metadata.interface';
|
||||||
|
|
||||||
export interface ObjectMetadataInterface {
|
export interface ObjectMetadataInterface {
|
||||||
id: string;
|
id: string;
|
||||||
@ -1,70 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
import { convertFieldMetadataToColumnActions } from 'src/metadata/field-metadata/utils/convert-field-metadata-to-column-action.util';
|
|
||||||
|
|
||||||
describe('convertFieldMetadataToColumnActions', () => {
|
|
||||||
it('should convert TEXT field metadata to column actions', () => {
|
|
||||||
const fieldMetadata = {
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
targetColumnMap: { value: 'name' },
|
|
||||||
defaultValue: { value: 'default text' },
|
|
||||||
} as any;
|
|
||||||
const columnActions = convertFieldMetadataToColumnActions(fieldMetadata);
|
|
||||||
expect(columnActions).toEqual([
|
|
||||||
{
|
|
||||||
action: 'CREATE',
|
|
||||||
columnName: 'name',
|
|
||||||
columnType: 'text',
|
|
||||||
defaultValue: "'default text'",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should convert LINK field metadata to column actions', () => {
|
|
||||||
const fieldMetadata = {
|
|
||||||
type: FieldMetadataType.LINK,
|
|
||||||
targetColumnMap: { label: 'linkLabel', url: 'linkURL' },
|
|
||||||
defaultValue: { label: 'http://example.com', url: 'Example' },
|
|
||||||
} as any;
|
|
||||||
const columnActions = convertFieldMetadataToColumnActions(fieldMetadata);
|
|
||||||
expect(columnActions).toEqual([
|
|
||||||
{
|
|
||||||
action: 'CREATE',
|
|
||||||
columnName: 'linkLabel',
|
|
||||||
columnType: 'varchar',
|
|
||||||
defaultValue: "'http://example.com'",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
action: 'CREATE',
|
|
||||||
columnName: 'linkURL',
|
|
||||||
columnType: 'varchar',
|
|
||||||
defaultValue: "'Example'",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should convert CURRENCY field metadata to column actions', () => {
|
|
||||||
const fieldMetadata = {
|
|
||||||
type: FieldMetadataType.CURRENCY,
|
|
||||||
targetColumnMap: {
|
|
||||||
amountMicros: 'moneyAmountMicros',
|
|
||||||
currencyCode: 'moneyCurrencyCode',
|
|
||||||
},
|
|
||||||
defaultValue: { amountMicros: 100 * 1_000_000, currencyCode: 'USD' },
|
|
||||||
} as any;
|
|
||||||
const columnActions = convertFieldMetadataToColumnActions(fieldMetadata);
|
|
||||||
expect(columnActions).toEqual([
|
|
||||||
{
|
|
||||||
action: 'CREATE',
|
|
||||||
columnName: 'moneyAmountMicros',
|
|
||||||
columnType: 'numeric',
|
|
||||||
defaultValue: 100 * 1_000_000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
action: 'CREATE',
|
|
||||||
columnName: 'moneyCurrencyCode',
|
|
||||||
columnType: 'varchar',
|
|
||||||
defaultValue: "'USD'",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,175 +0,0 @@
|
|||||||
import { FieldMetadataDefaultValue } from 'src/metadata/field-metadata/interfaces/field-metadata-default-value.interface';
|
|
||||||
|
|
||||||
import {
|
|
||||||
FieldMetadataEntity,
|
|
||||||
FieldMetadataType,
|
|
||||||
} from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
import {
|
|
||||||
WorkspaceMigrationColumnAction,
|
|
||||||
WorkspaceMigrationColumnActionType,
|
|
||||||
} from 'src/metadata/workspace-migration/workspace-migration.entity';
|
|
||||||
import { serializeDefaultValue } from 'src/metadata/field-metadata/utils/serialize-default-value';
|
|
||||||
|
|
||||||
export function convertFieldMetadataToColumnActions(
|
|
||||||
fieldMetadata: FieldMetadataEntity,
|
|
||||||
): WorkspaceMigrationColumnAction[] {
|
|
||||||
switch (fieldMetadata.type) {
|
|
||||||
case FieldMetadataType.UUID: {
|
|
||||||
const defaultValue =
|
|
||||||
fieldMetadata.defaultValue as FieldMetadataDefaultValue<FieldMetadataType.UUID>;
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
|
||||||
columnName: fieldMetadata.targetColumnMap.value,
|
|
||||||
columnType: 'uuid',
|
|
||||||
defaultValue: serializeDefaultValue(defaultValue?.value),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
case FieldMetadataType.TEXT: {
|
|
||||||
const defaultValue =
|
|
||||||
fieldMetadata.defaultValue as FieldMetadataDefaultValue<FieldMetadataType.TEXT>;
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
|
||||||
columnName: fieldMetadata.targetColumnMap.value,
|
|
||||||
columnType: 'text',
|
|
||||||
defaultValue: serializeDefaultValue(defaultValue?.value ?? ''),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
case FieldMetadataType.PHONE:
|
|
||||||
case FieldMetadataType.EMAIL: {
|
|
||||||
const defaultValue =
|
|
||||||
fieldMetadata.defaultValue as FieldMetadataDefaultValue<
|
|
||||||
FieldMetadataType.PHONE | FieldMetadataType.EMAIL
|
|
||||||
>;
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
|
||||||
columnName: fieldMetadata.targetColumnMap.value,
|
|
||||||
columnType: 'varchar',
|
|
||||||
defaultValue: serializeDefaultValue(defaultValue?.value ?? ''),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
case FieldMetadataType.NUMERIC: {
|
|
||||||
const defaultValue =
|
|
||||||
fieldMetadata.defaultValue as FieldMetadataDefaultValue<FieldMetadataType.NUMERIC>;
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
|
||||||
columnName: fieldMetadata.targetColumnMap.value,
|
|
||||||
columnType: 'numeric',
|
|
||||||
defaultValue: serializeDefaultValue(defaultValue?.value),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
case FieldMetadataType.NUMBER:
|
|
||||||
case FieldMetadataType.PROBABILITY: {
|
|
||||||
const defaultValue =
|
|
||||||
fieldMetadata.defaultValue as FieldMetadataDefaultValue<
|
|
||||||
FieldMetadataType.NUMBER | FieldMetadataType.PROBABILITY
|
|
||||||
>;
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
|
||||||
columnName: fieldMetadata.targetColumnMap.value,
|
|
||||||
columnType: 'float',
|
|
||||||
defaultValue: serializeDefaultValue(defaultValue?.value),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
case FieldMetadataType.BOOLEAN: {
|
|
||||||
const defaultValue =
|
|
||||||
fieldMetadata.defaultValue as FieldMetadataDefaultValue<FieldMetadataType.BOOLEAN>;
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
|
||||||
columnName: fieldMetadata.targetColumnMap.value,
|
|
||||||
columnType: 'boolean',
|
|
||||||
defaultValue: serializeDefaultValue(defaultValue?.value),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
case FieldMetadataType.DATE_TIME: {
|
|
||||||
const defaultValue =
|
|
||||||
fieldMetadata.defaultValue as FieldMetadataDefaultValue<FieldMetadataType.DATE_TIME>;
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
|
||||||
columnName: fieldMetadata.targetColumnMap.value,
|
|
||||||
columnType: 'timestamp',
|
|
||||||
defaultValue: serializeDefaultValue(defaultValue?.value),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
case FieldMetadataType.LINK: {
|
|
||||||
const defaultValue =
|
|
||||||
fieldMetadata.defaultValue as FieldMetadataDefaultValue<FieldMetadataType.LINK>;
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
|
||||||
columnName: fieldMetadata.targetColumnMap.label,
|
|
||||||
columnType: 'varchar',
|
|
||||||
defaultValue: serializeDefaultValue(defaultValue?.label ?? ''),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
|
||||||
columnName: fieldMetadata.targetColumnMap.url,
|
|
||||||
columnType: 'varchar',
|
|
||||||
defaultValue: serializeDefaultValue(defaultValue?.url ?? ''),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
case FieldMetadataType.CURRENCY: {
|
|
||||||
const defaultValue =
|
|
||||||
fieldMetadata.defaultValue as FieldMetadataDefaultValue<FieldMetadataType.CURRENCY>;
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
|
||||||
columnName: fieldMetadata.targetColumnMap.amountMicros,
|
|
||||||
columnType: 'numeric',
|
|
||||||
defaultValue: serializeDefaultValue(defaultValue?.amountMicros),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
|
||||||
columnName: fieldMetadata.targetColumnMap.currencyCode,
|
|
||||||
columnType: 'varchar',
|
|
||||||
defaultValue: serializeDefaultValue(defaultValue?.currencyCode ?? ''),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
case FieldMetadataType.FULL_NAME: {
|
|
||||||
const defaultValue =
|
|
||||||
fieldMetadata.defaultValue as FieldMetadataDefaultValue<FieldMetadataType.FULL_NAME>;
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
|
||||||
columnName: fieldMetadata.targetColumnMap.firstName,
|
|
||||||
columnType: 'varchar',
|
|
||||||
defaultValue: serializeDefaultValue(defaultValue?.firstName ?? ''),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
|
||||||
columnName: fieldMetadata.targetColumnMap.lastName,
|
|
||||||
columnType: 'varchar',
|
|
||||||
defaultValue: serializeDefaultValue(defaultValue?.lastName ?? ''),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw new Error(`Unknown type ${fieldMetadata.type}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -28,6 +28,9 @@ export function generateTargetColumnMap(
|
|||||||
case FieldMetadataType.PROBABILITY:
|
case FieldMetadataType.PROBABILITY:
|
||||||
case FieldMetadataType.BOOLEAN:
|
case FieldMetadataType.BOOLEAN:
|
||||||
case FieldMetadataType.DATE_TIME:
|
case FieldMetadataType.DATE_TIME:
|
||||||
|
case FieldMetadataType.RATING:
|
||||||
|
case FieldMetadataType.SELECT:
|
||||||
|
case FieldMetadataType.MULTI_SELECT:
|
||||||
return {
|
return {
|
||||||
value: columnName,
|
value: columnName,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
|
||||||
|
export const isCompositeFieldMetadataType = (
|
||||||
|
type: FieldMetadataType,
|
||||||
|
): type is
|
||||||
|
| FieldMetadataType.LINK
|
||||||
|
| FieldMetadataType.CURRENCY
|
||||||
|
| FieldMetadataType.FULL_NAME => {
|
||||||
|
return (
|
||||||
|
type === FieldMetadataType.LINK ||
|
||||||
|
type === FieldMetadataType.CURRENCY ||
|
||||||
|
type === FieldMetadataType.FULL_NAME
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
|
||||||
|
export const isEnumFieldMetadataType = (
|
||||||
|
type: FieldMetadataType,
|
||||||
|
): type is
|
||||||
|
| FieldMetadataType.RATING
|
||||||
|
| FieldMetadataType.SELECT
|
||||||
|
| FieldMetadataType.MULTI_SELECT => {
|
||||||
|
return (
|
||||||
|
type === FieldMetadataType.RATING ||
|
||||||
|
type === FieldMetadataType.SELECT ||
|
||||||
|
type === FieldMetadataType.MULTI_SELECT
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -10,7 +10,11 @@ export const serializeDefaultValue = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Dynamic default values
|
// Dynamic default values
|
||||||
if (typeof defaultValue === 'object' && 'type' in defaultValue) {
|
if (
|
||||||
|
!Array.isArray(defaultValue) &&
|
||||||
|
typeof defaultValue === 'object' &&
|
||||||
|
'type' in defaultValue
|
||||||
|
) {
|
||||||
switch (defaultValue.type) {
|
switch (defaultValue.type) {
|
||||||
case 'uuid':
|
case 'uuid':
|
||||||
return 'public.uuid_generate_v4()';
|
return 'public.uuid_generate_v4()';
|
||||||
@ -38,6 +42,10 @@ export const serializeDefaultValue = (
|
|||||||
return `'${defaultValue.toISOString()}'`;
|
return `'${defaultValue.toISOString()}'`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(defaultValue)) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof defaultValue === 'object') {
|
if (typeof defaultValue === 'object') {
|
||||||
return `'${JSON.stringify(defaultValue)}'`;
|
return `'${JSON.stringify(defaultValue)}'`;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,8 @@ export const validateDefaultValueBasedOnType = (
|
|||||||
case FieldMetadataType.TEXT:
|
case FieldMetadataType.TEXT:
|
||||||
case FieldMetadataType.PHONE:
|
case FieldMetadataType.PHONE:
|
||||||
case FieldMetadataType.EMAIL:
|
case FieldMetadataType.EMAIL:
|
||||||
case FieldMetadataType.ENUM:
|
case FieldMetadataType.RATING:
|
||||||
|
case FieldMetadataType.SELECT:
|
||||||
case FieldMetadataType.NUMERIC:
|
case FieldMetadataType.NUMERIC:
|
||||||
return (
|
return (
|
||||||
typeof defaultValue === 'object' &&
|
typeof defaultValue === 'object' &&
|
||||||
@ -82,6 +83,12 @@ export const validateDefaultValueBasedOnType = (
|
|||||||
typeof defaultValue.lastName === 'string'
|
typeof defaultValue.lastName === 'string'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
case FieldMetadataType.MULTI_SELECT:
|
||||||
|
return (
|
||||||
|
Array.isArray(defaultValue) &&
|
||||||
|
defaultValue.every((value) => typeof value === 'string')
|
||||||
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
ManyToOne,
|
ManyToOne,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
|
|
||||||
import { ObjectMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/object-metadata.interface';
|
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||||
|
|
||||||
import { FieldMetadataEntity } from 'src/metadata/field-metadata/field-metadata.entity';
|
import { FieldMetadataEntity } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
import { RelationMetadataEntity } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
import { RelationMetadataEntity } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
|
|
||||||
import { RelationMetadataInterface } from 'src/workspace/workspace-schema-builder/interfaces/relation-metadata.interface';
|
import { RelationMetadataInterface } from 'src/metadata/field-metadata/interfaces/relation-metadata.interface';
|
||||||
|
|
||||||
import { FieldMetadataEntity } from 'src/metadata/field-metadata/field-metadata.entity';
|
import { FieldMetadataEntity } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||||
|
|||||||
@ -0,0 +1,65 @@
|
|||||||
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { WorkspaceColumnActionOptions } from 'src/metadata/workspace-migration/interfaces/workspace-column-action-options.interface';
|
||||||
|
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||||
|
|
||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import {
|
||||||
|
WorkspaceMigrationColumnActionType,
|
||||||
|
WorkspaceMigrationColumnAlter,
|
||||||
|
WorkspaceMigrationColumnCreate,
|
||||||
|
} from 'src/metadata/workspace-migration/workspace-migration.entity';
|
||||||
|
import { serializeDefaultValue } from 'src/metadata/field-metadata/utils/serialize-default-value';
|
||||||
|
import { fieldMetadataTypeToColumnType } from 'src/metadata/workspace-migration/utils/field-metadata-type-to-column-type.util';
|
||||||
|
import { ColumnActionAbstractFactory } from 'src/metadata/workspace-migration/factories/column-action-abstract.factory';
|
||||||
|
|
||||||
|
export type BasicFieldMetadataType =
|
||||||
|
| FieldMetadataType.UUID
|
||||||
|
| FieldMetadataType.TEXT
|
||||||
|
| FieldMetadataType.PHONE
|
||||||
|
| FieldMetadataType.EMAIL
|
||||||
|
| FieldMetadataType.NUMERIC
|
||||||
|
| FieldMetadataType.NUMBER
|
||||||
|
| FieldMetadataType.PROBABILITY
|
||||||
|
| FieldMetadataType.BOOLEAN
|
||||||
|
| FieldMetadataType.DATE_TIME;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BasicColumnActionFactory extends ColumnActionAbstractFactory<BasicFieldMetadataType> {
|
||||||
|
protected readonly logger = new Logger(BasicColumnActionFactory.name);
|
||||||
|
|
||||||
|
protected handleCreateAction(
|
||||||
|
fieldMetadata: FieldMetadataInterface<BasicFieldMetadataType>,
|
||||||
|
options?: WorkspaceColumnActionOptions,
|
||||||
|
): WorkspaceMigrationColumnCreate {
|
||||||
|
const defaultValue =
|
||||||
|
fieldMetadata.defaultValue?.value ?? options?.defaultValue;
|
||||||
|
const serializedDefaultValue = serializeDefaultValue(defaultValue);
|
||||||
|
|
||||||
|
return {
|
||||||
|
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||||
|
columnName: fieldMetadata.targetColumnMap.value,
|
||||||
|
columnType: fieldMetadataTypeToColumnType(fieldMetadata.type),
|
||||||
|
isNullable: fieldMetadata.isNullable,
|
||||||
|
defaultValue: serializedDefaultValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected handleAlterAction(
|
||||||
|
previousFieldMetadata: FieldMetadataInterface<BasicFieldMetadataType>,
|
||||||
|
nextFieldMetadata: FieldMetadataInterface<BasicFieldMetadataType>,
|
||||||
|
options?: WorkspaceColumnActionOptions,
|
||||||
|
): WorkspaceMigrationColumnAlter {
|
||||||
|
const defaultValue =
|
||||||
|
nextFieldMetadata.defaultValue?.value ?? options?.defaultValue;
|
||||||
|
const serializedDefaultValue = serializeDefaultValue(defaultValue);
|
||||||
|
|
||||||
|
return {
|
||||||
|
action: WorkspaceMigrationColumnActionType.ALTER,
|
||||||
|
columnName: nextFieldMetadata.targetColumnMap.value,
|
||||||
|
columnType: fieldMetadataTypeToColumnType(nextFieldMetadata.type),
|
||||||
|
isNullable: nextFieldMetadata.isNullable,
|
||||||
|
defaultValue: serializedDefaultValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
import { Logger } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||||
|
import { WorkspaceColumnActionOptions } from 'src/metadata/workspace-migration/interfaces/workspace-column-action-options.interface';
|
||||||
|
import { WorkspaceColumnActionFactory } from 'src/metadata/workspace-migration/interfaces/workspace-column-action-factory.interface';
|
||||||
|
|
||||||
|
import {
|
||||||
|
WorkspaceMigrationColumnActionType,
|
||||||
|
WorkspaceMigrationColumnAction,
|
||||||
|
WorkspaceMigrationColumnCreate,
|
||||||
|
WorkspaceMigrationColumnAlter,
|
||||||
|
} from 'src/metadata/workspace-migration/workspace-migration.entity';
|
||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
|
||||||
|
export class ColumnActionAbstractFactory<
|
||||||
|
T extends FieldMetadataType | 'default',
|
||||||
|
> implements WorkspaceColumnActionFactory<T>
|
||||||
|
{
|
||||||
|
protected readonly logger = new Logger(ColumnActionAbstractFactory.name);
|
||||||
|
|
||||||
|
create(
|
||||||
|
action:
|
||||||
|
| WorkspaceMigrationColumnActionType.CREATE
|
||||||
|
| WorkspaceMigrationColumnActionType.ALTER,
|
||||||
|
previousFieldMetadata: FieldMetadataInterface<T> | undefined,
|
||||||
|
nextFieldMetadata: FieldMetadataInterface<T>,
|
||||||
|
options?: WorkspaceColumnActionOptions,
|
||||||
|
): WorkspaceMigrationColumnAction {
|
||||||
|
switch (action) {
|
||||||
|
case WorkspaceMigrationColumnActionType.CREATE:
|
||||||
|
return this.handleCreateAction(nextFieldMetadata, options);
|
||||||
|
case WorkspaceMigrationColumnActionType.ALTER: {
|
||||||
|
if (!previousFieldMetadata) {
|
||||||
|
throw new Error('Previous field metadata is required for alter');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.handleAlterAction(
|
||||||
|
previousFieldMetadata,
|
||||||
|
nextFieldMetadata,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
this.logger.error(`Invalid action: ${action}`);
|
||||||
|
|
||||||
|
throw new Error('[AbstractFactory]: invalid action');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected handleCreateAction(
|
||||||
|
_fieldMetadata: FieldMetadataInterface<T>,
|
||||||
|
_options?: WorkspaceColumnActionOptions,
|
||||||
|
): WorkspaceMigrationColumnCreate {
|
||||||
|
throw new Error('handleCreateAction method not implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected handleAlterAction(
|
||||||
|
_previousFieldMetadata: FieldMetadataInterface<T>,
|
||||||
|
_nextFieldMetadata: FieldMetadataInterface<T>,
|
||||||
|
_options?: WorkspaceColumnActionOptions,
|
||||||
|
): WorkspaceMigrationColumnAlter {
|
||||||
|
throw new Error('handleAlterAction method not implemented.');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { WorkspaceColumnActionOptions } from 'src/metadata/workspace-migration/interfaces/workspace-column-action-options.interface';
|
||||||
|
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||||
|
|
||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import {
|
||||||
|
WorkspaceMigrationColumnActionType,
|
||||||
|
WorkspaceMigrationColumnAlter,
|
||||||
|
WorkspaceMigrationColumnCreate,
|
||||||
|
} from 'src/metadata/workspace-migration/workspace-migration.entity';
|
||||||
|
import { serializeDefaultValue } from 'src/metadata/field-metadata/utils/serialize-default-value';
|
||||||
|
import { fieldMetadataTypeToColumnType } from 'src/metadata/workspace-migration/utils/field-metadata-type-to-column-type.util';
|
||||||
|
import { ColumnActionAbstractFactory } from 'src/metadata/workspace-migration/factories/column-action-abstract.factory';
|
||||||
|
|
||||||
|
export type EnumFieldMetadataType =
|
||||||
|
| FieldMetadataType.RATING
|
||||||
|
| FieldMetadataType.SELECT
|
||||||
|
| FieldMetadataType.MULTI_SELECT;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class EnumColumnActionFactory extends ColumnActionAbstractFactory<EnumFieldMetadataType> {
|
||||||
|
protected readonly logger = new Logger(EnumColumnActionFactory.name);
|
||||||
|
|
||||||
|
protected handleCreateAction(
|
||||||
|
fieldMetadata: FieldMetadataInterface<EnumFieldMetadataType>,
|
||||||
|
options: WorkspaceColumnActionOptions,
|
||||||
|
): WorkspaceMigrationColumnCreate {
|
||||||
|
const defaultValue =
|
||||||
|
fieldMetadata.defaultValue?.value ?? options?.defaultValue;
|
||||||
|
const serializedDefaultValue = serializeDefaultValue(defaultValue);
|
||||||
|
const enumOptions = fieldMetadata.options
|
||||||
|
? [...fieldMetadata.options.map((option) => option.value)]
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||||
|
columnName: fieldMetadata.targetColumnMap.value,
|
||||||
|
columnType: fieldMetadataTypeToColumnType(fieldMetadata.type),
|
||||||
|
enum: enumOptions,
|
||||||
|
isArray: fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||||
|
isNullable: fieldMetadata.isNullable,
|
||||||
|
defaultValue: serializedDefaultValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected handleAlterAction(
|
||||||
|
previousFieldMetadata: FieldMetadataInterface<EnumFieldMetadataType>,
|
||||||
|
nextFieldMetadata: FieldMetadataInterface<EnumFieldMetadataType>,
|
||||||
|
options: WorkspaceColumnActionOptions,
|
||||||
|
): WorkspaceMigrationColumnAlter {
|
||||||
|
const defaultValue =
|
||||||
|
nextFieldMetadata.defaultValue?.value ?? options?.defaultValue;
|
||||||
|
const serializedDefaultValue = serializeDefaultValue(defaultValue);
|
||||||
|
const enumOptions = nextFieldMetadata.options
|
||||||
|
? [
|
||||||
|
...nextFieldMetadata.options.map((option) => {
|
||||||
|
const previousOption = previousFieldMetadata.options?.find(
|
||||||
|
(previousOption) => previousOption.id === option.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
// The id is the same, but the value is different, so we need to alter the enum
|
||||||
|
if (previousOption && previousOption.value !== option.value) {
|
||||||
|
return {
|
||||||
|
from: previousOption.value,
|
||||||
|
to: option.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return option.value;
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
action: WorkspaceMigrationColumnActionType.ALTER,
|
||||||
|
columnName: nextFieldMetadata.targetColumnMap.value,
|
||||||
|
columnType: fieldMetadataTypeToColumnType(nextFieldMetadata.type),
|
||||||
|
enum: enumOptions,
|
||||||
|
isArray: nextFieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||||
|
isNullable: nextFieldMetadata.isNullable,
|
||||||
|
defaultValue: serializedDefaultValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { BasicColumnActionFactory } from 'src/metadata/workspace-migration/factories/basic-column-action.factory';
|
||||||
|
import { EnumColumnActionFactory } from 'src/metadata/workspace-migration/factories/enum-column-action.factory';
|
||||||
|
|
||||||
|
export const workspaceColumnActionFactories = [
|
||||||
|
BasicColumnActionFactory,
|
||||||
|
EnumColumnActionFactory,
|
||||||
|
];
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import { WorkspaceColumnActionOptions } from 'src/metadata/workspace-migration/interfaces/workspace-column-action-options.interface';
|
||||||
|
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||||
|
|
||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import {
|
||||||
|
WorkspaceMigrationColumnActionType,
|
||||||
|
WorkspaceMigrationColumnAction,
|
||||||
|
} from 'src/metadata/workspace-migration/workspace-migration.entity';
|
||||||
|
|
||||||
|
export interface WorkspaceColumnActionFactory<
|
||||||
|
T extends FieldMetadataType | 'default',
|
||||||
|
> {
|
||||||
|
create(
|
||||||
|
action:
|
||||||
|
| WorkspaceMigrationColumnActionType.CREATE
|
||||||
|
| WorkspaceMigrationColumnActionType.ALTER,
|
||||||
|
previousFieldMetadata: FieldMetadataInterface<T> | undefined,
|
||||||
|
nextFieldMetadata: FieldMetadataInterface<T>,
|
||||||
|
options?: WorkspaceColumnActionOptions,
|
||||||
|
): WorkspaceMigrationColumnAction;
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
export interface WorkspaceColumnActionOptions {
|
||||||
|
defaultValue?: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
|
||||||
|
export const fieldMetadataTypeToColumnType = <Type extends FieldMetadataType>(
|
||||||
|
fieldMetadataType: Type,
|
||||||
|
): string => {
|
||||||
|
/**
|
||||||
|
* Composite types are not implemented here, as they are flattened by their composite definitions.
|
||||||
|
* See src/metadata/field-metadata/composite-types for more information.
|
||||||
|
*/
|
||||||
|
switch (fieldMetadataType) {
|
||||||
|
case FieldMetadataType.UUID:
|
||||||
|
return 'uuid';
|
||||||
|
case FieldMetadataType.TEXT:
|
||||||
|
return 'text';
|
||||||
|
case FieldMetadataType.PHONE:
|
||||||
|
case FieldMetadataType.EMAIL:
|
||||||
|
return 'varchar';
|
||||||
|
case FieldMetadataType.NUMERIC:
|
||||||
|
return 'numeric';
|
||||||
|
case FieldMetadataType.NUMBER:
|
||||||
|
case FieldMetadataType.PROBABILITY:
|
||||||
|
return 'float';
|
||||||
|
case FieldMetadataType.BOOLEAN:
|
||||||
|
return 'boolean';
|
||||||
|
case FieldMetadataType.DATE_TIME:
|
||||||
|
return 'timestamp';
|
||||||
|
case FieldMetadataType.RATING:
|
||||||
|
case FieldMetadataType.SELECT:
|
||||||
|
case FieldMetadataType.MULTI_SELECT:
|
||||||
|
return 'enum';
|
||||||
|
default:
|
||||||
|
throw new Error(`Cannot convert ${fieldMetadataType} to column type.`);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -7,13 +7,29 @@ import {
|
|||||||
|
|
||||||
export enum WorkspaceMigrationColumnActionType {
|
export enum WorkspaceMigrationColumnActionType {
|
||||||
CREATE = 'CREATE',
|
CREATE = 'CREATE',
|
||||||
|
ALTER = 'ALTER',
|
||||||
RELATION = 'RELATION',
|
RELATION = 'RELATION',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type WorkspaceMigrationEnum = string | { from: string; to: string };
|
||||||
|
|
||||||
export type WorkspaceMigrationColumnCreate = {
|
export type WorkspaceMigrationColumnCreate = {
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE;
|
action: WorkspaceMigrationColumnActionType.CREATE;
|
||||||
columnName: string;
|
columnName: string;
|
||||||
columnType: string;
|
columnType: string;
|
||||||
|
enum?: WorkspaceMigrationEnum[];
|
||||||
|
isArray?: boolean;
|
||||||
|
isNullable?: boolean;
|
||||||
|
defaultValue?: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WorkspaceMigrationColumnAlter = {
|
||||||
|
action: WorkspaceMigrationColumnActionType.ALTER;
|
||||||
|
columnName: string;
|
||||||
|
columnType: string;
|
||||||
|
enum?: WorkspaceMigrationEnum[];
|
||||||
|
isArray?: boolean;
|
||||||
|
isNullable?: boolean;
|
||||||
defaultValue?: any;
|
defaultValue?: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -27,7 +43,11 @@ export type WorkspaceMigrationColumnRelation = {
|
|||||||
|
|
||||||
export type WorkspaceMigrationColumnAction = {
|
export type WorkspaceMigrationColumnAction = {
|
||||||
action: WorkspaceMigrationColumnActionType;
|
action: WorkspaceMigrationColumnActionType;
|
||||||
} & (WorkspaceMigrationColumnCreate | WorkspaceMigrationColumnRelation);
|
} & (
|
||||||
|
| WorkspaceMigrationColumnCreate
|
||||||
|
| WorkspaceMigrationColumnAlter
|
||||||
|
| WorkspaceMigrationColumnRelation
|
||||||
|
);
|
||||||
|
|
||||||
export type WorkspaceMigrationTableAction = {
|
export type WorkspaceMigrationTableAction = {
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@ -0,0 +1,186 @@
|
|||||||
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { WorkspaceColumnActionFactory } from 'src/metadata/workspace-migration/interfaces/workspace-column-action-factory.interface';
|
||||||
|
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||||
|
import { WorkspaceColumnActionOptions } from 'src/metadata/workspace-migration/interfaces/workspace-column-action-options.interface';
|
||||||
|
|
||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import { BasicColumnActionFactory } from 'src/metadata/workspace-migration/factories/basic-column-action.factory';
|
||||||
|
import { EnumColumnActionFactory } from 'src/metadata/workspace-migration/factories/enum-column-action.factory';
|
||||||
|
import {
|
||||||
|
WorkspaceMigrationColumnAction,
|
||||||
|
WorkspaceMigrationColumnActionType,
|
||||||
|
} from 'src/metadata/workspace-migration/workspace-migration.entity';
|
||||||
|
import { isCompositeFieldMetadataType } from 'src/metadata/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||||
|
import { linkObjectDefinition } from 'src/metadata/field-metadata/composite-types/link.composite-type';
|
||||||
|
import { currencyObjectDefinition } from 'src/metadata/field-metadata/composite-types/currency.composite-type';
|
||||||
|
import { fullNameObjectDefinition } from 'src/metadata/field-metadata/composite-types/full-name.composite-type';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WorkspaceMigrationFactory {
|
||||||
|
private readonly logger = new Logger(WorkspaceMigrationFactory.name);
|
||||||
|
private factoriesMap: Map<
|
||||||
|
FieldMetadataType,
|
||||||
|
{
|
||||||
|
factory: WorkspaceColumnActionFactory<any>;
|
||||||
|
options?: WorkspaceColumnActionOptions;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
private compositeDefinitions = new Map<string, FieldMetadataInterface[]>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly basicColumnActionFactory: BasicColumnActionFactory,
|
||||||
|
private readonly enumColumnActionFactory: EnumColumnActionFactory,
|
||||||
|
) {
|
||||||
|
this.factoriesMap = new Map<
|
||||||
|
FieldMetadataType,
|
||||||
|
{
|
||||||
|
factory: WorkspaceColumnActionFactory<any>;
|
||||||
|
options?: WorkspaceColumnActionOptions;
|
||||||
|
}
|
||||||
|
>([
|
||||||
|
[FieldMetadataType.UUID, { factory: this.basicColumnActionFactory }],
|
||||||
|
[
|
||||||
|
FieldMetadataType.TEXT,
|
||||||
|
{
|
||||||
|
factory: this.basicColumnActionFactory,
|
||||||
|
options: {
|
||||||
|
defaultValue: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
FieldMetadataType.PHONE,
|
||||||
|
{
|
||||||
|
factory: this.basicColumnActionFactory,
|
||||||
|
options: {
|
||||||
|
defaultValue: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
FieldMetadataType.EMAIL,
|
||||||
|
{
|
||||||
|
factory: this.basicColumnActionFactory,
|
||||||
|
options: {
|
||||||
|
defaultValue: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[FieldMetadataType.NUMERIC, { factory: this.basicColumnActionFactory }],
|
||||||
|
[FieldMetadataType.NUMBER, { factory: this.basicColumnActionFactory }],
|
||||||
|
[
|
||||||
|
FieldMetadataType.PROBABILITY,
|
||||||
|
{ factory: this.basicColumnActionFactory },
|
||||||
|
],
|
||||||
|
[FieldMetadataType.BOOLEAN, { factory: this.basicColumnActionFactory }],
|
||||||
|
[FieldMetadataType.DATE_TIME, { factory: this.basicColumnActionFactory }],
|
||||||
|
[FieldMetadataType.RATING, { factory: this.enumColumnActionFactory }],
|
||||||
|
[FieldMetadataType.SELECT, { factory: this.enumColumnActionFactory }],
|
||||||
|
[
|
||||||
|
FieldMetadataType.MULTI_SELECT,
|
||||||
|
{ factory: this.enumColumnActionFactory },
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
this.compositeDefinitions = new Map<string, FieldMetadataInterface[]>([
|
||||||
|
[FieldMetadataType.LINK, linkObjectDefinition.fields],
|
||||||
|
[FieldMetadataType.CURRENCY, currencyObjectDefinition.fields],
|
||||||
|
[FieldMetadataType.FULL_NAME, fullNameObjectDefinition.fields],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
createColumnActions(
|
||||||
|
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||||
|
fieldMetadata: FieldMetadataInterface,
|
||||||
|
): WorkspaceMigrationColumnAction[];
|
||||||
|
createColumnActions(
|
||||||
|
action: WorkspaceMigrationColumnActionType.ALTER,
|
||||||
|
previousFieldMetadata: FieldMetadataInterface,
|
||||||
|
nextFieldMetadata: FieldMetadataInterface,
|
||||||
|
): WorkspaceMigrationColumnAction[];
|
||||||
|
createColumnActions(
|
||||||
|
action:
|
||||||
|
| WorkspaceMigrationColumnActionType.CREATE
|
||||||
|
| WorkspaceMigrationColumnActionType.ALTER,
|
||||||
|
fieldMetadataOrPreviousFieldMetadata: FieldMetadataInterface,
|
||||||
|
undefinedOrnextFieldMetadata?: FieldMetadataInterface,
|
||||||
|
): WorkspaceMigrationColumnAction[] {
|
||||||
|
const previousFieldMetadata =
|
||||||
|
action === WorkspaceMigrationColumnActionType.ALTER
|
||||||
|
? fieldMetadataOrPreviousFieldMetadata
|
||||||
|
: undefined;
|
||||||
|
const nextFieldMetadata =
|
||||||
|
action === WorkspaceMigrationColumnActionType.CREATE
|
||||||
|
? fieldMetadataOrPreviousFieldMetadata
|
||||||
|
: undefinedOrnextFieldMetadata;
|
||||||
|
|
||||||
|
if (!nextFieldMetadata) {
|
||||||
|
this.logger.error(
|
||||||
|
`No field metadata provided for action ${action}`,
|
||||||
|
fieldMetadataOrPreviousFieldMetadata,
|
||||||
|
);
|
||||||
|
|
||||||
|
throw new Error(`No field metadata provided for action ${action}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's a composite field type, we need to create a column action for each of the fields
|
||||||
|
if (isCompositeFieldMetadataType(nextFieldMetadata.type)) {
|
||||||
|
const fieldMetadataCollection = this.compositeDefinitions.get(
|
||||||
|
nextFieldMetadata.type,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!fieldMetadataCollection) {
|
||||||
|
this.logger.error(
|
||||||
|
`No composite definition found for type ${nextFieldMetadata.type}`,
|
||||||
|
{
|
||||||
|
nextFieldMetadata,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`No composite definition found for type ${nextFieldMetadata.type}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fieldMetadataCollection.map((fieldMetadata) =>
|
||||||
|
this.createColumnAction(action, fieldMetadata, fieldMetadata),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we create a single column action
|
||||||
|
const columnAction = this.createColumnAction(
|
||||||
|
action,
|
||||||
|
previousFieldMetadata,
|
||||||
|
nextFieldMetadata,
|
||||||
|
);
|
||||||
|
|
||||||
|
return [columnAction];
|
||||||
|
}
|
||||||
|
|
||||||
|
private createColumnAction(
|
||||||
|
action:
|
||||||
|
| WorkspaceMigrationColumnActionType.CREATE
|
||||||
|
| WorkspaceMigrationColumnActionType.ALTER,
|
||||||
|
previousFieldMetadata: FieldMetadataInterface | undefined,
|
||||||
|
nextFieldMetadata: FieldMetadataInterface,
|
||||||
|
): WorkspaceMigrationColumnAction {
|
||||||
|
const { factory, options } =
|
||||||
|
this.factoriesMap.get(nextFieldMetadata.type) ?? {};
|
||||||
|
|
||||||
|
if (!factory) {
|
||||||
|
this.logger.error(`No factory found for type ${nextFieldMetadata.type}`, {
|
||||||
|
nextFieldMetadata,
|
||||||
|
});
|
||||||
|
|
||||||
|
throw new Error(`No factory found for type ${nextFieldMetadata.type}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return factory.create(
|
||||||
|
action,
|
||||||
|
previousFieldMetadata,
|
||||||
|
nextFieldMetadata,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,19 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { workspaceColumnActionFactories } from 'src/metadata/workspace-migration/factories/factories';
|
||||||
|
import { WorkspaceMigrationFactory } from 'src/metadata/workspace-migration/workspace-migration.factory';
|
||||||
|
|
||||||
import { WorkspaceMigrationService } from './workspace-migration.service';
|
import { WorkspaceMigrationService } from './workspace-migration.service';
|
||||||
import { WorkspaceMigrationEntity } from './workspace-migration.entity';
|
import { WorkspaceMigrationEntity } from './workspace-migration.entity';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([WorkspaceMigrationEntity], 'metadata')],
|
imports: [TypeOrmModule.forFeature([WorkspaceMigrationEntity], 'metadata')],
|
||||||
exports: [WorkspaceMigrationService],
|
providers: [
|
||||||
providers: [WorkspaceMigrationService],
|
...workspaceColumnActionFactories,
|
||||||
|
WorkspaceMigrationFactory,
|
||||||
|
WorkspaceMigrationService,
|
||||||
|
],
|
||||||
|
exports: [WorkspaceMigrationFactory, WorkspaceMigrationService],
|
||||||
})
|
})
|
||||||
export class WorkspaceMigrationModule {}
|
export class WorkspaceMigrationModule {}
|
||||||
|
|||||||
@ -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 { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
import {
|
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 {
|
export enum RelationDirection {
|
||||||
FROM = 'from',
|
FROM = 'from',
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { WorkspaceResolverBuilderMethodNames } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.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 { camelCase } from 'src/utils/camel-case';
|
import { camelCase } from 'src/utils/camel-case';
|
||||||
import { pascalCase } from 'src/utils/pascal-case';
|
import { pascalCase } from 'src/utils/pascal-case';
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
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;
|
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 { WorkspaceMigrationModule } from 'src/metadata/workspace-migration/workspace-migration.module';
|
||||||
import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/workspace-datasource.module';
|
import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/workspace-datasource.module';
|
||||||
import { WorkspaceCacheVersionModule } from 'src/metadata/workspace-cache-version/workspace-cache-version.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';
|
import { WorkspaceMigrationRunnerService } from './workspace-migration-runner.service';
|
||||||
|
|
||||||
@ -12,7 +13,7 @@ import { WorkspaceMigrationRunnerService } from './workspace-migration-runner.se
|
|||||||
WorkspaceMigrationModule,
|
WorkspaceMigrationModule,
|
||||||
WorkspaceCacheVersionModule,
|
WorkspaceCacheVersionModule,
|
||||||
],
|
],
|
||||||
|
providers: [WorkspaceMigrationRunnerService, WorkspaceMigrationEnumService],
|
||||||
exports: [WorkspaceMigrationRunnerService],
|
exports: [WorkspaceMigrationRunnerService],
|
||||||
providers: [WorkspaceMigrationRunnerService],
|
|
||||||
})
|
})
|
||||||
export class WorkspaceMigrationRunnerModule {}
|
export class WorkspaceMigrationRunnerModule {}
|
||||||
|
|||||||
@ -16,8 +16,10 @@ import {
|
|||||||
WorkspaceMigrationColumnActionType,
|
WorkspaceMigrationColumnActionType,
|
||||||
WorkspaceMigrationColumnCreate,
|
WorkspaceMigrationColumnCreate,
|
||||||
WorkspaceMigrationColumnRelation,
|
WorkspaceMigrationColumnRelation,
|
||||||
|
WorkspaceMigrationColumnAlter,
|
||||||
} from 'src/metadata/workspace-migration/workspace-migration.entity';
|
} from 'src/metadata/workspace-migration/workspace-migration.entity';
|
||||||
import { WorkspaceCacheVersionService } from 'src/metadata/workspace-cache-version/workspace-cache-version.service';
|
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';
|
import { customTableDefaultColumns } from './utils/custom-table-default-column.util';
|
||||||
|
|
||||||
@ -27,6 +29,7 @@ export class WorkspaceMigrationRunnerService {
|
|||||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||||
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
||||||
private readonly workspaceCacheVersionService: WorkspaceCacheVersionService,
|
private readonly workspaceCacheVersionService: WorkspaceCacheVersionService,
|
||||||
|
private readonly workspaceMigrationEnumService: WorkspaceMigrationEnumService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -168,6 +171,14 @@ export class WorkspaceMigrationRunnerService {
|
|||||||
columnMigration,
|
columnMigration,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case WorkspaceMigrationColumnActionType.ALTER:
|
||||||
|
await this.alterColumn(
|
||||||
|
queryRunner,
|
||||||
|
schemaName,
|
||||||
|
tableName,
|
||||||
|
columnMigration,
|
||||||
|
);
|
||||||
|
break;
|
||||||
case WorkspaceMigrationColumnActionType.RELATION:
|
case WorkspaceMigrationColumnActionType.RELATION:
|
||||||
await this.createForeignKey(
|
await this.createForeignKey(
|
||||||
queryRunner,
|
queryRunner,
|
||||||
@ -200,6 +211,7 @@ export class WorkspaceMigrationRunnerService {
|
|||||||
`${schemaName}.${tableName}`,
|
`${schemaName}.${tableName}`,
|
||||||
migrationColumn.columnName,
|
migrationColumn.columnName,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (hasColumn) {
|
if (hasColumn) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -210,11 +222,46 @@ export class WorkspaceMigrationRunnerService {
|
|||||||
name: migrationColumn.columnName,
|
name: migrationColumn.columnName,
|
||||||
type: migrationColumn.columnType,
|
type: migrationColumn.columnType,
|
||||||
default: migrationColumn.defaultValue,
|
default: migrationColumn.defaultValue,
|
||||||
|
enum: migrationColumn.enum?.filter(
|
||||||
|
(value): value is string => typeof value === 'string',
|
||||||
|
),
|
||||||
|
isArray: migrationColumn.isArray,
|
||||||
isNullable: true,
|
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(
|
private async createForeignKey(
|
||||||
queryRunner: QueryRunner,
|
queryRunner: QueryRunner,
|
||||||
schemaName: string,
|
schemaName: string,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
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()
|
@Injectable()
|
||||||
export class ArgsAliasFactory {
|
export class ArgsAliasFactory {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
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';
|
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 { ArgsAliasFactory } from './args-alias.factory';
|
||||||
import { ArgsStringFactory } from './args-string.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 { CreateManyQueryFactory } from './create-many-query.factory';
|
||||||
import { DeleteOneQueryFactory } from './delete-one-query.factory';
|
import { DeleteOneQueryFactory } from './delete-one-query.factory';
|
||||||
import { FieldAliasFacotry } from './field-alias.factory';
|
import { FieldAliasFacotry } from './field-alias.factory';
|
||||||
@ -14,7 +14,7 @@ import { DeleteManyQueryFactory } from './delete-many-query.factory';
|
|||||||
export const workspaceQueryBuilderFactories = [
|
export const workspaceQueryBuilderFactories = [
|
||||||
ArgsAliasFactory,
|
ArgsAliasFactory,
|
||||||
ArgsStringFactory,
|
ArgsStringFactory,
|
||||||
CompositeFieldAliasFactory,
|
RelationFieldAliasFactory,
|
||||||
CreateManyQueryFactory,
|
CreateManyQueryFactory,
|
||||||
DeleteOneQueryFactory,
|
DeleteOneQueryFactory,
|
||||||
FieldAliasFacotry,
|
FieldAliasFacotry,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
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()
|
@Injectable()
|
||||||
export class FieldAliasFacotry {
|
export class FieldAliasFacotry {
|
||||||
|
|||||||
@ -4,12 +4,12 @@ import { GraphQLResolveInfo } from 'graphql';
|
|||||||
import graphqlFields from 'graphql-fields';
|
import graphqlFields from 'graphql-fields';
|
||||||
import isEmpty from 'lodash.isempty';
|
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 { FieldAliasFacotry } from './field-alias.factory';
|
||||||
import { CompositeFieldAliasFactory } from './composite-field-alias.factory';
|
import { RelationFieldAliasFactory } from './relation-field-alias.factory';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FieldsStringFactory {
|
export class FieldsStringFactory {
|
||||||
@ -17,7 +17,7 @@ export class FieldsStringFactory {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly fieldAliasFactory: FieldAliasFacotry,
|
private readonly fieldAliasFactory: FieldAliasFacotry,
|
||||||
private readonly compositeFieldAliasFactory: CompositeFieldAliasFactory,
|
private readonly relationFieldAliasFactory: RelationFieldAliasFactory,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
create(
|
create(
|
||||||
@ -52,9 +52,9 @@ export class FieldsStringFactory {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
const fieldMetadata = fieldMetadataMap.get(fieldKey)!;
|
const fieldMetadata = fieldMetadataMap.get(fieldKey)!;
|
||||||
|
|
||||||
// If the field is a composite field, we need to create a special alias
|
// If the field is a relation field, we need to create a special alias
|
||||||
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
if (isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||||
const alias = this.compositeFieldAliasFactory.create(
|
const alias = this.relationFieldAliasFactory.create(
|
||||||
fieldKey,
|
fieldKey,
|
||||||
fieldValue,
|
fieldValue,
|
||||||
fieldMetadata,
|
fieldMetadata,
|
||||||
|
|||||||
@ -2,10 +2,9 @@ import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common';
|
|||||||
|
|
||||||
import { GraphQLResolveInfo } from 'graphql';
|
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 { isRelationFieldMetadataType } from 'src/workspace/utils/is-relation-field-metadata-type.util';
|
||||||
import { isCompositeFieldMetadataType } from 'src/workspace/utils/is-composite-field-metadata-type.util';
|
|
||||||
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
import {
|
import {
|
||||||
deduceRelationDirection,
|
deduceRelationDirection,
|
||||||
@ -17,8 +16,8 @@ import { FieldsStringFactory } from './fields-string.factory';
|
|||||||
import { ArgsStringFactory } from './args-string.factory';
|
import { ArgsStringFactory } from './args-string.factory';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CompositeFieldAliasFactory {
|
export class RelationFieldAliasFactory {
|
||||||
private logger = new Logger(CompositeFieldAliasFactory.name);
|
private logger = new Logger(RelationFieldAliasFactory.name);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => FieldsStringFactory))
|
@Inject(forwardRef(() => FieldsStringFactory))
|
||||||
@ -32,21 +31,11 @@ export class CompositeFieldAliasFactory {
|
|||||||
fieldMetadata: FieldMetadataInterface,
|
fieldMetadata: FieldMetadataInterface,
|
||||||
info: GraphQLResolveInfo,
|
info: GraphQLResolveInfo,
|
||||||
) {
|
) {
|
||||||
if (!isCompositeFieldMetadataType(fieldMetadata.type)) {
|
if (!isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||||
throw new Error(`Field ${fieldMetadata.name} is not a composite field`);
|
throw new Error(`Field ${fieldMetadata.name} is not a relation field`);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (fieldMetadata.type) {
|
return this.createRelationAlias(fieldKey, fieldValue, fieldMetadata, info);
|
||||||
case FieldMetadataType.RELATION:
|
|
||||||
return this.createRelationAlias(
|
|
||||||
fieldKey,
|
|
||||||
fieldValue,
|
|
||||||
fieldMetadata,
|
|
||||||
info,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private createRelationAlias(
|
private createRelationAlias(
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { GraphQLResolveInfo } from 'graphql';
|
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 {
|
export interface WorkspaceQueryBuilderOptions {
|
||||||
targetTableName: string;
|
targetTableName: string;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { GraphQLResolveInfo } from 'graphql';
|
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 {
|
export interface WorkspaceQueryRunnerOptions {
|
||||||
targetTableName: string;
|
targetTableName: string;
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
|||||||
|
|
||||||
import { IResolvers } from '@graphql-tools/utils';
|
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 { getResolverName } from 'src/workspace/utils/get-resolver-name.util';
|
||||||
import { UpdateManyResolverFactory } from 'src/workspace/workspace-resolver-builder/factories/update-many-resolver.factory';
|
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 { GraphQLFieldConfigMap, GraphQLObjectType } from 'graphql';
|
||||||
|
|
||||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
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 { pascalCase } from 'src/utils/pascal-case';
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
|||||||
import { GraphQLOutputType } from 'graphql';
|
import { GraphQLOutputType } from 'graphql';
|
||||||
|
|
||||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
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 {
|
import {
|
||||||
TypeMapperService,
|
TypeMapperService,
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
|||||||
import { GraphQLFieldConfigMap, GraphQLObjectType } from 'graphql';
|
import { GraphQLFieldConfigMap, GraphQLObjectType } from 'graphql';
|
||||||
|
|
||||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
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 { pascalCase } from 'src/utils/pascal-case';
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
|||||||
import { GraphQLOutputType } from 'graphql';
|
import { GraphQLOutputType } from 'graphql';
|
||||||
|
|
||||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
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 {
|
import {
|
||||||
TypeMapperService,
|
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';
|
} from 'graphql';
|
||||||
|
|
||||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
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 { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
import { TypeDefinitionsStorage } from 'src/workspace/workspace-schema-builder/storages/type-definitions.storage';
|
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 { 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 {
|
import {
|
||||||
RelationDirection,
|
RelationDirection,
|
||||||
deduceRelationDirection,
|
deduceRelationDirection,
|
||||||
@ -54,7 +54,7 @@ export class ExtendObjectTypeDefinitionFactory {
|
|||||||
objectMetadata.id,
|
objectMetadata.id,
|
||||||
kind,
|
kind,
|
||||||
);
|
);
|
||||||
const containsCompositeField = objectContainsCompositeField(objectMetadata);
|
const containsRelationField = objectContainsRelationField(objectMetadata);
|
||||||
|
|
||||||
if (!gqlType) {
|
if (!gqlType) {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
@ -71,7 +71,7 @@ export class ExtendObjectTypeDefinitionFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Security check to avoid extending an object that does not need to be extended
|
// Security check to avoid extending an object that does not need to be extended
|
||||||
if (!containsCompositeField) {
|
if (!containsRelationField) {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
`This object does not need to be extended: ${objectMetadata.id.toString()}`,
|
`This object does not need to be extended: ${objectMetadata.id.toString()}`,
|
||||||
{
|
{
|
||||||
@ -109,8 +109,8 @@ export class ExtendObjectTypeDefinitionFactory {
|
|||||||
const fields: GraphQLFieldConfigMap<any, any> = {};
|
const fields: GraphQLFieldConfigMap<any, any> = {};
|
||||||
|
|
||||||
for (const fieldMetadata of objectMetadata.fields) {
|
for (const fieldMetadata of objectMetadata.fields) {
|
||||||
// Ignore non composite fields as they are already defined
|
// Ignore relation fields as they are already defined
|
||||||
if (!isCompositeFieldMetadataType(fieldMetadata.type)) {
|
if (!isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { EnumTypeDefinitionFactory } from 'src/workspace/workspace-schema-builder/factories/enum-type-definition.factory';
|
||||||
|
|
||||||
import { ArgsFactory } from './args.factory';
|
import { ArgsFactory } from './args.factory';
|
||||||
import { InputTypeFactory } from './input-type.factory';
|
import { InputTypeFactory } from './input-type.factory';
|
||||||
import { InputTypeDefinitionFactory } from './input-type-definition.factory';
|
import { InputTypeDefinitionFactory } from './input-type-definition.factory';
|
||||||
@ -24,6 +26,7 @@ export const workspaceSchemaBuilderFactories = [
|
|||||||
InputTypeDefinitionFactory,
|
InputTypeDefinitionFactory,
|
||||||
OutputTypeFactory,
|
OutputTypeFactory,
|
||||||
ObjectTypeDefinitionFactory,
|
ObjectTypeDefinitionFactory,
|
||||||
|
EnumTypeDefinitionFactory,
|
||||||
RelationTypeFactory,
|
RelationTypeFactory,
|
||||||
ExtendObjectTypeDefinitionFactory,
|
ExtendObjectTypeDefinitionFactory,
|
||||||
FilterTypeFactory,
|
FilterTypeFactory,
|
||||||
|
|||||||
@ -3,11 +3,11 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { GraphQLInputFieldConfigMap, GraphQLInputObjectType } from 'graphql';
|
import { GraphQLInputFieldConfigMap, GraphQLInputObjectType } from 'graphql';
|
||||||
|
|
||||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
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 { pascalCase } from 'src/utils/pascal-case';
|
||||||
import { TypeMapperService } from 'src/workspace/workspace-schema-builder/services/type-mapper.service';
|
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 { FilterTypeFactory } from './filter-type.factory';
|
||||||
import {
|
import {
|
||||||
@ -68,8 +68,8 @@ export class FilterTypeDefinitionFactory {
|
|||||||
const fields: GraphQLInputFieldConfigMap = {};
|
const fields: GraphQLInputFieldConfigMap = {};
|
||||||
|
|
||||||
for (const fieldMetadata of objectMetadata.fields) {
|
for (const fieldMetadata of objectMetadata.fields) {
|
||||||
// Composite field types are generated during extension of object type definition
|
// Relation types are generated during extension of object type definition
|
||||||
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
if (isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||||
//continue;
|
//continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,23 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
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 { 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 {
|
import {
|
||||||
TypeMapperService,
|
TypeMapperService,
|
||||||
TypeOptions,
|
TypeOptions,
|
||||||
} from 'src/workspace/workspace-schema-builder/services/type-mapper.service';
|
} from 'src/workspace/workspace-schema-builder/services/type-mapper.service';
|
||||||
import { TypeDefinitionsStorage } from 'src/workspace/workspace-schema-builder/storages/type-definitions.storage';
|
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';
|
import { InputTypeDefinitionKind } from './input-type-definition.factory';
|
||||||
|
|
||||||
@ -27,34 +35,68 @@ export class FilterTypeFactory {
|
|||||||
buildOtions: WorkspaceBuildSchemaOptions,
|
buildOtions: WorkspaceBuildSchemaOptions,
|
||||||
typeOptions: TypeOptions,
|
typeOptions: TypeOptions,
|
||||||
): GraphQLInputType {
|
): GraphQLInputType {
|
||||||
let filterType = this.typeMapperService.mapToFilterType(
|
const target = isCompositeFieldMetadataType(fieldMetadata.type)
|
||||||
fieldMetadata.type,
|
? fieldMetadata.type.toString()
|
||||||
buildOtions.dateScalarMode,
|
: fieldMetadata.id;
|
||||||
buildOtions.numberScalarMode,
|
let filterType: GraphQLInputObjectType | GraphQLScalarType | undefined =
|
||||||
);
|
undefined;
|
||||||
|
|
||||||
if (!filterType) {
|
if (isEnumFieldMetadataType(fieldMetadata.type)) {
|
||||||
filterType = this.typeDefinitionsStorage.getInputTypeByKey(
|
filterType = this.createEnumFilterType(fieldMetadata);
|
||||||
fieldMetadata.type.toString(),
|
} else {
|
||||||
InputTypeDefinitionKind.Filter,
|
filterType = this.typeMapperService.mapToFilterType(
|
||||||
|
fieldMetadata.type,
|
||||||
|
buildOtions.dateScalarMode,
|
||||||
|
buildOtions.numberScalarMode,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!filterType) {
|
filterType ??= this.typeDefinitionsStorage.getInputTypeByKey(
|
||||||
this.logger.error(
|
target,
|
||||||
`Could not find a GraphQL type for ${fieldMetadata.type.toString()}`,
|
InputTypeDefinitionKind.Filter,
|
||||||
{
|
);
|
||||||
fieldMetadata,
|
}
|
||||||
buildOtions,
|
|
||||||
typeOptions,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
throw new Error(
|
if (!filterType) {
|
||||||
`Could not find a GraphQL type for ${fieldMetadata.type.toString()}`,
|
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);
|
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 { GraphQLInputFieldConfigMap, GraphQLInputObjectType } from 'graphql';
|
||||||
|
|
||||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
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 { 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';
|
import { InputTypeFactory } from './input-type.factory';
|
||||||
|
|
||||||
@ -53,14 +54,15 @@ export class InputTypeDefinitionFactory {
|
|||||||
const fields: GraphQLInputFieldConfigMap = {};
|
const fields: GraphQLInputFieldConfigMap = {};
|
||||||
|
|
||||||
for (const fieldMetadata of objectMetadata.fields) {
|
for (const fieldMetadata of objectMetadata.fields) {
|
||||||
// Composite field types are generated during extension of object type definition
|
// Relation field types are generated during extension of object type definition
|
||||||
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
if (isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||||
//continue;
|
//continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = this.inputTypeFactory.create(fieldMetadata, kind, options, {
|
const type = this.inputTypeFactory.create(fieldMetadata, kind, options, {
|
||||||
nullable: fieldMetadata.isNullable,
|
nullable: fieldMetadata.isNullable,
|
||||||
defaultValue: fieldMetadata.defaultValue,
|
defaultValue: fieldMetadata.defaultValue,
|
||||||
|
isArray: fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||||
});
|
});
|
||||||
|
|
||||||
fields[fieldMetadata.name] = {
|
fields[fieldMetadata.name] = {
|
||||||
|
|||||||
@ -3,13 +3,14 @@ import { Injectable, Logger } from '@nestjs/common';
|
|||||||
import { GraphQLInputType } from 'graphql';
|
import { GraphQLInputType } from 'graphql';
|
||||||
|
|
||||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
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 {
|
import {
|
||||||
TypeMapperService,
|
TypeMapperService,
|
||||||
TypeOptions,
|
TypeOptions,
|
||||||
} from 'src/workspace/workspace-schema-builder/services/type-mapper.service';
|
} from 'src/workspace/workspace-schema-builder/services/type-mapper.service';
|
||||||
import { TypeDefinitionsStorage } from 'src/workspace/workspace-schema-builder/storages/type-definitions.storage';
|
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';
|
import { InputTypeDefinitionKind } from './input-type-definition.factory';
|
||||||
|
|
||||||
@ -28,6 +29,9 @@ export class InputTypeFactory {
|
|||||||
buildOtions: WorkspaceBuildSchemaOptions,
|
buildOtions: WorkspaceBuildSchemaOptions,
|
||||||
typeOptions: TypeOptions,
|
typeOptions: TypeOptions,
|
||||||
): GraphQLInputType {
|
): GraphQLInputType {
|
||||||
|
const target = isCompositeFieldMetadataType(fieldMetadata.type)
|
||||||
|
? fieldMetadata.type.toString()
|
||||||
|
: fieldMetadata.id;
|
||||||
let inputType: GraphQLInputType | undefined =
|
let inputType: GraphQLInputType | undefined =
|
||||||
this.typeMapperService.mapToScalarType(
|
this.typeMapperService.mapToScalarType(
|
||||||
fieldMetadata.type,
|
fieldMetadata.type,
|
||||||
@ -35,27 +39,19 @@ export class InputTypeFactory {
|
|||||||
buildOtions.numberScalarMode,
|
buildOtions.numberScalarMode,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
inputType ??= this.typeDefinitionsStorage.getInputTypeByKey(target, kind);
|
||||||
|
|
||||||
|
inputType ??= this.typeDefinitionsStorage.getEnumTypeByKey(target);
|
||||||
|
|
||||||
if (!inputType) {
|
if (!inputType) {
|
||||||
inputType = this.typeDefinitionsStorage.getInputTypeByKey(
|
this.logger.error(`Could not find a GraphQL type for ${target}`, {
|
||||||
fieldMetadata.type.toString(),
|
fieldMetadata,
|
||||||
kind,
|
kind,
|
||||||
);
|
buildOtions,
|
||||||
|
typeOptions,
|
||||||
|
});
|
||||||
|
|
||||||
if (!inputType) {
|
throw new Error(`Could not find a GraphQL type for ${target}`);
|
||||||
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()}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.typeMapperService.mapToGqlType(inputType, typeOptions);
|
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 { 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 { 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';
|
import { ObjectTypeName, RootTypeFactory } from './root-type.factory';
|
||||||
|
|
||||||
|
|||||||
@ -3,10 +3,11 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { GraphQLFieldConfigMap, GraphQLObjectType } from 'graphql';
|
import { GraphQLFieldConfigMap, GraphQLObjectType } from 'graphql';
|
||||||
|
|
||||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
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 { 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';
|
import { OutputTypeFactory } from './output-type.factory';
|
||||||
|
|
||||||
@ -50,13 +51,14 @@ export class ObjectTypeDefinitionFactory {
|
|||||||
const fields: GraphQLFieldConfigMap<any, any> = {};
|
const fields: GraphQLFieldConfigMap<any, any> = {};
|
||||||
|
|
||||||
for (const fieldMetadata of objectMetadata.fields) {
|
for (const fieldMetadata of objectMetadata.fields) {
|
||||||
// Composite field types are generated during extension of object type definition
|
// Relation field types are generated during extension of object type definition
|
||||||
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
if (isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = this.outputTypeFactory.create(fieldMetadata, kind, options, {
|
const type = this.outputTypeFactory.create(fieldMetadata, kind, options, {
|
||||||
nullable: fieldMetadata.isNullable,
|
nullable: fieldMetadata.isNullable,
|
||||||
|
isArray: fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||||
});
|
});
|
||||||
|
|
||||||
fields[fieldMetadata.name] = {
|
fields[fieldMetadata.name] = {
|
||||||
|
|||||||
@ -3,10 +3,10 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { GraphQLInputFieldConfigMap, GraphQLInputObjectType } from 'graphql';
|
import { GraphQLInputFieldConfigMap, GraphQLInputObjectType } from 'graphql';
|
||||||
|
|
||||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
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 { 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 {
|
import {
|
||||||
InputTypeDefinition,
|
InputTypeDefinition,
|
||||||
@ -44,8 +44,8 @@ export class OrderByTypeDefinitionFactory {
|
|||||||
const fields: GraphQLInputFieldConfigMap = {};
|
const fields: GraphQLInputFieldConfigMap = {};
|
||||||
|
|
||||||
for (const fieldMetadata of objectMetadata.fields) {
|
for (const fieldMetadata of objectMetadata.fields) {
|
||||||
// Composite field types are generated during extension of object type definition
|
// Relation field types are generated during extension of object type definition
|
||||||
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
if (isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,13 +3,14 @@ import { Injectable, Logger } from '@nestjs/common';
|
|||||||
import { GraphQLInputType } from 'graphql';
|
import { GraphQLInputType } from 'graphql';
|
||||||
|
|
||||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
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 {
|
import {
|
||||||
TypeMapperService,
|
TypeMapperService,
|
||||||
TypeOptions,
|
TypeOptions,
|
||||||
} from 'src/workspace/workspace-schema-builder/services/type-mapper.service';
|
} from 'src/workspace/workspace-schema-builder/services/type-mapper.service';
|
||||||
import { TypeDefinitionsStorage } from 'src/workspace/workspace-schema-builder/storages/type-definitions.storage';
|
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';
|
import { InputTypeDefinitionKind } from './input-type-definition.factory';
|
||||||
|
|
||||||
@ -27,30 +28,26 @@ export class OrderByTypeFactory {
|
|||||||
buildOtions: WorkspaceBuildSchemaOptions,
|
buildOtions: WorkspaceBuildSchemaOptions,
|
||||||
typeOptions: TypeOptions,
|
typeOptions: TypeOptions,
|
||||||
): GraphQLInputType {
|
): GraphQLInputType {
|
||||||
|
const target = isCompositeFieldMetadataType(fieldMetadata.type)
|
||||||
|
? fieldMetadata.type.toString()
|
||||||
|
: fieldMetadata.id;
|
||||||
let orderByType = this.typeMapperService.mapToOrderByType(
|
let orderByType = this.typeMapperService.mapToOrderByType(
|
||||||
fieldMetadata.type,
|
fieldMetadata.type,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
orderByType ??= this.typeDefinitionsStorage.getInputTypeByKey(
|
||||||
|
target,
|
||||||
|
InputTypeDefinitionKind.OrderBy,
|
||||||
|
);
|
||||||
|
|
||||||
if (!orderByType) {
|
if (!orderByType) {
|
||||||
orderByType = this.typeDefinitionsStorage.getInputTypeByKey(
|
this.logger.error(`Could not find a GraphQL type for ${target}`, {
|
||||||
fieldMetadata.type.toString(),
|
fieldMetadata,
|
||||||
InputTypeDefinitionKind.OrderBy,
|
buildOtions,
|
||||||
);
|
typeOptions,
|
||||||
|
});
|
||||||
|
|
||||||
if (!orderByType) {
|
throw new Error(`Could not find a GraphQL type for ${target}`);
|
||||||
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()}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.typeMapperService.mapToGqlType(orderByType, typeOptions);
|
return this.typeMapperService.mapToGqlType(orderByType, typeOptions);
|
||||||
|
|||||||
@ -3,13 +3,14 @@ import { Injectable, Logger } from '@nestjs/common';
|
|||||||
import { GraphQLOutputType } from 'graphql';
|
import { GraphQLOutputType } from 'graphql';
|
||||||
|
|
||||||
import { WorkspaceBuildSchemaOptions } from 'src/workspace/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
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 {
|
import {
|
||||||
TypeMapperService,
|
TypeMapperService,
|
||||||
TypeOptions,
|
TypeOptions,
|
||||||
} from 'src/workspace/workspace-schema-builder/services/type-mapper.service';
|
} from 'src/workspace/workspace-schema-builder/services/type-mapper.service';
|
||||||
import { TypeDefinitionsStorage } from 'src/workspace/workspace-schema-builder/storages/type-definitions.storage';
|
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';
|
import { ObjectTypeDefinitionKind } from './object-type-definition.factory';
|
||||||
|
|
||||||
@ -28,6 +29,9 @@ export class OutputTypeFactory {
|
|||||||
buildOtions: WorkspaceBuildSchemaOptions,
|
buildOtions: WorkspaceBuildSchemaOptions,
|
||||||
typeOptions: TypeOptions,
|
typeOptions: TypeOptions,
|
||||||
): GraphQLOutputType {
|
): GraphQLOutputType {
|
||||||
|
const target = isCompositeFieldMetadataType(fieldMetadata.type)
|
||||||
|
? fieldMetadata.type.toString()
|
||||||
|
: fieldMetadata.id;
|
||||||
let gqlType: GraphQLOutputType | undefined =
|
let gqlType: GraphQLOutputType | undefined =
|
||||||
this.typeMapperService.mapToScalarType(
|
this.typeMapperService.mapToScalarType(
|
||||||
fieldMetadata.type,
|
fieldMetadata.type,
|
||||||
@ -35,26 +39,18 @@ export class OutputTypeFactory {
|
|||||||
buildOtions.numberScalarMode,
|
buildOtions.numberScalarMode,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
gqlType ??= this.typeDefinitionsStorage.getObjectTypeByKey(target, kind);
|
||||||
|
|
||||||
|
gqlType ??= this.typeDefinitionsStorage.getEnumTypeByKey(target);
|
||||||
|
|
||||||
if (!gqlType) {
|
if (!gqlType) {
|
||||||
gqlType = this.typeDefinitionsStorage.getObjectTypeByKey(
|
this.logger.error(`Could not find a GraphQL type for ${target}`, {
|
||||||
fieldMetadata.type.toString(),
|
fieldMetadata,
|
||||||
kind,
|
buildOtions,
|
||||||
);
|
typeOptions,
|
||||||
|
});
|
||||||
|
|
||||||
if (!gqlType) {
|
throw new Error(`Could not find a GraphQL type for ${target}`);
|
||||||
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()}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.typeMapperService.mapToGqlType(gqlType, typeOptions);
|
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 { 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 { 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';
|
import { ObjectTypeName, RootTypeFactory } from './root-type.factory';
|
||||||
|
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import { Injectable, Logger } from '@nestjs/common';
|
|||||||
|
|
||||||
import { GraphQLOutputType } from 'graphql';
|
import { GraphQLOutputType } 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 { 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 { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
import { TypeDefinitionsStorage } from 'src/workspace/workspace-schema-builder/storages/type-definitions.storage';
|
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 { 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 { 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 { TypeDefinitionsStorage } from 'src/workspace/workspace-schema-builder/storages/type-definitions.storage';
|
||||||
import { getResolverName } from 'src/workspace/utils/get-resolver-name.util';
|
import { getResolverName } from 'src/workspace/utils/get-resolver-name.util';
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from 'graphql';
|
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';
|
import { BigFloatScalarType } from 'src/workspace/workspace-schema-builder/graphql-types/scalars';
|
||||||
|
|
||||||
export const BigFloatFilterType = new GraphQLInputObjectType({
|
export const BigFloatFilterType = new GraphQLInputObjectType({
|
||||||
@ -13,6 +13,6 @@ export const BigFloatFilterType = new GraphQLInputObjectType({
|
|||||||
lt: { type: BigFloatScalarType },
|
lt: { type: BigFloatScalarType },
|
||||||
lte: { type: BigFloatScalarType },
|
lte: { type: BigFloatScalarType },
|
||||||
neq: { type: BigFloatScalarType },
|
neq: { type: BigFloatScalarType },
|
||||||
is: { type: FilterIsNullable },
|
is: { type: FilterIs },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {
|
|||||||
GraphQLInt,
|
GraphQLInt,
|
||||||
} from 'graphql';
|
} 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({
|
export const BigIntFilterType = new GraphQLInputObjectType({
|
||||||
name: 'BigIntFilter',
|
name: 'BigIntFilter',
|
||||||
@ -17,6 +17,6 @@ export const BigIntFilterType = new GraphQLInputObjectType({
|
|||||||
lt: { type: GraphQLInt },
|
lt: { type: GraphQLInt },
|
||||||
lte: { type: GraphQLInt },
|
lte: { type: GraphQLInt },
|
||||||
neq: { type: GraphQLInt },
|
neq: { type: GraphQLInt },
|
||||||
is: { type: FilterIsNullable },
|
is: { type: FilterIs },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { GraphQLBoolean, GraphQLInputObjectType } from 'graphql';
|
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({
|
export const BooleanFilterType = new GraphQLInputObjectType({
|
||||||
name: 'BooleanFilter',
|
name: 'BooleanFilter',
|
||||||
fields: {
|
fields: {
|
||||||
eq: { type: GraphQLBoolean },
|
eq: { type: GraphQLBoolean },
|
||||||
is: { type: FilterIsNullable },
|
is: { type: FilterIs },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from 'graphql';
|
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';
|
import { DateScalarType } from 'src/workspace/workspace-schema-builder/graphql-types/scalars';
|
||||||
|
|
||||||
export const DateFilterType = new GraphQLInputObjectType({
|
export const DateFilterType = new GraphQLInputObjectType({
|
||||||
@ -13,6 +13,6 @@ export const DateFilterType = new GraphQLInputObjectType({
|
|||||||
lt: { type: DateScalarType },
|
lt: { type: DateScalarType },
|
||||||
lte: { type: DateScalarType },
|
lte: { type: DateScalarType },
|
||||||
neq: { type: DateScalarType },
|
neq: { type: DateScalarType },
|
||||||
is: { type: FilterIsNullable },
|
is: { type: FilterIs },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from 'graphql';
|
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';
|
import { DateTimeScalarType } from 'src/workspace/workspace-schema-builder/graphql-types/scalars';
|
||||||
|
|
||||||
export const DatetimeFilterType = new GraphQLInputObjectType({
|
export const DatetimeFilterType = new GraphQLInputObjectType({
|
||||||
@ -13,6 +13,6 @@ export const DatetimeFilterType = new GraphQLInputObjectType({
|
|||||||
lt: { type: DateTimeScalarType },
|
lt: { type: DateTimeScalarType },
|
||||||
lte: { type: DateTimeScalarType },
|
lte: { type: DateTimeScalarType },
|
||||||
neq: { type: DateTimeScalarType },
|
neq: { type: DateTimeScalarType },
|
||||||
is: { type: FilterIsNullable },
|
is: { type: FilterIs },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { GraphQLEnumType } from 'graphql';
|
import { GraphQLEnumType } from 'graphql';
|
||||||
|
|
||||||
export const FilterIsNullable = new GraphQLEnumType({
|
export const FilterIs = new GraphQLEnumType({
|
||||||
name: 'FilterIsNullable',
|
name: 'FilterIs',
|
||||||
description: 'This enum to filter by nullability',
|
description: 'This enum to filter by nullability',
|
||||||
values: {
|
values: {
|
||||||
NULL: {
|
NULL: {
|
||||||
@ -5,7 +5,7 @@ import {
|
|||||||
GraphQLNonNull,
|
GraphQLNonNull,
|
||||||
} from 'graphql';
|
} 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({
|
export const FloatFilterType = new GraphQLInputObjectType({
|
||||||
name: 'FloatFilter',
|
name: 'FloatFilter',
|
||||||
@ -17,6 +17,6 @@ export const FloatFilterType = new GraphQLInputObjectType({
|
|||||||
lt: { type: GraphQLFloat },
|
lt: { type: GraphQLFloat },
|
||||||
lte: { type: GraphQLFloat },
|
lte: { type: GraphQLFloat },
|
||||||
neq: { type: GraphQLFloat },
|
neq: { type: GraphQLFloat },
|
||||||
is: { type: FilterIsNullable },
|
is: { type: FilterIs },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {
|
|||||||
GraphQLInt,
|
GraphQLInt,
|
||||||
} from 'graphql';
|
} 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({
|
export const IntFilterType = new GraphQLInputObjectType({
|
||||||
name: 'IntFilter',
|
name: 'IntFilter',
|
||||||
@ -17,6 +17,6 @@ export const IntFilterType = new GraphQLInputObjectType({
|
|||||||
lt: { type: GraphQLInt },
|
lt: { type: GraphQLInt },
|
||||||
lte: { type: GraphQLInt },
|
lte: { type: GraphQLInt },
|
||||||
neq: { type: GraphQLInt },
|
neq: { type: GraphQLInt },
|
||||||
is: { type: FilterIsNullable },
|
is: { type: FilterIs },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {
|
|||||||
GraphQLString,
|
GraphQLString,
|
||||||
} from 'graphql';
|
} 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({
|
export const StringFilterType = new GraphQLInputObjectType({
|
||||||
name: 'StringFilter',
|
name: 'StringFilter',
|
||||||
@ -22,6 +22,6 @@ export const StringFilterType = new GraphQLInputObjectType({
|
|||||||
ilike: { type: GraphQLString },
|
ilike: { type: GraphQLString },
|
||||||
regex: { type: GraphQLString },
|
regex: { type: GraphQLString },
|
||||||
iregex: { type: GraphQLString },
|
iregex: { type: GraphQLString },
|
||||||
is: { type: FilterIsNullable },
|
is: { type: FilterIs },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from 'graphql';
|
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';
|
import { TimeScalarType } from 'src/workspace/workspace-schema-builder/graphql-types/scalars';
|
||||||
|
|
||||||
export const TimeFilterType = new GraphQLInputObjectType({
|
export const TimeFilterType = new GraphQLInputObjectType({
|
||||||
@ -13,6 +13,6 @@ export const TimeFilterType = new GraphQLInputObjectType({
|
|||||||
lt: { type: TimeScalarType },
|
lt: { type: TimeScalarType },
|
||||||
lte: { type: TimeScalarType },
|
lte: { type: TimeScalarType },
|
||||||
neq: { type: TimeScalarType },
|
neq: { type: TimeScalarType },
|
||||||
is: { type: FilterIsNullable },
|
is: { type: FilterIs },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { GraphQLInputObjectType, GraphQLList } from 'graphql';
|
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';
|
import { UUIDScalarType } from 'src/workspace/workspace-schema-builder/graphql-types/scalars';
|
||||||
|
|
||||||
export const UUIDFilterType = new GraphQLInputObjectType({
|
export const UUIDFilterType = new GraphQLInputObjectType({
|
||||||
@ -9,6 +9,6 @@ export const UUIDFilterType = new GraphQLInputObjectType({
|
|||||||
eq: { type: UUIDScalarType },
|
eq: { type: UUIDScalarType },
|
||||||
in: { type: new GraphQLList(UUIDScalarType) },
|
in: { type: new GraphQLList(UUIDScalarType) },
|
||||||
neq: { type: UUIDScalarType },
|
neq: { type: UUIDScalarType },
|
||||||
is: { type: FilterIsNullable },
|
is: { type: FilterIs },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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 { InputTypeDefinitionKind } from 'src/workspace/workspace-schema-builder/factories/input-type-definition.factory';
|
||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
|
||||||
import { ObjectMetadataInterface } from './object-metadata.interface';
|
|
||||||
|
|
||||||
export interface ArgMetadata<T = any> {
|
export interface ArgMetadata<T = any> {
|
||||||
kind?: InputTypeDefinitionKind;
|
kind?: InputTypeDefinitionKind;
|
||||||
type?: FieldMetadataType;
|
type?: FieldMetadataType;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { FieldMetadataInterface } from './field-metadata.interface';
|
import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/field-metadata.interface';
|
||||||
|
|
||||||
export interface WorkspaceSchemaBuilderContext {
|
export interface WorkspaceSchemaBuilderContext {
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
|
|||||||
@ -75,7 +75,7 @@ export class TypeMapperService {
|
|||||||
fieldMetadataType: FieldMetadataType,
|
fieldMetadataType: FieldMetadataType,
|
||||||
dateScalarMode: DateScalarMode = 'isoDate',
|
dateScalarMode: DateScalarMode = 'isoDate',
|
||||||
numberScalarMode: NumberScalarMode = 'float',
|
numberScalarMode: NumberScalarMode = 'float',
|
||||||
): GraphQLInputObjectType | GraphQLScalarType<boolean, boolean> | undefined {
|
): GraphQLInputObjectType | GraphQLScalarType | undefined {
|
||||||
const dateFilter =
|
const dateFilter =
|
||||||
dateScalarMode === 'timestamp' ? DatetimeFilterType : DateFilterType;
|
dateScalarMode === 'timestamp' ? DatetimeFilterType : DateFilterType;
|
||||||
const numberScalar =
|
const numberScalar =
|
||||||
@ -84,7 +84,7 @@ export class TypeMapperService {
|
|||||||
// LINK and CURRENCY are handled in the factories because they are objects
|
// LINK and CURRENCY are handled in the factories because they are objects
|
||||||
const typeFilterMapping = new Map<
|
const typeFilterMapping = new Map<
|
||||||
FieldMetadataType,
|
FieldMetadataType,
|
||||||
GraphQLInputObjectType | GraphQLScalarType<boolean, boolean>
|
GraphQLInputObjectType | GraphQLScalarType
|
||||||
>([
|
>([
|
||||||
[FieldMetadataType.UUID, UUIDFilterType],
|
[FieldMetadataType.UUID, UUIDFilterType],
|
||||||
[FieldMetadataType.TEXT, StringFilterType],
|
[FieldMetadataType.TEXT, StringFilterType],
|
||||||
@ -115,6 +115,9 @@ export class TypeMapperService {
|
|||||||
[FieldMetadataType.NUMBER, OrderByDirectionType],
|
[FieldMetadataType.NUMBER, OrderByDirectionType],
|
||||||
[FieldMetadataType.NUMERIC, OrderByDirectionType],
|
[FieldMetadataType.NUMERIC, OrderByDirectionType],
|
||||||
[FieldMetadataType.PROBABILITY, OrderByDirectionType],
|
[FieldMetadataType.PROBABILITY, OrderByDirectionType],
|
||||||
|
[FieldMetadataType.RATING, OrderByDirectionType],
|
||||||
|
[FieldMetadataType.SELECT, OrderByDirectionType],
|
||||||
|
[FieldMetadataType.MULTI_SELECT, OrderByDirectionType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return typeOrderByMapping.get(fieldMetadataType);
|
return typeOrderByMapping.get(fieldMetadataType);
|
||||||
|
|||||||
@ -1,8 +1,13 @@
|
|||||||
import { Injectable, Scope } from '@nestjs/common';
|
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 { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import { EnumTypeDefinition } from 'src/workspace/workspace-schema-builder/factories/enum-type-definition.factory';
|
||||||
import {
|
import {
|
||||||
InputTypeDefinition,
|
InputTypeDefinition,
|
||||||
InputTypeDefinitionKind,
|
InputTypeDefinitionKind,
|
||||||
@ -15,6 +20,7 @@ import {
|
|||||||
// Must be scoped on REQUEST level
|
// Must be scoped on REQUEST level
|
||||||
@Injectable({ scope: Scope.REQUEST })
|
@Injectable({ scope: Scope.REQUEST })
|
||||||
export class TypeDefinitionsStorage {
|
export class TypeDefinitionsStorage {
|
||||||
|
private readonly enumTypeDefinitions = new Map<string, EnumTypeDefinition>();
|
||||||
private readonly objectTypeDefinitions = new Map<
|
private readonly objectTypeDefinitions = new Map<
|
||||||
string,
|
string,
|
||||||
ObjectTypeDefinition
|
ObjectTypeDefinition
|
||||||
@ -24,6 +30,10 @@ export class TypeDefinitionsStorage {
|
|||||||
InputTypeDefinition
|
InputTypeDefinition
|
||||||
>();
|
>();
|
||||||
|
|
||||||
|
addEnumTypes(enumDefs: EnumTypeDefinition[]) {
|
||||||
|
enumDefs.forEach((item) => this.enumTypeDefinitions.set(item.target, item));
|
||||||
|
}
|
||||||
|
|
||||||
addObjectTypes(objectDefs: ObjectTypeDefinition[]) {
|
addObjectTypes(objectDefs: ObjectTypeDefinition[]) {
|
||||||
objectDefs.forEach((item) =>
|
objectDefs.forEach((item) =>
|
||||||
this.objectTypeDefinitions.set(
|
this.objectTypeDefinitions.set(
|
||||||
@ -64,6 +74,10 @@ export class TypeDefinitionsStorage {
|
|||||||
)?.type;
|
)?.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEnumTypeByKey(target: string): GraphQLEnumType | undefined {
|
||||||
|
return this.enumTypeDefinitions.get(target)?.type;
|
||||||
|
}
|
||||||
|
|
||||||
getAllInputTypeDefinitions(): InputTypeDefinition[] {
|
getAllInputTypeDefinitions(): InputTypeDefinition[] {
|
||||||
return Array.from(this.inputTypeDefinitions.values());
|
return Array.from(this.inputTypeDefinitions.values());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,14 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
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 { FieldMetadataEntity } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
import { customTableDefaultColumns } from 'src/workspace/workspace-migration-runner/utils/custom-table-default-column.util';
|
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 { TypeDefinitionsStorage } from './storages/type-definitions.storage';
|
||||||
import {
|
import {
|
||||||
@ -15,16 +21,12 @@ import {
|
|||||||
} from './factories/input-type-definition.factory';
|
} from './factories/input-type-definition.factory';
|
||||||
import { getFieldMetadataType } from './utils/get-field-metadata-type.util';
|
import { getFieldMetadataType } from './utils/get-field-metadata-type.util';
|
||||||
import { WorkspaceBuildSchemaOptions } from './interfaces/workspace-build-schema-optionts.interface';
|
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 { FilterTypeDefinitionFactory } from './factories/filter-type-definition.factory';
|
||||||
import { ConnectionTypeDefinitionFactory } from './factories/connection-type-definition.factory';
|
import { ConnectionTypeDefinitionFactory } from './factories/connection-type-definition.factory';
|
||||||
import { EdgeTypeDefinitionFactory } from './factories/edge-type-definition.factory';
|
import { EdgeTypeDefinitionFactory } from './factories/edge-type-definition.factory';
|
||||||
import { OrderByTypeDefinitionFactory } from './factories/order-by-type-definition.factory';
|
import { OrderByTypeDefinitionFactory } from './factories/order-by-type-definition.factory';
|
||||||
import { ExtendObjectTypeDefinitionFactory } from './factories/extend-object-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
|
// Create a default field for each custom table default column
|
||||||
const defaultFields = customTableDefaultColumns.map((column) => {
|
const defaultFields = customTableDefaultColumns.map((column) => {
|
||||||
@ -42,6 +44,7 @@ export class TypeDefinitionsGenerator {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly typeDefinitionsStorage: TypeDefinitionsStorage,
|
private readonly typeDefinitionsStorage: TypeDefinitionsStorage,
|
||||||
private readonly objectTypeDefinitionFactory: ObjectTypeDefinitionFactory,
|
private readonly objectTypeDefinitionFactory: ObjectTypeDefinitionFactory,
|
||||||
|
private readonly enumTypeDefinitionFactory: EnumTypeDefinitionFactory,
|
||||||
private readonly inputTypeDefinitionFactory: InputTypeDefinitionFactory,
|
private readonly inputTypeDefinitionFactory: InputTypeDefinitionFactory,
|
||||||
private readonly filterTypeDefintionFactory: FilterTypeDefinitionFactory,
|
private readonly filterTypeDefintionFactory: FilterTypeDefinitionFactory,
|
||||||
private readonly orderByTypeDefinitionFactory: OrderByTypeDefinitionFactory,
|
private readonly orderByTypeDefinitionFactory: OrderByTypeDefinitionFactory,
|
||||||
@ -74,6 +77,7 @@ export class TypeDefinitionsGenerator {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Generate static objects first because they can be used in dynamic objects
|
// Generate static objects first because they can be used in dynamic objects
|
||||||
|
this.generateEnumTypeDefs(staticObjectMetadataCollection, options);
|
||||||
this.generateObjectTypeDefs(staticObjectMetadataCollection, options);
|
this.generateObjectTypeDefs(staticObjectMetadataCollection, options);
|
||||||
this.generateInputTypeDefs(staticObjectMetadataCollection, options);
|
this.generateInputTypeDefs(staticObjectMetadataCollection, options);
|
||||||
}
|
}
|
||||||
@ -89,6 +93,7 @@ export class TypeDefinitionsGenerator {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Generate dynamic objects
|
// Generate dynamic objects
|
||||||
|
this.generateEnumTypeDefs(dynamicObjectMetadataCollection, options);
|
||||||
this.generateObjectTypeDefs(dynamicObjectMetadataCollection, options);
|
this.generateObjectTypeDefs(dynamicObjectMetadataCollection, options);
|
||||||
this.generatePaginationTypeDefs(dynamicObjectMetadataCollection, options);
|
this.generatePaginationTypeDefs(dynamicObjectMetadataCollection, options);
|
||||||
this.generateInputTypeDefs(dynamicObjectMetadataCollection, options);
|
this.generateInputTypeDefs(dynamicObjectMetadataCollection, options);
|
||||||
@ -203,13 +208,26 @@ export class TypeDefinitionsGenerator {
|
|||||||
this.typeDefinitionsStorage.addInputTypes(inputTypeDefs);
|
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(
|
private generateExtendedObjectTypeDefs(
|
||||||
objectMetadataCollection: ObjectMetadataInterface[],
|
objectMetadataCollection: ObjectMetadataInterface[],
|
||||||
options: WorkspaceBuildSchemaOptions,
|
options: WorkspaceBuildSchemaOptions,
|
||||||
) {
|
) {
|
||||||
// Generate extended object type defs only for objects that contain composite fields
|
// Generate extended object type defs only for objects that contain composite fields
|
||||||
const objectMetadataCollectionWithCompositeFields =
|
const objectMetadataCollectionWithCompositeFields =
|
||||||
objectMetadataCollection.filter(objectContainsCompositeField);
|
objectMetadataCollection.filter(objectContainsRelationField);
|
||||||
const objectTypeDefs = objectMetadataCollectionWithCompositeFields.map(
|
const objectTypeDefs = objectMetadataCollectionWithCompositeFields.map(
|
||||||
(objectMetadata) =>
|
(objectMetadata) =>
|
||||||
this.extendObjectTypeDefinitionFactory.create(objectMetadata, options),
|
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 { GraphQLSchema } from 'graphql';
|
||||||
|
|
||||||
import { WorkspaceResolverBuilderMethods } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
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 { TypeDefinitionsGenerator } from './type-definitions.generator';
|
||||||
|
|
||||||
import { WorkspaceBuildSchemaOptions } from './interfaces/workspace-build-schema-optionts.interface';
|
import { WorkspaceBuildSchemaOptions } from './interfaces/workspace-build-schema-optionts.interface';
|
||||||
import { QueryTypeFactory } from './factories/query-type.factory';
|
import { QueryTypeFactory } from './factories/query-type.factory';
|
||||||
import { MutationTypeFactory } from './factories/mutation-type.factory';
|
import { MutationTypeFactory } from './factories/mutation-type.factory';
|
||||||
import { ObjectMetadataInterface } from './interfaces/object-metadata.interface';
|
|
||||||
import { OrphanedTypesFactory } from './factories/orphaned-types.factory';
|
import { OrphanedTypesFactory } from './factories/orphaned-types.factory';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|||||||
Reference in New Issue
Block a user