Convert metadata tables to camelCase (#2400)

* Convert metadata tables to camelCase

* datasourcemetadataid to datasourceid

* refactor metadata folders

* fix command

* move commands out of metadata

* fix seed

* rename objectId and fieldId in objectMetadataId and fieldMetadataId in FE

* fix field-metadata

* Fix

* Fix

* remove logs

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Weiko
2023-11-09 20:06:10 +01:00
committed by GitHub
parent 5622f42e7a
commit 1cf08c797f
238 changed files with 1851 additions and 2252 deletions

View File

@ -1,8 +1,12 @@
import { Field, InputType } from '@nestjs/graphql';
import { Field, HideField, InputType } from '@nestjs/graphql';
import { BeforeCreateOne } from '@ptc-org/nestjs-query-graphql';
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { BeforeCreateOneObject } from 'src/metadata/object-metadata/hooks/before-create-one-object.hook';
@InputType()
@BeforeCreateOne(BeforeCreateOneObject)
export class CreateObjectInput {
@IsString()
@IsNotEmpty()
@ -33,4 +37,10 @@ export class CreateObjectInput {
@IsOptional()
@Field({ nullable: true })
icon?: string;
@HideField()
dataSourceId: string;
@HideField()
workspaceId: string;
}

View File

@ -0,0 +1,64 @@
import { ObjectType, ID, Field, HideField } from '@nestjs/graphql';
import {
Authorize,
CursorConnection,
IDField,
QueryOptions,
} from '@ptc-org/nestjs-query-graphql';
import { FieldMetadataDTO } from 'src/metadata/field-metadata/dtos/field-metadata.dto';
@ObjectType('object')
@Authorize({
authorize: (context: any) => ({
workspaceId: { eq: context?.req?.user?.workspace?.id },
}),
})
@QueryOptions({
defaultResultSize: 10,
disableFilter: true,
disableSort: true,
maxResultsSize: 1000,
})
@CursorConnection('fields', () => FieldMetadataDTO)
export class ObjectMetadataDTO {
@IDField(() => ID)
id: string;
@Field()
dataSourceId: string;
@Field()
nameSingular: string;
@Field()
namePlural: string;
@Field()
labelSingular: string;
@Field()
labelPlural: string;
@Field({ nullable: true })
description: string;
@Field({ nullable: true })
icon: string;
@Field()
isCustom: boolean;
@Field()
isActive: boolean;
@HideField()
workspaceId: string;
@Field()
createdAt: Date;
@Field()
updatedAt: Date;
}

View File

@ -6,10 +6,10 @@ import {
} from '@ptc-org/nestjs-query-graphql';
import { DataSourceMetadataService } from 'src/metadata/data-source-metadata/data-source-metadata.service';
import { ObjectMetadata } from 'src/metadata/object-metadata/object-metadata.entity';
import { CreateObjectInput } from 'src/metadata/object-metadata/dtos/create-object.input';
@Injectable()
export class BeforeCreateOneObject<T extends ObjectMetadata>
export class BeforeCreateOneObject<T extends CreateObjectInput>
implements BeforeCreateOneHook<T, any>
{
constructor(readonly dataSourceMetadataService: DataSourceMetadataService) {}
@ -30,10 +30,7 @@ export class BeforeCreateOneObject<T extends ObjectMetadata>
);
instance.input.dataSourceId = lastDataSourceMetadata.id;
instance.input.targetTableName = `_${instance.input.namePlural}`;
instance.input.workspaceId = workspaceId;
instance.input.isActive = true;
instance.input.isCustom = true;
return instance;
}
}

View File

@ -1,44 +0,0 @@
import { SortDirection } from '@ptc-org/nestjs-query-core';
import {
AutoResolverOpts,
PagingStrategies,
ReadResolverOpts,
} from '@ptc-org/nestjs-query-graphql';
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
import { ObjectMetadata } from './object-metadata.entity';
import { ObjectMetadataService } from './services/object-metadata.service';
import { CreateObjectInput } from './dtos/create-object.input';
import { UpdateObjectInput } from './dtos/update-object.input';
export const objectMetadataAutoResolverOpts: AutoResolverOpts<
any,
any,
unknown,
unknown,
ReadResolverOpts<any>,
PagingStrategies
>[] = [
{
EntityClass: ObjectMetadata,
DTOClass: ObjectMetadata,
CreateDTOClass: CreateObjectInput,
UpdateDTOClass: UpdateObjectInput,
ServiceClass: ObjectMetadataService,
enableTotalCount: true,
pagingStrategy: PagingStrategies.CURSOR,
read: {
defaultSort: [{ field: 'id', direction: SortDirection.DESC }],
},
create: {
many: { disabled: true },
},
update: {
many: { disabled: true },
},
delete: { many: { disabled: true } },
guards: [JwtAuthGuard],
},
];

View File

@ -1,112 +0,0 @@
import { ObjectType, ID, Field } from '@nestjs/graphql';
import {
Column,
CreateDateColumn,
Entity,
OneToMany,
PrimaryGeneratedColumn,
Unique,
UpdateDateColumn,
} from 'typeorm';
import {
Authorize,
BeforeCreateOne,
CursorConnection,
IDField,
QueryOptions,
} from '@ptc-org/nestjs-query-graphql';
import { ObjectMetadataInterface } from 'src/tenant/schema-builder/interfaces/object-metadata.interface';
import { FieldMetadata } from 'src/metadata/field-metadata/field-metadata.entity';
import { RelationMetadata } from 'src/metadata/relation-metadata/relation-metadata.entity';
import { BeforeCreateOneObject } from './hooks/before-create-one-object.hook';
@Entity('object_metadata')
@ObjectType('object')
@BeforeCreateOne(BeforeCreateOneObject)
@Authorize({
authorize: (context: any) => ({
workspaceId: { eq: context?.req?.user?.workspace?.id },
}),
})
@QueryOptions({
defaultResultSize: 10,
disableFilter: true,
disableSort: true,
maxResultsSize: 1000,
})
@CursorConnection('fields', () => FieldMetadata)
@Unique('IndexOnNameSingularAndWorkspaceIdUnique', [
'nameSingular',
'workspaceId',
])
@Unique('IndexOnNamePluralAndWorkspaceIdUnique', ['namePlural', 'workspaceId'])
export class ObjectMetadata implements ObjectMetadataInterface {
@IDField(() => ID)
@PrimaryGeneratedColumn('uuid')
id: string;
@Field()
@Column({ nullable: false, name: 'data_source_id' })
dataSourceId: string;
@Field()
@Column({ nullable: false, name: 'name_singular' })
nameSingular: string;
@Field()
@Column({ nullable: false, name: 'name_plural' })
namePlural: string;
@Field()
@Column({ nullable: false, name: 'label_singular' })
labelSingular: string;
@Field()
@Column({ nullable: false, name: 'label_plural' })
labelPlural: string;
@Field({ nullable: true })
@Column({ nullable: true, name: 'description', type: 'text' })
description: string;
@Field({ nullable: true })
@Column({ nullable: true, name: 'icon' })
icon: string;
@Column({ nullable: false, name: 'target_table_name' })
targetTableName: string;
@Field()
@Column({ default: false, name: 'is_custom' })
isCustom: boolean;
@Field()
@Column({ default: false, name: 'is_active' })
isActive: boolean;
@Column({ nullable: false, name: 'workspace_id' })
workspaceId: string;
@OneToMany(() => FieldMetadata, (field) => field.object, {
cascade: true,
})
fields: FieldMetadata[];
@OneToMany(() => RelationMetadata, (relation) => relation.fromObjectMetadata)
fromRelations: RelationMetadata[];
@OneToMany(() => RelationMetadata, (relation) => relation.toObjectMetadata)
toRelations: RelationMetadata[];
@Field()
@CreateDateColumn({ name: 'created_at' })
createdAt: Date;
@Field()
@UpdateDateColumn({ name: 'updated_at' })
updatedAt: Date;
}

View File

@ -1,28 +1,56 @@
import { Module } from '@nestjs/common';
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import {
NestjsQueryGraphQLModule,
PagingStrategies,
} from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { SortDirection } from '@ptc-org/nestjs-query-core';
import { DataSourceMetadataModule } from 'src/metadata/data-source-metadata/data-source-metadata.module';
import { MigrationRunnerModule } from 'src/metadata/migration-runner/migration-runner.module';
import { TenantMigrationRunnerModule } from 'src/tenant-migration-runner/tenant-migration-runner.module';
import { TenantMigrationModule } from 'src/metadata/tenant-migration/tenant-migration.module';
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
import { ObjectMetadataEntity } from 'src/database/typeorm/metadata/entities/object-metadata.entity';
import { ObjectMetadata } from './object-metadata.entity';
import { objectMetadataAutoResolverOpts } from './object-metadata.auto-resolver-opts';
import { ObjectMetadataService } from './object-metadata.service';
import { ObjectMetadataService } from './services/object-metadata.service';
import { CreateObjectInput } from './dtos/create-object.input';
import { UpdateObjectInput } from './dtos/update-object.input';
import { ObjectMetadataDTO } from './dtos/object-metadata.dto';
@Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [
NestjsQueryTypeOrmModule.forFeature([ObjectMetadata], 'metadata'),
NestjsQueryTypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'),
DataSourceMetadataModule,
TenantMigrationModule,
MigrationRunnerModule,
TenantMigrationRunnerModule,
],
services: [ObjectMetadataService],
resolvers: objectMetadataAutoResolverOpts,
resolvers: [
{
EntityClass: ObjectMetadataEntity,
DTOClass: ObjectMetadataDTO,
CreateDTOClass: CreateObjectInput,
UpdateDTOClass: UpdateObjectInput,
ServiceClass: ObjectMetadataService,
enableTotalCount: true,
pagingStrategy: PagingStrategies.CURSOR,
read: {
defaultSort: [{ field: 'id', direction: SortDirection.DESC }],
},
create: {
many: { disabled: true },
},
update: {
many: { disabled: true },
},
delete: { many: { disabled: true } },
guards: [JwtAuthGuard],
},
],
}),
],
providers: [ObjectMetadataService],

View File

@ -7,30 +7,27 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Equal, In, Repository } from 'typeorm';
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
import { DeleteOneOptions } from '@ptc-org/nestjs-query-core';
import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service';
import { TenantMigrationTableAction } from 'src/metadata/tenant-migration/tenant-migration.entity';
import { MigrationRunnerService } from 'src/metadata/migration-runner/migration-runner.service';
import { ObjectMetadata } from 'src/metadata/object-metadata/object-metadata.entity';
import { standardObjectsMetadata } from 'src/metadata/standard-objects/standard-object-metadata';
import { TenantMigrationRunnerService } from 'src/tenant-migration-runner/tenant-migration-runner.service';
import { ObjectMetadataEntity } from 'src/database/typeorm/metadata/entities/object-metadata.entity';
import { TenantMigrationTableAction } from 'src/database/typeorm/metadata/entities/tenant-migration.entity';
import { CreateObjectInput } from './dtos/create-object.input';
@Injectable()
export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadata> {
export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEntity> {
constructor(
@InjectRepository(ObjectMetadata, 'metadata')
private readonly objectMetadataRepository: Repository<ObjectMetadata>,
@InjectRepository(ObjectMetadataEntity, 'metadata')
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
private readonly tenantMigrationService: TenantMigrationService,
private readonly migrationRunnerService: MigrationRunnerService,
private readonly migrationRunnerService: TenantMigrationRunnerService,
) {
super(objectMetadataRepository);
}
override async deleteOne(
id: string,
opts?: DeleteOneOptions<ObjectMetadata> | undefined,
): Promise<ObjectMetadata> {
override async deleteOne(id: string): Promise<ObjectMetadataEntity> {
const objectMetadata = await this.objectMetadataRepository.findOne({
where: { id },
});
@ -47,11 +44,18 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadata> {
throw new BadRequestException("Active objects can't be deleted");
}
return super.deleteOne(id, opts);
return super.deleteOne(id);
}
override async createOne(record: ObjectMetadata): Promise<ObjectMetadata> {
const createdObjectMetadata = await super.createOne(record);
override async createOne(
record: CreateObjectInput,
): Promise<ObjectMetadataEntity> {
const createdObjectMetadata = await super.createOne({
...record,
targetTableName: `_${record.nameSingular}`,
isActive: true,
isCustom: true,
});
await this.tenantMigrationService.createCustomMigration(
createdObjectMetadata.workspaceId,
@ -103,34 +107,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadata> {
});
}
/**
*
* Create all standard objects and fields metadata for a given workspace
*
* @param dataSourceMetadataId
* @param workspaceId
*/
public async createStandardObjectsAndFieldsMetadata(
dataSourceMetadataId: string,
workspaceId: string,
) {
await this.objectMetadataRepository.save(
Object.values(standardObjectsMetadata).map((objectMetadata) => ({
...objectMetadata,
dataSourceId: dataSourceMetadataId,
workspaceId,
isCustom: false,
isActive: true,
fields: objectMetadata.fields.map((field) => ({
...field,
workspaceId,
isCustom: false,
isActive: true,
})),
})),
);
}
public async deleteObjectsMetadata(workspaceId: string) {
await this.objectMetadataRepository.delete({ workspaceId });
}

View File

@ -1,38 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { ObjectMetadata } from 'src/metadata/object-metadata/object-metadata.entity';
import { MigrationRunnerService } from 'src/metadata/migration-runner/migration-runner.service';
import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service';
import { ObjectMetadataService } from './object-metadata.service';
describe('ObjectMetadataService', () => {
let service: ObjectMetadataService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
ObjectMetadataService,
{
provide: getRepositoryToken(ObjectMetadata, 'metadata'),
useValue: {},
},
{
provide: TenantMigrationService,
useValue: {},
},
{
provide: MigrationRunnerService,
useValue: {},
},
],
}).compile();
service = module.get<ObjectMetadataService>(ObjectMetadataService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});