Skip to content

Commit

Permalink
feat: expose resolvers on relation repository factories
Browse files Browse the repository at this point in the history
Signed-off-by: Miroslav Bajtoš <[email protected]>
  • Loading branch information
bajtos committed Jul 19, 2019
1 parent 2f69523 commit e1f6745
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 96 deletions.
10 changes: 7 additions & 3 deletions _SPIKE_.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ Model-specific repository classes (e.g. `CategoryRepository`) should register
inclusion resolvers for model relations, similarly to how we are creating
relation-repository factories now.

To make this process easier, relation-repository factories should provide
`inclusionResolver` property containing the appropriate `InclusionResolver`
implementation.

Conceptually:

```ts
Expand All @@ -166,7 +170,7 @@ export class CategoryRepository extends DefaultCrudRepository {
);

// add this line to register inclusion resolver
this.registerHasManyInclusion('products', this.productRepositoryGetter);
this.registerInclusion('products', this.products.inclusionResolver);
}
}
```
Expand All @@ -186,9 +190,9 @@ error for such relations (rather than a generic "unknown inclusion" error).
this.prohibitInclusion('accessTokens');

// implementation
this.inclusionResolvers.set(
this.registerInclusion(
relationName,
new RejectedInclusionResolver(relationName),
createRejectedInclusionResolver(relationName),
);
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class TodoListImageRepository extends DefaultCrudRepository<
'todoList',
todoListRepositoryGetter,
);
this.registerBelongsToInclusion('todoList', todoListRepositoryGetter);
this.registerInclusion('todoList', this.todoList.inclusionResolver);
}

protected async includeRelatedModels(
Expand Down
4 changes: 2 additions & 2 deletions examples/todo-list/src/repositories/todo-list.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ export class TodoListRepository extends DefaultCrudRepository<
'todos',
todoRepositoryGetter,
);
this.registerHasManyInclusion('todos', this.todoRepositoryGetter);
this.registerInclusion('todos', this.todos.inclusionResolver);

this.image = this.createHasOneRepositoryFactoryFor(
'image',
todoListImageRepositoryGetter,
);
this.registerHasOneInclusion('image', this.todoListImageRepositoryGetter);
this.registerInclusion('image', this.image.inclusionResolver);
}

public findByTitle(title: string) {
Expand Down
2 changes: 1 addition & 1 deletion examples/todo-list/src/repositories/todo.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class TodoRepository extends DefaultCrudRepository<
'todoList',
todoListRepositoryGetter,
);
this.registerBelongsToInclusion('todoList', todoListRepositoryGetter);
this.registerInclusion('todoList', this.todoList.inclusionResolver);
}

protected async includeRelatedModels(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,7 @@ export function retrieveIncludingRelationsSuite(

if (!hasInclusionResolvers(categoryRepo)) {
throw new Error(
`Repository class "${
categoryRepo.constructor.name
}" must provide a public "inclusionResolvers" property`,
`Repository class "${categoryRepo.constructor.name}" must provide a public "inclusionResolvers" property`,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,28 @@ import * as debugFactory from 'debug';
import {DataObject} from '../../common-types';
import {Entity} from '../../model';
import {EntityCrudRepository} from '../../repositories/repository';
import {BelongsToDefinition, Getter} from '../relation.types';
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:belongs-to-accessor');

export type BelongsToAccessor<Target extends Entity, SourceId> = (
sourceId: SourceId,
) => Promise<Target>;
export interface BelongsToAccessor<Target extends Entity, SourceId> {
/**
* Invoke the function to obtain HasManyRepository.
*/
(sourceId: SourceId): Promise<Target>;

/**
* Use `resolver` property to obtain an InclusionResolver for this relation.
*/
inclusionResolver: InclusionResolver;
}

/**
* Enforces a BelongsTo constraint on a repository
Expand All @@ -32,7 +45,10 @@ export function createBelongsToAccessor<
): BelongsToAccessor<Target, SourceId> {
const meta = resolveBelongsToMetadata(belongsToMetadata);
debug('Resolved BelongsTo relation metadata: %o', meta);
return async function getTargetInstanceOfBelongsTo(sourceId: SourceId) {
const result: BelongsToAccessor<
Target,
SourceId
> = async function getTargetInstanceOfBelongsTo(sourceId: SourceId) {
const foreignKey = meta.keyFrom;
const primaryKey = meta.keyTo;
const sourceModel = await sourceRepository.findById(sourceId);
Expand All @@ -45,4 +61,10 @@ export function createBelongsToAccessor<
);
return constrainedRepo.get();
};

result.inclusionResolver = createBelongsToInclusionResolver(
meta,
targetRepoGetter,
);
return result;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,30 @@ import * as debugFactory from 'debug';
import {DataObject} from '../../common-types';
import {Entity} from '../../model';
import {EntityCrudRepository} from '../../repositories/repository';
import {Getter, HasManyDefinition} from '../relation.types';
import {Getter, HasManyDefinition, InclusionResolver} from '../relation.types';
import {resolveHasManyMetadata} from './has-many.helpers';
import {createHasManyInclusionResolver} from './has-many.inclusion-resolver';
import {
DefaultHasManyRepository,
HasManyRepository,
} from './has-many.repository';

const debug = debugFactory('loopback:repository:has-many-repository-factory');

export type HasManyRepositoryFactory<Target extends Entity, ForeignKeyType> = (
fkValue: ForeignKeyType,
) => HasManyRepository<Target>;
export interface HasManyRepositoryFactory<
Target extends Entity,
ForeignKeyType
> {
/**
* Invoke the function to obtain HasManyRepository.
*/
(fkValue: ForeignKeyType): HasManyRepository<Target>;

/**
* Use `resolver` property to obtain an InclusionResolver for this relation.
*/
inclusionResolver: InclusionResolver;
}

/**
* Enforces a constraint on a repository based on a relationship contract
Expand All @@ -43,7 +55,9 @@ export function createHasManyRepositoryFactory<
): HasManyRepositoryFactory<Target, ForeignKeyType> {
const meta = resolveHasManyMetadata(relationMetadata);
debug('Resolved HasMany relation metadata: %o', meta);
return function(fkValue: ForeignKeyType) {
const result: HasManyRepositoryFactory<Target, ForeignKeyType> = function(
fkValue: ForeignKeyType,
) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const constraint: any = {[meta.keyTo]: fkValue};
return new DefaultHasManyRepository<
Expand All @@ -52,4 +66,9 @@ export function createHasManyRepositoryFactory<
EntityCrudRepository<Target, TargetID>
>(targetRepositoryGetter, constraint as DataObject<Target>);
};
result.inclusionResolver = createHasManyInclusionResolver(
meta,
targetRepositoryGetter,
);
return result;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,27 @@ import * as debugFactory from 'debug';
import {DataObject} from '../../common-types';
import {Entity} from '../../model';
import {EntityCrudRepository} from '../../repositories/repository';
import {Getter, HasOneDefinition} from '../relation.types';
import {Getter, HasOneDefinition, InclusionResolver} from '../relation.types';
import {resolveHasOneMetadata} from './has-one.helpers';
import {createHasOneInclusionResolver} from './has-one.inclusion-resolver';
import {DefaultHasOneRepository, HasOneRepository} from './has-one.repository';

const debug = debugFactory('loopback:repository:has-one-repository-factory');

export type HasOneRepositoryFactory<Target extends Entity, ForeignKeyType> = (
fkValue: ForeignKeyType,
) => HasOneRepository<Target>;
export interface HasOneRepositoryFactory<
Target extends Entity,
ForeignKeyType
> {
/**
* Invoke the function to obtain HasManyRepository.
*/
(fkValue: ForeignKeyType): HasOneRepository<Target>;

/**
* Use `resolver` property to obtain an InclusionResolver for this relation.
*/
inclusionResolver: InclusionResolver;
}

/**
* Enforces a constraint on a repository based on a relationship contract
Expand All @@ -40,7 +52,9 @@ export function createHasOneRepositoryFactory<
): HasOneRepositoryFactory<Target, ForeignKeyType> {
const meta = resolveHasOneMetadata(relationMetadata);
debug('Resolved HasOne relation metadata: %o', meta);
return function(fkValue: ForeignKeyType) {
const result: HasOneRepositoryFactory<Target, ForeignKeyType> = function(
fkValue: ForeignKeyType,
) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const constraint: any = {[meta.keyTo]: fkValue};
return new DefaultHasOneRepository<
Expand All @@ -49,4 +63,9 @@ export function createHasOneRepositoryFactory<
EntityCrudRepository<Target, TargetID>
>(targetRepositoryGetter, constraint as DataObject<Target>);
};
result.inclusionResolver = createHasOneInclusionResolver(
meta,
targetRepositoryGetter,
);
return result;
}
75 changes: 4 additions & 71 deletions packages/repository/src/repositories/legacy-juggler-bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ import {
BelongsToAccessor,
BelongsToDefinition,
createBelongsToAccessor,
createBelongsToInclusionResolver,
createHasManyInclusionResolver,
createHasManyRepositoryFactory,
createHasOneRepositoryFactory,
HasManyDefinition,
Expand All @@ -35,7 +33,6 @@ import {
InclusionResolver,
RelationMetadata,
} from '../relations';
import {createHasOneInclusionResolver} from '../relations/has-one/has-one.inclusion-resolver';
import {isTypeResolver, resolveType} from '../type-resolver';
import {EntityCrudRepository, WithCapabilities} from './repository';

Expand Down Expand Up @@ -264,74 +261,12 @@ export class DefaultCrudRepository<

/**
* TODO - add docs
* @param relationName
* @param targetRepoGetter
*/
protected registerHasManyInclusion<
Target extends Entity,
TargetID,
TargetRelations extends object
>(
relationName: string,
targetRepoGetter: Getter<
EntityCrudRepository<Target, TargetID, TargetRelations>
>,
) {
this.inclusionResolvers.set(
relationName,
createHasManyInclusionResolver(
this.getRelationDefinition(relationName) as HasManyDefinition,
targetRepoGetter,
),
);
}

/**
* TODO - add docs
* @param relationName
* @param targetRepoGetter
*/
protected registerHasOneInclusion<
Target extends Entity,
TargetID,
TargetRelations extends object
>(
protected registerInclusion(
relationName: string,
targetRepoGetter: Getter<
EntityCrudRepository<Target, TargetID, TargetRelations>
>,
resolver: InclusionResolver,
) {
this.inclusionResolvers.set(
relationName,
createHasOneInclusionResolver(
this.getRelationDefinition(relationName) as HasOneDefinition,
targetRepoGetter,
),
);
}

/**
* TODO - add docs
* @param relationName
* @param targetRepoGetter
*/
protected registerBelongsToInclusion<
Target extends Entity,
TargetID,
TargetRelations extends object
>(
relationName: string,
targetRepoGetter: Getter<
EntityCrudRepository<Target, TargetID, TargetRelations>
>,
) {
this.inclusionResolvers.set(
relationName,
createBelongsToInclusionResolver(
this.getRelationDefinition(relationName) as BelongsToDefinition,
targetRepoGetter,
),
);
this.inclusionResolvers.set(relationName, resolver);
}

protected getRelationDefinition(relationName: string): RelationMetadata {
Expand Down Expand Up @@ -609,9 +544,7 @@ export class DefaultCrudRepository<
if (relName in data) {
if (options.relations === 'throw') {
throw new Error(
`Navigational properties are not allowed in model data (model "${
this.entityClass.modelName
}" property "${relName}")`,
`Navigational properties are not allowed in model data (model "${this.entityClass.modelName}" property "${relName}")`,
);
}

Expand Down

0 comments on commit e1f6745

Please sign in to comment.