@loopback/repository 1.15.3 → 1.17.0
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/CHANGELOG.md +57 -0
- package/dist/common-types.d.ts +11 -2
- package/dist/common-types.js +1 -0
- package/dist/common-types.js.map +1 -1
- package/dist/decorators/model.decorator.js +7 -6
- package/dist/decorators/model.decorator.js.map +1 -1
- package/dist/decorators/repository.decorator.js +5 -2
- package/dist/decorators/repository.decorator.js.map +1 -1
- package/dist/define-model-class.d.ts +55 -0
- package/dist/define-model-class.js +60 -0
- package/dist/define-model-class.js.map +1 -0
- package/dist/errors/invalid-relation.error.js +2 -1
- package/dist/errors/invalid-relation.error.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mixins/repository.mixin.js +11 -9
- package/dist/mixins/repository.mixin.js.map +1 -1
- package/dist/model.d.ts +1 -1
- package/dist/model.js +2 -2
- package/dist/model.js.map +1 -1
- package/dist/query.d.ts +1 -1
- package/dist/query.js +8 -5
- package/dist/query.js.map +1 -1
- package/dist/relations/belongs-to/belongs-to-accessor.js +12 -3
- package/dist/relations/belongs-to/belongs-to-accessor.js.map +1 -1
- package/dist/relations/belongs-to/belongs-to.helpers.js +5 -2
- package/dist/relations/belongs-to/belongs-to.helpers.js.map +1 -1
- package/dist/relations/has-many/has-many-repository.factory.js +5 -2
- package/dist/relations/has-many/has-many-repository.factory.js.map +1 -1
- package/dist/relations/has-many/has-many.helpers.js +18 -4
- package/dist/relations/has-many/has-many.helpers.js.map +1 -1
- package/dist/relations/has-many/has-many.inclusion-resolver.js +5 -2
- package/dist/relations/has-many/has-many.inclusion-resolver.js.map +1 -1
- package/dist/relations/has-one/has-one-repository.factory.js +5 -2
- package/dist/relations/has-one/has-one-repository.factory.js.map +1 -1
- package/dist/relations/has-one/has-one.helpers.js +14 -3
- package/dist/relations/has-one/has-one.helpers.js.map +1 -1
- package/dist/relations/has-one/has-one.inclusion-resolver.js.map +1 -1
- package/dist/relations/relation.decorator.js +2 -1
- package/dist/relations/relation.decorator.js.map +1 -1
- package/dist/relations/relation.helpers.d.ts +1 -1
- package/dist/relations/relation.helpers.js +18 -13
- package/dist/relations/relation.helpers.js.map +1 -1
- package/dist/relations/relation.types.d.ts +52 -5
- package/dist/relations/relation.types.js.map +1 -1
- package/dist/repositories/kv.repository.bridge.d.ts +3 -3
- package/dist/repositories/kv.repository.bridge.js +1 -1
- package/dist/repositories/kv.repository.bridge.js.map +1 -1
- package/dist/repositories/legacy-juggler-bridge.d.ts +20 -1
- package/dist/repositories/legacy-juggler-bridge.js +75 -20
- package/dist/repositories/legacy-juggler-bridge.js.map +1 -1
- package/dist/types/array.js +5 -2
- package/dist/types/array.js.map +1 -1
- package/dist/types/buffer.d.ts +1 -1
- package/dist/types/buffer.js +8 -4
- package/dist/types/buffer.js.map +1 -1
- package/dist/types/date.js +5 -2
- package/dist/types/date.js.map +1 -1
- package/dist/types/number.js +5 -2
- package/dist/types/number.js.map +1 -1
- package/dist/types/object.d.ts +1 -1
- package/dist/types/object.js +5 -2
- package/dist/types/object.js.map +1 -1
- package/dist/types/union.js +5 -2
- package/dist/types/union.js.map +1 -1
- package/package.json +11 -11
- package/src/common-types.ts +15 -5
- package/src/decorators/model.decorator.ts +5 -5
- package/src/decorators/repository.decorator.ts +1 -1
- package/src/define-model-class.ts +88 -0
- package/src/errors/invalid-relation.error.ts +1 -1
- package/src/index.ts +1 -0
- package/src/mixins/repository.mixin.ts +7 -9
- package/src/model.ts +2 -2
- package/src/query.ts +14 -4
- package/src/relations/belongs-to/belongs-to-accessor.ts +8 -2
- package/src/relations/belongs-to/belongs-to.helpers.ts +1 -1
- package/src/relations/has-many/has-many-repository.factory.ts +1 -1
- package/src/relations/has-many/has-many.helpers.ts +15 -4
- package/src/relations/has-many/has-many.inclusion-resolver.ts +6 -3
- package/src/relations/has-one/has-one-repository.factory.ts +1 -1
- package/src/relations/has-one/has-one.helpers.ts +11 -2
- package/src/relations/has-one/has-one.inclusion-resolver.ts +1 -1
- package/src/relations/relation.decorator.ts +1 -1
- package/src/relations/relation.helpers.ts +17 -13
- package/src/relations/relation.types.ts +58 -4
- package/src/repositories/kv.repository.bridge.ts +5 -8
- package/src/repositories/legacy-juggler-bridge.ts +75 -13
- package/src/types/array.ts +1 -1
- package/src/types/buffer.ts +4 -4
- package/src/types/date.ts +1 -1
- package/src/types/number.ts +1 -1
- package/src/types/object.ts +2 -2
- package/src/types/union.ts +1 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Copyright IBM Corp. 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 * as assert from 'assert';
|
|
7
|
+
import {DataObject, PrototypeOf} from './common-types';
|
|
8
|
+
import {Model, ModelDefinition} from './model';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create (define) a new model class with the given name and definition.
|
|
12
|
+
*
|
|
13
|
+
* Example usage:
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* const Product = defineModelClass(Entity, new ModelDefinition('Product'));
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* To enable type safety, you should describe properties of your model:
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* const Product = defineModelClass<
|
|
23
|
+
* typeof Entity,
|
|
24
|
+
* {id: number, name: string}
|
|
25
|
+
* >(Entity, new ModelDefinition('Product'));
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* If your model allows arbitrary (free-form) properties, then add `AnyObject`
|
|
29
|
+
* to the type describing model properties.
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* const Product = defineModelClass<
|
|
33
|
+
* typeof Entity,
|
|
34
|
+
* AnyObject & {id: number},
|
|
35
|
+
* >(Entity, new ModelDefinition('Product'));
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @param base The base model to extend, typically Model or Entity.
|
|
39
|
+
* You can also use your own base class, e.g. `User`.
|
|
40
|
+
* @param definition Definition of the model to create.
|
|
41
|
+
* @template BaseCtor Constructor type of the base class,
|
|
42
|
+
* e.g `typeof Model` or `typeof Entity`
|
|
43
|
+
* @template Props Interface describing model properties,
|
|
44
|
+
* e.g. `{title: string}` or `AnyObject & {id: number}`.
|
|
45
|
+
*/
|
|
46
|
+
export function defineModelClass<
|
|
47
|
+
BaseCtor extends typeof Model,
|
|
48
|
+
Props extends object = {}
|
|
49
|
+
>(
|
|
50
|
+
base: BaseCtor /* Model or Entity */,
|
|
51
|
+
definition: ModelDefinition,
|
|
52
|
+
): DynamicModelCtor<BaseCtor, Props> {
|
|
53
|
+
const modelName = definition.name;
|
|
54
|
+
const defineNamedModelClass = new Function(
|
|
55
|
+
base.name,
|
|
56
|
+
`return class ${modelName} extends ${base.name} {}`,
|
|
57
|
+
);
|
|
58
|
+
const modelClass = defineNamedModelClass(base) as DynamicModelCtor<
|
|
59
|
+
BaseCtor,
|
|
60
|
+
Props
|
|
61
|
+
>;
|
|
62
|
+
assert.equal(modelClass.name, modelName);
|
|
63
|
+
modelClass.definition = definition;
|
|
64
|
+
return modelClass;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* A type describing a model class created via `defineModelClass`.
|
|
69
|
+
*
|
|
70
|
+
* Assuming template arguments `BaseCtor` and `Props`, this type describes
|
|
71
|
+
* a class constructor with the following properties:
|
|
72
|
+
* - a constructor function accepting `DataObject<Props>` as the only argument,
|
|
73
|
+
* this argument is optional
|
|
74
|
+
* - all static fields (properties, methods) from `BaseCtor` are inherited and
|
|
75
|
+
* available as static fields on the dynamic class
|
|
76
|
+
* - all prototype fields from `BaseCtor` prototype are inherited and available
|
|
77
|
+
* as prototype fields on the dynamic class
|
|
78
|
+
*/
|
|
79
|
+
export type DynamicModelCtor<
|
|
80
|
+
BaseCtor extends typeof Model,
|
|
81
|
+
Props extends object
|
|
82
|
+
> = BaseCtor & {
|
|
83
|
+
/** Model constructor accepting partial model data. */
|
|
84
|
+
new (data?: DataObject<PrototypeOf<BaseCtor> & Props>): PrototypeOf<
|
|
85
|
+
BaseCtor
|
|
86
|
+
> &
|
|
87
|
+
Props;
|
|
88
|
+
};
|
|
@@ -18,7 +18,7 @@ export class InvalidRelationError<Props extends object = {}> extends Error {
|
|
|
18
18
|
extraProperties?: Props,
|
|
19
19
|
) {
|
|
20
20
|
const {name, type, source} = relationMeta;
|
|
21
|
-
const model =
|
|
21
|
+
const model = source?.modelName || '<Unknown Model>';
|
|
22
22
|
const message = `Invalid ${type} definition for ${model}#${name}: ${reason}`;
|
|
23
23
|
super(message);
|
|
24
24
|
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import {Binding, BindingScope, createBindingFromClass} from '@loopback/context';
|
|
7
7
|
import {Application} from '@loopback/core';
|
|
8
|
-
import
|
|
8
|
+
import debugFactory from 'debug';
|
|
9
9
|
import {Class} from '../common-types';
|
|
10
10
|
import {SchemaMigrationOptions} from '../datasource';
|
|
11
11
|
import {juggler, Repository} from '../repositories';
|
|
@@ -29,12 +29,6 @@ const debug = debugFactory('loopback:repository:mixin');
|
|
|
29
29
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
30
|
export function RepositoryMixin<T extends Class<any>>(superClass: T) {
|
|
31
31
|
return class extends superClass {
|
|
32
|
-
// A mixin class has to take in a type any[] argument!
|
|
33
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
-
constructor(...args: any[]) {
|
|
35
|
-
super(...args);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
32
|
/**
|
|
39
33
|
* Add a repository to this application.
|
|
40
34
|
*
|
|
@@ -116,13 +110,17 @@ export function RepositoryMixin<T extends Class<any>>(superClass: T) {
|
|
|
116
110
|
): Binding<D> {
|
|
117
111
|
// We have an instance of
|
|
118
112
|
if (dataSource instanceof juggler.DataSource) {
|
|
119
|
-
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
114
|
+
name = name || dataSource.name;
|
|
115
|
+
const key = `datasources.${name}`;
|
|
120
116
|
return this.bind(key)
|
|
121
117
|
.to(dataSource)
|
|
122
118
|
.tag('datasource');
|
|
123
119
|
} else if (typeof dataSource === 'function') {
|
|
120
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
121
|
+
name = name || dataSource.dataSourceName;
|
|
124
122
|
const binding = createBindingFromClass(dataSource, {
|
|
125
|
-
name
|
|
123
|
+
name,
|
|
126
124
|
namespace: 'datasources',
|
|
127
125
|
type: 'datasource',
|
|
128
126
|
defaultScope: BindingScope.SINGLETON,
|
package/src/model.ts
CHANGED
package/src/query.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
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
|
|
6
|
+
import assert from 'assert';
|
|
7
7
|
import {AnyObject} from './common-types';
|
|
8
8
|
|
|
9
9
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
@@ -170,8 +170,18 @@ export type Fields<MT = AnyObject> = {[P in keyof MT]?: boolean};
|
|
|
170
170
|
* `{relation: 'aRelationName', scope: {<AFilterObject>}}`
|
|
171
171
|
*/
|
|
172
172
|
export interface Inclusion<MT extends object = AnyObject> {
|
|
173
|
+
// ^^^ TODO(semver-major): remove the generic argument <MT>
|
|
174
|
+
|
|
173
175
|
relation: string;
|
|
174
|
-
|
|
176
|
+
|
|
177
|
+
// Technically, we should restrict the filter to target model.
|
|
178
|
+
// That is unfortunately rather difficult to achieve, because
|
|
179
|
+
// an Entity does not provide type information about related models.
|
|
180
|
+
// We could use {ModelName}WithRelations interface for first-level inclusion,
|
|
181
|
+
// but that won't handle second-level (and deeper) inclusions.
|
|
182
|
+
// To keep things simple, we allow any filter here, effectively turning off
|
|
183
|
+
// compiler checks.
|
|
184
|
+
scope?: Filter<AnyObject>;
|
|
175
185
|
}
|
|
176
186
|
|
|
177
187
|
/**
|
|
@@ -248,7 +258,7 @@ export class WhereBuilder<MT extends object = AnyObject> {
|
|
|
248
258
|
where: Where<MT>;
|
|
249
259
|
|
|
250
260
|
constructor(w?: Where<MT>) {
|
|
251
|
-
this.where = w
|
|
261
|
+
this.where = w ?? {};
|
|
252
262
|
}
|
|
253
263
|
|
|
254
264
|
private add(w: Where<MT>): this {
|
|
@@ -454,7 +464,7 @@ export class FilterBuilder<MT extends object = AnyObject> {
|
|
|
454
464
|
filter: Filter<MT>;
|
|
455
465
|
|
|
456
466
|
constructor(f?: Filter<MT>) {
|
|
457
|
-
this.filter = f
|
|
467
|
+
this.filter = f ?? {};
|
|
458
468
|
}
|
|
459
469
|
|
|
460
470
|
/**
|
|
@@ -3,7 +3,7 @@
|
|
|
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
|
|
6
|
+
import debugFactory from 'debug';
|
|
7
7
|
import {DataObject} from '../../common-types';
|
|
8
8
|
import {Entity} from '../../model';
|
|
9
9
|
import {EntityCrudRepository} from '../../repositories/repository';
|
|
@@ -13,8 +13,8 @@ import {
|
|
|
13
13
|
InclusionResolver,
|
|
14
14
|
} from '../relation.types';
|
|
15
15
|
import {resolveBelongsToMetadata} from './belongs-to.helpers';
|
|
16
|
-
import {DefaultBelongsToRepository} from './belongs-to.repository';
|
|
17
16
|
import {createBelongsToInclusionResolver} from './belongs-to.inclusion-resolver';
|
|
17
|
+
import {DefaultBelongsToRepository} from './belongs-to.repository';
|
|
18
18
|
|
|
19
19
|
const debug = debugFactory('loopback:repository:belongs-to-accessor');
|
|
20
20
|
|
|
@@ -53,6 +53,12 @@ export function createBelongsToAccessor<
|
|
|
53
53
|
const primaryKey = meta.keyTo;
|
|
54
54
|
const sourceModel = await sourceRepository.findById(sourceId);
|
|
55
55
|
const foreignKeyValue = sourceModel[foreignKey as keyof Source];
|
|
56
|
+
// workaround to check referential integrity.
|
|
57
|
+
// should be removed once the memory connector ref integrity is done
|
|
58
|
+
// GH issue: https://github.com/strongloop/loopback-next/issues/2333
|
|
59
|
+
if (!foreignKeyValue) {
|
|
60
|
+
return (undefined as unknown) as Target;
|
|
61
|
+
}
|
|
56
62
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
63
|
const constraint: any = {[primaryKey]: foreignKeyValue};
|
|
58
64
|
const constrainedRepo = new DefaultBelongsToRepository(
|
|
@@ -3,7 +3,7 @@
|
|
|
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
|
|
6
|
+
import debugFactory from 'debug';
|
|
7
7
|
import {InvalidRelationError} from '../../errors';
|
|
8
8
|
import {isTypeResolver} from '../../type-resolver';
|
|
9
9
|
import {BelongsToDefinition, RelationType} from '../relation.types';
|
|
@@ -3,7 +3,7 @@
|
|
|
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
|
|
6
|
+
import debugFactory from 'debug';
|
|
7
7
|
import {DataObject} from '../../common-types';
|
|
8
8
|
import {Entity} from '../../model';
|
|
9
9
|
import {EntityCrudRepository} from '../../repositories/repository';
|
|
@@ -3,7 +3,7 @@
|
|
|
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
|
|
6
|
+
import debugFactory from 'debug';
|
|
7
7
|
import {camelCase} from 'lodash';
|
|
8
8
|
import {InvalidRelationError} from '../../errors';
|
|
9
9
|
import {isTypeResolver} from '../../type-resolver';
|
|
@@ -50,8 +50,16 @@ export function resolveHasManyMetadata(
|
|
|
50
50
|
throw new InvalidRelationError(reason, relationMeta);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
// keyFrom defaults to id property
|
|
54
|
+
let keyFrom;
|
|
55
|
+
if (
|
|
56
|
+
relationMeta.keyFrom &&
|
|
57
|
+
relationMeta.source.definition.properties[relationMeta.keyFrom]
|
|
58
|
+
) {
|
|
59
|
+
keyFrom = relationMeta.keyFrom;
|
|
60
|
+
} else {
|
|
61
|
+
keyFrom = sourceModel.getIdProperties()[0];
|
|
62
|
+
}
|
|
55
63
|
// Make sure that if it already keys to the foreign key property,
|
|
56
64
|
// the key exists in the target model
|
|
57
65
|
if (relationMeta.keyTo && targetModelProperties[relationMeta.keyTo]) {
|
|
@@ -72,5 +80,8 @@ export function resolveHasManyMetadata(
|
|
|
72
80
|
throw new InvalidRelationError(reason, relationMeta);
|
|
73
81
|
}
|
|
74
82
|
|
|
75
|
-
return Object.assign(relationMeta, {
|
|
83
|
+
return Object.assign(relationMeta, {
|
|
84
|
+
keyFrom,
|
|
85
|
+
keyTo: defaultFkName,
|
|
86
|
+
} as HasManyResolvedDefinition);
|
|
76
87
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
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
|
|
6
|
+
import debugFactory from 'debug';
|
|
7
7
|
import {AnyObject, Options} from '../../common-types';
|
|
8
8
|
import {Entity} from '../../model';
|
|
9
9
|
import {Filter, Inclusion} from '../../query';
|
|
@@ -42,7 +42,7 @@ export function createHasManyInclusionResolver<
|
|
|
42
42
|
|
|
43
43
|
return async function fetchHasManyModels(
|
|
44
44
|
entities: Entity[],
|
|
45
|
-
inclusion: Inclusion<
|
|
45
|
+
inclusion: Inclusion<AnyObject>,
|
|
46
46
|
options?: Options,
|
|
47
47
|
): Promise<((Target & TargetRelations)[] | undefined)[]> {
|
|
48
48
|
if (!entities.length) return [];
|
|
@@ -55,7 +55,10 @@ export function createHasManyInclusionResolver<
|
|
|
55
55
|
const targetKey = relationMeta.keyTo as StringKeyOf<Target>;
|
|
56
56
|
|
|
57
57
|
debug('Parameters:', {sourceKey, sourceIds, targetKey});
|
|
58
|
-
debug(
|
|
58
|
+
debug(
|
|
59
|
+
'sourceId types',
|
|
60
|
+
sourceIds.map(i => typeof i),
|
|
61
|
+
);
|
|
59
62
|
|
|
60
63
|
const targetRepo = await getTargetRepo();
|
|
61
64
|
const targetsFound = await findByForeignKeys(
|
|
@@ -3,7 +3,7 @@
|
|
|
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
|
|
6
|
+
import debugFactory from 'debug';
|
|
7
7
|
import {DataObject} from '../../common-types';
|
|
8
8
|
import {Entity} from '../../model';
|
|
9
9
|
import {EntityCrudRepository} from '../../repositories/repository';
|
|
@@ -3,7 +3,7 @@
|
|
|
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
|
|
6
|
+
import debugFactory from 'debug';
|
|
7
7
|
import {camelCase} from 'lodash';
|
|
8
8
|
import {InvalidRelationError} from '../../errors';
|
|
9
9
|
import {isTypeResolver} from '../../type-resolver';
|
|
@@ -50,7 +50,16 @@ export function resolveHasOneMetadata(
|
|
|
50
50
|
throw new InvalidRelationError(reason, relationMeta);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
// keyFrom defaults to id property
|
|
54
|
+
let keyFrom;
|
|
55
|
+
if (
|
|
56
|
+
relationMeta.keyFrom &&
|
|
57
|
+
relationMeta.source.definition.properties[relationMeta.keyFrom]
|
|
58
|
+
) {
|
|
59
|
+
keyFrom = relationMeta.keyFrom;
|
|
60
|
+
} else {
|
|
61
|
+
keyFrom = sourceModel.getIdProperties()[0];
|
|
62
|
+
}
|
|
54
63
|
|
|
55
64
|
// Make sure that if it already keys to the foreign key property,
|
|
56
65
|
// the key exists in the target model
|
|
@@ -39,7 +39,7 @@ export function createHasOneInclusionResolver<
|
|
|
39
39
|
|
|
40
40
|
return async function fetchHasOneModel(
|
|
41
41
|
entities: Entity[],
|
|
42
|
-
inclusion: Inclusion<
|
|
42
|
+
inclusion: Inclusion<AnyObject>,
|
|
43
43
|
options?: Options,
|
|
44
44
|
): Promise<((Target & TargetRelations) | undefined)[]> {
|
|
45
45
|
if (!entities.length) return [];
|
|
@@ -33,7 +33,7 @@ export function getModelRelations(
|
|
|
33
33
|
): RelationDefinitionMap {
|
|
34
34
|
// Build model definitions if `@model` is missing
|
|
35
35
|
const modelDef = buildModelDefinition(modelCtor);
|
|
36
|
-
return
|
|
36
|
+
return modelDef?.relations || {};
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
//
|
|
@@ -3,9 +3,9 @@
|
|
|
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
|
|
8
|
-
import
|
|
6
|
+
import assert from 'assert';
|
|
7
|
+
import debugFactory from 'debug';
|
|
8
|
+
import _ from 'lodash';
|
|
9
9
|
import {
|
|
10
10
|
AnyObject,
|
|
11
11
|
Entity,
|
|
@@ -14,13 +14,14 @@ import {
|
|
|
14
14
|
Inclusion,
|
|
15
15
|
Options,
|
|
16
16
|
Where,
|
|
17
|
+
FilterBuilder,
|
|
17
18
|
} from '..';
|
|
18
19
|
const debug = debugFactory('loopback:repository:relation-helpers');
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Finds model instances that contain any of the provided foreign key values.
|
|
22
23
|
*
|
|
23
|
-
* @param targetRepository - The target repository where the model instances are found
|
|
24
|
+
* @param targetRepository - The target repository where the related model instances are found
|
|
24
25
|
* @param fkName - Name of the foreign key
|
|
25
26
|
* @param fkValues - One value or array of values of the foreign key to be included
|
|
26
27
|
* @param scope - Additional scope constraints (not currently supported)
|
|
@@ -37,12 +38,6 @@ export async function findByForeignKeys<
|
|
|
37
38
|
scope?: Filter<Target>,
|
|
38
39
|
options?: Options,
|
|
39
40
|
): Promise<(Target & TargetRelations)[]> {
|
|
40
|
-
// throw error if scope is defined and non-empty
|
|
41
|
-
// see https://github.com/strongloop/loopback-next/issues/3453
|
|
42
|
-
if (scope && !_.isEmpty(scope)) {
|
|
43
|
-
throw new Error('scope is not supported');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
41
|
let value;
|
|
47
42
|
|
|
48
43
|
if (Array.isArray(fkValues)) {
|
|
@@ -60,9 +55,15 @@ export async function findByForeignKeys<
|
|
|
60
55
|
}
|
|
61
56
|
|
|
62
57
|
const where = ({[fkName]: value} as unknown) as Where<Target>;
|
|
63
|
-
const targetFilter = {where};
|
|
64
58
|
|
|
65
|
-
|
|
59
|
+
if (scope && !_.isEmpty(scope)) {
|
|
60
|
+
// combine where clause to scope filter
|
|
61
|
+
scope = new FilterBuilder(scope).impose({where}).filter;
|
|
62
|
+
} else {
|
|
63
|
+
scope = {where} as Filter<Target>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return targetRepository.find(scope, options);
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
export type StringKeyOf<T> = Extract<keyof T, string>;
|
|
@@ -186,7 +187,10 @@ export function flattenTargetsOfOneToManyRelation<Target extends Entity>(
|
|
|
186
187
|
): (Target[] | undefined)[] {
|
|
187
188
|
debug('flattenTargetsOfOneToManyRelation');
|
|
188
189
|
debug('sourceIds', sourceIds);
|
|
189
|
-
debug(
|
|
190
|
+
debug(
|
|
191
|
+
'sourceId types',
|
|
192
|
+
sourceIds.map(i => typeof i),
|
|
193
|
+
);
|
|
190
194
|
debug('targetEntities', targetEntities);
|
|
191
195
|
debug('targetKey', targetKey);
|
|
192
196
|
|
|
@@ -3,7 +3,7 @@
|
|
|
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 {Options} from '../common-types';
|
|
6
|
+
import {AnyObject, Options} from '../common-types';
|
|
7
7
|
import {Entity} from '../model';
|
|
8
8
|
import {Inclusion} from '../query';
|
|
9
9
|
import {TypeResolver} from '../type-resolver';
|
|
@@ -59,13 +59,62 @@ export interface HasManyDefinition extends RelationDefinitionBase {
|
|
|
59
59
|
targetsMany: true;
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
|
-
* The foreign key used by the target model.
|
|
62
|
+
* keyTo: The foreign key used by the target model for this relation.
|
|
63
|
+
* keyFrom: The source key used by the source model for this relation.
|
|
63
64
|
*
|
|
64
65
|
* E.g. when a Customer has many Order instances, then keyTo is "customerId".
|
|
65
66
|
* Note that "customerId" is the default FK assumed by the framework, users
|
|
66
67
|
* can provide a custom FK name by setting "keyTo".
|
|
68
|
+
* And Customer.id is keyFrom. keyFrom defaults to the id property of a model.
|
|
69
|
+
* Users can provide a custom source key name by setting "keyTo".
|
|
70
|
+
*
|
|
67
71
|
*/
|
|
68
72
|
keyTo?: string;
|
|
73
|
+
keyFrom?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* A `hasManyThrough` relation defines a many-to-many connection with another model.
|
|
78
|
+
* This relation indicates that the declaring model can be matched with zero or more
|
|
79
|
+
* instances of another model by proceeding through a third model.
|
|
80
|
+
*
|
|
81
|
+
* Warning: The hasManyThrough interface is experimental and is subject to change.
|
|
82
|
+
* If backwards-incompatible changes are made, a new major version may not be
|
|
83
|
+
* released.
|
|
84
|
+
*/
|
|
85
|
+
export interface HasManyThroughDefinition extends RelationDefinitionBase {
|
|
86
|
+
type: RelationType.hasMany;
|
|
87
|
+
targetsMany: true;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The foreign key in the source model, e.g. Customer#id.
|
|
91
|
+
*/
|
|
92
|
+
keyFrom: string;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The primary key of the target model, e.g Seller#id.
|
|
96
|
+
*/
|
|
97
|
+
keyTo: string;
|
|
98
|
+
|
|
99
|
+
through: {
|
|
100
|
+
/**
|
|
101
|
+
* The through model of this relation.
|
|
102
|
+
*
|
|
103
|
+
* E.g. when a Customer has many Order instances and a Seller has many Order instances,
|
|
104
|
+
* then Order is through.
|
|
105
|
+
*/
|
|
106
|
+
model: TypeResolver<Entity, typeof Entity>;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The foreign key of the source model defined in the through model, e.g. Order#customerId
|
|
110
|
+
*/
|
|
111
|
+
keyFrom: string;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The foreign key of the target model defined in the through model, e.g. Order#sellerId
|
|
115
|
+
*/
|
|
116
|
+
keyTo: string;
|
|
117
|
+
};
|
|
69
118
|
}
|
|
70
119
|
|
|
71
120
|
export interface BelongsToDefinition extends RelationDefinitionBase {
|
|
@@ -88,13 +137,17 @@ export interface HasOneDefinition extends RelationDefinitionBase {
|
|
|
88
137
|
targetsMany: false;
|
|
89
138
|
|
|
90
139
|
/**
|
|
91
|
-
* The foreign key used by the target model.
|
|
140
|
+
* keyTo: The foreign key used by the target model for this relation.
|
|
141
|
+
* keyFrom: The source key used by the source model for this relation.
|
|
92
142
|
*
|
|
93
143
|
* E.g. when a Customer has one Address instance, then keyTo is "customerId".
|
|
94
144
|
* Note that "customerId" is the default FK assumed by the framework, users
|
|
95
145
|
* can provide a custom FK name by setting "keyTo".
|
|
146
|
+
* And Customer.id is keyFrom. keyFrom defaults to the id property of a model.
|
|
147
|
+
* Users can provide a custom source key name by setting "keyTo".
|
|
96
148
|
*/
|
|
97
149
|
keyTo?: string;
|
|
150
|
+
keyFrom?: string;
|
|
98
151
|
}
|
|
99
152
|
|
|
100
153
|
/**
|
|
@@ -102,6 +155,7 @@ export interface HasOneDefinition extends RelationDefinitionBase {
|
|
|
102
155
|
*/
|
|
103
156
|
export type RelationMetadata =
|
|
104
157
|
| HasManyDefinition
|
|
158
|
+
| HasManyThroughDefinition
|
|
105
159
|
| BelongsToDefinition
|
|
106
160
|
| HasOneDefinition
|
|
107
161
|
// TODO(bajtos) add other relation types and remove RelationDefinitionBase once
|
|
@@ -126,7 +180,7 @@ export type InclusionResolver<S extends Entity, T extends Entity> = (
|
|
|
126
180
|
/**
|
|
127
181
|
* Inclusion requested by the user (e.g. scope constraints to apply).
|
|
128
182
|
*/
|
|
129
|
-
inclusion: Inclusion
|
|
183
|
+
inclusion: Inclusion<AnyObject>,
|
|
130
184
|
/**
|
|
131
185
|
* Generic options object, e.g. carrying the Transaction object.
|
|
132
186
|
*/
|
|
@@ -3,14 +3,11 @@
|
|
|
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
|
-
|
|
8
|
-
import {Options, DataObject} from '../common-types';
|
|
6
|
+
import legacy from 'loopback-datasource-juggler';
|
|
7
|
+
import {DataObject, Options} from '../common-types';
|
|
9
8
|
import {Entity} from '../model';
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
import {juggler, ensurePromise} from './legacy-juggler-bridge';
|
|
9
|
+
import {KeyValueFilter, KeyValueRepository} from './kv.repository';
|
|
10
|
+
import {ensurePromise, juggler} from './legacy-juggler-bridge';
|
|
14
11
|
|
|
15
12
|
/**
|
|
16
13
|
* Polyfill for Symbol.asyncIterator
|
|
@@ -103,7 +100,7 @@ class AsyncKeyIteratorImpl implements AsyncIterator<string> {
|
|
|
103
100
|
next() {
|
|
104
101
|
const key = ensurePromise<string | undefined>(this.keys.next());
|
|
105
102
|
return key.then(k => {
|
|
106
|
-
return {done: k === undefined, value: k
|
|
103
|
+
return {done: k === undefined, value: k ?? ''};
|
|
107
104
|
});
|
|
108
105
|
}
|
|
109
106
|
}
|