Commit 85fd5c0b authored by nanahira's avatar nanahira

clean omit fields in relations

parent 109829e6
...@@ -24,6 +24,7 @@ import { ...@@ -24,6 +24,7 @@ import {
ReturnMessageDto, ReturnMessageDto,
} from 'nesties'; } from 'nesties';
import { getNotInResultFields } from './utility/metadata'; import { getNotInResultFields } from './utility/metadata';
import { getTypeormRelations } from './utility/get-typeorm-relations';
export type EntityId<T extends { id: any }> = T['id']; export type EntityId<T extends { id: any }> = T['id'];
export interface RelationDef { export interface RelationDef {
...@@ -83,15 +84,39 @@ export class CrudBase<T extends ValidCrudEntity<T>> { ...@@ -83,15 +84,39 @@ export class CrudBase<T extends ValidCrudEntity<T>> {
} }
_cleanEntityNotInResultFields(ent: T): T { _cleanEntityNotInResultFields(ent: T): T {
const visited = new Set();
const runSingleObject = (o: any, cl) => {
if (visited.has(o)) {
return o;
}
const fields = getNotInResultFields( const fields = getNotInResultFields(
this.entityClass, cl,
this.crudOptions.keepEntityVersioningDates, this.crudOptions.keepEntityVersioningDates,
); );
for (const field of fields) { for (const field of fields) {
delete (ent as any)[field]; delete o[field];
}
visited.add(o);
const relations = getTypeormRelations(cl);
for (const relation of relations) {
const propertyName = relation.propertyName as string;
if (o[propertyName]) {
if (Array.isArray(o[propertyName])) {
o[propertyName] = o[propertyName].map((r) =>
runSingleObject(r, relation.propertyClass),
);
} else {
o[propertyName] = runSingleObject(
o[propertyName],
relation.propertyClass,
);
}
} }
return ent; }
return o;
};
return runSingleObject(ent, this.entityClass);
} }
cleanEntityNotInResultFields<E extends T | T[]>(ents: E): E { cleanEntityNotInResultFields<E extends T | T[]>(ents: E): E {
...@@ -203,11 +228,8 @@ export class CrudBase<T extends ValidCrudEntity<T>> { ...@@ -203,11 +228,8 @@ export class CrudBase<T extends ValidCrudEntity<T>> {
results = results.concat(savedChunk); results = results.concat(savedChunk);
} }
return { return {
results: this.cleanEntityNotInResultFields(results), results,
skipped: skipped.map((e) => ({ skipped,
...e,
entry: this.cleanEntityNotInResultFields(e.entry),
})),
}; };
} catch (e) { } catch (e) {
this.log.error( this.log.error(
...@@ -448,9 +470,14 @@ export class CrudBase<T extends ValidCrudEntity<T>> { ...@@ -448,9 +470,14 @@ export class CrudBase<T extends ValidCrudEntity<T>> {
} }
async importEntities( async importEntities(
ents: T[], _ents: T[],
extraChecking?: (ent: T) => string | Promise<string>, extraChecking?: (ent: T) => string | Promise<string>,
) { ) {
const ents = _ents.map((ent) => {
const newEnt = new this.entityClass();
Object.assign(newEnt, ent);
return newEnt;
});
const invalidResults = _.compact( const invalidResults = _.compact(
await Promise.all( await Promise.all(
ents.map(async (ent) => { ents.map(async (ent) => {
...@@ -484,6 +511,7 @@ export class CrudBase<T extends ValidCrudEntity<T>> { ...@@ -484,6 +511,7 @@ export class CrudBase<T extends ValidCrudEntity<T>> {
results.map((r) => { results.map((r) => {
const entry = new this.importEntryDto(); const entry = new this.importEntryDto();
Object.assign(entry, r); Object.assign(entry, r);
entry.entry = this.cleanEntityNotInResultFields(r.entry);
return entry; return entry;
}), }),
); );
......
...@@ -35,8 +35,8 @@ import { OperationObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.i ...@@ -35,8 +35,8 @@ import { OperationObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.i
import _ from 'lodash'; import _ from 'lodash';
import { getNotInResultFields, getSpecificFields } from '../utility/metadata'; import { getNotInResultFields, getSpecificFields } from '../utility/metadata';
import { RenameClass } from '../utility/rename-class'; import { RenameClass } from '../utility/rename-class';
import { getMetadataArgsStorage } from 'typeorm';
import { DECORATORS } from '@nestjs/swagger/dist/constants'; import { DECORATORS } from '@nestjs/swagger/dist/constants';
import { getTypeormRelations } from '../utility/get-typeorm-relations';
export interface RestfulFactoryOptions<T> { export interface RestfulFactoryOptions<T> {
fieldsToOmit?: (keyof T)[]; fieldsToOmit?: (keyof T)[];
...@@ -54,9 +54,7 @@ export class RestfulFactory<T> { ...@@ -54,9 +54,7 @@ export class RestfulFactory<T> {
readonly fieldsToOmit = _.uniq([ readonly fieldsToOmit = _.uniq([
...(getSpecificFields(this.entityClass, 'notColumn') as (keyof T)[]), ...(getSpecificFields(this.entityClass, 'notColumn') as (keyof T)[]),
...(this.options.fieldsToOmit || []), ...(this.options.fieldsToOmit || []),
...getMetadataArgsStorage() ...getTypeormRelations(this.entityClass).map((r) => r.propertyName),
.relations.filter((r) => r.target === this.entityClass)
.map((r) => r.propertyName as keyof T),
]); ]);
private readonly basicInputDto = OmitType( private readonly basicInputDto = OmitType(
this.entityClass, this.entityClass,
...@@ -99,18 +97,9 @@ export class RestfulFactory<T> { ...@@ -99,18 +97,9 @@ export class RestfulFactory<T> {
...(this.options.outputFieldsToOmit || []), ...(this.options.outputFieldsToOmit || []),
]); ]);
const resultDto = OmitType(this.entityClass, [...outputFieldsToOmit]); const resultDto = OmitType(this.entityClass, [...outputFieldsToOmit]);
const { relations } = getMetadataArgsStorage(); const relations = getTypeormRelations(this.entityClass);
for (const relation of relations) { for (const relation of relations) {
if ( if (outputFieldsToOmit.has(relation.propertyName)) continue;
outputFieldsToOmit.has(relation.propertyName as keyof T) ||
relation.target !== this.entityClass
)
continue;
const relationClassFactory = relation.type;
// check if it's a callable function
if (typeof relationClassFactory !== 'function') continue;
const relationClass = (relationClassFactory as () => AnyClass)();
if (typeof relationClass !== 'function') continue;
const replace = (useClass: [AnyClass]) => { const replace = (useClass: [AnyClass]) => {
const oldApiProperty = const oldApiProperty =
Reflect.getMetadata( Reflect.getMetadata(
...@@ -118,14 +107,13 @@ export class RestfulFactory<T> { ...@@ -118,14 +107,13 @@ export class RestfulFactory<T> {
this.entityClass.prototype, this.entityClass.prototype,
relation.propertyName, relation.propertyName,
) || {}; ) || {};
const isArray = relation.relationType.endsWith('-many');
ApiProperty({ ApiProperty({
...oldApiProperty, ...oldApiProperty,
required: false, required: false,
type: () => (isArray ? [useClass[0]] : useClass[0]), type: () => (relation.isArray ? [useClass[0]] : useClass[0]),
})(resultDto.prototype, relation.propertyName); })(resultDto.prototype, relation.propertyName);
}; };
const existing = this.__resolveVisited.get(relationClass); const existing = this.__resolveVisited.get(relation.propertyClass);
if (existing) { if (existing) {
replace(existing); replace(existing);
} else { } else {
...@@ -133,17 +121,17 @@ export class RestfulFactory<T> { ...@@ -133,17 +121,17 @@ export class RestfulFactory<T> {
this.__resolveVisited.set(this.entityClass, [null]); this.__resolveVisited.set(this.entityClass, [null]);
} }
const relationFactory = new RestfulFactory( const relationFactory = new RestfulFactory(
relationClass, relation.propertyClass,
{ {
entityClassName: `${this.getEntityClassName()}${ entityClassName: `${this.getEntityClassName()}${
relationClass.name relation.propertyClass.name
}`, }`,
}, },
this.__resolveVisited, this.__resolveVisited,
); );
const relationResultDto = relationFactory.entityResultDto; const relationResultDto = relationFactory.entityResultDto;
replace([relationResultDto]); replace([relationResultDto]);
this.__resolveVisited.set(relationClass, [relationResultDto]); this.__resolveVisited.set(relation.propertyClass, [relationResultDto]);
} }
} }
const res = RenameClass( const res = RenameClass(
......
import { AnyClass, ClassType } from 'nesties';
import { getMetadataArgsStorage } from 'typeorm';
export function getTypeormRelations<T>(cl: ClassType<T>) {
const relations = getMetadataArgsStorage().relations.filter(
(r) => r.target === cl,
);
return relations.map((relation) => {
const isArray = relation.relationType.endsWith('-many');
const relationClassFactory = relation.type;
// check if it's a callable function
let propertyClass: AnyClass;
if (typeof relationClassFactory === 'function') {
const relationClass = (relationClassFactory as () => AnyClass)();
if (typeof relationClass === 'function') {
propertyClass = relationClass;
}
}
if (!propertyClass) {
propertyClass = Reflect.getMetadata(
'design:type',
cl.prototype,
relation.propertyName,
);
}
return {
isArray,
propertyClass,
propertyName: relation.propertyName as keyof T & string,
};
});
}
export function getTypeormRelationsMap<T>(cl: ClassType<T>) {
return Object.fromEntries(
getTypeormRelations(cl).map((r) => [r.propertyName, r]),
) as {
[key in keyof T]: {
isArray: boolean;
propertyClass: ClassType<T[key]>;
propertyName: key;
};
};
}
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