@loopback/repository 2.5.1 → 2.9.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/decorators/metadata.d.ts +1 -1
- package/dist/decorators/metadata.js +6 -6
- package/dist/decorators/metadata.js.map +1 -1
- package/dist/decorators/model.decorator.d.ts +1 -1
- package/dist/decorators/model.decorator.js +10 -10
- package/dist/decorators/model.decorator.js.map +1 -1
- package/dist/decorators/repository.decorator.js +4 -4
- package/dist/decorators/repository.decorator.js.map +1 -1
- package/dist/define-model-class.d.ts +2 -2
- package/dist/mixins/repository.mixin.d.ts +82 -43
- package/dist/mixins/repository.mixin.js +52 -15
- package/dist/mixins/repository.mixin.js.map +1 -1
- package/dist/model.js +3 -2
- package/dist/model.js.map +1 -1
- package/dist/query.d.ts +30 -0
- package/dist/query.js +50 -0
- package/dist/query.js.map +1 -1
- package/dist/relations/belongs-to/belongs-to.decorator.js +2 -2
- package/dist/relations/belongs-to/belongs-to.decorator.js.map +1 -1
- package/dist/relations/belongs-to/belongs-to.repository.d.ts +1 -1
- package/dist/relations/has-many/has-many-through-repository.factory.d.ts +11 -0
- package/dist/relations/has-many/has-many-through-repository.factory.js +28 -0
- package/dist/relations/has-many/has-many-through-repository.factory.js.map +1 -0
- package/dist/relations/has-many/has-many-through.helpers.d.ts +87 -14
- package/dist/relations/has-many/has-many-through.helpers.js +114 -20
- package/dist/relations/has-many/has-many-through.helpers.js.map +1 -1
- package/dist/relations/has-many/has-many-through.repository.d.ts +38 -2
- package/dist/relations/has-many/has-many-through.repository.js +71 -0
- package/dist/relations/has-many/has-many-through.repository.js.map +1 -1
- package/dist/relations/has-many/has-many.helpers.js +3 -2
- package/dist/relations/has-many/has-many.helpers.js.map +1 -1
- package/dist/relations/has-many/has-many.repository.d.ts +1 -1
- package/dist/relations/has-many/index.d.ts +4 -2
- package/dist/relations/has-many/index.js +4 -2
- package/dist/relations/has-many/index.js.map +1 -1
- package/dist/relations/has-one/has-one.helpers.js +2 -1
- package/dist/relations/has-one/has-one.helpers.js.map +1 -1
- package/dist/relations/has-one/has-one.repository.d.ts +1 -1
- package/dist/relations/relation.decorator.js +7 -7
- package/dist/relations/relation.decorator.js.map +1 -1
- package/dist/relations/relation.types.d.ts +1 -1
- package/dist/relations/relation.types.js +2 -2
- package/dist/relations/relation.types.js.map +1 -1
- package/dist/repositories/constraint-utils.d.ts +10 -0
- package/dist/repositories/constraint-utils.js +16 -1
- package/dist/repositories/constraint-utils.js.map +1 -1
- package/dist/repositories/legacy-juggler-bridge.d.ts +33 -2
- package/dist/repositories/legacy-juggler-bridge.js +34 -0
- package/dist/repositories/legacy-juggler-bridge.js.map +1 -1
- package/dist/type-resolver.js +2 -1
- package/dist/type-resolver.js.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/null.js +1 -1
- package/dist/types/null.js.map +1 -1
- package/package.json +14 -12
- 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 +2 -2
- package/src/mixins/repository.mixin.ts +70 -41
- package/src/model.ts +2 -2
- package/src/query.ts +55 -0
- package/src/relations/belongs-to/belongs-to.decorator.ts +1 -1
- package/src/relations/belongs-to/belongs-to.repository.ts +1 -1
- package/src/relations/has-many/has-many-through-repository.factory.ts +100 -0
- package/src/relations/has-many/has-many-through.helpers.ts +138 -21
- package/src/relations/has-many/has-many-through.repository.ts +174 -2
- package/src/relations/has-many/has-many.helpers.ts +2 -3
- package/src/relations/has-many/has-many.repository.ts +1 -1
- package/src/relations/has-many/index.ts +4 -2
- package/src/relations/has-one/has-one.helpers.ts +1 -2
- package/src/relations/has-one/has-one.repository.ts +1 -1
- package/src/relations/relation.decorator.ts +2 -2
- package/src/relations/relation.types.ts +1 -1
- package/src/repositories/constraint-utils.ts +17 -0
- package/src/repositories/legacy-juggler-bridge.ts +59 -1
- package/src/type-resolver.ts +2 -1
- package/src/types/index.ts +1 -1
- package/src/types/null.ts +1 -1
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loopback/repository",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.9.0",
|
|
4
|
+
"description": "Define and implement a common set of interfaces for interacting with databases",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"engines": {
|
|
8
|
-
"node": ">=10"
|
|
8
|
+
"node": ">=10.16"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"acceptance": "lb-mocha \"dist/__tests__/acceptance/**/*.js\"",
|
|
@@ -18,19 +18,21 @@
|
|
|
18
18
|
"author": "IBM Corp.",
|
|
19
19
|
"copyright.owner": "IBM Corp.",
|
|
20
20
|
"license": "MIT",
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
21
24
|
"devDependencies": {
|
|
22
|
-
"@loopback/build": "^
|
|
23
|
-
"@loopback/eslint-config": "^
|
|
24
|
-
"@loopback/testlab": "^3.
|
|
25
|
+
"@loopback/build": "^6.1.0",
|
|
26
|
+
"@loopback/eslint-config": "^8.0.3",
|
|
27
|
+
"@loopback/testlab": "^3.2.0",
|
|
25
28
|
"@types/bson": "^4.0.2",
|
|
26
|
-
"@types/json-schema": "^7.0.
|
|
27
|
-
"@types/lodash": "^4.14.
|
|
28
|
-
"@types/node": "^10.17.
|
|
29
|
+
"@types/json-schema": "^7.0.5",
|
|
30
|
+
"@types/lodash": "^4.14.157",
|
|
31
|
+
"@types/node": "^10.17.26",
|
|
29
32
|
"bson": "4.0.4"
|
|
30
33
|
},
|
|
31
34
|
"dependencies": {
|
|
32
|
-
"@loopback/
|
|
33
|
-
"@loopback/core": "^2.7.0",
|
|
35
|
+
"@loopback/core": "^2.9.1",
|
|
34
36
|
"@types/debug": "^4.1.5",
|
|
35
37
|
"debug": "^4.1.1",
|
|
36
38
|
"lodash": "^4.17.15",
|
|
@@ -48,5 +50,5 @@
|
|
|
48
50
|
"url": "https://github.com/strongloop/loopback-next.git",
|
|
49
51
|
"directory": "packages/repository"
|
|
50
52
|
},
|
|
51
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "b89db3d3b8be6a36e63e91c2331d217fda7538de"
|
|
52
54
|
}
|
|
@@ -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 {InspectionOptions, MetadataInspector} from '@loopback/
|
|
6
|
+
import {InspectionOptions, MetadataInspector} from '@loopback/core';
|
|
7
7
|
import {ModelDefinition, RelationDefinitionMap} from '../model';
|
|
8
8
|
import {RELATIONS_KEY} from '../relations';
|
|
9
9
|
import {
|
|
@@ -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 {Context, inject, Injection} from '@loopback/
|
|
6
|
+
import {Context, inject, Injection} from '@loopback/core';
|
|
7
7
|
import assert from 'assert';
|
|
8
8
|
import {Class} from '../common-types';
|
|
9
9
|
import {DataSource} from '../datasource';
|
|
@@ -79,10 +79,10 @@ export function defineModelClass<
|
|
|
79
79
|
export type DynamicModelCtor<
|
|
80
80
|
BaseCtor extends typeof Model,
|
|
81
81
|
Props extends object
|
|
82
|
-
> =
|
|
82
|
+
> = {
|
|
83
83
|
/** Model constructor accepting partial model data. */
|
|
84
84
|
new (data?: DataObject<PrototypeOf<BaseCtor> & Props>): PrototypeOf<
|
|
85
85
|
BaseCtor
|
|
86
86
|
> &
|
|
87
87
|
Props;
|
|
88
|
-
};
|
|
88
|
+
} & BaseCtor;
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
BindingFromClassOptions,
|
|
9
9
|
BindingScope,
|
|
10
10
|
createBindingFromClass,
|
|
11
|
-
} from '@loopback/
|
|
11
|
+
} from '@loopback/core';
|
|
12
12
|
import {
|
|
13
13
|
Application,
|
|
14
14
|
Component,
|
|
@@ -27,35 +27,9 @@ const debug = debugFactory('loopback:repository:mixin');
|
|
|
27
27
|
|
|
28
28
|
// FIXME(rfeng): Workaround for https://github.com/microsoft/rushstack/pull/1867
|
|
29
29
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
30
|
-
import
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
JSONObject,
|
|
34
|
-
Provider,
|
|
35
|
-
Context,
|
|
36
|
-
ContextSubscriptionManager,
|
|
37
|
-
ContextEvent,
|
|
38
|
-
Interceptor,
|
|
39
|
-
InterceptorBindingOptions,
|
|
40
|
-
ResolutionOptions,
|
|
41
|
-
BindingKey,
|
|
42
|
-
ValueOrPromise,
|
|
43
|
-
ContextEventObserver,
|
|
44
|
-
ContextObserver,
|
|
45
|
-
Subscription,
|
|
46
|
-
BindingComparator,
|
|
47
|
-
ContextView,
|
|
48
|
-
ResolutionSession,
|
|
49
|
-
BindingCreationPolicy,
|
|
50
|
-
ContextInspectOptions,
|
|
51
|
-
} from '@loopback/context';
|
|
52
|
-
import {
|
|
53
|
-
Server,
|
|
54
|
-
ApplicationConfig,
|
|
55
|
-
ApplicationMetadata,
|
|
56
|
-
LifeCycleObserver,
|
|
57
|
-
ServiceOptions,
|
|
58
|
-
} from '@loopback/core';
|
|
30
|
+
import * as loopbackContext from '@loopback/core';
|
|
31
|
+
import * as loopbackCore from '@loopback/core';
|
|
32
|
+
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
59
33
|
|
|
60
34
|
/**
|
|
61
35
|
* A mixin class for Application that creates a .repository()
|
|
@@ -70,6 +44,12 @@ import {
|
|
|
70
44
|
* Please note: the members in the mixin function are documented in a dummy class
|
|
71
45
|
* called <a href="#RepositoryMixinDoc">RepositoryMixinDoc</a>
|
|
72
46
|
*
|
|
47
|
+
* @param superClass - Application class
|
|
48
|
+
* @returns A new class that extends the super class with repository related
|
|
49
|
+
* methods
|
|
50
|
+
*
|
|
51
|
+
* @typeParam T - Type of the application class as the target for the mixin
|
|
52
|
+
*
|
|
73
53
|
*/
|
|
74
54
|
export function RepositoryMixin<T extends MixinTarget<Application>>(
|
|
75
55
|
superClass: T,
|
|
@@ -214,35 +194,66 @@ export function RepositoryMixin<T extends MixinTarget<Application>>(
|
|
|
214
194
|
*/
|
|
215
195
|
// Unfortunately, TypeScript does not allow overriding methods inherited
|
|
216
196
|
// from mapped types. https://github.com/microsoft/TypeScript/issues/38496
|
|
217
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-
|
|
197
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
218
198
|
// @ts-ignore
|
|
219
199
|
public component<C extends Component = Component>(
|
|
220
200
|
componentCtor: Constructor<C>,
|
|
221
201
|
nameOrOptions?: string | BindingFromClassOptions,
|
|
222
202
|
) {
|
|
223
203
|
const binding = super.component(componentCtor, nameOrOptions);
|
|
224
|
-
this.
|
|
204
|
+
const instance = this.getSync<C & RepositoryComponent>(binding.key);
|
|
205
|
+
this.mountComponentRepositories(instance);
|
|
206
|
+
this.mountComponentModels(instance);
|
|
225
207
|
return binding;
|
|
226
208
|
}
|
|
227
209
|
|
|
228
210
|
/**
|
|
229
211
|
* Get an instance of a component and mount all it's
|
|
230
212
|
* repositories. This function is intended to be used internally
|
|
231
|
-
* by component()
|
|
213
|
+
* by `component()`.
|
|
214
|
+
*
|
|
215
|
+
* NOTE: Calling `mountComponentRepositories` with a component class
|
|
216
|
+
* constructor is deprecated. You should instantiate the component
|
|
217
|
+
* yourself and provide the component instance instead.
|
|
232
218
|
*
|
|
233
|
-
* @param
|
|
219
|
+
* @param componentInstanceOrClass - The component to mount repositories of
|
|
220
|
+
* @internal
|
|
234
221
|
*/
|
|
235
|
-
mountComponentRepositories(
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
222
|
+
mountComponentRepositories(
|
|
223
|
+
// accept also component class to preserve backwards compatibility
|
|
224
|
+
// TODO(semver-major) Remove support for component class constructor
|
|
225
|
+
componentInstanceOrClass: Class<unknown> | RepositoryComponent,
|
|
226
|
+
) {
|
|
227
|
+
const component = resolveComponentInstance(this);
|
|
240
228
|
|
|
241
|
-
if (
|
|
242
|
-
for (const repo of
|
|
229
|
+
if (component.repositories) {
|
|
230
|
+
for (const repo of component.repositories) {
|
|
243
231
|
this.repository(repo);
|
|
244
232
|
}
|
|
245
233
|
}
|
|
234
|
+
|
|
235
|
+
// `Readonly<Application>` is a hack to remove protected members
|
|
236
|
+
// and thus allow `this` to be passed as a value for `ctx`
|
|
237
|
+
function resolveComponentInstance(ctx: Readonly<Application>) {
|
|
238
|
+
if (typeof componentInstanceOrClass !== 'function')
|
|
239
|
+
return componentInstanceOrClass;
|
|
240
|
+
|
|
241
|
+
const componentName = componentInstanceOrClass.name;
|
|
242
|
+
const componentKey = `${CoreBindings.COMPONENTS}.${componentName}`;
|
|
243
|
+
return ctx.getSync<RepositoryComponent>(componentKey);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Bind all model classes provided by a component.
|
|
249
|
+
* @param component
|
|
250
|
+
* @internal
|
|
251
|
+
*/
|
|
252
|
+
mountComponentModels(component: RepositoryComponent) {
|
|
253
|
+
if (!component.models) return;
|
|
254
|
+
for (const m of component.models) {
|
|
255
|
+
this.model(m);
|
|
256
|
+
}
|
|
246
257
|
}
|
|
247
258
|
|
|
248
259
|
/**
|
|
@@ -287,6 +298,24 @@ export function RepositoryMixin<T extends MixinTarget<Application>>(
|
|
|
287
298
|
};
|
|
288
299
|
}
|
|
289
300
|
|
|
301
|
+
/**
|
|
302
|
+
* This interface describes additional Component properties
|
|
303
|
+
* allowing components to contribute Repository-related artifacts.
|
|
304
|
+
*/
|
|
305
|
+
export interface RepositoryComponent {
|
|
306
|
+
/**
|
|
307
|
+
* An optional list of Repository classes to bind for dependency injection
|
|
308
|
+
* via `app.repository()` API.
|
|
309
|
+
*/
|
|
310
|
+
repositories?: Class<Repository<Model>>[];
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* An optional list of Model classes to bind for dependency injection
|
|
314
|
+
* via `app.model()` API.
|
|
315
|
+
*/
|
|
316
|
+
models?: Class<Model>[];
|
|
317
|
+
}
|
|
318
|
+
|
|
290
319
|
/**
|
|
291
320
|
* Normalize name or options to `BindingFromClassOptions`
|
|
292
321
|
* @param nameOrOptions - Name or options for binding from class
|
package/src/model.ts
CHANGED
|
@@ -216,7 +216,7 @@ function asObject(value: any, options?: Options): any {
|
|
|
216
216
|
*/
|
|
217
217
|
export abstract class Model {
|
|
218
218
|
static get modelName(): string {
|
|
219
|
-
return
|
|
219
|
+
return this.definition?.name || this.name;
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
static definition: ModelDefinition;
|
|
@@ -268,7 +268,7 @@ export abstract class Model {
|
|
|
268
268
|
const def = (this.constructor as typeof Model).definition;
|
|
269
269
|
const obj: AnyObject = {};
|
|
270
270
|
|
|
271
|
-
if (options
|
|
271
|
+
if (options?.ignoreUnknownProperties === false) {
|
|
272
272
|
const hiddenProperties: string[] = def?.settings.hiddenProperties || [];
|
|
273
273
|
for (const p in this) {
|
|
274
274
|
if (!hiddenProperties.includes(p)) {
|
package/src/query.ts
CHANGED
|
@@ -442,6 +442,61 @@ export class WhereBuilder<MT extends object = AnyObject> {
|
|
|
442
442
|
return this;
|
|
443
443
|
}
|
|
444
444
|
|
|
445
|
+
/**
|
|
446
|
+
* Add a `like` condition
|
|
447
|
+
* @param key - Property name
|
|
448
|
+
* @param val - Regexp condition
|
|
449
|
+
*/
|
|
450
|
+
like<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
|
|
451
|
+
const w: Where<MT> = {};
|
|
452
|
+
w[key] = {like: val};
|
|
453
|
+
return this.add(w);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Add a `nlike` condition
|
|
458
|
+
* @param key - Property name
|
|
459
|
+
* @param val - Regexp condition
|
|
460
|
+
*/
|
|
461
|
+
nlike<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
|
|
462
|
+
const w: Where<MT> = {};
|
|
463
|
+
w[key] = {nlike: val};
|
|
464
|
+
return this.add(w);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Add a `ilike` condition
|
|
469
|
+
* @param key - Property name
|
|
470
|
+
* @param val - Regexp condition
|
|
471
|
+
*/
|
|
472
|
+
ilike<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
|
|
473
|
+
const w: Where<MT> = {};
|
|
474
|
+
w[key] = {ilike: val};
|
|
475
|
+
return this.add(w);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Add a `nilike` condition
|
|
480
|
+
* @param key - Property name
|
|
481
|
+
* @param val - Regexp condition
|
|
482
|
+
*/
|
|
483
|
+
nilike<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
|
|
484
|
+
const w: Where<MT> = {};
|
|
485
|
+
w[key] = {nilike: val};
|
|
486
|
+
return this.add(w);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Add a `regexp` condition
|
|
491
|
+
* @param key - Property name
|
|
492
|
+
* @param val - Regexp condition
|
|
493
|
+
*/
|
|
494
|
+
regexp<K extends KeyOf<MT>>(key: K, val: string | RegExp): this {
|
|
495
|
+
const w: Where<MT> = {};
|
|
496
|
+
w[key] = {regexp: val};
|
|
497
|
+
return this.add(w);
|
|
498
|
+
}
|
|
499
|
+
|
|
445
500
|
/**
|
|
446
501
|
* Get the where object
|
|
447
502
|
*/
|
|
@@ -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 {MetadataInspector} from '@loopback/
|
|
6
|
+
import {MetadataInspector} from '@loopback/core';
|
|
7
7
|
import {property} from '../../decorators/model.decorator';
|
|
8
8
|
import {Entity, EntityResolver, PropertyDefinition} from '../../model';
|
|
9
9
|
import {relation} from '../relation.decorator';
|
|
@@ -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 {Getter} from '@loopback/
|
|
6
|
+
import {Getter} from '@loopback/core';
|
|
7
7
|
import {DataObject, Options} from '../../common-types';
|
|
8
8
|
import {EntityNotFoundError} from '../../errors';
|
|
9
9
|
import {Entity} from '../../model';
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Copyright IBM Corp. 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
|
+
import {
|
|
6
|
+
DataObject,
|
|
7
|
+
Entity,
|
|
8
|
+
EntityCrudRepository,
|
|
9
|
+
Getter,
|
|
10
|
+
HasManyDefinition,
|
|
11
|
+
} from '../..';
|
|
12
|
+
import {
|
|
13
|
+
createTargetConstraintFromThrough,
|
|
14
|
+
createThroughConstraintFromSource,
|
|
15
|
+
createThroughConstraintFromTarget,
|
|
16
|
+
getTargetKeysFromThroughModels,
|
|
17
|
+
resolveHasManyThroughMetadata,
|
|
18
|
+
} from './has-many-through.helpers';
|
|
19
|
+
import {
|
|
20
|
+
DefaultHasManyThroughRepository,
|
|
21
|
+
HasManyThroughRepository,
|
|
22
|
+
} from './has-many-through.repository';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* a factory to generate hasManyThrough repository class.
|
|
26
|
+
*
|
|
27
|
+
* Warning: The hasManyThrough interface is experimental and is subject to change.
|
|
28
|
+
* If backwards-incompatible changes are made, a new major version may not be
|
|
29
|
+
* released.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
export type HasManyThroughRepositoryFactory<
|
|
33
|
+
TargetEntity extends Entity,
|
|
34
|
+
TargetID,
|
|
35
|
+
ThroughEntity extends Entity,
|
|
36
|
+
SourceID
|
|
37
|
+
> = (
|
|
38
|
+
fkValue: SourceID,
|
|
39
|
+
) => HasManyThroughRepository<TargetEntity, TargetID, ThroughEntity>;
|
|
40
|
+
|
|
41
|
+
export function createHasManyThroughRepositoryFactory<
|
|
42
|
+
Target extends Entity,
|
|
43
|
+
TargetID,
|
|
44
|
+
Through extends Entity,
|
|
45
|
+
ThroughID,
|
|
46
|
+
SourceID
|
|
47
|
+
>(
|
|
48
|
+
relationMetadata: HasManyDefinition,
|
|
49
|
+
targetRepositoryGetter: Getter<EntityCrudRepository<Target, TargetID>>,
|
|
50
|
+
throughRepositoryGetter: Getter<EntityCrudRepository<Through, ThroughID>>,
|
|
51
|
+
): HasManyThroughRepositoryFactory<Target, TargetID, Through, SourceID> {
|
|
52
|
+
const meta = resolveHasManyThroughMetadata(relationMetadata);
|
|
53
|
+
const result = function (fkValue: SourceID) {
|
|
54
|
+
function getTargetConstraintFromThroughModels(
|
|
55
|
+
throughInstances: Through[],
|
|
56
|
+
): DataObject<Target> {
|
|
57
|
+
return createTargetConstraintFromThrough<Target, Through>(
|
|
58
|
+
meta,
|
|
59
|
+
throughInstances,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
function getTargetKeys(throughInstances: Through[]): TargetID[] {
|
|
63
|
+
return getTargetKeysFromThroughModels(meta, throughInstances);
|
|
64
|
+
}
|
|
65
|
+
function getThroughConstraintFromSource(): DataObject<Through> {
|
|
66
|
+
const constraint: DataObject<Through> = createThroughConstraintFromSource<
|
|
67
|
+
Through,
|
|
68
|
+
SourceID
|
|
69
|
+
>(meta, fkValue);
|
|
70
|
+
return constraint;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getThroughConstraintFromTarget(
|
|
74
|
+
fkValues: TargetID[],
|
|
75
|
+
): DataObject<Through> {
|
|
76
|
+
const constraint: DataObject<Through> = createThroughConstraintFromTarget<
|
|
77
|
+
Through,
|
|
78
|
+
TargetID
|
|
79
|
+
>(meta, fkValues);
|
|
80
|
+
return constraint;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return new DefaultHasManyThroughRepository<
|
|
84
|
+
Target,
|
|
85
|
+
TargetID,
|
|
86
|
+
EntityCrudRepository<Target, TargetID>,
|
|
87
|
+
Through,
|
|
88
|
+
ThroughID,
|
|
89
|
+
EntityCrudRepository<Through, ThroughID>
|
|
90
|
+
>(
|
|
91
|
+
targetRepositoryGetter,
|
|
92
|
+
throughRepositoryGetter,
|
|
93
|
+
getTargetConstraintFromThroughModels,
|
|
94
|
+
getTargetKeys,
|
|
95
|
+
getThroughConstraintFromSource,
|
|
96
|
+
getThroughConstraintFromTarget,
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
// Copyright IBM Corp. 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
|
+
|
|
1
6
|
import debugFactory from 'debug';
|
|
2
7
|
import {camelCase} from 'lodash';
|
|
3
8
|
import {
|
|
4
9
|
DataObject,
|
|
10
|
+
deduplicate,
|
|
5
11
|
Entity,
|
|
6
12
|
HasManyDefinition,
|
|
7
13
|
InvalidRelationError,
|
|
@@ -21,10 +27,9 @@ export type HasManyThroughResolvedDefinition = HasManyDefinition & {
|
|
|
21
27
|
};
|
|
22
28
|
|
|
23
29
|
/**
|
|
24
|
-
* Creates constraint
|
|
25
|
-
* @param relationMeta - hasManyThrough metadata
|
|
26
|
-
* @param throughInstances -
|
|
27
|
-
* @internal
|
|
30
|
+
* Creates target constraint based on through models
|
|
31
|
+
* @param relationMeta - resolved hasManyThrough metadata
|
|
32
|
+
* @param throughInstances - an array of through instances
|
|
28
33
|
*
|
|
29
34
|
* @example
|
|
30
35
|
* ```ts
|
|
@@ -38,33 +43,40 @@ export type HasManyThroughResolvedDefinition = HasManyDefinition & {
|
|
|
38
43
|
* keyTo: 'productId',
|
|
39
44
|
* },
|
|
40
45
|
* };
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
* createTargetConstraintFromThrough(resolvedMetadata,[{
|
|
47
|
+
id: 2,
|
|
48
|
+
categoryId: 2,
|
|
49
|
+
productId: 8,
|
|
50
|
+
}]);
|
|
51
|
+
* >>> {id: 8}
|
|
52
|
+
* createTargetConstraintFromThrough(resolvedMetadata, [
|
|
43
53
|
{
|
|
44
54
|
id: 2,
|
|
45
55
|
categoryId: 2,
|
|
46
56
|
productId: 8,
|
|
47
57
|
}, {
|
|
48
|
-
id:
|
|
58
|
+
id: 1,
|
|
49
59
|
categoryId: 2,
|
|
50
60
|
productId: 9,
|
|
51
61
|
}
|
|
52
62
|
]);
|
|
63
|
+
|
|
64
|
+
>>> {id: {inq: [9, 8]}}
|
|
53
65
|
* ```
|
|
54
66
|
*/
|
|
55
|
-
export function
|
|
67
|
+
export function createTargetConstraintFromThrough<
|
|
56
68
|
Target extends Entity,
|
|
57
69
|
Through extends Entity
|
|
58
70
|
>(
|
|
59
71
|
relationMeta: HasManyThroughResolvedDefinition,
|
|
60
72
|
throughInstances: Through[],
|
|
61
73
|
): DataObject<Target> {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
(throughInstance: Through) =>
|
|
66
|
-
throughInstance[targetFkName as keyof Through],
|
|
74
|
+
const fkValues = getTargetKeysFromThroughModels(
|
|
75
|
+
relationMeta,
|
|
76
|
+
throughInstances,
|
|
67
77
|
);
|
|
78
|
+
const targetPrimaryKey = relationMeta.keyTo;
|
|
79
|
+
|
|
68
80
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
69
81
|
const constraint: any = {
|
|
70
82
|
[targetPrimaryKey]: fkValues.length === 1 ? fkValues[0] : {inq: fkValues},
|
|
@@ -73,11 +85,64 @@ export function createTargetConstraint<
|
|
|
73
85
|
}
|
|
74
86
|
|
|
75
87
|
/**
|
|
76
|
-
*
|
|
88
|
+
* Returns an array of target fks of the given throughInstances.
|
|
77
89
|
*
|
|
78
|
-
* @param relationMeta - hasManyThrough metadata
|
|
79
|
-
* @param
|
|
80
|
-
*
|
|
90
|
+
* @param relationMeta - resolved hasManyThrough metadata
|
|
91
|
+
* @param throughInstances - an array of through instances
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```ts
|
|
95
|
+
* const resolvedMetadata = {
|
|
96
|
+
* // .. other props
|
|
97
|
+
* keyFrom: 'id',
|
|
98
|
+
* keyTo: 'id',
|
|
99
|
+
* through: {
|
|
100
|
+
* model: () => CategoryProductLink,
|
|
101
|
+
* keyFrom: 'categoryId',
|
|
102
|
+
* keyTo: 'productId',
|
|
103
|
+
* },
|
|
104
|
+
* };
|
|
105
|
+
* getTargetKeysFromThroughModels(resolvedMetadata,[{
|
|
106
|
+
id: 2,
|
|
107
|
+
categoryId: 2,
|
|
108
|
+
productId: 8,
|
|
109
|
+
}]);
|
|
110
|
+
* >>> [8]
|
|
111
|
+
* getTargetKeysFromThroughModels(resolvedMetadata, [
|
|
112
|
+
{
|
|
113
|
+
id: 2,
|
|
114
|
+
categoryId: 2,
|
|
115
|
+
productId: 8,
|
|
116
|
+
}, {
|
|
117
|
+
id: 1,
|
|
118
|
+
categoryId: 2,
|
|
119
|
+
productId: 9,
|
|
120
|
+
}
|
|
121
|
+
]);
|
|
122
|
+
>>> [8, 9]
|
|
123
|
+
*/
|
|
124
|
+
export function getTargetKeysFromThroughModels<
|
|
125
|
+
Through extends Entity,
|
|
126
|
+
TargetID
|
|
127
|
+
>(
|
|
128
|
+
relationMeta: HasManyThroughResolvedDefinition,
|
|
129
|
+
throughInstances: Through[],
|
|
130
|
+
): TargetID[] {
|
|
131
|
+
const targetFkName = relationMeta.through.keyTo;
|
|
132
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
133
|
+
let fkValues: any = throughInstances.map(
|
|
134
|
+
(throughInstance: Through) =>
|
|
135
|
+
throughInstance[targetFkName as keyof Through],
|
|
136
|
+
);
|
|
137
|
+
fkValues = deduplicate(fkValues);
|
|
138
|
+
return fkValues as TargetID[];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Creates through constraint based on the source key
|
|
143
|
+
*
|
|
144
|
+
* @param relationMeta - resolved hasManyThrough metadata
|
|
145
|
+
* @param fkValue - foreign key of the source instance
|
|
81
146
|
* @internal
|
|
82
147
|
*
|
|
83
148
|
* @example
|
|
@@ -92,18 +157,70 @@ export function createTargetConstraint<
|
|
|
92
157
|
* keyTo: 'productId',
|
|
93
158
|
* },
|
|
94
159
|
* };
|
|
95
|
-
*
|
|
160
|
+
* createThroughConstraintFromSource(resolvedMetadata, 1);
|
|
161
|
+
*
|
|
162
|
+
* >>> {categoryId: 1}
|
|
96
163
|
* ```
|
|
97
164
|
*/
|
|
98
|
-
export function
|
|
165
|
+
export function createThroughConstraintFromSource<
|
|
166
|
+
Through extends Entity,
|
|
167
|
+
SourceID
|
|
168
|
+
>(
|
|
99
169
|
relationMeta: HasManyThroughResolvedDefinition,
|
|
100
|
-
fkValue:
|
|
170
|
+
fkValue: SourceID,
|
|
101
171
|
): DataObject<Through> {
|
|
102
172
|
const sourceFkName = relationMeta.through.keyFrom;
|
|
103
173
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
104
174
|
const constraint: any = {[sourceFkName]: fkValue};
|
|
105
175
|
return constraint;
|
|
106
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Creates through constraint based on the target foreign key
|
|
179
|
+
*
|
|
180
|
+
* @param relationMeta - resolved hasManyThrough metadata
|
|
181
|
+
* @param fkValue an array of the target instance foreign keys
|
|
182
|
+
* @internal
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```ts
|
|
186
|
+
* const resolvedMetadata = {
|
|
187
|
+
* // .. other props
|
|
188
|
+
* keyFrom: 'id',
|
|
189
|
+
* keyTo: 'id',
|
|
190
|
+
* through: {
|
|
191
|
+
* model: () => CategoryProductLink,
|
|
192
|
+
* keyFrom: 'categoryId',
|
|
193
|
+
* keyTo: 'productId',
|
|
194
|
+
* },
|
|
195
|
+
* };
|
|
196
|
+
* createThroughConstraintFromTarget(resolvedMetadata, [3]);
|
|
197
|
+
*
|
|
198
|
+
* >>> {productId: 3}
|
|
199
|
+
*
|
|
200
|
+
* createThroughConstraintFromTarget(resolvedMetadata, [3,4]);
|
|
201
|
+
*
|
|
202
|
+
* >>> {productId: {inq:[3,4]}}
|
|
203
|
+
*/
|
|
204
|
+
export function createThroughConstraintFromTarget<
|
|
205
|
+
Through extends Entity,
|
|
206
|
+
TargetID
|
|
207
|
+
>(
|
|
208
|
+
relationMeta: HasManyThroughResolvedDefinition,
|
|
209
|
+
fkValues: TargetID[],
|
|
210
|
+
): DataObject<Through> {
|
|
211
|
+
if (fkValues === undefined || fkValues.length === 0) {
|
|
212
|
+
throw new Error('"fkValue" must be provided');
|
|
213
|
+
}
|
|
214
|
+
const targetFkName = relationMeta.through.keyTo;
|
|
215
|
+
|
|
216
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
217
|
+
const constraint: any =
|
|
218
|
+
fkValues.length === 1
|
|
219
|
+
? {[targetFkName]: fkValues[0]}
|
|
220
|
+
: {[targetFkName]: {inq: fkValues}};
|
|
221
|
+
|
|
222
|
+
return constraint as DataObject<Through>;
|
|
223
|
+
}
|
|
107
224
|
|
|
108
225
|
/**
|
|
109
226
|
* Resolves given hasMany metadata if target is specified to be a resolver.
|
|
@@ -142,7 +259,7 @@ export function resolveHasManyThroughMetadata(
|
|
|
142
259
|
relationMeta.keyTo &&
|
|
143
260
|
targetModelProperties[relationMeta.keyTo]
|
|
144
261
|
) {
|
|
145
|
-
// The
|
|
262
|
+
// The explicit cast is needed because of a limitation of type inference
|
|
146
263
|
return relationMeta as HasManyThroughResolvedDefinition;
|
|
147
264
|
}
|
|
148
265
|
|