Commit 9bf0a6c5 authored by nanahira's avatar nanahira

auto set omit fields for create and update

parent 4de16764
import { Expose } from 'class-transformer'; import { Expose } from 'class-transformer';
import { IsOptional } from 'class-validator'; import { IsOptional } from 'class-validator';
import { Metadata } from '../utility/metadata';
import { MergePropertyDecorators } from './merge'; import { MergePropertyDecorators } from './merge';
export const NotWritable = () => export const NotWritable = () =>
MergePropertyDecorators([Expose({ groups: ['r'] }), IsOptional()]); MergePropertyDecorators([
export const NotChangeable = () => Expose({ groups: ['r', 'c'] }); Expose({ groups: ['r'] }),
IsOptional(),
Metadata.set('notWritable', true, 'notWritableFields'),
]);
export const NotChangeable = () =>
MergePropertyDecorators([
Expose({ groups: ['r', 'c'] }),
Metadata.set('notChangeable', true, 'notChangeableFields'),
]);
...@@ -28,7 +28,7 @@ import { ...@@ -28,7 +28,7 @@ import {
import { CreatePipe, GetPipe, UpdatePipe } from './pipes'; import { CreatePipe, GetPipe, UpdatePipe } from './pipes';
import { OperationObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface'; import { OperationObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';
import _ from 'lodash'; import _ from 'lodash';
import { reflector } from '../utility/metadata'; import { getSpecificFields } from '../utility/metadata';
export interface RestfulFactoryOptions<T> { export interface RestfulFactoryOptions<T> {
fieldsToOmit?: (keyof T)[]; fieldsToOmit?: (keyof T)[];
...@@ -39,16 +39,25 @@ export class RestfulFactory<T> { ...@@ -39,16 +39,25 @@ export class RestfulFactory<T> {
readonly entityArrayReturnMessageDto = PaginatedReturnMessageDto( readonly entityArrayReturnMessageDto = PaginatedReturnMessageDto(
this.entityClass, this.entityClass,
); );
readonly fieldsToOmit: (keyof T)[] = _.uniq([ readonly fieldsToOmit = _.uniq([
...(reflector ...(getSpecificFields(this.entityClass, 'notColumn') as (keyof T)[]),
.getArray('notColumnFields', this.entityClass)
.filter((field) =>
reflector.get('notColumn', this.entityClass, field),
) as (keyof T)[]),
...(this.options.fieldsToOmit || []), ...(this.options.fieldsToOmit || []),
]); ]);
readonly createDto = OmitType(this.entityClass, this.fieldsToOmit); private readonly basicDto = OmitType(
readonly updateDto = PartialType(this.createDto); this.entityClass,
this.fieldsToOmit,
) as ClassType<T>;
readonly createDto = OmitType(
this.entityClass,
getSpecificFields(this.entityClass, 'notWritable') as (keyof T)[],
) as ClassType<T>;
readonly findAllDto = PartialType(this.basicDto) as ClassType<T>;
readonly updateDto = PartialType(
OmitType(
this.createDto,
getSpecificFields(this.entityClass, 'notChangeable') as (keyof T)[],
),
) as ClassType<T>;
// eslint-disable-next-line @typescript-eslint/ban-types // eslint-disable-next-line @typescript-eslint/ban-types
readonly idType: Function = Reflect.getMetadata( readonly idType: Function = Reflect.getMetadata(
'design:type', 'design:type',
......
import { MetadataSetter, Reflector } from "typed-reflector"; import { MetadataSetter, Reflector } from 'typed-reflector';
export interface MetadataArrayMap { export interface SpecificFields {
notColumnFields: string;
}
export interface MetadataMap {
notColumn: boolean; notColumn: boolean;
notWritable: boolean;
notChangeable: boolean;
} }
type MetadataArrayMap = { [K in keyof SpecificFields as `${K}Fields`]: string };
export const Metadata = new MetadataSetter<MetadataMap, MetadataArrayMap>(); export const Metadata = new MetadataSetter<SpecificFields, MetadataArrayMap>();
export const reflector = new Reflector<MetadataMap, MetadataArrayMap>(); export const reflector = new Reflector<SpecificFields, MetadataArrayMap>();
export function getSpecificFields(obj: any, type: keyof SpecificFields) {
return reflector
.getArray(`${type}Fields`, obj)
.filter((field) => reflector.get(type, obj, field));
}
import { plainToInstance } from 'class-transformer'; import { plainToInstance } from 'class-transformer';
import { validateSync } from 'class-validator'; import { validateSync } from 'class-validator';
import { RestfulFactory } from '../src/decorators'; import { RestfulFactory } from '../src/decorators';
import { getSpecificFields } from '../src/utility/metadata';
import { Gender, User, User2 } from './utility/user'; import { Gender, User, User2 } from './utility/user';
describe('entity', () => { describe('entity', () => {
...@@ -23,7 +24,7 @@ describe('entity', () => { ...@@ -23,7 +24,7 @@ describe('entity', () => {
).not.toEqual([]); ).not.toEqual([]);
expect( expect(
validateSync( validateSync(
plainToInstance(User, { name: 'John', age: 20, gender: 'foo' }), plainToInstance(User, { name: 123123, age: 20, gender: Gender.M }),
), ),
).not.toEqual([]); ).not.toEqual([]);
}); });
...@@ -37,7 +38,12 @@ describe('entity', () => { ...@@ -37,7 +38,12 @@ describe('entity', () => {
expect(validateSync(user2)).toEqual([]); expect(validateSync(user2)).toEqual([]);
expect( expect(
validateSync( validateSync(
plainToInstance(User2, { name: 'John', age: 20, gender: Gender.M }), plainToInstance(User2, {
name: 'John',
age: 20,
gender: Gender.M,
createdAt: new Date(),
}),
), ),
).not.toEqual([]); ).not.toEqual([]);
}); });
...@@ -45,5 +51,11 @@ describe('entity', () => { ...@@ -45,5 +51,11 @@ describe('entity', () => {
it('should set omit fields', () => { it('should set omit fields', () => {
const factory = new RestfulFactory(User); const factory = new RestfulFactory(User);
expect(factory.fieldsToOmit.includes('createTime')).toBe(true); expect(factory.fieldsToOmit.includes('createTime')).toBe(true);
expect(getSpecificFields(User, 'notWritable').includes('createdAt')).toBe(
true,
);
expect(getSpecificFields(User, 'notChangeable').includes('gender')).toBe(
true,
);
}); });
}); });
import { Entity, Index } from 'typeorm'; import { Entity, Index } from 'typeorm';
import { EnumColumn, IntColumn, StringColumn } from '../../src/decorators'; import { DateColumn, EnumColumn, IntColumn, NotChangeable, NotColumn, NotWritable, StringColumn } from '../../src/decorators';
import { IdBase, StringIdBase } from '../../src/bases'; import { IdBase, StringIdBase } from '../../src/bases';
export enum Gender { export enum Gender {
...@@ -18,8 +18,16 @@ export class User extends IdBase() { ...@@ -18,8 +18,16 @@ export class User extends IdBase() {
@IntColumn('int', { unsigned: true }) @IntColumn('int', { unsigned: true })
age: number; age: number;
@NotChangeable()
@EnumColumn(Gender) @EnumColumn(Gender)
gender: Gender; gender: Gender;
@NotWritable()
@DateColumn()
createdAt: Date;
@NotColumn()
birthday: Date;
} }
@Entity() @Entity()
...@@ -33,6 +41,14 @@ export class User2 extends StringIdBase({ length: 20 }) { ...@@ -33,6 +41,14 @@ export class User2 extends StringIdBase({ length: 20 }) {
@IntColumn('int', { unsigned: true }) @IntColumn('int', { unsigned: true })
age: number; age: number;
@NotChangeable()
@EnumColumn(Gender) @EnumColumn(Gender)
gender: Gender; gender: Gender;
@NotWritable()
@DateColumn()
createdAt: Date;
@NotColumn()
birthday: Date;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment