@loopback/repository
Version:
Define and implement a common set of interfaces for interacting with databases
104 lines (97 loc) • 3.88 kB
text/typescript
// Copyright IBM Corp. and LoopBack contributors 2018,2019. All Rights Reserved.
// Node module: @loopback/repository
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
import debugFactory from 'debug';
import {DataObject} from '../../common-types';
import {Entity} from '../../model';
import {EntityCrudRepository} from '../../repositories/repository';
import {
BelongsToDefinition,
Getter,
InclusionResolver,
} from '../relation.types';
import {resolveBelongsToMetadata} from './belongs-to.helpers';
import {createBelongsToInclusionResolver} from './belongs-to.inclusion-resolver';
import {DefaultBelongsToRepository} from './belongs-to.repository';
const debug = debugFactory('loopback:repository:relations:belongs-to:accessor');
export interface BelongsToAccessor<Target extends Entity, SourceId> {
/**
* Invoke the function to obtain HasManyRepository.
*/
(sourceId: SourceId, polymorphicTypes?: string | string[]): Promise<Target>;
/**
* Use `resolver` property to obtain an InclusionResolver for this relation.
*/
inclusionResolver: InclusionResolver<Entity, Target>;
}
/**
* Enforces a BelongsTo constraint on a repository
* If the target model is polymorphic, i.e. stored within different repositories,
* supply the targetRepositoryGetter with a dictionary in the form of {[typeName: string]: repositoryGetter}
*/
export function createBelongsToAccessor<
Target extends Entity,
TargetId,
Source extends Entity,
SourceId,
>(
belongsToMetadata: BelongsToDefinition,
targetRepositoryGetter:
| Getter<EntityCrudRepository<Target, TargetId>>
| {
[repoType: string]: Getter<EntityCrudRepository<Target, TargetId>>;
},
sourceRepository: EntityCrudRepository<Source, SourceId>,
): BelongsToAccessor<Target, SourceId> {
const meta = resolveBelongsToMetadata(belongsToMetadata);
// resolve the repositoryGetter into a dictionary
if (typeof targetRepositoryGetter === 'function') {
targetRepositoryGetter = {
[meta.target().name]: targetRepositoryGetter as Getter<
EntityCrudRepository<Target, TargetId>
>,
};
}
debug('Resolved BelongsTo relation metadata: %o', meta);
const result: BelongsToAccessor<Target, SourceId> =
async function getTargetInstanceOfBelongsTo(
sourceId: SourceId,
polymorphicTypes?: string | string[],
) {
if (meta.polymorphic !== false) {
if (!polymorphicTypes || polymorphicTypes.length === 0) {
console.warn(
'It is highly recommended to specify the polymorphicTypes param when using polymorphic types.',
);
}
}
const foreignKey = meta.keyFrom;
const primaryKey = meta.keyTo;
const sourceModel = await sourceRepository.findById(sourceId);
const foreignKeyValue = sourceModel[foreignKey as keyof Source];
// workaround to check referential integrity.
// should be removed once the memory connector ref integrity is done
// GH issue: https://github.com/loopbackio/loopback-next/issues/2333
if (!foreignKeyValue) {
return undefined as unknown as Target;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const constraint: any = {[primaryKey]: foreignKeyValue};
const constrainedRepo = new DefaultBelongsToRepository(
targetRepositoryGetter as {
[repoType: string]: Getter<EntityCrudRepository<Target, TargetId>>;
},
constraint as DataObject<Target>,
belongsToMetadata.target,
);
return constrainedRepo.get({polymorphicType: polymorphicTypes});
};
result.inclusionResolver = createBelongsToInclusionResolver(
meta,
targetRepositoryGetter as {
[repoType: string]: Getter<EntityCrudRepository<Target, TargetId>>;
},
);
return result;
}