| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- import {Service} from '@e22m4u/js-service';
- import {cloneDeep} from '../utils/index.js';
- import {RelationType} from '../definition/index.js';
- import {InvalidArgumentError} from '../errors/index.js';
- import {RepositoryRegistry} from '../repository/index.js';
- import {ModelDefinitionUtils} from '../definition/index.js';
- /**
- * Has one resolver.
- */
- export class HasOneResolver extends Service {
- /**
- * Include to.
- *
- * @param {object[]} entities
- * @param {string} sourceName
- * @param {string} targetName
- * @param {string} relationName
- * @param {string} foreignKey
- * @param {object|undefined} scope
- * @returns {Promise<void>}
- */
- async includeTo(
- entities,
- sourceName,
- targetName,
- relationName,
- foreignKey,
- scope = undefined,
- ) {
- if (!entities || !Array.isArray(entities))
- throw new InvalidArgumentError(
- 'The parameter "entities" of HasOneResolver.includeTo requires ' +
- 'an Array of Object, but %v given.',
- entities,
- );
- if (!sourceName || typeof sourceName !== 'string')
- throw new InvalidArgumentError(
- 'The parameter "sourceName" of HasOneResolver.includeTo requires ' +
- 'a non-empty String, but %v given.',
- sourceName,
- );
- if (!targetName || typeof targetName !== 'string')
- throw new InvalidArgumentError(
- 'The parameter "targetName" of HasOneResolver.includeTo requires ' +
- 'a non-empty String, but %v given.',
- targetName,
- );
- if (!relationName || typeof relationName !== 'string')
- throw new InvalidArgumentError(
- 'The parameter "relationName" of HasOneResolver.includeTo requires ' +
- 'a non-empty String, but %v given.',
- relationName,
- );
- if (!foreignKey || typeof foreignKey !== 'string')
- throw new InvalidArgumentError(
- 'The parameter "foreignKey" of HasOneResolver.includeTo requires ' +
- 'a non-empty String, but %v given.',
- foreignKey,
- );
- if (scope && (typeof scope !== 'object' || Array.isArray(scope)))
- throw new InvalidArgumentError(
- 'The provided parameter "scope" of HasOneResolver.includeTo ' +
- 'should be an Object, but %v given.',
- scope,
- );
- const sourcePkPropName =
- this.getService(ModelDefinitionUtils).getPrimaryKeyAsPropertyName(
- sourceName,
- );
- const sourceIds = [];
- entities.forEach(entity => {
- if (!entity || typeof entity !== 'object' || Array.isArray(entity))
- throw new InvalidArgumentError(
- 'The parameter "entities" of HasOneResolver.includeTo requires ' +
- 'an Array of Object, but %v given.',
- entity,
- );
- const sourceId = entity[sourcePkPropName];
- if (sourceIds.includes(sourceId)) return;
- sourceIds.push(sourceId);
- });
- const promises = [];
- const targetRepository =
- this.getService(RepositoryRegistry).getRepository(targetName);
- scope = scope ? cloneDeep(scope) : {};
- const targetBySourceId = new Map();
- sourceIds.forEach(sourceId => {
- const filter = cloneDeep(scope);
- filter.where = {
- and: [{[foreignKey]: sourceId}, ...(scope.where ? [scope.where] : [])],
- };
- filter.limit = 1;
- promises.push(
- targetRepository.find(filter).then(result => {
- if (result.length) targetBySourceId.set(sourceId, result[0]);
- }),
- );
- });
- await Promise.all(promises);
- Array.from(targetBySourceId.keys()).forEach(sourceId => {
- const sources = entities.filter(v => v[sourcePkPropName] === sourceId);
- sources.forEach(v => (v[relationName] = targetBySourceId.get(sourceId)));
- });
- }
- /**
- * Include polymorphic to.
- *
- * @param {object[]} entities
- * @param {string} sourceName
- * @param {string} targetName
- * @param {string} relationName
- * @param {string} foreignKey
- * @param {string} discriminator
- * @param {object|undefined} scope
- * @returns {Promise<void>}
- */
- async includePolymorphicTo(
- entities,
- sourceName,
- targetName,
- relationName,
- foreignKey,
- discriminator,
- scope = undefined,
- ) {
- if (!entities || !Array.isArray(entities))
- throw new InvalidArgumentError(
- 'The parameter "entities" of HasOneResolver.includePolymorphicTo requires ' +
- 'an Array of Object, but %v given.',
- entities,
- );
- if (!sourceName || typeof sourceName !== 'string')
- throw new InvalidArgumentError(
- 'The parameter "sourceName" of HasOneResolver.includePolymorphicTo requires ' +
- 'a non-empty String, but %v given.',
- sourceName,
- );
- if (!targetName || typeof targetName !== 'string')
- throw new InvalidArgumentError(
- 'The parameter "targetName" of HasOneResolver.includePolymorphicTo requires ' +
- 'a non-empty String, but %v given.',
- targetName,
- );
- if (!relationName || typeof relationName !== 'string')
- throw new InvalidArgumentError(
- 'The parameter "relationName" of HasOneResolver.includePolymorphicTo requires ' +
- 'a non-empty String, but %v given.',
- relationName,
- );
- if (!foreignKey || typeof foreignKey !== 'string')
- throw new InvalidArgumentError(
- 'The parameter "foreignKey" of HasOneResolver.includePolymorphicTo requires ' +
- 'a non-empty String, but %v given.',
- foreignKey,
- );
- if (!discriminator || typeof discriminator !== 'string')
- throw new InvalidArgumentError(
- 'The parameter "discriminator" of HasOneResolver.includePolymorphicTo requires ' +
- 'a non-empty String, but %v given.',
- discriminator,
- );
- if (scope && (typeof scope !== 'object' || Array.isArray(scope)))
- throw new InvalidArgumentError(
- 'The provided parameter "scope" of HasOneResolver.includePolymorphicTo ' +
- 'should be an Object, but %v given.',
- scope,
- );
- const sourcePkPropName =
- this.getService(ModelDefinitionUtils).getPrimaryKeyAsPropertyName(
- sourceName,
- );
- const sourceIds = [];
- entities.forEach(entity => {
- if (!entity || typeof entity !== 'object' || Array.isArray(entity))
- throw new InvalidArgumentError(
- 'The parameter "entities" of HasOneResolver.includePolymorphicTo requires ' +
- 'an Array of Object, but %v given.',
- entity,
- );
- const sourceId = entity[sourcePkPropName];
- if (sourceIds.includes(sourceId)) return;
- sourceIds.push(sourceId);
- });
- const promises = [];
- const targetRepository =
- this.getService(RepositoryRegistry).getRepository(targetName);
- scope = scope ? cloneDeep(scope) : {};
- const targetBySourceId = new Map();
- sourceIds.forEach(sourceId => {
- const filter = cloneDeep(scope);
- filter.where = {
- and: [
- {[foreignKey]: sourceId, [discriminator]: sourceName},
- ...(scope.where ? [scope.where] : []),
- ],
- };
- filter.limit = 1;
- promises.push(
- targetRepository.find(filter).then(result => {
- if (result.length) targetBySourceId.set(sourceId, result[0]);
- }),
- );
- });
- await Promise.all(promises);
- Array.from(targetBySourceId.keys()).forEach(sourceId => {
- const sources = entities.filter(v => v[sourcePkPropName] === sourceId);
- sources.forEach(v => (v[relationName] = targetBySourceId.get(sourceId)));
- });
- }
- /**
- * Include polymorphic by relation name.
- *
- * @param {object[]} entities
- * @param {string} sourceName
- * @param {string} targetName
- * @param {string} relationName
- * @param {string} targetRelationName
- * @param {object|undefined} scope
- * @returns {Promise<void>}
- */
- async includePolymorphicByRelationName(
- entities,
- sourceName,
- targetName,
- relationName,
- targetRelationName,
- scope = undefined,
- ) {
- if (!entities || !Array.isArray(entities))
- throw new InvalidArgumentError(
- 'The parameter "entities" of HasOneResolver.includePolymorphicByRelationName requires ' +
- 'an Array of Object, but %v given.',
- entities,
- );
- if (!sourceName || typeof sourceName !== 'string')
- throw new InvalidArgumentError(
- 'The parameter "sourceName" of HasOneResolver.includePolymorphicByRelationName requires ' +
- 'a non-empty String, but %v given.',
- sourceName,
- );
- if (!targetName || typeof targetName !== 'string')
- throw new InvalidArgumentError(
- 'The parameter "targetName" of HasOneResolver.includePolymorphicByRelationName requires ' +
- 'a non-empty String, but %v given.',
- targetName,
- );
- if (!relationName || typeof relationName !== 'string')
- throw new InvalidArgumentError(
- 'The parameter "relationName" of HasOneResolver.includePolymorphicByRelationName requires ' +
- 'a non-empty String, but %v given.',
- relationName,
- );
- if (!targetRelationName || typeof targetRelationName !== 'string')
- throw new InvalidArgumentError(
- 'The parameter "targetRelationName" of HasOneResolver.includePolymorphicByRelationName requires ' +
- 'a non-empty String, but %v given.',
- targetRelationName,
- );
- if (scope && (typeof scope !== 'object' || Array.isArray(scope)))
- throw new InvalidArgumentError(
- 'The provided parameter "scope" of HasOneResolver.includePolymorphicByRelationName ' +
- 'should be an Object, but %v given.',
- scope,
- );
- const targetRelationDef = this.getService(
- ModelDefinitionUtils,
- ).getRelationDefinitionByName(targetName, targetRelationName);
- if (targetRelationDef.type !== RelationType.BELONGS_TO)
- throw new InvalidArgumentError(
- 'The relation %v of the model %v is a polymorphic "hasOne" relation, ' +
- 'so it requires the target relation %v to be a polymorphic "belongsTo", ' +
- 'but %v type given.',
- relationName,
- sourceName,
- targetRelationName,
- targetRelationDef.type,
- );
- if (!targetRelationDef.polymorphic)
- throw new InvalidArgumentError(
- 'The relation %v of the model %v is a polymorphic "hasOne" relation, ' +
- 'so it requires the target relation %v to be a polymorphic too.',
- relationName,
- sourceName,
- targetRelationName,
- );
- const foreignKey =
- targetRelationDef.foreignKey || `${targetRelationName}Id`;
- const discriminator =
- targetRelationDef.discriminator || `${targetRelationName}Type`;
- return this.includePolymorphicTo(
- entities,
- sourceName,
- targetName,
- relationName,
- foreignKey,
- discriminator,
- scope,
- );
- }
- }
|