Add identifier fields to ObjectMetadata (#2616)

* Add indentifier fields to ObjectMetadata

* Add indentifier fields to ObjectMetadata

* Add indentifier fields to ObjectMetadata

* temporarily block name/label edition
This commit is contained in:
Weiko
2023-11-21 18:41:48 +01:00
committed by GitHub
parent 726e375616
commit c74bde28b8
17 changed files with 408 additions and 94 deletions

View File

@ -8,6 +8,7 @@ import {
import {
Authorize,
BeforeDeleteOne,
FilterableField,
IDField,
QueryOptions,
@ -16,6 +17,7 @@ import {
import { RelationMetadataDTO } from 'src/metadata/relation-metadata/dtos/relation-metadata.dto';
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
import { BeforeDeleteOneField } from 'src/metadata/field-metadata/hooks/before-delete-one-field.hook';
registerEnumType(FieldMetadataType, {
name: 'FieldMetadataType',
@ -33,6 +35,7 @@ registerEnumType(FieldMetadataType, {
disableSort: true,
maxResultsSize: 1000,
})
@BeforeDeleteOne(BeforeDeleteOneField)
@Relation('toRelationMetadata', () => RelationMetadataDTO, {
nullable: true,
})

View File

@ -1,8 +1,12 @@
import { Field, InputType } from '@nestjs/graphql';
import { BeforeUpdateOne } from '@ptc-org/nestjs-query-graphql';
import { IsBoolean, IsOptional, IsString } from 'class-validator';
import { BeforeUpdateOneField } from 'src/metadata/field-metadata/hooks/before-update-one-field.hook';
@InputType()
@BeforeUpdateOne(BeforeUpdateOneField)
export class UpdateFieldInput {
@IsString()
@IsOptional()

View File

@ -1,5 +1,4 @@
import {
BadRequestException,
ConflictException,
Injectable,
NotFoundException,
@ -8,12 +7,10 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
import { DeleteOneOptions } from '@ptc-org/nestjs-query-core';
import { WorkspaceMigrationRunnerService } from 'src/workspace/workspace-migration-runner/workspace-migration-runner.service';
import { WorkspaceMigrationService } from 'src/metadata/workspace-migration/workspace-migration.service';
import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service';
import { FieldMetadataDTO } from 'src/metadata/field-metadata/dtos/field-metadata.dto';
import { CreateFieldInput } from 'src/metadata/field-metadata/dtos/create-field.input';
import { WorkspaceMigrationTableAction } from 'src/metadata/workspace-migration/workspace-migration.entity';
import { generateTargetColumnMap } from 'src/metadata/field-metadata/utils/generate-target-column-map.util';
@ -34,30 +31,6 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
super(fieldMetadataRepository);
}
override async deleteOne(
id: string,
opts?: DeleteOneOptions<FieldMetadataDTO> | undefined,
): Promise<FieldMetadataEntity> {
const fieldMetadata = await this.fieldMetadataRepository.findOne({
where: { id },
});
if (!fieldMetadata) {
throw new NotFoundException('Field does not exist');
}
if (!fieldMetadata.isCustom) {
throw new BadRequestException("Standard fields can't be deleted");
}
if (fieldMetadata.isActive) {
throw new BadRequestException("Active fields can't be deleted");
}
// TODO: delete associated relation-metadata and field-metadata from the relation
return super.deleteOne(id, opts);
}
override async createOne(
record: CreateFieldInput,
): Promise<FieldMetadataEntity> {
@ -108,6 +81,15 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
return createdFieldMetadata;
}
public async findOneWithinWorkspace(
fieldMetadataId: string,
workspaceId: string,
) {
return this.fieldMetadataRepository.findOne({
where: { id: fieldMetadataId, workspaceId },
});
}
public async deleteFieldsMetadata(workspaceId: string) {
await this.fieldMetadataRepository.delete({ workspaceId });
}

View File

@ -0,0 +1,55 @@
import {
BadRequestException,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import {
BeforeDeleteOneHook,
DeleteOneInputType,
} from '@ptc-org/nestjs-query-graphql';
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
import { FieldMetadataService } from 'src/metadata/field-metadata/field-metadata.service';
@Injectable()
export class BeforeDeleteOneField implements BeforeDeleteOneHook<any> {
constructor(readonly fieldMetadataService: FieldMetadataService) {}
async run(
instance: DeleteOneInputType,
context: any,
): Promise<DeleteOneInputType> {
const workspaceId = context?.req?.user?.workspace?.id;
if (!workspaceId) {
throw new UnauthorizedException();
}
const fieldMetadata =
await this.fieldMetadataService.findOneWithinWorkspace(
instance.id.toString(),
workspaceId,
);
if (!fieldMetadata) {
throw new BadRequestException('Field does not exist');
}
if (!fieldMetadata.isCustom) {
throw new BadRequestException("Standard Fields can't be deleted");
}
if (fieldMetadata.isActive) {
throw new BadRequestException("Active fields can't be deleted");
}
if (fieldMetadata.type === FieldMetadataType.RELATION) {
throw new BadRequestException(
"Relation fields can't be deleted, you need to delete the RelationMetadata instead",
);
}
return instance;
}
}

View File

@ -0,0 +1,57 @@
import {
BadRequestException,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import {
BeforeUpdateOneHook,
UpdateOneInputType,
} from '@ptc-org/nestjs-query-graphql';
import { UpdateFieldInput } from 'src/metadata/field-metadata/dtos/update-field.input';
import { FieldMetadataService } from 'src/metadata/field-metadata/field-metadata.service';
@Injectable()
export class BeforeUpdateOneField<T extends UpdateFieldInput>
implements BeforeUpdateOneHook<T, any>
{
constructor(readonly fieldMetadataService: FieldMetadataService) {}
// TODO: this logic could be moved to a policy guard
async run(
instance: UpdateOneInputType<T>,
context: any,
): Promise<UpdateOneInputType<T>> {
const workspaceId = context?.req?.user?.workspace?.id;
if (!workspaceId) {
throw new UnauthorizedException();
}
const fieldMetadata =
await this.fieldMetadataService.findOneWithinWorkspace(
instance.id.toString(),
workspaceId,
);
if (!fieldMetadata) {
throw new BadRequestException('Field does not exist');
}
if (!fieldMetadata.isCustom) {
throw new BadRequestException("Standard Fields can't be updated");
}
this.checkIfFieldIsEditable(instance.update);
return instance;
}
// This is temporary until we properly use the MigrationRunner to update column names
private checkIfFieldIsEditable(update: UpdateFieldInput) {
if (update.name || update.label) {
throw new BadRequestException("Field's name and label can't be updated");
}
}
}