@loopback/repository 4.1.2 → 5.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/dist/connectors/connector.js +1 -1
- package/dist/connectors/connector.js.map +1 -1
- package/dist/connectors/crud.connector.js +1 -1
- package/dist/connectors/crud.connector.js.map +1 -1
- package/dist/connectors/index.js +1 -1
- package/dist/connectors/index.js.map +1 -1
- package/dist/connectors/kv.connector.js +1 -1
- package/dist/connectors/kv.connector.js.map +1 -1
- package/dist/datasource.js +1 -1
- package/dist/datasource.js.map +1 -1
- package/dist/decorators/index.js +1 -1
- package/dist/decorators/index.js.map +1 -1
- package/dist/decorators/metadata.js +1 -1
- package/dist/decorators/metadata.js.map +1 -1
- package/dist/decorators/model.decorator.js +1 -1
- package/dist/decorators/model.decorator.js.map +1 -1
- package/dist/decorators/repository.decorator.js +1 -1
- package/dist/decorators/repository.decorator.js.map +1 -1
- package/dist/define-model-class.js +1 -1
- package/dist/define-model-class.js.map +1 -1
- package/dist/define-repository-class.js +1 -1
- package/dist/define-repository-class.js.map +1 -1
- package/dist/errors/entity-not-found.error.js +1 -1
- package/dist/errors/entity-not-found.error.js.map +1 -1
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.js +2 -1
- package/dist/errors/index.js.map +1 -1
- package/dist/errors/invalid-polymorphism.error.d.ts +5 -0
- package/dist/errors/invalid-polymorphism.error.js +22 -0
- package/dist/errors/invalid-polymorphism.error.js.map +1 -0
- package/dist/errors/invalid-relation.error.d.ts +1 -1
- package/dist/errors/invalid-relation.error.js +1 -1
- package/dist/errors/invalid-relation.error.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mixins/index.js +1 -1
- package/dist/mixins/index.js.map +1 -1
- package/dist/mixins/repository.mixin.d.ts +49 -50
- package/dist/mixins/repository.mixin.js +1 -1
- package/dist/mixins/repository.mixin.js.map +1 -1
- package/dist/model.d.ts +7 -1
- package/dist/model.js +15 -1
- package/dist/model.js.map +1 -1
- package/dist/relations/belongs-to/belongs-to.accessor.d.ts +6 -2
- package/dist/relations/belongs-to/belongs-to.accessor.js +19 -6
- package/dist/relations/belongs-to/belongs-to.accessor.js.map +1 -1
- package/dist/relations/belongs-to/belongs-to.decorator.js +1 -1
- package/dist/relations/belongs-to/belongs-to.decorator.js.map +1 -1
- package/dist/relations/belongs-to/belongs-to.helpers.d.ts +3 -0
- package/dist/relations/belongs-to/belongs-to.helpers.js +33 -9
- package/dist/relations/belongs-to/belongs-to.helpers.js.map +1 -1
- package/dist/relations/belongs-to/belongs-to.inclusion-resolver.d.ts +5 -2
- package/dist/relations/belongs-to/belongs-to.inclusion-resolver.js +82 -9
- package/dist/relations/belongs-to/belongs-to.inclusion-resolver.js.map +1 -1
- package/dist/relations/belongs-to/belongs-to.repository.d.ts +28 -5
- package/dist/relations/belongs-to/belongs-to.repository.js +50 -11
- package/dist/relations/belongs-to/belongs-to.repository.js.map +1 -1
- package/dist/relations/belongs-to/index.d.ts +2 -2
- package/dist/relations/belongs-to/index.js +3 -3
- package/dist/relations/belongs-to/index.js.map +1 -1
- package/dist/relations/has-many/has-many-through.helpers.d.ts +3 -0
- package/dist/relations/has-many/has-many-through.helpers.js +26 -2
- package/dist/relations/has-many/has-many-through.helpers.js.map +1 -1
- package/dist/relations/has-many/has-many-through.inclusion-resolver.d.ts +3 -1
- package/dist/relations/has-many/has-many-through.inclusion-resolver.js +93 -22
- package/dist/relations/has-many/has-many-through.inclusion-resolver.js.map +1 -1
- package/dist/relations/has-many/has-many-through.repository-factory.d.ts +3 -1
- package/dist/relations/has-many/has-many-through.repository-factory.js +8 -2
- package/dist/relations/has-many/has-many-through.repository-factory.js.map +1 -1
- package/dist/relations/has-many/has-many-through.repository.d.ts +66 -11
- package/dist/relations/has-many/has-many-through.repository.js +212 -36
- package/dist/relations/has-many/has-many-through.repository.js.map +1 -1
- package/dist/relations/has-many/has-many.decorator.js +1 -1
- package/dist/relations/has-many/has-many.decorator.js.map +1 -1
- package/dist/relations/has-many/has-many.helpers.js +1 -1
- package/dist/relations/has-many/has-many.helpers.js.map +1 -1
- package/dist/relations/has-many/has-many.inclusion-resolver.js +1 -1
- package/dist/relations/has-many/has-many.inclusion-resolver.js.map +1 -1
- package/dist/relations/has-many/has-many.repository-factory.js +1 -1
- package/dist/relations/has-many/has-many.repository-factory.js.map +1 -1
- package/dist/relations/has-many/has-many.repository.js +1 -1
- package/dist/relations/has-many/has-many.repository.js.map +1 -1
- package/dist/relations/has-many/index.d.ts +2 -2
- package/dist/relations/has-many/index.js +3 -3
- package/dist/relations/has-many/index.js.map +1 -1
- package/dist/relations/has-one/has-one.decorator.js +1 -1
- package/dist/relations/has-one/has-one.decorator.js.map +1 -1
- package/dist/relations/has-one/has-one.helpers.d.ts +3 -0
- package/dist/relations/has-one/has-one.helpers.js +35 -9
- package/dist/relations/has-one/has-one.helpers.js.map +1 -1
- package/dist/relations/has-one/has-one.inclusion-resolver.d.ts +6 -3
- package/dist/relations/has-one/has-one.inclusion-resolver.js +83 -8
- package/dist/relations/has-one/has-one.inclusion-resolver.js.map +1 -1
- package/dist/relations/has-one/has-one.repository-factory.d.ts +9 -3
- package/dist/relations/has-one/has-one.repository-factory.js +14 -4
- package/dist/relations/has-one/has-one.repository-factory.js.map +1 -1
- package/dist/relations/has-one/has-one.repository.d.ts +58 -11
- package/dist/relations/has-one/has-one.repository.js +119 -16
- package/dist/relations/has-one/has-one.repository.js.map +1 -1
- package/dist/relations/has-one/index.js +1 -1
- package/dist/relations/has-one/index.js.map +1 -1
- package/dist/relations/index.d.ts +2 -0
- package/dist/relations/index.js +3 -1
- package/dist/relations/index.js.map +1 -1
- package/dist/relations/references-many/index.d.ts +4 -0
- package/dist/relations/references-many/index.js +12 -0
- package/dist/relations/references-many/index.js.map +1 -0
- package/dist/relations/references-many/references-many.accessor.d.ts +17 -0
- package/dist/relations/references-many/references-many.accessor.js +40 -0
- package/dist/relations/references-many/references-many.accessor.js.map +1 -0
- package/dist/relations/references-many/references-many.decorator.d.ts +11 -0
- package/dist/relations/references-many/references-many.decorator.js +73 -0
- package/dist/relations/references-many/references-many.decorator.js.map +1 -0
- package/dist/relations/references-many/references-many.helpers.d.ts +17 -0
- package/dist/relations/references-many/references-many.helpers.js +63 -0
- package/dist/relations/references-many/references-many.helpers.js.map +1 -0
- package/dist/relations/references-many/references-many.inclusion-resolver.d.ts +14 -0
- package/dist/relations/references-many/references-many.inclusion-resolver.js +42 -0
- package/dist/relations/references-many/references-many.inclusion-resolver.js.map +1 -0
- package/dist/relations/references-many/references-many.repository.d.ts +28 -0
- package/dist/relations/references-many/references-many.repository.js +33 -0
- package/dist/relations/references-many/references-many.repository.js.map +1 -0
- package/dist/relations/relation.decorator.d.ts +0 -6
- package/dist/relations/relation.decorator.js +2 -14
- package/dist/relations/relation.decorator.js.map +1 -1
- package/dist/relations/relation.filter.solver.d.ts +2 -0
- package/dist/relations/relation.filter.solver.js +57 -0
- package/dist/relations/relation.filter.solver.js.map +1 -0
- package/dist/relations/relation.helpers.js +18 -3
- package/dist/relations/relation.helpers.js.map +1 -1
- package/dist/relations/relation.types.d.ts +52 -1
- package/dist/relations/relation.types.js +1 -1
- package/dist/relations/relation.types.js.map +1 -1
- package/dist/repositories/constraint-utils.js +1 -1
- package/dist/repositories/constraint-utils.js.map +1 -1
- package/dist/repositories/index.d.ts +2 -2
- package/dist/repositories/index.js +3 -3
- package/dist/repositories/index.js.map +1 -1
- package/dist/repositories/kv.repository.bridge.js +1 -1
- package/dist/repositories/kv.repository.bridge.js.map +1 -1
- package/dist/repositories/kv.repository.d.ts +2 -2
- package/dist/repositories/kv.repository.js +1 -1
- package/dist/repositories/kv.repository.js.map +1 -1
- package/dist/repositories/legacy-juggler-bridge.d.ts +36 -9
- package/dist/repositories/legacy-juggler-bridge.js +38 -16
- package/dist/repositories/legacy-juggler-bridge.js.map +1 -1
- package/dist/repositories/repository.js +1 -1
- package/dist/repositories/repository.js.map +1 -1
- package/dist/type-resolver.js +1 -1
- package/dist/type-resolver.js.map +1 -1
- package/dist/types/any.js +1 -1
- package/dist/types/any.js.map +1 -1
- package/dist/types/array.js +1 -1
- package/dist/types/array.js.map +1 -1
- package/dist/types/boolean.js +1 -1
- package/dist/types/boolean.js.map +1 -1
- package/dist/types/buffer.js +1 -1
- package/dist/types/buffer.js.map +1 -1
- package/dist/types/date.js +1 -1
- package/dist/types/date.js.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/model.js +1 -1
- package/dist/types/model.js.map +1 -1
- package/dist/types/null.js +1 -1
- package/dist/types/null.js.map +1 -1
- package/dist/types/number.js +1 -1
- package/dist/types/number.js.map +1 -1
- package/dist/types/object.js +1 -1
- package/dist/types/object.js.map +1 -1
- package/dist/types/string.js +1 -1
- package/dist/types/string.js.map +1 -1
- package/dist/types/type.js +1 -1
- package/dist/types/type.js.map +1 -1
- package/dist/types/union.js +1 -1
- package/dist/types/union.js.map +1 -1
- package/package.json +15 -15
- package/src/connectors/connector.ts +1 -1
- package/src/connectors/crud.connector.ts +1 -1
- package/src/connectors/index.ts +1 -1
- package/src/connectors/kv.connector.ts +1 -1
- package/src/datasource.ts +1 -1
- package/src/decorators/index.ts +1 -1
- package/src/decorators/metadata.ts +1 -1
- package/src/decorators/model.decorator.ts +1 -1
- package/src/decorators/repository.decorator.ts +1 -1
- package/src/define-model-class.ts +1 -1
- package/src/define-repository-class.ts +1 -1
- package/src/errors/entity-not-found.error.ts +1 -1
- package/src/errors/index.ts +2 -1
- package/src/errors/invalid-polymorphism.error.ts +28 -0
- package/src/errors/invalid-relation.error.ts +2 -2
- package/src/index.ts +1 -1
- package/src/mixins/index.ts +1 -1
- package/src/mixins/repository.mixin.ts +1 -6
- package/src/model.ts +20 -1
- package/src/relations/belongs-to/belongs-to.accessor.ts +36 -7
- package/src/relations/belongs-to/belongs-to.decorator.ts +2 -2
- package/src/relations/belongs-to/belongs-to.helpers.ts +37 -10
- package/src/relations/belongs-to/belongs-to.inclusion-resolver.ts +109 -18
- package/src/relations/belongs-to/belongs-to.repository.ts +78 -18
- package/src/relations/belongs-to/index.ts +3 -3
- package/src/relations/has-many/has-many-through.helpers.ts +28 -2
- package/src/relations/has-many/has-many-through.inclusion-resolver.ts +118 -27
- package/src/relations/has-many/has-many-through.repository-factory.ts +22 -4
- package/src/relations/has-many/has-many-through.repository.ts +344 -77
- package/src/relations/has-many/has-many.decorator.ts +1 -1
- package/src/relations/has-many/has-many.helpers.ts +1 -1
- package/src/relations/has-many/has-many.inclusion-resolver.ts +1 -1
- package/src/relations/has-many/has-many.repository-factory.ts +1 -1
- package/src/relations/has-many/has-many.repository.ts +1 -1
- package/src/relations/has-many/index.ts +3 -3
- package/src/relations/has-one/has-one.decorator.ts +1 -1
- package/src/relations/has-one/has-one.helpers.ts +41 -14
- package/src/relations/has-one/has-one.inclusion-resolver.ts +110 -16
- package/src/relations/has-one/has-one.repository-factory.ts +35 -7
- package/src/relations/has-one/has-one.repository.ts +189 -36
- package/src/relations/has-one/index.ts +1 -1
- package/src/relations/index.ts +3 -1
- package/src/relations/references-many/index.ts +9 -0
- package/src/relations/references-many/references-many.accessor.ts +76 -0
- package/src/relations/references-many/references-many.decorator.ts +100 -0
- package/src/relations/references-many/references-many.helpers.ts +82 -0
- package/src/relations/references-many/references-many.inclusion-resolver.ts +80 -0
- package/src/relations/references-many/references-many.repository.ts +55 -0
- package/src/relations/relation.decorator.ts +1 -13
- package/src/relations/relation.filter.solver.ts +56 -0
- package/src/relations/relation.helpers.ts +18 -3
- package/src/relations/relation.types.ts +52 -1
- package/src/repositories/constraint-utils.ts +1 -1
- package/src/repositories/index.ts +3 -3
- package/src/repositories/kv.repository.bridge.ts +1 -1
- package/src/repositories/kv.repository.ts +3 -3
- package/src/repositories/legacy-juggler-bridge.ts +80 -16
- package/src/repositories/repository.ts +1 -1
- package/src/type-resolver.ts +1 -1
- package/src/types/any.ts +1 -1
- package/src/types/array.ts +1 -1
- package/src/types/boolean.ts +1 -1
- package/src/types/buffer.ts +1 -1
- package/src/types/date.ts +1 -1
- package/src/types/index.ts +1 -1
- package/src/types/model.ts +1 -1
- package/src/types/null.ts +1 -1
- package/src/types/number.ts +1 -1
- package/src/types/object.ts +1 -1
- package/src/types/string.ts +1 -1
- package/src/types/type.ts +1 -1
- package/src/types/union.ts +1 -1
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/repository
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
import {DecoratorFactory, MetadataInspector} from '@loopback/core';
|
|
7
|
+
import {property} from '../../decorators';
|
|
8
|
+
import {Entity, EntityResolver, PropertyDefinition} from '../../model';
|
|
9
|
+
import {relation} from '../relation.decorator';
|
|
10
|
+
import {ReferencesManyDefinition, RelationType} from '../relation.types';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Decorator for referencesMany
|
|
14
|
+
* @param targetResolver - A resolver function that returns the target model for
|
|
15
|
+
* a referencesMany relation
|
|
16
|
+
* @param definition - Optional metadata for setting up a referencesMany relation
|
|
17
|
+
* @param propertyDefinition - Optional metadata for setting up the property
|
|
18
|
+
* @returns A property decorator
|
|
19
|
+
*/
|
|
20
|
+
export function referencesMany<T extends Entity>(
|
|
21
|
+
targetResolver: EntityResolver<T>,
|
|
22
|
+
definition?: Partial<ReferencesManyDefinition>,
|
|
23
|
+
propertyDefinition?: Partial<PropertyDefinition>,
|
|
24
|
+
) {
|
|
25
|
+
return function (decoratedTarget: Entity, decoratedKey: string) {
|
|
26
|
+
const propType =
|
|
27
|
+
MetadataInspector.getDesignTypeForProperty(
|
|
28
|
+
decoratedTarget,
|
|
29
|
+
decoratedKey,
|
|
30
|
+
) ?? propertyDefinition?.type;
|
|
31
|
+
|
|
32
|
+
if (!propType) {
|
|
33
|
+
const fullPropName = DecoratorFactory.getTargetName(
|
|
34
|
+
decoratedTarget,
|
|
35
|
+
decoratedKey,
|
|
36
|
+
);
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Cannot infer type of model property ${fullPropName} because ` +
|
|
39
|
+
'TypeScript compiler option `emitDecoratorMetadata` is not set. ' +
|
|
40
|
+
'Please enable `emitDecoratorMetadata` or use the third argument of ' +
|
|
41
|
+
'`@referencesMany` decorator to specify the property type explicitly.',
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const sourceKeyType = MetadataInspector.getDesignTypeForProperty(
|
|
46
|
+
targetResolver().prototype,
|
|
47
|
+
definition?.keyTo ?? 'id',
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
if (!sourceKeyType) {
|
|
51
|
+
const fullPropName = DecoratorFactory.getTargetName(
|
|
52
|
+
targetResolver().prototype,
|
|
53
|
+
definition?.keyTo ?? 'id',
|
|
54
|
+
);
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Cannot infer type of model property ${fullPropName} because ` +
|
|
57
|
+
'TypeScript compiler option `emitDecoratorMetadata` is not set. ' +
|
|
58
|
+
'Please enable `emitDecoratorMetadata` or use the second argument of ' +
|
|
59
|
+
'`@referencesMany` decorator to specify the property type explicitly.',
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const propMeta: PropertyDefinition = Object.assign(
|
|
64
|
+
{},
|
|
65
|
+
// properties provided by the caller
|
|
66
|
+
propertyDefinition,
|
|
67
|
+
// properties enforced by the decorator
|
|
68
|
+
{
|
|
69
|
+
type: propType,
|
|
70
|
+
itemType: sourceKeyType,
|
|
71
|
+
// TODO(bajtos) Make the foreign key required once our REST API layer
|
|
72
|
+
// allows controller methods to exclude required properties
|
|
73
|
+
// required: true,
|
|
74
|
+
},
|
|
75
|
+
);
|
|
76
|
+
property(propMeta)(decoratedTarget, decoratedKey);
|
|
77
|
+
|
|
78
|
+
// @referencesMany() is typically decorating the foreign key property,
|
|
79
|
+
// e.g. customerIds. We need to strip the trailing "Ids" suffix from the name.
|
|
80
|
+
const relationName = decoratedKey.replace(/Ids$/, 's');
|
|
81
|
+
|
|
82
|
+
const meta: ReferencesManyDefinition = Object.assign(
|
|
83
|
+
// default values, can be customized by the caller
|
|
84
|
+
{
|
|
85
|
+
keyFrom: decoratedKey,
|
|
86
|
+
name: relationName,
|
|
87
|
+
},
|
|
88
|
+
// properties provided by the caller
|
|
89
|
+
definition,
|
|
90
|
+
// properties enforced by the decorator
|
|
91
|
+
{
|
|
92
|
+
type: RelationType.referencesMany,
|
|
93
|
+
targetsMany: true,
|
|
94
|
+
source: decoratedTarget.constructor,
|
|
95
|
+
target: targetResolver,
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
relation(meta)(decoratedTarget, decoratedKey);
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2019. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/repository
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
import debugFactory from 'debug';
|
|
7
|
+
import {camelCase} from 'lodash';
|
|
8
|
+
import {InvalidRelationError} from '../../errors';
|
|
9
|
+
import {isTypeResolver} from '../../type-resolver';
|
|
10
|
+
import {ReferencesManyDefinition, RelationType} from '../relation.types';
|
|
11
|
+
|
|
12
|
+
const debug = debugFactory(
|
|
13
|
+
'loopback:repository:relations:references-many:helpers',
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Relation definition with optional metadata (e.g. `keyTo`) filled in.
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
export type ReferencesManyResolvedDefinition = ReferencesManyDefinition & {
|
|
21
|
+
keyFrom: string;
|
|
22
|
+
keyTo: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Resolves given referencesMany metadata if target is specified to be a resolver.
|
|
27
|
+
* Mainly used to infer what the `keyTo` property should be from the target's
|
|
28
|
+
* property id metadata
|
|
29
|
+
* @param relationMeta - referencesMany metadata to resolve
|
|
30
|
+
* @internal
|
|
31
|
+
*/
|
|
32
|
+
export function resolveReferencesManyMetadata(
|
|
33
|
+
relationMeta: ReferencesManyDefinition,
|
|
34
|
+
) {
|
|
35
|
+
if ((relationMeta.type as RelationType) !== RelationType.referencesMany) {
|
|
36
|
+
const reason = 'relation type must be ReferencesMany';
|
|
37
|
+
throw new InvalidRelationError(reason, relationMeta);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!isTypeResolver(relationMeta.target)) {
|
|
41
|
+
const reason = 'target must be a type resolver';
|
|
42
|
+
throw new InvalidRelationError(reason, relationMeta);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const sourceModel = relationMeta.source;
|
|
46
|
+
if (!sourceModel || !sourceModel.modelName) {
|
|
47
|
+
const reason = 'source model must be defined';
|
|
48
|
+
throw new InvalidRelationError(reason, relationMeta);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const targetModel = relationMeta.target();
|
|
52
|
+
const targetName = targetModel.modelName;
|
|
53
|
+
debug('Resolved model %s from given metadata: %o', targetName, targetModel);
|
|
54
|
+
|
|
55
|
+
let keyFrom;
|
|
56
|
+
if (
|
|
57
|
+
relationMeta.keyFrom &&
|
|
58
|
+
relationMeta.source.definition.properties[relationMeta.keyFrom]
|
|
59
|
+
) {
|
|
60
|
+
keyFrom = relationMeta.keyFrom;
|
|
61
|
+
} else {
|
|
62
|
+
keyFrom = camelCase(targetName + '_ids');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const targetProperties = targetModel.definition.properties;
|
|
66
|
+
debug('relation metadata from %o: %o', targetName, targetProperties);
|
|
67
|
+
|
|
68
|
+
if (relationMeta.keyTo && targetProperties[relationMeta.keyTo]) {
|
|
69
|
+
// The explicit cast is needed because of a limitation of type inference
|
|
70
|
+
return Object.assign(relationMeta, {
|
|
71
|
+
keyFrom,
|
|
72
|
+
}) as ReferencesManyResolvedDefinition;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const targetPrimaryKey = targetModel.definition.idProperties()[0];
|
|
76
|
+
if (!targetPrimaryKey) {
|
|
77
|
+
const reason = `${targetName} does not have any primary key (id property)`;
|
|
78
|
+
throw new InvalidRelationError(reason, relationMeta);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return Object.assign(relationMeta, {keyFrom, keyTo: targetPrimaryKey});
|
|
82
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/repository
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
import {Filter, InclusionFilter} from '@loopback/filter';
|
|
7
|
+
import {AnyObject, Options} from '../../common-types';
|
|
8
|
+
import {Entity} from '../../model';
|
|
9
|
+
import {EntityCrudRepository} from '../../repositories';
|
|
10
|
+
import {
|
|
11
|
+
deduplicate,
|
|
12
|
+
findByForeignKeys,
|
|
13
|
+
flattenTargetsOfOneToOneRelation,
|
|
14
|
+
StringKeyOf,
|
|
15
|
+
} from '../relation.helpers';
|
|
16
|
+
import {
|
|
17
|
+
Getter,
|
|
18
|
+
InclusionResolver,
|
|
19
|
+
ReferencesManyDefinition,
|
|
20
|
+
} from '../relation.types';
|
|
21
|
+
import {resolveReferencesManyMetadata} from './references-many.helpers';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates InclusionResolver for ReferencesMany relation.
|
|
25
|
+
* Notice that this function only generates the inclusionResolver.
|
|
26
|
+
* It doesn't register it for the source repository.
|
|
27
|
+
*
|
|
28
|
+
* Notice: scope field for inclusion is not supported yet
|
|
29
|
+
*
|
|
30
|
+
* @param meta - resolved ReferencesManyMetadata
|
|
31
|
+
* @param getTargetRepo - target repository i.e where related instances are
|
|
32
|
+
*/
|
|
33
|
+
export function createReferencesManyInclusionResolver<
|
|
34
|
+
Target extends Entity,
|
|
35
|
+
TargetIds,
|
|
36
|
+
TargetRelations extends object,
|
|
37
|
+
>(
|
|
38
|
+
meta: ReferencesManyDefinition,
|
|
39
|
+
getTargetRepo: Getter<
|
|
40
|
+
EntityCrudRepository<Target, TargetIds, TargetRelations>
|
|
41
|
+
>,
|
|
42
|
+
): InclusionResolver<Entity, Target> {
|
|
43
|
+
const relationMeta = resolveReferencesManyMetadata(meta);
|
|
44
|
+
|
|
45
|
+
return async function fetchIncludedModels(
|
|
46
|
+
entities: Entity[],
|
|
47
|
+
inclusion: InclusionFilter,
|
|
48
|
+
options?: Options,
|
|
49
|
+
): Promise<(Target & TargetRelations)[][]> {
|
|
50
|
+
if (!entities.length) return [];
|
|
51
|
+
|
|
52
|
+
const sourceKey = relationMeta.keyFrom;
|
|
53
|
+
const sourceMap = entities.map(e => (e as AnyObject)[sourceKey]);
|
|
54
|
+
const sourceIds = sourceMap.flat();
|
|
55
|
+
const targetKey = relationMeta.keyTo as StringKeyOf<Target>;
|
|
56
|
+
const dedupedSourceIds = deduplicate(sourceIds);
|
|
57
|
+
|
|
58
|
+
const scope =
|
|
59
|
+
typeof inclusion === 'string' ? {} : (inclusion.scope as Filter<Target>);
|
|
60
|
+
|
|
61
|
+
const targetRepo = await getTargetRepo();
|
|
62
|
+
const targetsFound = await findByForeignKeys(
|
|
63
|
+
targetRepo,
|
|
64
|
+
targetKey,
|
|
65
|
+
dedupedSourceIds.filter(e => e),
|
|
66
|
+
scope,
|
|
67
|
+
options,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
return sourceMap.map(chainIds => {
|
|
71
|
+
if (!chainIds) return [];
|
|
72
|
+
const targets = flattenTargetsOfOneToOneRelation(
|
|
73
|
+
chainIds,
|
|
74
|
+
targetsFound,
|
|
75
|
+
targetKey,
|
|
76
|
+
);
|
|
77
|
+
return targets.filter((v): v is Target & TargetRelations => v != null);
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/repository
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
import {Getter} from '@loopback/core';
|
|
7
|
+
import {DataObject, Options} from '../../common-types';
|
|
8
|
+
import {EntityNotFoundError} from '../../errors';
|
|
9
|
+
import {Entity} from '../../model';
|
|
10
|
+
import {constrainFilter, EntityCrudRepository} from '../../repositories';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* CRUD operations for a target repository of a ReferencesMany relation
|
|
14
|
+
*/
|
|
15
|
+
export interface ReferencesManyRepository<Target extends Entity> {
|
|
16
|
+
/**
|
|
17
|
+
* Gets the target model instance
|
|
18
|
+
* @param options
|
|
19
|
+
* @returns A promise resolved with the target object or rejected
|
|
20
|
+
* with an EntityNotFoundError when target model instance was not found.
|
|
21
|
+
*/
|
|
22
|
+
get(options?: Options): Promise<Target>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class DefaultReferencesManyRepository<
|
|
26
|
+
TargetEntity extends Entity,
|
|
27
|
+
TargetIds,
|
|
28
|
+
TargetRepository extends EntityCrudRepository<TargetEntity, TargetIds>,
|
|
29
|
+
> implements ReferencesManyRepository<TargetEntity>
|
|
30
|
+
{
|
|
31
|
+
/**
|
|
32
|
+
* Constructor of DefaultReferencesManyEntityCrudRepository
|
|
33
|
+
* @param getTargetRepository - the getter of the related target model repository instance
|
|
34
|
+
* @param constraint - the key value pair representing foreign key name to constrain
|
|
35
|
+
* the target repository instance
|
|
36
|
+
*/
|
|
37
|
+
constructor(
|
|
38
|
+
public getTargetRepository: Getter<TargetRepository>,
|
|
39
|
+
public constraint: DataObject<TargetEntity>,
|
|
40
|
+
) {}
|
|
41
|
+
|
|
42
|
+
async get(options?: Options): Promise<TargetEntity> {
|
|
43
|
+
const targetRepo = await this.getTargetRepository();
|
|
44
|
+
const result = await targetRepo.find(
|
|
45
|
+
constrainFilter(undefined, this.constraint),
|
|
46
|
+
options,
|
|
47
|
+
);
|
|
48
|
+
if (!result.length) {
|
|
49
|
+
// We don't have a direct access to the foreign key value here :(
|
|
50
|
+
const id = 'constraint ' + JSON.stringify(this.constraint);
|
|
51
|
+
throw new EntityNotFoundError(targetRepo.entityClass, id);
|
|
52
|
+
}
|
|
53
|
+
return result[0];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright IBM Corp. 2018,2020. All Rights Reserved.
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
|
|
2
2
|
// Node module: @loopback/repository
|
|
3
3
|
// This file is licensed under the MIT License.
|
|
4
4
|
// License text available at https://opensource.org/licenses/MIT
|
|
@@ -76,15 +76,3 @@ export function referencesOne(definition?: Object) {
|
|
|
76
76
|
decoratorName: '@referencesOne',
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Decorator for referencesMany
|
|
82
|
-
* @param definition
|
|
83
|
-
* @returns A property decorator
|
|
84
|
-
*/
|
|
85
|
-
export function referencesMany(definition?: Object) {
|
|
86
|
-
const rel = Object.assign({type: RelationType.referencesMany}, definition);
|
|
87
|
-
return PropertyDecoratorFactory.createDecorator(RELATIONS_KEY, rel, {
|
|
88
|
-
decoratorName: '@referencesMany',
|
|
89
|
-
});
|
|
90
|
-
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {Fields} from '@loopback/filter';
|
|
2
|
+
import {cloneDeep} from 'lodash';
|
|
3
|
+
|
|
4
|
+
export function includeFieldIfNot<MT>(
|
|
5
|
+
fields: Fields<MT> | undefined,
|
|
6
|
+
fieldToInclude: Extract<keyof MT, string>,
|
|
7
|
+
): false | Fields<MT> {
|
|
8
|
+
if (!fields) {
|
|
9
|
+
return false;
|
|
10
|
+
} else if (Array.isArray(fields)) {
|
|
11
|
+
const fieldsCloned: {[P in keyof MT]?: boolean} = fields.reduce(
|
|
12
|
+
(prev, current) => ({...prev, [current]: true}),
|
|
13
|
+
{},
|
|
14
|
+
);
|
|
15
|
+
if (Object.keys(fieldsCloned).length > 0) {
|
|
16
|
+
if (fieldsCloned[fieldToInclude] === true) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
fieldsCloned[fieldToInclude] = true;
|
|
20
|
+
return fieldsCloned;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const fieldsCloned = cloneDeep(fields);
|
|
26
|
+
if (Object.keys(fieldsCloned).length > 0) {
|
|
27
|
+
let containsTrue = false;
|
|
28
|
+
for (const k in fieldsCloned) {
|
|
29
|
+
if (fieldsCloned[k] === true) {
|
|
30
|
+
containsTrue = true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
for (const k in fieldsCloned) {
|
|
34
|
+
if (k === fieldToInclude) {
|
|
35
|
+
if (fieldsCloned[k] === true) {
|
|
36
|
+
return false;
|
|
37
|
+
} else {
|
|
38
|
+
if (containsTrue) {
|
|
39
|
+
fieldsCloned[k] = true;
|
|
40
|
+
} else {
|
|
41
|
+
delete fieldsCloned[k];
|
|
42
|
+
}
|
|
43
|
+
return fieldsCloned;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (containsTrue) {
|
|
48
|
+
fieldsCloned[fieldToInclude] = true;
|
|
49
|
+
return fieldsCloned;
|
|
50
|
+
} else {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright IBM Corp. 2019,2020. All Rights Reserved.
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
|
|
2
2
|
// Node module: @loopback/repository
|
|
3
3
|
// This file is licensed under the MIT License.
|
|
4
4
|
// License text available at https://opensource.org/licenses/MIT
|
|
@@ -62,7 +62,7 @@ export async function findByForeignKeys<
|
|
|
62
62
|
useScopeFilterGlobally = true;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
// This code is to keep backward
|
|
65
|
+
// This code is to keep backward compatibility.
|
|
66
66
|
// See https://github.com/loopbackio/loopback-next/issues/6832 for more info.
|
|
67
67
|
if (scope?.totalLimit) {
|
|
68
68
|
scope.limit = scope.totalLimit;
|
|
@@ -122,7 +122,22 @@ export async function includeRelatedModels<
|
|
|
122
122
|
options?: Options,
|
|
123
123
|
): Promise<(T & Relations)[]> {
|
|
124
124
|
entities = cloneDeep(entities);
|
|
125
|
-
|
|
125
|
+
if (options?.polymorphicType) {
|
|
126
|
+
include = include?.filter(inclusionFilter => {
|
|
127
|
+
if (typeof inclusionFilter === 'string') {
|
|
128
|
+
return true;
|
|
129
|
+
} else {
|
|
130
|
+
if (
|
|
131
|
+
inclusionFilter.targetType === undefined ||
|
|
132
|
+
inclusionFilter.targetType === options?.polymorphicType
|
|
133
|
+
) {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
} else {
|
|
139
|
+
include = cloneDeep(include);
|
|
140
|
+
}
|
|
126
141
|
const result = entities as (T & Relations)[];
|
|
127
142
|
if (!include) return result;
|
|
128
143
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright IBM Corp. 2018,2020. All Rights Reserved.
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
|
|
2
2
|
// Node module: @loopback/repository
|
|
3
3
|
// This file is licensed under the MIT License.
|
|
4
4
|
// License text available at https://opensource.org/licenses/MIT
|
|
@@ -74,6 +74,12 @@ export interface HasManyDefinition extends RelationDefinitionBase {
|
|
|
74
74
|
keyTo?: string;
|
|
75
75
|
keyFrom?: string;
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* With current architecture design, polymorphic type cannot be supported without through
|
|
79
|
+
* Consider using Source-hasMany->Through->hasOne->Target(polymorphic) for one-to-many relations
|
|
80
|
+
*/
|
|
81
|
+
// polymorphic?: boolean | {discriminator: string};
|
|
82
|
+
|
|
77
83
|
/**
|
|
78
84
|
* Description of the through model of the hasManyThrough relation.
|
|
79
85
|
*
|
|
@@ -107,6 +113,19 @@ export interface HasManyDefinition extends RelationDefinitionBase {
|
|
|
107
113
|
* The foreign key of the target model defined in the through model, e.g. CategoryProductLink#productId
|
|
108
114
|
*/
|
|
109
115
|
keyTo?: string;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The polymorphism of the target model. The discriminator is a key of *through* model.
|
|
119
|
+
* If the target model is not polymorphic, then the value should be left undefined or false;
|
|
120
|
+
* If the key on through model indicating the concrete class of the through instance is default
|
|
121
|
+
* i.e. camelCase(classNameOf(targetModelInstance)) + "Id"
|
|
122
|
+
* then the discriminator field can be undefined
|
|
123
|
+
*
|
|
124
|
+
* With current architecture design, polymorphic type cannot be supported without through
|
|
125
|
+
* Consider using Source hasMany Through hasOne Target(polymorphic)
|
|
126
|
+
* or Source hasMany Through belongsTo Target(polymorphic) for one-to-many relations
|
|
127
|
+
*/
|
|
128
|
+
polymorphic?: boolean | {discriminator: string};
|
|
110
129
|
};
|
|
111
130
|
}
|
|
112
131
|
|
|
@@ -123,6 +142,14 @@ export interface BelongsToDefinition extends RelationDefinitionBase {
|
|
|
123
142
|
* The primary key of the target model, e.g Customer#id.
|
|
124
143
|
*/
|
|
125
144
|
keyTo?: string;
|
|
145
|
+
/**
|
|
146
|
+
* The polymorphism of the target model. The discriminator is a key of source model.
|
|
147
|
+
* If the target model is not polymorphic, then the value should be left undefined or false;
|
|
148
|
+
* If the key on source model indicating the concrete class of the target instance is default
|
|
149
|
+
* i.e. camelCase(classNameOf(throughModelInstance)) + "Id"
|
|
150
|
+
* Then the discriminator field can be undefined
|
|
151
|
+
*/
|
|
152
|
+
polymorphic?: boolean | {discriminator: string};
|
|
126
153
|
}
|
|
127
154
|
|
|
128
155
|
export interface HasOneDefinition extends RelationDefinitionBase {
|
|
@@ -141,6 +168,29 @@ export interface HasOneDefinition extends RelationDefinitionBase {
|
|
|
141
168
|
*/
|
|
142
169
|
keyTo?: string;
|
|
143
170
|
keyFrom?: string;
|
|
171
|
+
/**
|
|
172
|
+
* The polymorphism of the target model. The discriminator is a key of source model.
|
|
173
|
+
* If the target model is not polymorphic, then the value should be left undefined or false;
|
|
174
|
+
* If the key on source model indicating the concrete class of the target instance is default
|
|
175
|
+
* i.e. camelCase(classNameOf(throughModelInstance)) + "Id"
|
|
176
|
+
* Then the discriminator field can be undefined
|
|
177
|
+
*/
|
|
178
|
+
polymorphic?: boolean | {discriminator: string};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export interface ReferencesManyDefinition extends RelationDefinitionBase {
|
|
182
|
+
type: RelationType.referencesMany;
|
|
183
|
+
targetsMany: true;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* keyTo: The foreign key used by the target model for this relation.
|
|
187
|
+
* keyFrom: The source key used by the source model for this relation.
|
|
188
|
+
*
|
|
189
|
+
* TODO(bajtos) Add relation description.
|
|
190
|
+
*
|
|
191
|
+
*/
|
|
192
|
+
keyTo?: string;
|
|
193
|
+
keyFrom?: string;
|
|
144
194
|
}
|
|
145
195
|
|
|
146
196
|
/**
|
|
@@ -150,6 +200,7 @@ export type RelationMetadata =
|
|
|
150
200
|
| HasManyDefinition
|
|
151
201
|
| BelongsToDefinition
|
|
152
202
|
| HasOneDefinition
|
|
203
|
+
| ReferencesManyDefinition
|
|
153
204
|
// TODO(bajtos) add other relation types and remove RelationDefinitionBase once
|
|
154
205
|
// all relation types are covered.
|
|
155
206
|
| RelationDefinitionBase;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright IBM Corp. 2018,2020. All Rights Reserved.
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
|
|
2
2
|
// Node module: @loopback/repository
|
|
3
3
|
// This file is licensed under the MIT License.
|
|
4
4
|
// License text available at https://opensource.org/licenses/MIT
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
// Copyright IBM Corp. 2018,2020. All Rights Reserved.
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
|
|
2
2
|
// Node module: @loopback/repository
|
|
3
3
|
// This file is licensed under the MIT License.
|
|
4
4
|
// License text available at https://opensource.org/licenses/MIT
|
|
5
5
|
|
|
6
|
+
export * from './constraint-utils';
|
|
6
7
|
export * from './kv.repository';
|
|
7
|
-
export * from './legacy-juggler-bridge';
|
|
8
8
|
export * from './kv.repository.bridge';
|
|
9
|
+
export * from './legacy-juggler-bridge';
|
|
9
10
|
export * from './repository';
|
|
10
|
-
export * from './constraint-utils';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright IBM Corp. 2018,2020. All Rights Reserved.
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
|
|
2
2
|
// Node module: @loopback/repository
|
|
3
3
|
// This file is licensed under the MIT License.
|
|
4
4
|
// License text available at https://opensource.org/licenses/MIT
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// Copyright IBM Corp. 2018,2020. All Rights Reserved.
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
|
|
2
2
|
// Node module: @loopback/repository
|
|
3
3
|
// This file is licensed under the MIT License.
|
|
4
4
|
// License text available at https://opensource.org/licenses/MIT
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {Options, DataObject} from '../common-types';
|
|
6
|
+
import {DataObject, Options} from '../common-types';
|
|
8
7
|
import {Model} from '../model';
|
|
8
|
+
import {Repository} from './repository';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Filter for keys
|