import { EntityDTO } from './types' /** * Base CRUD conversion service from which all generated conversion services inherit * @template Entity The entity class to perform the conversions on */ export abstract class CrudConversionService { /** * Blacklisted non-relation properties * All properties listed here are arrays that are *not* relations * See the `.dynamicEntityToDto()` method of this class for more informations */ private readonly _blacklisted: Array = [] /** * Generate a DTO from the primary properties of an entity and its relations * NOTE: This method has a notable performance cost * @param entity An entity object * @returns A promise holding the related DTO */ async dynamicEntityToDto(entity: Entity): Promise> { /** The entity DTO (initially empty) */ const dto: { [K in keyof Entity]: unknown } = {} as any // Filter and map all entries of the original entity for (const [prop, value] of Object.entries(entity) as Array<[keyof Entity, any]>) { // Keep primitive fields if ( value === undefined || value === null || value === false || value === true || value.constructor === Number || value.constructor === String ) { dto[prop] = value } // Map relations to simple arrays of identifiers // We check if a prop is a relation by looking at its type: if it's an array, then it's considered a relation. else if (Array.isArray(value) && !this._blacklisted.includes(prop)) { // But this check is only because we have no way to ensure an empty array will contain a relation // If the array contains data, and items are not all objects with a string "id" property, then it's not a relation! // In such case, we "blacklist" the property to ensure we won't consider it a relation anymore if (value.length !== 0 && (!value[0] || typeof value[0].id !== 'string')) { this._blacklisted.push(prop) } else { // Else, the array is considered a relation and so map it to an array of string identifiers dto[prop] = value.map(entity => entity.id) } } } // Hurra! return dto as any } }