Files
twenty/server/src/metadata/object-metadata/object-metadata.service.ts
Charles Bochet f5e1d7825a Removing Prisma and Grapql-nestjs-prisma resolvers (#2574)
* Some cleaning

* Fix seeds

* Fix all sign in, sign up flow and apiKey optimistic rendering

* Fix
2023-11-19 18:25:47 +01:00

251 lines
8.1 KiB
TypeScript

import {
BadRequestException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Equal, In, Repository } from 'typeorm';
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
import { WorkspaceMigrationService } from 'src/metadata/workspace-migration/workspace-migration.service';
import { WorkspaceMigrationRunnerService } from 'src/workspace/workspace-migration-runner/workspace-migration-runner.service';
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationColumnCreate,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
import { ObjectMetadataEntity } from './object-metadata.entity';
import { CreateObjectInput } from './dtos/create-object.input';
@Injectable()
export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEntity> {
constructor(
@InjectRepository(ObjectMetadataEntity, 'metadata')
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
private readonly dataSourceService: DataSourceService,
private readonly typeORMService: TypeORMService,
private readonly workspaceMigrationService: WorkspaceMigrationService,
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
) {
super(objectMetadataRepository);
}
override async deleteOne(id: string): Promise<ObjectMetadataEntity> {
const objectMetadata = await this.objectMetadataRepository.findOne({
where: { id },
});
if (!objectMetadata) {
throw new NotFoundException('Object does not exist');
}
if (!objectMetadata.isCustom) {
throw new BadRequestException("Standard Objects can't be deleted");
}
if (objectMetadata.isActive) {
throw new BadRequestException("Active objects can't be deleted");
}
return super.deleteOne(id);
}
override async createOne(
record: CreateObjectInput,
): Promise<ObjectMetadataEntity> {
const createdObjectMetadata = await super.createOne({
...record,
targetTableName: `_${record.nameSingular}`,
isActive: true,
isCustom: true,
isSystem: false,
fields:
// Creating default fields.
// No need to create a custom migration for this though as the default columns are already
// created with default values which is not supported yet by workspace migrations.
[
{
type: FieldMetadataType.UUID,
name: 'id',
label: 'Id',
targetColumnMap: {
value: 'id',
},
icon: 'Icon123',
description: 'Id',
isNullable: true,
isActive: true,
isCustom: false,
isSystem: true,
workspaceId: record.workspaceId,
defaultValue: { type: 'uuid' },
},
{
type: FieldMetadataType.DATE,
name: 'createdAt',
label: 'Creation date',
targetColumnMap: {
value: 'createdAt',
},
icon: 'IconCalendar',
description: 'Creation date',
isNullable: true,
isActive: true,
isCustom: false,
workspaceId: record.workspaceId,
defaultValue: { type: 'now' },
},
{
type: FieldMetadataType.DATE,
name: 'updatedAt',
label: 'Update date',
targetColumnMap: {
value: 'updatedAt',
},
icon: 'IconCalendar',
description: 'Update date',
isNullable: true,
isActive: true,
isCustom: false,
workspaceId: record.workspaceId,
defaultValue: { type: 'now' },
},
{
type: FieldMetadataType.TEXT,
name: 'name',
label: 'Name',
targetColumnMap: {
value: 'name',
},
icon: 'IconAbc',
description: 'Name',
isNullable: true,
isActive: true,
isCustom: false,
workspaceId: record.workspaceId,
defaultValue: { value: 'Untitled' },
},
],
});
await this.workspaceMigrationService.createCustomMigration(
createdObjectMetadata.workspaceId,
[
{
name: createdObjectMetadata.targetTableName,
action: 'create',
} satisfies WorkspaceMigrationTableAction,
// This is temporary until we implement mainIdentifier
{
name: createdObjectMetadata.targetTableName,
action: 'alter',
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
columnName: 'name',
columnType: 'varchar',
defaultValue: "'Untitled'",
} satisfies WorkspaceMigrationColumnCreate,
],
} satisfies WorkspaceMigrationTableAction,
],
);
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
createdObjectMetadata.workspaceId,
);
const dataSourceMetadata =
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
createdObjectMetadata.workspaceId,
);
const workspaceDataSource = await this.typeORMService.connectToDataSource(
dataSourceMetadata,
);
const view = await workspaceDataSource?.query(
`INSERT INTO ${dataSourceMetadata.schema}."view"
("objectMetadataId", "type", "name")
VALUES ('${createdObjectMetadata.id}', 'table', 'All ${createdObjectMetadata.namePlural}') RETURNING *`,
);
createdObjectMetadata.fields.map(async (field, index) => {
if (field.name === 'id') {
return;
}
await workspaceDataSource?.query(
`INSERT INTO ${dataSourceMetadata.schema}."viewField"
("fieldMetadataId", "position", "isVisible", "size", "viewId")
VALUES ('${field.id}', '${index}', true, 180, '${view[0].id}') RETURNING *`,
);
});
return createdObjectMetadata;
}
public async getObjectMetadataFromWorkspaceId(workspaceId: string) {
return this.objectMetadataRepository.find({
where: { workspaceId },
relations: [
'fields',
'fields.fromRelationMetadata',
'fields.fromRelationMetadata.fromObjectMetadata',
'fields.fromRelationMetadata.toObjectMetadata',
'fields.fromRelationMetadata.toObjectMetadata.fields',
'fields.toRelationMetadata',
'fields.toRelationMetadata.fromObjectMetadata',
'fields.toRelationMetadata.fromObjectMetadata.fields',
'fields.toRelationMetadata.toObjectMetadata',
],
});
}
public async getObjectMetadataFromDataSourceId(dataSourceId: string) {
return this.objectMetadataRepository.find({
where: { dataSourceId },
relations: [
'fields',
'fields.fromRelationMetadata',
'fields.fromRelationMetadata.fromObjectMetadata',
'fields.fromRelationMetadata.toObjectMetadata',
'fields.fromRelationMetadata.toObjectMetadata.fields',
'fields.toRelationMetadata',
'fields.toRelationMetadata.fromObjectMetadata',
'fields.toRelationMetadata.fromObjectMetadata.fields',
'fields.toRelationMetadata.toObjectMetadata',
],
});
}
public async findOneWithinWorkspace(
objectMetadataId: string,
workspaceId: string,
) {
return this.objectMetadataRepository.findOne({
where: { id: objectMetadataId, workspaceId },
});
}
public async findManyWithinWorkspace(
objectMetadataIds: string[],
workspaceId: string,
) {
return this.objectMetadataRepository.findBy({
id: In(objectMetadataIds),
workspaceId: Equal(workspaceId),
});
}
public async deleteObjectsMetadata(workspaceId: string) {
await this.objectMetadataRepository.delete({ workspaceId });
}
}