@e22m4u/js-repository 0.0.31

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.
Files changed (183) hide show
  1. package/.c8rc +9 -0
  2. package/.commitlintrc +5 -0
  3. package/.editorconfig +13 -0
  4. package/.eslintignore +1 -0
  5. package/.eslintrc.cjs +27 -0
  6. package/.husky/commit-msg +4 -0
  7. package/.husky/pre-commit +9 -0
  8. package/.mocharc.cjs +7 -0
  9. package/.prettierrc +7 -0
  10. package/LICENSE +21 -0
  11. package/README.md +523 -0
  12. package/mocha.setup.js +10 -0
  13. package/package.json +57 -0
  14. package/src/adapter/adapter-loader.d.ts +16 -0
  15. package/src/adapter/adapter-loader.js +63 -0
  16. package/src/adapter/adapter-loader.spec.js +31 -0
  17. package/src/adapter/adapter-registry.d.ts +14 -0
  18. package/src/adapter/adapter-registry.js +36 -0
  19. package/src/adapter/adapter-registry.spec.js +36 -0
  20. package/src/adapter/adapter.d.ts +118 -0
  21. package/src/adapter/adapter.js +181 -0
  22. package/src/adapter/adapter.spec.js +144 -0
  23. package/src/adapter/builtin/memory-adapter.d.ts +118 -0
  24. package/src/adapter/builtin/memory-adapter.js +342 -0
  25. package/src/adapter/builtin/memory-adapter.spec.js +2925 -0
  26. package/src/adapter/decorator/data-sanitizing-decorator.d.ts +13 -0
  27. package/src/adapter/decorator/data-sanitizing-decorator.js +44 -0
  28. package/src/adapter/decorator/data-sanitizing-decorator.spec.js +59 -0
  29. package/src/adapter/decorator/data-validation-decorator.d.ts +13 -0
  30. package/src/adapter/decorator/data-validation-decorator.js +41 -0
  31. package/src/adapter/decorator/data-validation-decorator.spec.js +59 -0
  32. package/src/adapter/decorator/default-values-decorator.d.ts +13 -0
  33. package/src/adapter/decorator/default-values-decorator.js +57 -0
  34. package/src/adapter/decorator/default-values-decorator.spec.js +141 -0
  35. package/src/adapter/decorator/fields-filtering-decorator.d.ts +13 -0
  36. package/src/adapter/decorator/fields-filtering-decorator.js +72 -0
  37. package/src/adapter/decorator/fields-filtering-decorator.spec.js +119 -0
  38. package/src/adapter/decorator/inclusion-decorator.d.ts +13 -0
  39. package/src/adapter/decorator/inclusion-decorator.js +78 -0
  40. package/src/adapter/decorator/inclusion-decorator.spec.js +117 -0
  41. package/src/adapter/decorator/index.d.ts +5 -0
  42. package/src/adapter/decorator/index.js +5 -0
  43. package/src/adapter/index.d.ts +3 -0
  44. package/src/adapter/index.js +3 -0
  45. package/src/definition/datasource/datasource-definition-validator.d.ts +14 -0
  46. package/src/definition/datasource/datasource-definition-validator.js +33 -0
  47. package/src/definition/datasource/datasource-definition-validator.spec.js +63 -0
  48. package/src/definition/datasource/datasource-definition.d.ts +7 -0
  49. package/src/definition/datasource/index.d.ts +2 -0
  50. package/src/definition/datasource/index.js +1 -0
  51. package/src/definition/definition-registry.d.ts +50 -0
  52. package/src/definition/definition-registry.js +98 -0
  53. package/src/definition/definition-registry.spec.js +78 -0
  54. package/src/definition/index.d.ts +3 -0
  55. package/src/definition/index.js +3 -0
  56. package/src/definition/model/index.d.ts +7 -0
  57. package/src/definition/model/index.js +6 -0
  58. package/src/definition/model/model-data-sanitizer.d.ts +15 -0
  59. package/src/definition/model/model-data-sanitizer.js +33 -0
  60. package/src/definition/model/model-data-validator.d.ts +32 -0
  61. package/src/definition/model/model-data-validator.js +144 -0
  62. package/src/definition/model/model-data-validator.spec.js +1889 -0
  63. package/src/definition/model/model-definition-utils.d.ts +161 -0
  64. package/src/definition/model/model-definition-utils.js +371 -0
  65. package/src/definition/model/model-definition-utils.spec.js +1474 -0
  66. package/src/definition/model/model-definition-validator.d.ts +14 -0
  67. package/src/definition/model/model-definition-validator.js +83 -0
  68. package/src/definition/model/model-definition-validator.spec.js +143 -0
  69. package/src/definition/model/model-definition.d.ts +28 -0
  70. package/src/definition/model/properties/data-type.d.ts +11 -0
  71. package/src/definition/model/properties/data-type.js +11 -0
  72. package/src/definition/model/properties/default-values-definition-validator.d.ts +15 -0
  73. package/src/definition/model/properties/default-values-definition-validator.js +53 -0
  74. package/src/definition/model/properties/default-values-definition-validator.spec.js +136 -0
  75. package/src/definition/model/properties/index.d.ts +5 -0
  76. package/src/definition/model/properties/index.js +4 -0
  77. package/src/definition/model/properties/primary-keys-definition-validator.d.ts +15 -0
  78. package/src/definition/model/properties/primary-keys-definition-validator.js +55 -0
  79. package/src/definition/model/properties/primary-keys-definition-validator.spec.js +145 -0
  80. package/src/definition/model/properties/properties-definition-validator.d.ts +15 -0
  81. package/src/definition/model/properties/properties-definition-validator.js +194 -0
  82. package/src/definition/model/properties/properties-definition-validator.spec.js +373 -0
  83. package/src/definition/model/properties/property-definition.d.ts +20 -0
  84. package/src/definition/model/relations/index.d.ts +3 -0
  85. package/src/definition/model/relations/index.js +2 -0
  86. package/src/definition/model/relations/relation-definition.d.ts +254 -0
  87. package/src/definition/model/relations/relation-type.d.ts +9 -0
  88. package/src/definition/model/relations/relation-type.js +9 -0
  89. package/src/definition/model/relations/relations-definition-validator.d.ts +15 -0
  90. package/src/definition/model/relations/relations-definition-validator.js +449 -0
  91. package/src/definition/model/relations/relations-definition-validator.spec.js +772 -0
  92. package/src/errors/index.d.ts +3 -0
  93. package/src/errors/index.js +3 -0
  94. package/src/errors/invalid-argument-error.d.ts +6 -0
  95. package/src/errors/invalid-argument-error.js +6 -0
  96. package/src/errors/invalid-argument-error.spec.js +33 -0
  97. package/src/errors/invalid-operator-value-error.d.ts +13 -0
  98. package/src/errors/invalid-operator-value-error.js +24 -0
  99. package/src/errors/invalid-operator-value-error.spec.js +11 -0
  100. package/src/errors/not-implemented-error.d.ts +6 -0
  101. package/src/errors/not-implemented-error.js +6 -0
  102. package/src/errors/not-implemented-error.spec.js +33 -0
  103. package/src/filter/fields-clause-tool.d.ts +38 -0
  104. package/src/filter/fields-clause-tool.js +88 -0
  105. package/src/filter/fields-clause-tool.spec.js +133 -0
  106. package/src/filter/filter.d.ts +335 -0
  107. package/src/filter/include-clause-tool.d.ts +53 -0
  108. package/src/filter/include-clause-tool.js +364 -0
  109. package/src/filter/include-clause-tool.spec.js +653 -0
  110. package/src/filter/index.d.ts +7 -0
  111. package/src/filter/index.js +6 -0
  112. package/src/filter/operator-clause-tool.d.ts +223 -0
  113. package/src/filter/operator-clause-tool.js +515 -0
  114. package/src/filter/operator-clause-tool.spec.js +1064 -0
  115. package/src/filter/order-clause-tool.d.ts +32 -0
  116. package/src/filter/order-clause-tool.js +97 -0
  117. package/src/filter/order-clause-tool.spec.js +438 -0
  118. package/src/filter/slice-clause-tool.d.ts +30 -0
  119. package/src/filter/slice-clause-tool.js +65 -0
  120. package/src/filter/slice-clause-tool.spec.js +117 -0
  121. package/src/filter/where-clause-tool.d.ts +23 -0
  122. package/src/filter/where-clause-tool.js +165 -0
  123. package/src/filter/where-clause-tool.spec.js +280 -0
  124. package/src/index.d.ts +9 -0
  125. package/src/index.js +8 -0
  126. package/src/relations/belongs-to-resolver.d.ts +46 -0
  127. package/src/relations/belongs-to-resolver.js +242 -0
  128. package/src/relations/belongs-to-resolver.spec.js +1047 -0
  129. package/src/relations/has-many-resolver.d.ts +67 -0
  130. package/src/relations/has-many-resolver.js +317 -0
  131. package/src/relations/has-many-resolver.spec.js +2911 -0
  132. package/src/relations/has-one-resolver.d.ts +67 -0
  133. package/src/relations/has-one-resolver.js +311 -0
  134. package/src/relations/has-one-resolver.spec.js +2274 -0
  135. package/src/relations/index.d.ts +4 -0
  136. package/src/relations/index.js +4 -0
  137. package/src/relations/references-many-resolver.d.ts +27 -0
  138. package/src/relations/references-many-resolver.js +113 -0
  139. package/src/relations/references-many-resolver.spec.js +631 -0
  140. package/src/repository/index.d.ts +2 -0
  141. package/src/repository/index.js +2 -0
  142. package/src/repository/repository-registry.d.ts +29 -0
  143. package/src/repository/repository-registry.js +57 -0
  144. package/src/repository/repository-registry.spec.js +38 -0
  145. package/src/repository/repository.d.ts +164 -0
  146. package/src/repository/repository.js +207 -0
  147. package/src/repository/repository.spec.js +202 -0
  148. package/src/schema.d.ts +37 -0
  149. package/src/schema.js +41 -0
  150. package/src/types.d.ts +30 -0
  151. package/src/utils/capitalize.d.ts +6 -0
  152. package/src/utils/capitalize.js +10 -0
  153. package/src/utils/capitalize.spec.js +14 -0
  154. package/src/utils/clone-deep.d.ts +6 -0
  155. package/src/utils/clone-deep.js +61 -0
  156. package/src/utils/clone-deep.spec.js +28 -0
  157. package/src/utils/exclude-object-keys.d.ts +10 -0
  158. package/src/utils/exclude-object-keys.js +20 -0
  159. package/src/utils/exclude-object-keys.spec.js +49 -0
  160. package/src/utils/get-ctor-name.d.ts +6 -0
  161. package/src/utils/get-ctor-name.js +11 -0
  162. package/src/utils/get-ctor-name.spec.js +17 -0
  163. package/src/utils/get-value-by-path.d.ts +12 -0
  164. package/src/utils/get-value-by-path.js +23 -0
  165. package/src/utils/get-value-by-path.spec.js +36 -0
  166. package/src/utils/index.d.ts +10 -0
  167. package/src/utils/index.js +10 -0
  168. package/src/utils/is-ctor.d.ts +7 -0
  169. package/src/utils/is-ctor.js +10 -0
  170. package/src/utils/is-ctor.spec.js +26 -0
  171. package/src/utils/is-pure-object.d.ts +6 -0
  172. package/src/utils/is-pure-object.js +15 -0
  173. package/src/utils/is-pure-object.spec.js +25 -0
  174. package/src/utils/select-object-keys.d.ts +10 -0
  175. package/src/utils/select-object-keys.js +37 -0
  176. package/src/utils/select-object-keys.spec.js +40 -0
  177. package/src/utils/singularize.d.ts +6 -0
  178. package/src/utils/singularize.js +22 -0
  179. package/src/utils/singularize.spec.js +23 -0
  180. package/src/utils/string-to-regexp.d.ts +10 -0
  181. package/src/utils/string-to-regexp.js +22 -0
  182. package/src/utils/string-to-regexp.spec.js +35 -0
  183. package/tsconfig.json +9 -0
@@ -0,0 +1,2274 @@
1
+ import {expect} from 'chai';
2
+ import {Schema} from '../schema.js';
3
+ import {format} from '@e22m4u/js-format';
4
+ import {DataType} from '../definition/index.js';
5
+ import {RelationType} from '../definition/index.js';
6
+ import {HasOneResolver} from './has-one-resolver.js';
7
+ import {DEFAULT_PRIMARY_KEY_PROPERTY_NAME as DEF_PK} from '../definition/index.js';
8
+
9
+ describe('HasOneResolver', function () {
10
+ describe('includeTo', function () {
11
+ it('requires the "entities" parameter to be an array', async function () {
12
+ const S = new Schema();
13
+ const R = S.getService(HasOneResolver);
14
+ const error = v =>
15
+ format(
16
+ 'The parameter "entities" of HasOneResolver.includeTo requires ' +
17
+ 'an Array of Object, but %s given.',
18
+ v,
19
+ );
20
+ const throwable = v =>
21
+ R.includeTo(
22
+ v,
23
+ 'sourceName',
24
+ 'targetName',
25
+ 'relationName',
26
+ 'foreignKey',
27
+ );
28
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
29
+ await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
30
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
31
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
32
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
33
+ await expect(throwable({})).to.be.rejectedWith(error('Object'));
34
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
35
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
36
+ });
37
+
38
+ it('requires elements of the "entities" parameter to be an Object', async function () {
39
+ const S = new Schema();
40
+ S.defineModel({name: 'source'});
41
+ const R = S.getService(HasOneResolver);
42
+ const error = v =>
43
+ format(
44
+ 'The parameter "entities" of HasOneResolver.includeTo requires ' +
45
+ 'an Array of Object, but %s given.',
46
+ v,
47
+ );
48
+ const throwable = v =>
49
+ R.includeTo([v], 'source', 'target', 'relationName', 'foreignKey');
50
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
51
+ await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
52
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
53
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
54
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
55
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
56
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
57
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
58
+ });
59
+
60
+ it('requires the "sourceName" parameter to be a non-empty string', async function () {
61
+ const S = new Schema();
62
+ const R = S.getService(HasOneResolver);
63
+ const error = v =>
64
+ format(
65
+ 'The parameter "sourceName" of HasOneResolver.includeTo requires ' +
66
+ 'a non-empty String, but %s given.',
67
+ v,
68
+ );
69
+ const throwable = v =>
70
+ R.includeTo([], v, 'targetName', 'relationName', 'foreignKey');
71
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
72
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
73
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
74
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
75
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
76
+ await expect(throwable({})).to.be.rejectedWith(error('Object'));
77
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
78
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
79
+ });
80
+
81
+ it('requires the "targetName" parameter to be a non-empty string', async function () {
82
+ const S = new Schema();
83
+ const R = S.getService(HasOneResolver);
84
+ const error = v =>
85
+ format(
86
+ 'The parameter "targetName" of HasOneResolver.includeTo requires ' +
87
+ 'a non-empty String, but %s given.',
88
+ v,
89
+ );
90
+ const throwable = v =>
91
+ R.includeTo([], 'sourceName', v, 'relationName', 'foreignKey');
92
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
93
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
94
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
95
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
96
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
97
+ await expect(throwable({})).to.be.rejectedWith(error('Object'));
98
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
99
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
100
+ });
101
+
102
+ it('requires the "relationName" parameter to be a non-empty string', async function () {
103
+ const S = new Schema();
104
+ const R = S.getService(HasOneResolver);
105
+ const error = v =>
106
+ format(
107
+ 'The parameter "relationName" of HasOneResolver.includeTo requires ' +
108
+ 'a non-empty String, but %s given.',
109
+ v,
110
+ );
111
+ const throwable = v =>
112
+ R.includeTo([], 'sourceName', 'targetName', v, 'foreignKey');
113
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
114
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
115
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
116
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
117
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
118
+ await expect(throwable({})).to.be.rejectedWith(error('Object'));
119
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
120
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
121
+ });
122
+
123
+ it('requires the "foreignKey" parameter to be a non-empty string', async function () {
124
+ const S = new Schema();
125
+ const R = S.getService(HasOneResolver);
126
+ const error = v =>
127
+ format(
128
+ 'The parameter "foreignKey" of HasOneResolver.includeTo requires ' +
129
+ 'a non-empty String, but %s given.',
130
+ v,
131
+ );
132
+ const throwable = v =>
133
+ R.includeTo([], 'sourceName', 'targetName', 'relationName', v);
134
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
135
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
136
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
137
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
138
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
139
+ await expect(throwable({})).to.be.rejectedWith(error('Object'));
140
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
141
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
142
+ });
143
+
144
+ it('requires the provided parameter "scope" to be an object', async function () {
145
+ const S = new Schema();
146
+ const R = S.getService(HasOneResolver);
147
+ const error = v =>
148
+ format(
149
+ 'The provided parameter "scope" of HasOneResolver.includeTo ' +
150
+ 'should be an Object, but %s given.',
151
+ v,
152
+ );
153
+ const throwable = v =>
154
+ R.includeTo(
155
+ [],
156
+ 'sourceName',
157
+ 'targetName',
158
+ 'relationName',
159
+ 'foreignKey',
160
+ v,
161
+ );
162
+ await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
163
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
164
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
165
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
166
+ });
167
+
168
+ it('throws an error if a target model is not found', async function () {
169
+ const S = new Schema();
170
+ S.defineModel({name: 'source'});
171
+ const R = S.getService(HasOneResolver);
172
+ const promise = R.includeTo(
173
+ [],
174
+ 'source',
175
+ 'target',
176
+ 'relationName',
177
+ 'foreignKey',
178
+ );
179
+ await expect(promise).to.be.rejectedWith(
180
+ 'The model "target" is not defined',
181
+ );
182
+ });
183
+
184
+ it('throws an error if a target model does not have datasource', async function () {
185
+ const S = new Schema();
186
+ S.defineModel({name: 'source'});
187
+ S.defineModel({name: 'target'});
188
+ const R = S.getService(HasOneResolver);
189
+ const promise = R.includeTo(
190
+ [],
191
+ 'source',
192
+ 'target',
193
+ 'relationName',
194
+ 'foreignKey',
195
+ );
196
+ await expect(promise).to.be.rejectedWith(
197
+ 'The model "target" does not have a specified datasource.',
198
+ );
199
+ });
200
+
201
+ it('does not throw an error if a relation target is not exist', async function () {
202
+ const S = new Schema();
203
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
204
+ S.defineModel({name: 'source', datasource: 'datasource'});
205
+ S.defineModel({name: 'target', datasource: 'datasource'});
206
+ const sourceRel = S.getRepository('source');
207
+ const source = await sourceRel.create({});
208
+ const R = S.getService(HasOneResolver);
209
+ await R.includeTo([source], 'source', 'target', 'child', 'parentId');
210
+ expect(source).to.be.eql({
211
+ [DEF_PK]: source[DEF_PK],
212
+ });
213
+ });
214
+
215
+ it('includes if a primary key is not defined in the source model', async function () {
216
+ const S = new Schema();
217
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
218
+ S.defineModel({name: 'source', datasource: 'datasource'});
219
+ S.defineModel({name: 'target', datasource: 'datasource'});
220
+ const sourceRep = S.getRepository('source');
221
+ const targetRep = S.getRepository('target');
222
+ const source = await sourceRep.create({});
223
+ expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
224
+ const target = await targetRep.create({parentId: source[DEF_PK]});
225
+ expect(target).to.be.eql({
226
+ [DEF_PK]: target[DEF_PK],
227
+ parentId: source[DEF_PK],
228
+ });
229
+ const R = S.getService(HasOneResolver);
230
+ await R.includeTo([source], 'source', 'target', 'child', 'parentId');
231
+ expect(source).to.be.eql({
232
+ [DEF_PK]: source[DEF_PK],
233
+ child: {
234
+ id: target[DEF_PK],
235
+ parentId: source[DEF_PK],
236
+ },
237
+ });
238
+ });
239
+
240
+ it('includes if the source model has a custom primary key', async function () {
241
+ const S = new Schema();
242
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
243
+ S.defineModel({
244
+ name: 'source',
245
+ datasource: 'datasource',
246
+ properties: {
247
+ myId: {
248
+ type: DataType.NUMBER,
249
+ primaryKey: true,
250
+ },
251
+ },
252
+ });
253
+ S.defineModel({name: 'target', datasource: 'datasource'});
254
+ const sourceRep = S.getRepository('source');
255
+ const targetRep = S.getRepository('target');
256
+ const source = await sourceRep.create({});
257
+ expect(source).to.be.eql({myId: source.myId});
258
+ const target = await targetRep.create({parentId: source.myId});
259
+ expect(target).to.be.eql({
260
+ [DEF_PK]: target[DEF_PK],
261
+ parentId: source.myId,
262
+ });
263
+ const R = S.getService(HasOneResolver);
264
+ await R.includeTo([source], 'source', 'target', 'child', 'parentId');
265
+ expect(source).to.be.eql({
266
+ myId: source.myId,
267
+ child: {
268
+ [DEF_PK]: target[DEF_PK],
269
+ parentId: source.myId,
270
+ },
271
+ });
272
+ });
273
+
274
+ it('includes if the target model has a custom primary key', async function () {
275
+ const S = new Schema();
276
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
277
+ S.defineModel({name: 'source', datasource: 'datasource'});
278
+ S.defineModel({
279
+ name: 'target',
280
+ datasource: 'datasource',
281
+ properties: {
282
+ myId: {
283
+ type: DataType.NUMBER,
284
+ primaryKey: true,
285
+ },
286
+ },
287
+ });
288
+ const sourceRep = S.getRepository('source');
289
+ const targetRep = S.getRepository('target');
290
+ const source = await sourceRep.create({});
291
+ expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
292
+ const target = await targetRep.create({parentId: source[DEF_PK]});
293
+ expect(target).to.be.eql({
294
+ myId: target.myId,
295
+ parentId: source[DEF_PK],
296
+ });
297
+ const R = S.getService(HasOneResolver);
298
+ await R.includeTo([source], 'source', 'target', 'child', 'parentId');
299
+ expect(source).to.be.eql({
300
+ [DEF_PK]: source[DEF_PK],
301
+ child: {
302
+ myId: target.myId,
303
+ parentId: source[DEF_PK],
304
+ },
305
+ });
306
+ });
307
+
308
+ it('uses a where clause of the given scope to filter the relation target', async function () {
309
+ const S = new Schema();
310
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
311
+ S.defineModel({name: 'source', datasource: 'datasource'});
312
+ S.defineModel({name: 'target', datasource: 'datasource'});
313
+ const sourceRep = S.getRepository('source');
314
+ const targetRep = S.getRepository('target');
315
+ const source = await sourceRep.create({});
316
+ expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
317
+ const target1 = await targetRep.create({
318
+ featured: false,
319
+ parentId: source[DEF_PK],
320
+ });
321
+ expect(target1).to.be.eql({
322
+ [DEF_PK]: target1[DEF_PK],
323
+ featured: false,
324
+ parentId: source[DEF_PK],
325
+ });
326
+ const target2 = await targetRep.create({
327
+ featured: true,
328
+ parentId: source[DEF_PK],
329
+ });
330
+ expect(target2).to.be.eql({
331
+ [DEF_PK]: target2[DEF_PK],
332
+ featured: true,
333
+ parentId: source[DEF_PK],
334
+ });
335
+ const R = S.getService(HasOneResolver);
336
+ await R.includeTo([source], 'source', 'target', 'child', 'parentId', {
337
+ where: {featured: false},
338
+ });
339
+ expect(source).to.be.eql({
340
+ [DEF_PK]: source[DEF_PK],
341
+ child: {
342
+ [DEF_PK]: target1[DEF_PK],
343
+ featured: false,
344
+ parentId: source[DEF_PK],
345
+ },
346
+ });
347
+ await R.includeTo([source], 'source', 'target', 'child', 'parentId', {
348
+ where: {featured: true},
349
+ });
350
+ expect(source).to.be.eql({
351
+ [DEF_PK]: source[DEF_PK],
352
+ child: {
353
+ [DEF_PK]: target2[DEF_PK],
354
+ featured: true,
355
+ parentId: source[DEF_PK],
356
+ },
357
+ });
358
+ });
359
+
360
+ it('uses a fields clause of the given scope to filter the relation target', async function () {
361
+ const S = new Schema();
362
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
363
+ S.defineModel({name: 'source', datasource: 'datasource'});
364
+ S.defineModel({name: 'target', datasource: 'datasource'});
365
+ const sourceRep = S.getRepository('source');
366
+ const targetRep = S.getRepository('target');
367
+ const source = await sourceRep.create({});
368
+ expect(source).to.be.eql({
369
+ [DEF_PK]: source[DEF_PK],
370
+ });
371
+ const target = await targetRep.create({
372
+ foo: 'fooVal',
373
+ bar: 'barVal',
374
+ parentId: source[DEF_PK],
375
+ });
376
+ expect(target).to.be.eql({
377
+ [DEF_PK]: target[DEF_PK],
378
+ foo: target.foo,
379
+ bar: target.bar,
380
+ parentId: source[DEF_PK],
381
+ });
382
+ const R = S.getService(HasOneResolver);
383
+ await R.includeTo([source], 'source', 'target', 'child', 'parentId', {
384
+ fields: [DEF_PK, 'bar'],
385
+ });
386
+ expect(source).to.be.eql({
387
+ [DEF_PK]: source[DEF_PK],
388
+ child: {
389
+ [DEF_PK]: target[DEF_PK],
390
+ bar: target.bar,
391
+ },
392
+ });
393
+ });
394
+
395
+ it('uses an include clause of the given scope to resolve target relations', async function () {
396
+ const S = new Schema();
397
+ S.defineDatasource({
398
+ name: 'datasource',
399
+ adapter: 'memory',
400
+ });
401
+ S.defineModel({
402
+ name: 'modelA',
403
+ datasource: 'datasource',
404
+ properties: {
405
+ id: {
406
+ type: DataType.NUMBER,
407
+ primaryKey: true,
408
+ },
409
+ source: {
410
+ type: DataType.STRING,
411
+ default: 'modelA',
412
+ },
413
+ },
414
+ relations: {
415
+ child: {
416
+ type: RelationType.HAS_ONE,
417
+ model: 'modelB',
418
+ foreignKey: 'parentId',
419
+ },
420
+ },
421
+ });
422
+ S.defineModel({
423
+ name: 'modelB',
424
+ datasource: 'datasource',
425
+ properties: {
426
+ id: {
427
+ type: DataType.NUMBER,
428
+ primaryKey: true,
429
+ },
430
+ source: {
431
+ type: DataType.STRING,
432
+ default: 'modelB',
433
+ },
434
+ },
435
+ relations: {
436
+ child: {
437
+ type: RelationType.HAS_ONE,
438
+ model: 'modelC',
439
+ foreignKey: 'parentId',
440
+ },
441
+ },
442
+ });
443
+ S.defineModel({
444
+ name: 'modelC',
445
+ datasource: 'datasource',
446
+ properties: {
447
+ id: {
448
+ type: DataType.NUMBER,
449
+ primaryKey: true,
450
+ },
451
+ source: {
452
+ type: DataType.STRING,
453
+ default: 'modelC',
454
+ },
455
+ },
456
+ });
457
+ const aRep = S.getRepository('modelA');
458
+ const bRep = S.getRepository('modelB');
459
+ const cRep = S.getRepository('modelC');
460
+ const a = await aRep.create({});
461
+ const b = await bRep.create({parentId: a.id});
462
+ const c = await cRep.create({parentId: b.id});
463
+ expect(a).to.be.eql({
464
+ id: a.id,
465
+ source: 'modelA',
466
+ });
467
+ expect(b).to.be.eql({
468
+ id: b.id,
469
+ source: 'modelB',
470
+ parentId: a.id,
471
+ });
472
+ expect(c).to.be.eql({
473
+ id: c.id,
474
+ source: 'modelC',
475
+ parentId: b.id,
476
+ });
477
+ const R = S.getService(HasOneResolver);
478
+ await R.includeTo([a], 'modelA', 'modelB', 'child', 'parentId', {
479
+ include: 'child',
480
+ });
481
+ expect(a).to.be.eql({
482
+ id: a.id,
483
+ source: 'modelA',
484
+ child: {
485
+ id: b.id,
486
+ source: 'modelB',
487
+ parentId: a.id,
488
+ child: {
489
+ id: c.id,
490
+ source: 'modelC',
491
+ parentId: b.id,
492
+ },
493
+ },
494
+ });
495
+ });
496
+
497
+ it('does not break the "and" operator of the given "where" clause', async function () {
498
+ const S = new Schema();
499
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
500
+ S.defineModel({name: 'source', datasource: 'datasource'});
501
+ S.defineModel({name: 'target', datasource: 'datasource'});
502
+ const sourceRep = S.getRepository('source');
503
+ const targetRep = S.getRepository('target');
504
+ const source = await sourceRep.create({});
505
+ expect(source).to.be.eql({
506
+ [DEF_PK]: source[DEF_PK],
507
+ });
508
+ const target1 = await targetRep.create({
509
+ featured: false,
510
+ parentId: source[DEF_PK],
511
+ });
512
+ expect(target1).to.be.eql({
513
+ [DEF_PK]: target1[DEF_PK],
514
+ featured: false,
515
+ parentId: source[DEF_PK],
516
+ });
517
+ const target2 = await targetRep.create({
518
+ featured: true,
519
+ parentId: source[DEF_PK],
520
+ });
521
+ expect(target2).to.be.eql({
522
+ [DEF_PK]: target2[DEF_PK],
523
+ featured: true,
524
+ parentId: source[DEF_PK],
525
+ });
526
+ const R = S.getService(HasOneResolver);
527
+ await R.includeTo([source], 'source', 'target', 'child', 'parentId', {
528
+ where: {and: [{featured: false}]},
529
+ });
530
+ expect(source).to.be.eql({
531
+ [DEF_PK]: source[DEF_PK],
532
+ child: {
533
+ [DEF_PK]: target1[DEF_PK],
534
+ featured: false,
535
+ parentId: source[DEF_PK],
536
+ },
537
+ });
538
+ delete source.child;
539
+ await R.includeTo([source], 'source', 'target', 'child', 'parentId', {
540
+ where: {and: [{featured: true}]},
541
+ });
542
+ expect(source).to.be.eql({
543
+ [DEF_PK]: source[DEF_PK],
544
+ child: {
545
+ [DEF_PK]: target2[DEF_PK],
546
+ featured: true,
547
+ parentId: source[DEF_PK],
548
+ },
549
+ });
550
+ });
551
+ });
552
+
553
+ describe('includePolymorphicTo', function () {
554
+ it('requires the "entities" parameter to be an array', async function () {
555
+ const S = new Schema();
556
+ const R = S.getService(HasOneResolver);
557
+ const error = v =>
558
+ format(
559
+ 'The parameter "entities" of HasOneResolver.includePolymorphicTo requires ' +
560
+ 'an Array of Object, but %s given.',
561
+ v,
562
+ );
563
+ const throwable = v =>
564
+ R.includePolymorphicTo(
565
+ v,
566
+ 'sourceName',
567
+ 'targetName',
568
+ 'relationName',
569
+ 'foreignKey',
570
+ 'discriminator',
571
+ );
572
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
573
+ await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
574
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
575
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
576
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
577
+ await expect(throwable({})).to.be.rejectedWith(error('Object'));
578
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
579
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
580
+ });
581
+
582
+ it('requires elements of the "entities" parameter to be an Object', async function () {
583
+ const S = new Schema();
584
+ S.defineModel({name: 'source'});
585
+ const R = S.getService(HasOneResolver);
586
+ const error = v =>
587
+ format(
588
+ 'The parameter "entities" of HasOneResolver.includePolymorphicTo requires ' +
589
+ 'an Array of Object, but %s given.',
590
+ v,
591
+ );
592
+ const throwable = v =>
593
+ R.includePolymorphicTo(
594
+ [v],
595
+ 'source',
596
+ 'target',
597
+ 'relationName',
598
+ 'foreignKey',
599
+ 'discriminator',
600
+ );
601
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
602
+ await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
603
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
604
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
605
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
606
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
607
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
608
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
609
+ });
610
+
611
+ it('requires the "sourceName" parameter to be a non-empty string', async function () {
612
+ const S = new Schema();
613
+ const R = S.getService(HasOneResolver);
614
+ const error = v =>
615
+ format(
616
+ 'The parameter "sourceName" of HasOneResolver.includePolymorphicTo requires ' +
617
+ 'a non-empty String, but %s given.',
618
+ v,
619
+ );
620
+ const throwable = v =>
621
+ R.includePolymorphicTo(
622
+ [],
623
+ v,
624
+ 'targetName',
625
+ 'relationName',
626
+ 'foreignKey',
627
+ 'discriminator',
628
+ );
629
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
630
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
631
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
632
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
633
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
634
+ await expect(throwable({})).to.be.rejectedWith(error('Object'));
635
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
636
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
637
+ });
638
+
639
+ it('requires the "targetName" parameter to be a non-empty string', async function () {
640
+ const S = new Schema();
641
+ const R = S.getService(HasOneResolver);
642
+ const error = v =>
643
+ format(
644
+ 'The parameter "targetName" of HasOneResolver.includePolymorphicTo requires ' +
645
+ 'a non-empty String, but %s given.',
646
+ v,
647
+ );
648
+ const throwable = v =>
649
+ R.includePolymorphicTo(
650
+ [],
651
+ 'sourceName',
652
+ v,
653
+ 'relationName',
654
+ 'foreignKey',
655
+ 'discriminator',
656
+ );
657
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
658
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
659
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
660
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
661
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
662
+ await expect(throwable({})).to.be.rejectedWith(error('Object'));
663
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
664
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
665
+ });
666
+
667
+ it('requires the "relationName" parameter to be a non-empty string', async function () {
668
+ const S = new Schema();
669
+ const R = S.getService(HasOneResolver);
670
+ const error = v =>
671
+ format(
672
+ 'The parameter "relationName" of HasOneResolver.includePolymorphicTo requires ' +
673
+ 'a non-empty String, but %s given.',
674
+ v,
675
+ );
676
+ const throwable = v =>
677
+ R.includePolymorphicTo(
678
+ [],
679
+ 'sourceName',
680
+ 'targetName',
681
+ v,
682
+ 'foreignKey',
683
+ 'discriminator',
684
+ );
685
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
686
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
687
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
688
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
689
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
690
+ await expect(throwable({})).to.be.rejectedWith(error('Object'));
691
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
692
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
693
+ });
694
+
695
+ it('requires the "foreignKey" parameter to be a non-empty string', async function () {
696
+ const S = new Schema();
697
+ const R = S.getService(HasOneResolver);
698
+ const error = v =>
699
+ format(
700
+ 'The parameter "foreignKey" of HasOneResolver.includePolymorphicTo requires ' +
701
+ 'a non-empty String, but %s given.',
702
+ v,
703
+ );
704
+ const throwable = v =>
705
+ R.includePolymorphicTo(
706
+ [],
707
+ 'sourceName',
708
+ 'targetName',
709
+ 'relationName',
710
+ v,
711
+ 'discriminator',
712
+ );
713
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
714
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
715
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
716
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
717
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
718
+ await expect(throwable({})).to.be.rejectedWith(error('Object'));
719
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
720
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
721
+ });
722
+
723
+ it('requires the "discriminator" parameter to be a non-empty string', async function () {
724
+ const S = new Schema();
725
+ const R = S.getService(HasOneResolver);
726
+ const error = v =>
727
+ format(
728
+ 'The parameter "discriminator" of HasOneResolver.includePolymorphicTo requires ' +
729
+ 'a non-empty String, but %s given.',
730
+ v,
731
+ );
732
+ const throwable = v =>
733
+ R.includePolymorphicTo(
734
+ [],
735
+ 'sourceName',
736
+ 'targetName',
737
+ 'relationName',
738
+ 'foreignKey',
739
+ v,
740
+ );
741
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
742
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
743
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
744
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
745
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
746
+ await expect(throwable({})).to.be.rejectedWith(error('Object'));
747
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
748
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
749
+ });
750
+
751
+ it('requires the provided parameter "scope" to be an object', async function () {
752
+ const S = new Schema();
753
+ const R = S.getService(HasOneResolver);
754
+ const error = v =>
755
+ format(
756
+ 'The provided parameter "scope" of HasOneResolver.includePolymorphicTo ' +
757
+ 'should be an Object, but %s given.',
758
+ v,
759
+ );
760
+ const throwable = v =>
761
+ R.includePolymorphicTo(
762
+ [],
763
+ 'sourceName',
764
+ 'targetName',
765
+ 'relationName',
766
+ 'foreignKey',
767
+ 'discriminator',
768
+ v,
769
+ );
770
+ await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
771
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
772
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
773
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
774
+ });
775
+
776
+ it('throws an error if the given target model is not found', async function () {
777
+ const S = new Schema();
778
+ S.defineModel({name: 'source'});
779
+ const R = S.getService(HasOneResolver);
780
+ const entity = {[DEF_PK]: 1};
781
+ const promise = R.includePolymorphicTo(
782
+ [entity],
783
+ 'source',
784
+ 'target',
785
+ 'child',
786
+ 'parentId',
787
+ 'parentType',
788
+ );
789
+ await expect(promise).to.be.rejectedWith(
790
+ 'The model "target" is not defined',
791
+ );
792
+ });
793
+
794
+ it('throws an error if the given target model does not have a datasource', async function () {
795
+ const S = new Schema();
796
+ S.defineModel({name: 'source'});
797
+ S.defineModel({name: 'target'});
798
+ const R = S.getService(HasOneResolver);
799
+ const entity = {[DEF_PK]: 1};
800
+ const promise = R.includePolymorphicTo(
801
+ [entity],
802
+ 'source',
803
+ 'target',
804
+ 'child',
805
+ 'parentId',
806
+ 'parentType',
807
+ );
808
+ await expect(promise).to.be.rejectedWith(
809
+ 'The model "target" does not have a specified datasource.',
810
+ );
811
+ });
812
+
813
+ it('does not throw an error if a relation target is not found', async function () {
814
+ const S = new Schema();
815
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
816
+ S.defineModel({name: 'source', datasource: 'datasource'});
817
+ S.defineModel({name: 'target', datasource: 'datasource'});
818
+ const sourceRel = S.getRepository('source');
819
+ const source = await sourceRel.create({});
820
+ expect(source).to.be.eql({
821
+ [DEF_PK]: source[DEF_PK],
822
+ });
823
+ const R = S.getService(HasOneResolver);
824
+ await R.includePolymorphicTo(
825
+ [source],
826
+ 'source',
827
+ 'target',
828
+ 'child',
829
+ 'parentId',
830
+ 'parentType',
831
+ );
832
+ expect(source).to.be.eql({
833
+ [DEF_PK]: source[DEF_PK],
834
+ });
835
+ });
836
+
837
+ it('does not include an entity with a not matched discriminator value', async function () {
838
+ const S = new Schema();
839
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
840
+ S.defineModel({name: 'source', datasource: 'datasource'});
841
+ S.defineModel({name: 'target', datasource: 'datasource'});
842
+ const sourceRel = S.getRepository('source');
843
+ const targetRel = S.getRepository('target');
844
+ const source = await sourceRel.create({});
845
+ expect(source).to.be.eql({
846
+ [DEF_PK]: source[DEF_PK],
847
+ });
848
+ const target = await targetRel.create({
849
+ parentId: source[DEF_PK],
850
+ parentType: 'unknown',
851
+ });
852
+ expect(target).to.be.eql({
853
+ [DEF_PK]: target[DEF_PK],
854
+ parentId: source[DEF_PK],
855
+ parentType: 'unknown',
856
+ });
857
+ const R = S.getService(HasOneResolver);
858
+ await R.includePolymorphicTo(
859
+ [source],
860
+ 'source',
861
+ 'target',
862
+ 'child',
863
+ 'parentId',
864
+ 'parentType',
865
+ );
866
+ expect(source).to.be.eql({
867
+ [DEF_PK]: source[DEF_PK],
868
+ });
869
+ });
870
+
871
+ it('includes if a primary key is not defined in the source model', async function () {
872
+ const S = new Schema();
873
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
874
+ S.defineModel({name: 'source', datasource: 'datasource'});
875
+ S.defineModel({name: 'target', datasource: 'datasource'});
876
+ const sourceRep = S.getRepository('source');
877
+ const targetRep = S.getRepository('target');
878
+ const source = await sourceRep.create({});
879
+ expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
880
+ const target = await targetRep.create({
881
+ parentId: source[DEF_PK],
882
+ parentType: 'source',
883
+ });
884
+ expect(target).to.be.eql({
885
+ [DEF_PK]: target[DEF_PK],
886
+ parentId: source[DEF_PK],
887
+ parentType: target.parentType,
888
+ });
889
+ const R = S.getService(HasOneResolver);
890
+ await R.includePolymorphicTo(
891
+ [source],
892
+ 'source',
893
+ 'target',
894
+ 'child',
895
+ 'parentId',
896
+ 'parentType',
897
+ );
898
+ expect(source).to.be.eql({
899
+ [DEF_PK]: source[DEF_PK],
900
+ child: {
901
+ id: target[DEF_PK],
902
+ parentId: source[DEF_PK],
903
+ parentType: target.parentType,
904
+ },
905
+ });
906
+ });
907
+
908
+ it('includes if the source model has a custom primary key', async function () {
909
+ const S = new Schema();
910
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
911
+ S.defineModel({
912
+ name: 'source',
913
+ datasource: 'datasource',
914
+ properties: {
915
+ myId: {
916
+ type: DataType.NUMBER,
917
+ primaryKey: true,
918
+ },
919
+ },
920
+ });
921
+ S.defineModel({name: 'target', datasource: 'datasource'});
922
+ const sourceRep = S.getRepository('source');
923
+ const targetRep = S.getRepository('target');
924
+ const source = await sourceRep.create({});
925
+ expect(source).to.be.eql({myId: source.myId});
926
+ const target = await targetRep.create({
927
+ parentId: source.myId,
928
+ parentType: 'source',
929
+ });
930
+ expect(target).to.be.eql({
931
+ [DEF_PK]: target[DEF_PK],
932
+ parentId: source.myId,
933
+ parentType: target.parentType,
934
+ });
935
+ const R = S.getService(HasOneResolver);
936
+ await R.includePolymorphicTo(
937
+ [source],
938
+ 'source',
939
+ 'target',
940
+ 'child',
941
+ 'parentId',
942
+ 'parentType',
943
+ );
944
+ expect(source).to.be.eql({
945
+ myId: source.myId,
946
+ child: {
947
+ [DEF_PK]: target[DEF_PK],
948
+ parentId: source.myId,
949
+ parentType: target.parentType,
950
+ },
951
+ });
952
+ });
953
+
954
+ it('includes if the target model has a custom primary key', async function () {
955
+ const S = new Schema();
956
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
957
+ S.defineModel({name: 'source', datasource: 'datasource'});
958
+ S.defineModel({
959
+ name: 'target',
960
+ datasource: 'datasource',
961
+ properties: {
962
+ myId: {
963
+ type: DataType.NUMBER,
964
+ primaryKey: true,
965
+ },
966
+ },
967
+ });
968
+ const sourceRep = S.getRepository('source');
969
+ const targetRep = S.getRepository('target');
970
+ const source = await sourceRep.create({});
971
+ expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
972
+ const target = await targetRep.create({
973
+ parentId: source[DEF_PK],
974
+ parentType: 'source',
975
+ });
976
+ expect(target).to.be.eql({
977
+ myId: target.myId,
978
+ parentId: source[DEF_PK],
979
+ parentType: target.parentType,
980
+ });
981
+ const R = S.getService(HasOneResolver);
982
+ await R.includePolymorphicTo(
983
+ [source],
984
+ 'source',
985
+ 'target',
986
+ 'child',
987
+ 'parentId',
988
+ 'parentType',
989
+ );
990
+ expect(source).to.be.eql({
991
+ [DEF_PK]: source[DEF_PK],
992
+ child: {
993
+ myId: target.myId,
994
+ parentId: source[DEF_PK],
995
+ parentType: target.parentType,
996
+ },
997
+ });
998
+ });
999
+
1000
+ it('uses a where clause of the given scope to filter the relation target', async function () {
1001
+ const S = new Schema();
1002
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
1003
+ S.defineModel({name: 'source', datasource: 'datasource'});
1004
+ S.defineModel({name: 'target', datasource: 'datasource'});
1005
+ const sourceRep = S.getRepository('source');
1006
+ const targetRep = S.getRepository('target');
1007
+ const source = await sourceRep.create({});
1008
+ expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
1009
+ const target1 = await targetRep.create({
1010
+ featured: false,
1011
+ parentId: source[DEF_PK],
1012
+ parentType: 'source',
1013
+ });
1014
+ expect(target1).to.be.eql({
1015
+ [DEF_PK]: target1[DEF_PK],
1016
+ featured: false,
1017
+ parentId: source[DEF_PK],
1018
+ parentType: target1.parentType,
1019
+ });
1020
+ const target2 = await targetRep.create({
1021
+ featured: true,
1022
+ parentId: source[DEF_PK],
1023
+ parentType: 'source',
1024
+ });
1025
+ expect(target2).to.be.eql({
1026
+ [DEF_PK]: target2[DEF_PK],
1027
+ featured: true,
1028
+ parentId: source[DEF_PK],
1029
+ parentType: target2.parentType,
1030
+ });
1031
+ const R = S.getService(HasOneResolver);
1032
+ await R.includePolymorphicTo(
1033
+ [source],
1034
+ 'source',
1035
+ 'target',
1036
+ 'child',
1037
+ 'parentId',
1038
+ 'parentType',
1039
+ {where: {featured: false}},
1040
+ );
1041
+ expect(source).to.be.eql({
1042
+ [DEF_PK]: source[DEF_PK],
1043
+ child: {
1044
+ [DEF_PK]: target1[DEF_PK],
1045
+ featured: false,
1046
+ parentId: source[DEF_PK],
1047
+ parentType: target1.parentType,
1048
+ },
1049
+ });
1050
+ await R.includePolymorphicTo(
1051
+ [source],
1052
+ 'source',
1053
+ 'target',
1054
+ 'child',
1055
+ 'parentId',
1056
+ 'parentType',
1057
+ {where: {featured: true}},
1058
+ );
1059
+ expect(source).to.be.eql({
1060
+ [DEF_PK]: source[DEF_PK],
1061
+ child: {
1062
+ [DEF_PK]: target2[DEF_PK],
1063
+ featured: true,
1064
+ parentId: source[DEF_PK],
1065
+ parentType: target2.parentType,
1066
+ },
1067
+ });
1068
+ });
1069
+
1070
+ it('uses a fields clause of the given scope to filter the relation target', async function () {
1071
+ const S = new Schema();
1072
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
1073
+ S.defineModel({name: 'source', datasource: 'datasource'});
1074
+ S.defineModel({name: 'target', datasource: 'datasource'});
1075
+ const sourceRep = S.getRepository('source');
1076
+ const targetRep = S.getRepository('target');
1077
+ const source = await sourceRep.create({});
1078
+ expect(source).to.be.eql({
1079
+ [DEF_PK]: source[DEF_PK],
1080
+ });
1081
+ const target = await targetRep.create({
1082
+ foo: 'fooVal',
1083
+ bar: 'barVal',
1084
+ parentId: source[DEF_PK],
1085
+ parentType: 'source',
1086
+ });
1087
+ expect(target).to.be.eql({
1088
+ [DEF_PK]: target[DEF_PK],
1089
+ foo: target.foo,
1090
+ bar: target.bar,
1091
+ parentId: source[DEF_PK],
1092
+ parentType: target.parentType,
1093
+ });
1094
+ const R = S.getService(HasOneResolver);
1095
+ await R.includePolymorphicTo(
1096
+ [source],
1097
+ 'source',
1098
+ 'target',
1099
+ 'child',
1100
+ 'parentId',
1101
+ 'parentType',
1102
+ {fields: [DEF_PK, 'bar']},
1103
+ );
1104
+ expect(source).to.be.eql({
1105
+ [DEF_PK]: source[DEF_PK],
1106
+ child: {
1107
+ [DEF_PK]: target[DEF_PK],
1108
+ bar: target.bar,
1109
+ },
1110
+ });
1111
+ });
1112
+
1113
+ it('uses an include clause of the given scope to resolve target relations', async function () {
1114
+ const S = new Schema();
1115
+ S.defineDatasource({
1116
+ name: 'datasource',
1117
+ adapter: 'memory',
1118
+ });
1119
+ S.defineModel({
1120
+ name: 'modelA',
1121
+ datasource: 'datasource',
1122
+ properties: {
1123
+ id: {
1124
+ type: DataType.NUMBER,
1125
+ primaryKey: true,
1126
+ },
1127
+ source: {
1128
+ type: DataType.STRING,
1129
+ default: 'modelA',
1130
+ },
1131
+ },
1132
+ relations: {
1133
+ child: {
1134
+ type: RelationType.HAS_ONE,
1135
+ model: 'modelB',
1136
+ polymorphic: true,
1137
+ foreignKey: 'parentId',
1138
+ discriminator: 'parentType',
1139
+ },
1140
+ },
1141
+ });
1142
+ S.defineModel({
1143
+ name: 'modelB',
1144
+ datasource: 'datasource',
1145
+ properties: {
1146
+ id: {
1147
+ type: DataType.NUMBER,
1148
+ primaryKey: true,
1149
+ },
1150
+ source: {
1151
+ type: DataType.STRING,
1152
+ default: 'modelB',
1153
+ },
1154
+ },
1155
+ relations: {
1156
+ child: {
1157
+ type: RelationType.HAS_ONE,
1158
+ model: 'modelC',
1159
+ polymorphic: true,
1160
+ foreignKey: 'parentId',
1161
+ discriminator: 'parentType',
1162
+ },
1163
+ },
1164
+ });
1165
+ S.defineModel({
1166
+ name: 'modelC',
1167
+ datasource: 'datasource',
1168
+ properties: {
1169
+ id: {
1170
+ type: DataType.NUMBER,
1171
+ primaryKey: true,
1172
+ },
1173
+ source: {
1174
+ type: DataType.STRING,
1175
+ default: 'modelC',
1176
+ },
1177
+ },
1178
+ });
1179
+ const aRep = S.getRepository('modelA');
1180
+ const bRep = S.getRepository('modelB');
1181
+ const cRep = S.getRepository('modelC');
1182
+ const a = await aRep.create({});
1183
+ const b = await bRep.create({parentId: a.id, parentType: 'modelA'});
1184
+ const c = await cRep.create({parentId: b.id, parentType: 'modelB'});
1185
+ expect(a).to.be.eql({
1186
+ id: a.id,
1187
+ source: 'modelA',
1188
+ });
1189
+ expect(b).to.be.eql({
1190
+ id: b.id,
1191
+ source: 'modelB',
1192
+ parentId: a.id,
1193
+ parentType: 'modelA',
1194
+ });
1195
+ expect(c).to.be.eql({
1196
+ id: c.id,
1197
+ source: 'modelC',
1198
+ parentId: b.id,
1199
+ parentType: 'modelB',
1200
+ });
1201
+ const R = S.getService(HasOneResolver);
1202
+ await R.includePolymorphicTo(
1203
+ [a],
1204
+ 'modelA',
1205
+ 'modelB',
1206
+ 'child',
1207
+ 'parentId',
1208
+ 'parentType',
1209
+ {include: 'child'},
1210
+ );
1211
+ expect(a).to.be.eql({
1212
+ id: a.id,
1213
+ source: 'modelA',
1214
+ child: {
1215
+ id: b.id,
1216
+ source: 'modelB',
1217
+ parentId: a.id,
1218
+ parentType: 'modelA',
1219
+ child: {
1220
+ id: c.id,
1221
+ source: 'modelC',
1222
+ parentId: b.id,
1223
+ parentType: 'modelB',
1224
+ },
1225
+ },
1226
+ });
1227
+ });
1228
+
1229
+ it('does not break the "and" operator of the given "where" clause', async function () {
1230
+ const S = new Schema();
1231
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
1232
+ S.defineModel({name: 'source', datasource: 'datasource'});
1233
+ S.defineModel({name: 'target', datasource: 'datasource'});
1234
+ const sourceRep = S.getRepository('source');
1235
+ const targetRep = S.getRepository('target');
1236
+ const source = await sourceRep.create({});
1237
+ expect(source).to.be.eql({
1238
+ [DEF_PK]: source[DEF_PK],
1239
+ });
1240
+ const target1 = await targetRep.create({
1241
+ featured: false,
1242
+ parentId: source[DEF_PK],
1243
+ parentType: 'source',
1244
+ });
1245
+ expect(target1).to.be.eql({
1246
+ [DEF_PK]: target1[DEF_PK],
1247
+ featured: false,
1248
+ parentId: source[DEF_PK],
1249
+ parentType: target1.parentType,
1250
+ });
1251
+ const target2 = await targetRep.create({
1252
+ featured: true,
1253
+ parentId: source[DEF_PK],
1254
+ parentType: 'source',
1255
+ });
1256
+ expect(target2).to.be.eql({
1257
+ [DEF_PK]: target2[DEF_PK],
1258
+ featured: true,
1259
+ parentId: source[DEF_PK],
1260
+ parentType: target2.parentType,
1261
+ });
1262
+ const R = S.getService(HasOneResolver);
1263
+ await R.includePolymorphicTo(
1264
+ [source],
1265
+ 'source',
1266
+ 'target',
1267
+ 'child',
1268
+ 'parentId',
1269
+ 'parentType',
1270
+ {where: {and: [{featured: false}]}},
1271
+ );
1272
+ expect(source).to.be.eql({
1273
+ [DEF_PK]: source[DEF_PK],
1274
+ child: {
1275
+ [DEF_PK]: target1[DEF_PK],
1276
+ featured: false,
1277
+ parentId: source[DEF_PK],
1278
+ parentType: target1.parentType,
1279
+ },
1280
+ });
1281
+ delete source.child;
1282
+ await R.includePolymorphicTo(
1283
+ [source],
1284
+ 'source',
1285
+ 'target',
1286
+ 'child',
1287
+ 'parentId',
1288
+ 'parentType',
1289
+ {where: {and: [{featured: true}]}},
1290
+ );
1291
+ expect(source).to.be.eql({
1292
+ [DEF_PK]: source[DEF_PK],
1293
+ child: {
1294
+ [DEF_PK]: target2[DEF_PK],
1295
+ featured: true,
1296
+ parentId: source[DEF_PK],
1297
+ parentType: target2.parentType,
1298
+ },
1299
+ });
1300
+ });
1301
+ });
1302
+
1303
+ describe('includePolymorphicByRelationName', function () {
1304
+ it('requires the "entities" parameter to be an array', async function () {
1305
+ const S = new Schema();
1306
+ const R = S.getService(HasOneResolver);
1307
+ const error = v =>
1308
+ format(
1309
+ 'The parameter "entities" of HasOneResolver.includePolymorphicByRelationName requires ' +
1310
+ 'an Array of Object, but %s given.',
1311
+ v,
1312
+ );
1313
+ const throwable = v =>
1314
+ R.includePolymorphicByRelationName(
1315
+ v,
1316
+ 'sourceName',
1317
+ 'targetName',
1318
+ 'relationName',
1319
+ 'targetRelationName',
1320
+ );
1321
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
1322
+ await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
1323
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
1324
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
1325
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
1326
+ await expect(throwable({})).to.be.rejectedWith(error('Object'));
1327
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
1328
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
1329
+ });
1330
+
1331
+ it('requires elements of the "entities" parameter to be an Object', async function () {
1332
+ const S = new Schema();
1333
+ S.defineModel({name: 'source'});
1334
+ S.defineModel({
1335
+ name: 'target',
1336
+ relations: {
1337
+ parent: {
1338
+ type: RelationType.BELONGS_TO,
1339
+ polymorphic: true,
1340
+ },
1341
+ },
1342
+ });
1343
+ const R = S.getService(HasOneResolver);
1344
+ const error = v =>
1345
+ format(
1346
+ 'The parameter "entities" of HasOneResolver.includePolymorphicTo requires ' +
1347
+ 'an Array of Object, but %s given.',
1348
+ v,
1349
+ );
1350
+ const throwable = v =>
1351
+ R.includePolymorphicByRelationName(
1352
+ [v],
1353
+ 'source',
1354
+ 'target',
1355
+ 'child',
1356
+ 'parent',
1357
+ );
1358
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
1359
+ await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
1360
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
1361
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
1362
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
1363
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
1364
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
1365
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
1366
+ });
1367
+
1368
+ it('requires the "sourceName" parameter to be a non-empty string', async function () {
1369
+ const S = new Schema();
1370
+ const R = S.getService(HasOneResolver);
1371
+ const error = v =>
1372
+ format(
1373
+ 'The parameter "sourceName" of HasOneResolver.includePolymorphicByRelationName requires ' +
1374
+ 'a non-empty String, but %s given.',
1375
+ v,
1376
+ );
1377
+ const throwable = v =>
1378
+ R.includePolymorphicByRelationName(
1379
+ [],
1380
+ v,
1381
+ 'targetName',
1382
+ 'relationName',
1383
+ 'targetRelationName',
1384
+ );
1385
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
1386
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
1387
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
1388
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
1389
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
1390
+ await expect(throwable({})).to.be.rejectedWith(error('Object'));
1391
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
1392
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
1393
+ });
1394
+
1395
+ it('requires the "targetName" parameter to be a non-empty string', async function () {
1396
+ const S = new Schema();
1397
+ const R = S.getService(HasOneResolver);
1398
+ const error = v =>
1399
+ format(
1400
+ 'The parameter "targetName" of HasOneResolver.includePolymorphicByRelationName requires ' +
1401
+ 'a non-empty String, but %s given.',
1402
+ v,
1403
+ );
1404
+ const throwable = v =>
1405
+ R.includePolymorphicByRelationName(
1406
+ [],
1407
+ 'sourceName',
1408
+ v,
1409
+ 'relationName',
1410
+ 'targetRelationName',
1411
+ );
1412
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
1413
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
1414
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
1415
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
1416
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
1417
+ await expect(throwable({})).to.be.rejectedWith(error('Object'));
1418
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
1419
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
1420
+ });
1421
+
1422
+ it('requires the "relationName" parameter to be a non-empty string', async function () {
1423
+ const S = new Schema();
1424
+ const R = S.getService(HasOneResolver);
1425
+ const error = v =>
1426
+ format(
1427
+ 'The parameter "relationName" of HasOneResolver.includePolymorphicByRelationName requires ' +
1428
+ 'a non-empty String, but %s given.',
1429
+ v,
1430
+ );
1431
+ const throwable = v =>
1432
+ R.includePolymorphicByRelationName(
1433
+ [],
1434
+ 'sourceName',
1435
+ 'targetName',
1436
+ v,
1437
+ 'targetRelationName',
1438
+ );
1439
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
1440
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
1441
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
1442
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
1443
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
1444
+ await expect(throwable({})).to.be.rejectedWith(error('Object'));
1445
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
1446
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
1447
+ });
1448
+
1449
+ it('requires the "targetRelationName" parameter to be a non-empty string', async function () {
1450
+ const S = new Schema();
1451
+ const R = S.getService(HasOneResolver);
1452
+ const error = v =>
1453
+ format(
1454
+ 'The parameter "targetRelationName" of HasOneResolver.includePolymorphicByRelationName requires ' +
1455
+ 'a non-empty String, but %s given.',
1456
+ v,
1457
+ );
1458
+ const throwable = v =>
1459
+ R.includePolymorphicByRelationName(
1460
+ [],
1461
+ 'sourceName',
1462
+ 'targetName',
1463
+ 'relationName',
1464
+ v,
1465
+ );
1466
+ await expect(throwable('')).to.be.rejectedWith(error('""'));
1467
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
1468
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
1469
+ await expect(throwable(false)).to.be.rejectedWith(error('false'));
1470
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
1471
+ await expect(throwable({})).to.be.rejectedWith(error('Object'));
1472
+ await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
1473
+ await expect(throwable(null)).to.be.rejectedWith(error('null'));
1474
+ });
1475
+
1476
+ it('requires the provided parameter "scope" to be an object', async function () {
1477
+ const S = new Schema();
1478
+ const R = S.getService(HasOneResolver);
1479
+ const error = v =>
1480
+ format(
1481
+ 'The provided parameter "scope" of HasOneResolver.includePolymorphicByRelationName ' +
1482
+ 'should be an Object, but %s given.',
1483
+ v,
1484
+ );
1485
+ const throwable = v =>
1486
+ R.includePolymorphicByRelationName(
1487
+ [],
1488
+ 'sourceName',
1489
+ 'targetName',
1490
+ 'relationName',
1491
+ 'targetRelationName',
1492
+ v,
1493
+ );
1494
+ await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
1495
+ await expect(throwable(10)).to.be.rejectedWith(error('10'));
1496
+ await expect(throwable(true)).to.be.rejectedWith(error('true'));
1497
+ await expect(throwable([])).to.be.rejectedWith(error('Array'));
1498
+ });
1499
+
1500
+ it('throws an error if the given target model is not found', async function () {
1501
+ const S = new Schema();
1502
+ S.defineModel({name: 'source'});
1503
+ const R = S.getService(HasOneResolver);
1504
+ const entity = {[DEF_PK]: 1};
1505
+ const promise = R.includePolymorphicByRelationName(
1506
+ [entity],
1507
+ 'source',
1508
+ 'target',
1509
+ 'child',
1510
+ 'parent',
1511
+ );
1512
+ await expect(promise).to.be.rejectedWith(
1513
+ 'The model "target" is not defined',
1514
+ );
1515
+ });
1516
+
1517
+ it('throws an error if the given target model does not have the given relation name', async function () {
1518
+ const S = new Schema();
1519
+ S.defineModel({name: 'source'});
1520
+ S.defineModel({name: 'target'});
1521
+ const R = S.getService(HasOneResolver);
1522
+ const entity = {[DEF_PK]: 1};
1523
+ const promise = R.includePolymorphicByRelationName(
1524
+ [entity],
1525
+ 'source',
1526
+ 'target',
1527
+ 'child',
1528
+ 'parent',
1529
+ );
1530
+ await expect(promise).to.be.rejectedWith(
1531
+ 'The model "target" does not have relation name "parent".',
1532
+ );
1533
+ });
1534
+
1535
+ it('throws an error if the target relation is not "belongsTo"', async function () {
1536
+ const S = new Schema();
1537
+ S.defineModel({name: 'source'});
1538
+ S.defineModel({
1539
+ name: 'target',
1540
+ relations: {
1541
+ parent: {
1542
+ type: RelationType.REFERENCES_MANY,
1543
+ model: 'source',
1544
+ },
1545
+ },
1546
+ });
1547
+ const R = S.getService(HasOneResolver);
1548
+ const entity = {[DEF_PK]: 1};
1549
+ const promise = R.includePolymorphicByRelationName(
1550
+ [entity],
1551
+ 'source',
1552
+ 'target',
1553
+ 'child',
1554
+ 'parent',
1555
+ );
1556
+ await expect(promise).to.be.rejectedWith(
1557
+ 'The relation "child" of the model "source" is a polymorphic "hasOne" relation, ' +
1558
+ 'so it requires the target relation "parent" to be a polymorphic "belongsTo", ' +
1559
+ 'but "referencesMany" type given.',
1560
+ );
1561
+ });
1562
+
1563
+ it('throws an error if the target relation is not polymorphic', async function () {
1564
+ const S = new Schema();
1565
+ S.defineModel({name: 'source'});
1566
+ S.defineModel({
1567
+ name: 'target',
1568
+ relations: {
1569
+ parent: {
1570
+ type: RelationType.BELONGS_TO,
1571
+ model: 'source',
1572
+ },
1573
+ },
1574
+ });
1575
+ const R = S.getService(HasOneResolver);
1576
+ const entity = {[DEF_PK]: 1};
1577
+ const promise = R.includePolymorphicByRelationName(
1578
+ [entity],
1579
+ 'source',
1580
+ 'target',
1581
+ 'child',
1582
+ 'parent',
1583
+ );
1584
+ await expect(promise).to.be.rejectedWith(
1585
+ 'The relation "child" of the model "source" is a polymorphic ' +
1586
+ '"hasOne" relation, so it requires the target relation "parent" ' +
1587
+ 'to be a polymorphic too.',
1588
+ );
1589
+ });
1590
+
1591
+ it('throws an error if the given target model does not have a datasource', async function () {
1592
+ const S = new Schema();
1593
+ S.defineModel({name: 'source'});
1594
+ S.defineModel({
1595
+ name: 'target',
1596
+ relations: {
1597
+ parent: {
1598
+ type: RelationType.BELONGS_TO,
1599
+ polymorphic: true,
1600
+ },
1601
+ },
1602
+ });
1603
+ const R = S.getService(HasOneResolver);
1604
+ const entity = {[DEF_PK]: 1};
1605
+ const promise = R.includePolymorphicByRelationName(
1606
+ [entity],
1607
+ 'source',
1608
+ 'target',
1609
+ 'child',
1610
+ 'parent',
1611
+ );
1612
+ await expect(promise).to.be.rejectedWith(
1613
+ 'The model "target" does not have a specified datasource.',
1614
+ );
1615
+ });
1616
+
1617
+ it('does not throw an error if a relation target is not found', async function () {
1618
+ const S = new Schema();
1619
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
1620
+ S.defineModel({name: 'source', datasource: 'datasource'});
1621
+ S.defineModel({
1622
+ name: 'target',
1623
+ datasource: 'datasource',
1624
+ relations: {
1625
+ parent: {
1626
+ type: RelationType.BELONGS_TO,
1627
+ polymorphic: true,
1628
+ },
1629
+ },
1630
+ });
1631
+ const sourceRel = S.getRepository('source');
1632
+ const source = await sourceRel.create({});
1633
+ expect(source).to.be.eql({
1634
+ [DEF_PK]: source[DEF_PK],
1635
+ });
1636
+ const R = S.getService(HasOneResolver);
1637
+ await R.includePolymorphicByRelationName(
1638
+ [source],
1639
+ 'source',
1640
+ 'target',
1641
+ 'child',
1642
+ 'parent',
1643
+ );
1644
+ expect(source).to.be.eql({
1645
+ [DEF_PK]: source[DEF_PK],
1646
+ });
1647
+ });
1648
+
1649
+ it('does not include an entity with a not matched discriminator value', async function () {
1650
+ const S = new Schema();
1651
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
1652
+ S.defineModel({name: 'source', datasource: 'datasource'});
1653
+ S.defineModel({
1654
+ name: 'target',
1655
+ datasource: 'datasource',
1656
+ relations: {
1657
+ parent: {
1658
+ type: RelationType.BELONGS_TO,
1659
+ polymorphic: true,
1660
+ },
1661
+ },
1662
+ });
1663
+ const sourceRel = S.getRepository('source');
1664
+ const targetRel = S.getRepository('target');
1665
+ const source = await sourceRel.create({});
1666
+ expect(source).to.be.eql({
1667
+ [DEF_PK]: source[DEF_PK],
1668
+ });
1669
+ const target = await targetRel.create({
1670
+ parentId: source[DEF_PK],
1671
+ parentType: 'unknown',
1672
+ });
1673
+ expect(target).to.be.eql({
1674
+ [DEF_PK]: target[DEF_PK],
1675
+ parentId: source[DEF_PK],
1676
+ parentType: 'unknown',
1677
+ });
1678
+ const R = S.getService(HasOneResolver);
1679
+ await R.includePolymorphicByRelationName(
1680
+ [source],
1681
+ 'source',
1682
+ 'target',
1683
+ 'child',
1684
+ 'parent',
1685
+ );
1686
+ expect(source).to.be.eql({
1687
+ [DEF_PK]: source[DEF_PK],
1688
+ });
1689
+ });
1690
+
1691
+ it('includes if a primary key is not defined in the source model', async function () {
1692
+ const S = new Schema();
1693
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
1694
+ S.defineModel({name: 'source', datasource: 'datasource'});
1695
+ S.defineModel({
1696
+ name: 'target',
1697
+ datasource: 'datasource',
1698
+ relations: {
1699
+ parent: {
1700
+ type: RelationType.BELONGS_TO,
1701
+ polymorphic: true,
1702
+ },
1703
+ },
1704
+ });
1705
+ const sourceRep = S.getRepository('source');
1706
+ const targetRep = S.getRepository('target');
1707
+ const source = await sourceRep.create({});
1708
+ expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
1709
+ const target = await targetRep.create({
1710
+ parentId: source[DEF_PK],
1711
+ parentType: 'source',
1712
+ });
1713
+ expect(target).to.be.eql({
1714
+ [DEF_PK]: target[DEF_PK],
1715
+ parentId: source[DEF_PK],
1716
+ parentType: target.parentType,
1717
+ });
1718
+ const R = S.getService(HasOneResolver);
1719
+ await R.includePolymorphicByRelationName(
1720
+ [source],
1721
+ 'source',
1722
+ 'target',
1723
+ 'child',
1724
+ 'parent',
1725
+ );
1726
+ expect(source).to.be.eql({
1727
+ [DEF_PK]: source[DEF_PK],
1728
+ child: {
1729
+ id: target[DEF_PK],
1730
+ parentId: source[DEF_PK],
1731
+ parentType: target.parentType,
1732
+ },
1733
+ });
1734
+ });
1735
+
1736
+ it('includes if the source model has a custom primary key', async function () {
1737
+ const S = new Schema();
1738
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
1739
+ S.defineModel({
1740
+ name: 'source',
1741
+ datasource: 'datasource',
1742
+ properties: {
1743
+ myId: {
1744
+ type: DataType.NUMBER,
1745
+ primaryKey: true,
1746
+ },
1747
+ },
1748
+ });
1749
+ S.defineModel({
1750
+ name: 'target',
1751
+ datasource: 'datasource',
1752
+ relations: {
1753
+ parent: {
1754
+ type: RelationType.BELONGS_TO,
1755
+ polymorphic: true,
1756
+ },
1757
+ },
1758
+ });
1759
+ const sourceRep = S.getRepository('source');
1760
+ const targetRep = S.getRepository('target');
1761
+ const source = await sourceRep.create({});
1762
+ expect(source).to.be.eql({myId: source.myId});
1763
+ const target = await targetRep.create({
1764
+ parentId: source.myId,
1765
+ parentType: 'source',
1766
+ });
1767
+ expect(target).to.be.eql({
1768
+ [DEF_PK]: target[DEF_PK],
1769
+ parentId: source.myId,
1770
+ parentType: target.parentType,
1771
+ });
1772
+ const R = S.getService(HasOneResolver);
1773
+ await R.includePolymorphicByRelationName(
1774
+ [source],
1775
+ 'source',
1776
+ 'target',
1777
+ 'child',
1778
+ 'parent',
1779
+ );
1780
+ expect(source).to.be.eql({
1781
+ myId: source.myId,
1782
+ child: {
1783
+ [DEF_PK]: target[DEF_PK],
1784
+ parentId: source.myId,
1785
+ parentType: target.parentType,
1786
+ },
1787
+ });
1788
+ });
1789
+
1790
+ it('includes if the target model has a custom primary key', async function () {
1791
+ const S = new Schema();
1792
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
1793
+ S.defineModel({name: 'source', datasource: 'datasource'});
1794
+ S.defineModel({
1795
+ name: 'target',
1796
+ datasource: 'datasource',
1797
+ properties: {
1798
+ myId: {
1799
+ type: DataType.NUMBER,
1800
+ primaryKey: true,
1801
+ },
1802
+ },
1803
+ relations: {
1804
+ parent: {
1805
+ type: RelationType.BELONGS_TO,
1806
+ polymorphic: true,
1807
+ },
1808
+ },
1809
+ });
1810
+ const sourceRep = S.getRepository('source');
1811
+ const targetRep = S.getRepository('target');
1812
+ const source = await sourceRep.create({});
1813
+ expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
1814
+ const target = await targetRep.create({
1815
+ parentId: source[DEF_PK],
1816
+ parentType: 'source',
1817
+ });
1818
+ expect(target).to.be.eql({
1819
+ myId: target.myId,
1820
+ parentId: source[DEF_PK],
1821
+ parentType: target.parentType,
1822
+ });
1823
+ const R = S.getService(HasOneResolver);
1824
+ await R.includePolymorphicByRelationName(
1825
+ [source],
1826
+ 'source',
1827
+ 'target',
1828
+ 'child',
1829
+ 'parent',
1830
+ );
1831
+ expect(source).to.be.eql({
1832
+ [DEF_PK]: source[DEF_PK],
1833
+ child: {
1834
+ myId: target.myId,
1835
+ parentId: source[DEF_PK],
1836
+ parentType: target.parentType,
1837
+ },
1838
+ });
1839
+ });
1840
+
1841
+ it('includes if the target model has a custom "foreignKey"', async function () {
1842
+ const S = new Schema();
1843
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
1844
+ S.defineModel({name: 'source', datasource: 'datasource'});
1845
+ S.defineModel({
1846
+ name: 'target',
1847
+ datasource: 'datasource',
1848
+ properties: {
1849
+ myId: {
1850
+ type: DataType.NUMBER,
1851
+ primaryKey: true,
1852
+ },
1853
+ },
1854
+ relations: {
1855
+ parent: {
1856
+ type: RelationType.BELONGS_TO,
1857
+ polymorphic: true,
1858
+ foreignKey: 'relationId',
1859
+ },
1860
+ },
1861
+ });
1862
+ const sourceRep = S.getRepository('source');
1863
+ const targetRep = S.getRepository('target');
1864
+ const source = await sourceRep.create({});
1865
+ expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
1866
+ const target = await targetRep.create({
1867
+ relationId: source[DEF_PK],
1868
+ parentType: 'source',
1869
+ });
1870
+ expect(target).to.be.eql({
1871
+ myId: target.myId,
1872
+ relationId: source[DEF_PK],
1873
+ parentType: target.parentType,
1874
+ });
1875
+ const R = S.getService(HasOneResolver);
1876
+ await R.includePolymorphicByRelationName(
1877
+ [source],
1878
+ 'source',
1879
+ 'target',
1880
+ 'child',
1881
+ 'parent',
1882
+ );
1883
+ expect(source).to.be.eql({
1884
+ [DEF_PK]: source[DEF_PK],
1885
+ child: {
1886
+ myId: target.myId,
1887
+ relationId: source[DEF_PK],
1888
+ parentType: target.parentType,
1889
+ },
1890
+ });
1891
+ });
1892
+
1893
+ it('includes if the target model has a custom "discriminator"', async function () {
1894
+ const S = new Schema();
1895
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
1896
+ S.defineModel({name: 'source', datasource: 'datasource'});
1897
+ S.defineModel({
1898
+ name: 'target',
1899
+ datasource: 'datasource',
1900
+ properties: {
1901
+ myId: {
1902
+ type: DataType.NUMBER,
1903
+ primaryKey: true,
1904
+ },
1905
+ },
1906
+ relations: {
1907
+ parent: {
1908
+ type: RelationType.BELONGS_TO,
1909
+ polymorphic: true,
1910
+ discriminator: 'relationType',
1911
+ },
1912
+ },
1913
+ });
1914
+ const sourceRep = S.getRepository('source');
1915
+ const targetRep = S.getRepository('target');
1916
+ const source = await sourceRep.create({});
1917
+ expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
1918
+ const target = await targetRep.create({
1919
+ parentId: source[DEF_PK],
1920
+ relationType: 'source',
1921
+ });
1922
+ expect(target).to.be.eql({
1923
+ myId: target.myId,
1924
+ parentId: source[DEF_PK],
1925
+ relationType: target.relationType,
1926
+ });
1927
+ const R = S.getService(HasOneResolver);
1928
+ await R.includePolymorphicByRelationName(
1929
+ [source],
1930
+ 'source',
1931
+ 'target',
1932
+ 'child',
1933
+ 'parent',
1934
+ );
1935
+ expect(source).to.be.eql({
1936
+ [DEF_PK]: source[DEF_PK],
1937
+ child: {
1938
+ myId: target.myId,
1939
+ parentId: source[DEF_PK],
1940
+ relationType: target.relationType,
1941
+ },
1942
+ });
1943
+ });
1944
+
1945
+ it('uses a where clause of the given scope to filter the relation target', async function () {
1946
+ const S = new Schema();
1947
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
1948
+ S.defineModel({name: 'source', datasource: 'datasource'});
1949
+ S.defineModel({
1950
+ name: 'target',
1951
+ datasource: 'datasource',
1952
+ relations: {
1953
+ parent: {
1954
+ type: RelationType.BELONGS_TO,
1955
+ polymorphic: true,
1956
+ },
1957
+ },
1958
+ });
1959
+ const sourceRep = S.getRepository('source');
1960
+ const targetRep = S.getRepository('target');
1961
+ const source = await sourceRep.create({});
1962
+ expect(source).to.be.eql({[DEF_PK]: source[DEF_PK]});
1963
+ const target1 = await targetRep.create({
1964
+ featured: false,
1965
+ parentId: source[DEF_PK],
1966
+ parentType: 'source',
1967
+ });
1968
+ expect(target1).to.be.eql({
1969
+ [DEF_PK]: target1[DEF_PK],
1970
+ featured: false,
1971
+ parentId: source[DEF_PK],
1972
+ parentType: target1.parentType,
1973
+ });
1974
+ const target2 = await targetRep.create({
1975
+ featured: true,
1976
+ parentId: source[DEF_PK],
1977
+ parentType: 'source',
1978
+ });
1979
+ expect(target2).to.be.eql({
1980
+ [DEF_PK]: target2[DEF_PK],
1981
+ featured: true,
1982
+ parentId: source[DEF_PK],
1983
+ parentType: target2.parentType,
1984
+ });
1985
+ const R = S.getService(HasOneResolver);
1986
+ await R.includePolymorphicByRelationName(
1987
+ [source],
1988
+ 'source',
1989
+ 'target',
1990
+ 'child',
1991
+ 'parent',
1992
+ {where: {featured: false}},
1993
+ );
1994
+ expect(source).to.be.eql({
1995
+ [DEF_PK]: source[DEF_PK],
1996
+ child: {
1997
+ [DEF_PK]: target1[DEF_PK],
1998
+ featured: false,
1999
+ parentId: source[DEF_PK],
2000
+ parentType: target1.parentType,
2001
+ },
2002
+ });
2003
+ await R.includePolymorphicByRelationName(
2004
+ [source],
2005
+ 'source',
2006
+ 'target',
2007
+ 'child',
2008
+ 'parent',
2009
+ {where: {featured: true}},
2010
+ );
2011
+ expect(source).to.be.eql({
2012
+ [DEF_PK]: source[DEF_PK],
2013
+ child: {
2014
+ [DEF_PK]: target2[DEF_PK],
2015
+ featured: true,
2016
+ parentId: source[DEF_PK],
2017
+ parentType: target2.parentType,
2018
+ },
2019
+ });
2020
+ });
2021
+
2022
+ it('uses a fields clause of the given scope to filter the relation target', async function () {
2023
+ const S = new Schema();
2024
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
2025
+ S.defineModel({name: 'source', datasource: 'datasource'});
2026
+ S.defineModel({
2027
+ name: 'target',
2028
+ datasource: 'datasource',
2029
+ relations: {
2030
+ parent: {
2031
+ type: RelationType.BELONGS_TO,
2032
+ polymorphic: true,
2033
+ },
2034
+ },
2035
+ });
2036
+ const sourceRep = S.getRepository('source');
2037
+ const targetRep = S.getRepository('target');
2038
+ const source = await sourceRep.create({});
2039
+ expect(source).to.be.eql({
2040
+ [DEF_PK]: source[DEF_PK],
2041
+ });
2042
+ const target = await targetRep.create({
2043
+ foo: 'fooVal',
2044
+ bar: 'barVal',
2045
+ parentId: source[DEF_PK],
2046
+ parentType: 'source',
2047
+ });
2048
+ expect(target).to.be.eql({
2049
+ [DEF_PK]: target[DEF_PK],
2050
+ foo: target.foo,
2051
+ bar: target.bar,
2052
+ parentId: source[DEF_PK],
2053
+ parentType: target.parentType,
2054
+ });
2055
+ const R = S.getService(HasOneResolver);
2056
+ await R.includePolymorphicByRelationName(
2057
+ [source],
2058
+ 'source',
2059
+ 'target',
2060
+ 'child',
2061
+ 'parent',
2062
+ {fields: [DEF_PK, 'bar']},
2063
+ );
2064
+ expect(source).to.be.eql({
2065
+ [DEF_PK]: source[DEF_PK],
2066
+ child: {
2067
+ [DEF_PK]: target[DEF_PK],
2068
+ bar: target.bar,
2069
+ },
2070
+ });
2071
+ });
2072
+
2073
+ it('uses an include clause of the given scope to resolve target relations', async function () {
2074
+ const S = new Schema();
2075
+ S.defineDatasource({
2076
+ name: 'datasource',
2077
+ adapter: 'memory',
2078
+ });
2079
+ S.defineModel({
2080
+ name: 'modelA',
2081
+ datasource: 'datasource',
2082
+ properties: {
2083
+ id: {
2084
+ type: DataType.NUMBER,
2085
+ primaryKey: true,
2086
+ },
2087
+ source: {
2088
+ type: DataType.STRING,
2089
+ default: 'modelA',
2090
+ },
2091
+ },
2092
+ relations: {
2093
+ child: {
2094
+ type: RelationType.HAS_ONE,
2095
+ model: 'modelB',
2096
+ polymorphic: 'parent',
2097
+ },
2098
+ },
2099
+ });
2100
+ S.defineModel({
2101
+ name: 'modelB',
2102
+ datasource: 'datasource',
2103
+ properties: {
2104
+ id: {
2105
+ type: DataType.NUMBER,
2106
+ primaryKey: true,
2107
+ },
2108
+ source: {
2109
+ type: DataType.STRING,
2110
+ default: 'modelB',
2111
+ },
2112
+ },
2113
+ relations: {
2114
+ parent: {
2115
+ type: RelationType.BELONGS_TO,
2116
+ polymorphic: true,
2117
+ },
2118
+ child: {
2119
+ type: RelationType.HAS_ONE,
2120
+ model: 'modelC',
2121
+ polymorphic: 'parent',
2122
+ },
2123
+ },
2124
+ });
2125
+ S.defineModel({
2126
+ name: 'modelC',
2127
+ datasource: 'datasource',
2128
+ properties: {
2129
+ id: {
2130
+ type: DataType.NUMBER,
2131
+ primaryKey: true,
2132
+ },
2133
+ source: {
2134
+ type: DataType.STRING,
2135
+ default: 'modelC',
2136
+ },
2137
+ },
2138
+ relations: {
2139
+ parent: {
2140
+ type: RelationType.BELONGS_TO,
2141
+ polymorphic: true,
2142
+ },
2143
+ },
2144
+ });
2145
+ const aRep = S.getRepository('modelA');
2146
+ const bRep = S.getRepository('modelB');
2147
+ const cRep = S.getRepository('modelC');
2148
+ const a = await aRep.create({});
2149
+ const b = await bRep.create({parentId: a.id, parentType: 'modelA'});
2150
+ const c = await cRep.create({parentId: b.id, parentType: 'modelB'});
2151
+ expect(a).to.be.eql({
2152
+ id: a.id,
2153
+ source: 'modelA',
2154
+ });
2155
+ expect(b).to.be.eql({
2156
+ id: b.id,
2157
+ source: 'modelB',
2158
+ parentId: a.id,
2159
+ parentType: 'modelA',
2160
+ });
2161
+ expect(c).to.be.eql({
2162
+ id: c.id,
2163
+ source: 'modelC',
2164
+ parentId: b.id,
2165
+ parentType: 'modelB',
2166
+ });
2167
+ const R = S.getService(HasOneResolver);
2168
+ await R.includePolymorphicByRelationName(
2169
+ [a],
2170
+ 'modelA',
2171
+ 'modelB',
2172
+ 'child',
2173
+ 'parent',
2174
+ {include: 'child'},
2175
+ );
2176
+ expect(a).to.be.eql({
2177
+ id: a.id,
2178
+ source: 'modelA',
2179
+ child: {
2180
+ id: b.id,
2181
+ source: 'modelB',
2182
+ parentId: a.id,
2183
+ parentType: 'modelA',
2184
+ child: {
2185
+ id: c.id,
2186
+ source: 'modelC',
2187
+ parentId: b.id,
2188
+ parentType: 'modelB',
2189
+ },
2190
+ },
2191
+ });
2192
+ });
2193
+
2194
+ it('does not break the "and" operator of the given "where" clause', async function () {
2195
+ const S = new Schema();
2196
+ S.defineDatasource({name: 'datasource', adapter: 'memory'});
2197
+ S.defineModel({name: 'source', datasource: 'datasource'});
2198
+ S.defineModel({
2199
+ name: 'target',
2200
+ datasource: 'datasource',
2201
+ relations: {
2202
+ parent: {
2203
+ type: RelationType.BELONGS_TO,
2204
+ polymorphic: true,
2205
+ },
2206
+ },
2207
+ });
2208
+ const sourceRep = S.getRepository('source');
2209
+ const targetRep = S.getRepository('target');
2210
+ const source = await sourceRep.create({});
2211
+ expect(source).to.be.eql({
2212
+ [DEF_PK]: source[DEF_PK],
2213
+ });
2214
+ const target1 = await targetRep.create({
2215
+ featured: false,
2216
+ parentId: source[DEF_PK],
2217
+ parentType: 'source',
2218
+ });
2219
+ expect(target1).to.be.eql({
2220
+ [DEF_PK]: target1[DEF_PK],
2221
+ featured: false,
2222
+ parentId: source[DEF_PK],
2223
+ parentType: target1.parentType,
2224
+ });
2225
+ const target2 = await targetRep.create({
2226
+ featured: true,
2227
+ parentId: source[DEF_PK],
2228
+ parentType: 'source',
2229
+ });
2230
+ expect(target2).to.be.eql({
2231
+ [DEF_PK]: target2[DEF_PK],
2232
+ featured: true,
2233
+ parentId: source[DEF_PK],
2234
+ parentType: target2.parentType,
2235
+ });
2236
+ const R = S.getService(HasOneResolver);
2237
+ await R.includePolymorphicByRelationName(
2238
+ [source],
2239
+ 'source',
2240
+ 'target',
2241
+ 'child',
2242
+ 'parent',
2243
+ {where: {and: [{featured: false}]}},
2244
+ );
2245
+ expect(source).to.be.eql({
2246
+ [DEF_PK]: source[DEF_PK],
2247
+ child: {
2248
+ [DEF_PK]: target1[DEF_PK],
2249
+ featured: false,
2250
+ parentId: source[DEF_PK],
2251
+ parentType: target1.parentType,
2252
+ },
2253
+ });
2254
+ delete source.child;
2255
+ await R.includePolymorphicByRelationName(
2256
+ [source],
2257
+ 'source',
2258
+ 'target',
2259
+ 'child',
2260
+ 'parent',
2261
+ {where: {and: [{featured: true}]}},
2262
+ );
2263
+ expect(source).to.be.eql({
2264
+ [DEF_PK]: source[DEF_PK],
2265
+ child: {
2266
+ [DEF_PK]: target2[DEF_PK],
2267
+ featured: true,
2268
+ parentId: source[DEF_PK],
2269
+ parentType: target2.parentType,
2270
+ },
2271
+ });
2272
+ });
2273
+ });
2274
+ });