json-rest-api 1.0.14 → 1.0.15
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/connectors_ideas/fastify.js +66 -68
- package/connectors_ideas/koa-plugin.js +50 -51
- package/eslint.config.js +11 -2
- package/examples/url-override-example.js +63 -63
- package/index.js +13 -13
- package/lib/enhanced-logger.js +51 -51
- package/lib/error-formatter.js +61 -61
- package/lib/rest-api-errors.js +28 -28
- package/package.json +1 -1
- package/plugins/core/bulk-operations-plugin.js +142 -145
- package/plugins/core/connectors/express-plugin.js +210 -217
- package/plugins/core/connectors/lib/busboy-detector.js +84 -84
- package/plugins/core/connectors/lib/formidable-detector.js +50 -50
- package/plugins/core/connectors/lib/request-helpers.js +74 -74
- package/plugins/core/file-handling-plugin.js +125 -125
- package/plugins/core/lib/anyapi/anyapi-registry.js +254 -254
- package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +287 -287
- package/plugins/core/lib/anyapi/schema-utils.js +101 -101
- package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +41 -41
- package/plugins/core/lib/dbTablesOperations.js +134 -135
- package/plugins/core/lib/fractional-positioning.js +74 -76
- package/plugins/core/lib/querying/knex-cross-table-search.js +320 -320
- package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +144 -144
- package/plugins/core/lib/querying/knex-pagination-helpers.js +164 -164
- package/plugins/core/lib/querying/knex-process-includes.js +50 -50
- package/plugins/core/lib/querying/knex-query-helpers-base.js +31 -31
- package/plugins/core/lib/querying/knex-query-helpers.js +451 -452
- package/plugins/core/lib/querying/knex-relationship-includes.js +1160 -1170
- package/plugins/core/lib/querying/knex-window-queries.js +89 -89
- package/plugins/core/lib/querying/url-helpers.js +35 -35
- package/plugins/core/lib/querying-writing/compile-schemas.js +97 -98
- package/plugins/core/lib/querying-writing/connectors-query-parser.js +31 -31
- package/plugins/core/lib/querying-writing/database-capabilities.js +94 -94
- package/plugins/core/lib/querying-writing/database-value-normalizers.js +76 -76
- package/plugins/core/lib/querying-writing/default-data-helpers.js +27 -27
- package/plugins/core/lib/querying-writing/field-utils.js +44 -42
- package/plugins/core/lib/querying-writing/knex-constants.js +15 -16
- package/plugins/core/lib/querying-writing/knex-field-helpers.js +117 -115
- package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +41 -41
- package/plugins/core/lib/querying-writing/payload-validators.js +275 -275
- package/plugins/core/lib/querying-writing/schema-helpers.js +129 -129
- package/plugins/core/lib/querying-writing/scope-validations.js +143 -146
- package/plugins/core/lib/querying-writing/simplified-helpers.js +133 -129
- package/plugins/core/lib/storage/storage-adapter.js +68 -68
- package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +31 -31
- package/plugins/core/lib/writing/many-to-many-manipulations.js +81 -81
- package/plugins/core/lib/writing/relationship-processor.js +58 -58
- package/plugins/core/rest-api-access.js +8 -8
- package/plugins/core/rest-api-anyapi-knex-plugin.js +920 -920
- package/plugins/core/rest-api-cors-plugin.js +170 -171
- package/plugins/core/rest-api-knex-plugin.js +482 -493
- package/plugins/core/rest-api-multihome-plugin.js +247 -251
- package/plugins/core/rest-api-plugin-hooks/compile-resource-schemas.js +5 -5
- package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +20 -21
- package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +44 -44
- package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +23 -23
- package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +15 -15
- package/plugins/core/rest-api-plugin-methods/add-route.js +9 -9
- package/plugins/core/rest-api-plugin-methods/check-permissions.js +5 -6
- package/plugins/core/rest-api-plugin-methods/common.js +295 -297
- package/plugins/core/rest-api-plugin-methods/delete-relationship.js +35 -37
- package/plugins/core/rest-api-plugin-methods/delete.js +57 -59
- package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +172 -171
- package/plugins/core/rest-api-plugin-methods/get-related.js +96 -96
- package/plugins/core/rest-api-plugin-methods/get-relationship.js +16 -18
- package/plugins/core/rest-api-plugin-methods/get.js +86 -86
- package/plugins/core/rest-api-plugin-methods/patch-relationship.js +16 -17
- package/plugins/core/rest-api-plugin-methods/patch.js +63 -64
- package/plugins/core/rest-api-plugin-methods/post-relationship.js +54 -55
- package/plugins/core/rest-api-plugin-methods/post.js +61 -62
- package/plugins/core/rest-api-plugin-methods/put.js +86 -87
- package/plugins/core/rest-api-plugin-methods/query.js +88 -88
- package/plugins/core/rest-api-plugin-methods/release.js +5 -6
- package/plugins/core/rest-api-plugin.js +76 -78
- package/plugins/core/rest-api-positioning-plugin.js +251 -252
- package/plugins/core/socketio-plugin.js +240 -236
- package/plugins/storage/local-storage.js +103 -103
- package/plugins/storage/s3-storage.js +41 -42
- package/quickTest.js +37 -37
- package/scripts/debug-anyapi-review-filter.mjs +18 -18
- package/scripts/inspect-anyapi-descriptor-country.mjs +10 -10
- package/scripts/inspect-anyapi-descriptor.mjs +10 -10
- package/scripts/run-docs.js +112 -113
- package/test-debug.js +28 -28
- package/tests/TEST_TEMPLATE.test.js +51 -51
- package/tests/anyapi-basic.test.js +42 -42
- package/tests/anyapi-cursor-pagination.test.js +48 -48
- package/tests/bulk-operations.test.js +208 -208
- package/tests/computed-fields.test.js +204 -204
- package/tests/cors.test.js +412 -413
- package/tests/curl.test.js +275 -276
- package/tests/custom-idproperty-relationships.test.js +266 -266
- package/tests/field-getters.test.js +132 -132
- package/tests/field-setters.test.js +110 -110
- package/tests/fixtures/api-configs.js +699 -699
- package/tests/helpers/storage-mode.js +32 -32
- package/tests/helpers/test-utils.js +76 -76
- package/tests/include-depth-validation.test.js +76 -76
- package/tests/includes.test.js +145 -145
- package/tests/multihome.test.js +175 -175
- package/tests/nested-includes.test.js +255 -255
- package/tests/pagination-cursor-multifield.test.js +164 -164
- package/tests/pagination-enhanced.test.js +225 -225
- package/tests/polymorphic-relationships.test.js +96 -96
- package/tests/polymorphic-simple.test.js +44 -44
- package/tests/positioning-advanced.test.js +226 -226
- package/tests/positioning-extended.test.js +399 -399
- package/tests/positioning-multifilter.test.js +204 -204
- package/tests/positioning.test.js +266 -266
- package/tests/queries.test.js +184 -184
- package/tests/query-limits.test.js +154 -155
- package/tests/relationship-endpoints.test.js +127 -127
- package/tests/relationships.test.js +178 -179
- package/tests/rest-api-access.test.js +86 -86
- package/tests/rest-api.test.js +103 -103
- package/tests/searchschema-merge.test.js +93 -93
- package/tests/socketio.test.js +326 -326
- package/tests/virtual-fields.test.js +164 -164
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Scope configuration validators for relationship definitions
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* @description
|
|
5
5
|
* These validators run at scope registration time to ensure:
|
|
6
6
|
* - Polymorphic relationships are properly configured
|
|
@@ -9,15 +9,14 @@
|
|
|
9
9
|
* - Referenced scopes actually exist in the system
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
/**
|
|
14
13
|
* Validates all relationships in a scope configuration
|
|
15
|
-
*
|
|
14
|
+
*
|
|
16
15
|
* @param {Object} params - Validation parameters
|
|
17
16
|
* @param {Object} params.context - Event context with scopeName and scopeOptions
|
|
18
17
|
* @param {Object} params.scopes - All registered scopes for validation
|
|
19
18
|
* @throws {Error} If any relationship is misconfigured
|
|
20
|
-
*
|
|
19
|
+
*
|
|
21
20
|
* @example
|
|
22
21
|
* // Input: Valid polymorphic relationship
|
|
23
22
|
* validateRelationships({
|
|
@@ -42,7 +41,7 @@
|
|
|
42
41
|
* scopes: { posts: {}, videos: {} } // Referenced types exist
|
|
43
42
|
* });
|
|
44
43
|
* // Output: No error (valid configuration)
|
|
45
|
-
*
|
|
44
|
+
*
|
|
46
45
|
* @example
|
|
47
46
|
* // Input: Missing required field
|
|
48
47
|
* validateRelaationships({
|
|
@@ -63,9 +62,9 @@
|
|
|
63
62
|
* scopes: { documents: {} }
|
|
64
63
|
* });
|
|
65
64
|
* // Throws: Error
|
|
66
|
-
* // "Invalid polymorphic relationship 'attachable' in scope 'attachments':
|
|
65
|
+
* // "Invalid polymorphic relationship 'attachable' in scope 'attachments':
|
|
67
66
|
* // belongsToPolymorphic.idField must be specified"
|
|
68
|
-
*
|
|
67
|
+
*
|
|
69
68
|
* @example
|
|
70
69
|
* // Input: HasMany without foreignKey
|
|
71
70
|
* validateRelationships({
|
|
@@ -86,7 +85,7 @@
|
|
|
86
85
|
* // Throws: Error
|
|
87
86
|
* // "Invalid hasMany relationship 'posts' in scope 'users':
|
|
88
87
|
* // hasMany relationship requires foreignKey to be specified"
|
|
89
|
-
*
|
|
88
|
+
*
|
|
90
89
|
* @example
|
|
91
90
|
* // Input: Complex activity feed with multiple polymorphic relationships
|
|
92
91
|
* validateRelationships({
|
|
@@ -120,19 +119,19 @@
|
|
|
120
119
|
* scopes: { posts: {}, comments: {}, users: {}, api_clients: {} }
|
|
121
120
|
* });
|
|
122
121
|
* // Output: No error (both polymorphic relationships valid)
|
|
123
|
-
*
|
|
122
|
+
*
|
|
124
123
|
* @description
|
|
125
124
|
* Used by:
|
|
126
125
|
* - rest-api-plugin on 'scope:added' event
|
|
127
126
|
* - Runs automatically when scopes are registered
|
|
128
|
-
*
|
|
127
|
+
*
|
|
129
128
|
* Purpose:
|
|
130
129
|
* - Catches configuration errors at startup, not runtime
|
|
131
130
|
* - Validates polymorphic relationship structure
|
|
132
131
|
* - Ensures referenced scope types exist
|
|
133
132
|
* - Validates hasMany/manyToMany foreign keys
|
|
134
133
|
* - Provides clear error messages for debugging
|
|
135
|
-
*
|
|
134
|
+
*
|
|
136
135
|
* Data flow:
|
|
137
136
|
* 1. Extracts relationships from scope options
|
|
138
137
|
* 2. For each relationship, checks its type
|
|
@@ -141,59 +140,58 @@
|
|
|
141
140
|
* 5. Validates manyToMany: through, foreignKey, otherKey
|
|
142
141
|
* 6. Throws descriptive error on first validation failure
|
|
143
142
|
*/
|
|
144
|
-
export function validateRelationships({ context, scopes }) {
|
|
145
|
-
const { scopeName, scopeOptions } = context
|
|
146
|
-
const relationships = scopeOptions.relationships || {}
|
|
147
|
-
|
|
143
|
+
export function validateRelationships ({ context, scopes }) {
|
|
144
|
+
const { scopeName, scopeOptions } = context
|
|
145
|
+
const relationships = scopeOptions.relationships || {}
|
|
146
|
+
|
|
148
147
|
for (const [relName, relDef] of Object.entries(relationships)) {
|
|
149
148
|
if (relDef.belongsToPolymorphic) {
|
|
150
|
-
const validation = validatePolymorphicRelationship(relDef, scopeName, scopes)
|
|
149
|
+
const validation = validatePolymorphicRelationship(relDef, scopeName, scopes)
|
|
151
150
|
if (!validation.valid) {
|
|
152
151
|
throw new Error(
|
|
153
152
|
`Invalid polymorphic relationship '${relName}' in scope '${scopeName}': ${validation.error}`
|
|
154
|
-
)
|
|
153
|
+
)
|
|
155
154
|
}
|
|
156
155
|
}
|
|
157
|
-
|
|
156
|
+
|
|
158
157
|
// Validate based on relationship type
|
|
159
158
|
if (relDef.type === 'hasMany') {
|
|
160
|
-
const validation = validateHasManyRelationship(relDef, relName, scopeName)
|
|
159
|
+
const validation = validateHasManyRelationship(relDef, relName, scopeName)
|
|
161
160
|
if (!validation.valid) {
|
|
162
161
|
throw new Error(
|
|
163
162
|
`Invalid hasMany relationship '${relName}' in scope '${scopeName}': ${validation.error}`
|
|
164
|
-
)
|
|
163
|
+
)
|
|
165
164
|
}
|
|
166
165
|
}
|
|
167
|
-
|
|
166
|
+
|
|
168
167
|
if (relDef.type === 'hasOne') {
|
|
169
|
-
const validation = validateHasOneRelationship(relDef, relName, scopeName)
|
|
168
|
+
const validation = validateHasOneRelationship(relDef, relName, scopeName)
|
|
170
169
|
if (!validation.valid) {
|
|
171
170
|
throw new Error(
|
|
172
171
|
`Invalid hasOne relationship '${relName}' in scope '${scopeName}': ${validation.error}`
|
|
173
|
-
)
|
|
172
|
+
)
|
|
174
173
|
}
|
|
175
174
|
}
|
|
176
|
-
|
|
175
|
+
|
|
177
176
|
if (relDef.type === 'manyToMany') {
|
|
178
|
-
const validation = validateManyToManyRelationship(relDef, relName, scopeName)
|
|
177
|
+
const validation = validateManyToManyRelationship(relDef, relName, scopeName)
|
|
179
178
|
if (!validation.valid) {
|
|
180
179
|
throw new Error(
|
|
181
180
|
`Invalid manyToMany relationship '${relName}' in scope '${scopeName}': ${validation.error}`
|
|
182
|
-
)
|
|
181
|
+
)
|
|
183
182
|
}
|
|
184
183
|
}
|
|
185
184
|
}
|
|
186
185
|
}
|
|
187
186
|
|
|
188
|
-
|
|
189
187
|
/**
|
|
190
188
|
* Validates a polymorphic relationship definition
|
|
191
|
-
*
|
|
189
|
+
*
|
|
192
190
|
* @param {Object} relDef - Relationship definition with belongsToPolymorphic
|
|
193
191
|
* @param {string} scopeName - Scope being registered (for error messages)
|
|
194
192
|
* @param {Object} scopes - All registered scopes for validation
|
|
195
193
|
* @returns {Object} Validation result {valid: boolean, error?: string}
|
|
196
|
-
*
|
|
194
|
+
*
|
|
197
195
|
* @example
|
|
198
196
|
* // Input: Valid polymorphic configuration
|
|
199
197
|
* const relDef = {
|
|
@@ -205,7 +203,7 @@ export function validateRelationships({ context, scopes }) {
|
|
|
205
203
|
* };
|
|
206
204
|
* validatePolymorphicRelationship(relDef, 'comments', scopes);
|
|
207
205
|
* // Output: { valid: true }
|
|
208
|
-
*
|
|
206
|
+
*
|
|
209
207
|
* @example
|
|
210
208
|
* // Input: Missing required types array
|
|
211
209
|
* const relDef = {
|
|
@@ -216,11 +214,11 @@ export function validateRelationships({ context, scopes }) {
|
|
|
216
214
|
* }
|
|
217
215
|
* };
|
|
218
216
|
* validatePolymorphicRelationship(relDef, 'comments', scopes);
|
|
219
|
-
* // Output: {
|
|
220
|
-
* // valid: false,
|
|
221
|
-
* // error: 'belongsToPolymorphic.types must be a non-empty array'
|
|
217
|
+
* // Output: {
|
|
218
|
+
* // valid: false,
|
|
219
|
+
* // error: 'belongsToPolymorphic.types must be a non-empty array'
|
|
222
220
|
* // }
|
|
223
|
-
*
|
|
221
|
+
*
|
|
224
222
|
* @example
|
|
225
223
|
* // Input: References non-existent scope
|
|
226
224
|
* const relDef = {
|
|
@@ -231,11 +229,11 @@ export function validateRelationships({ context, scopes }) {
|
|
|
231
229
|
* }
|
|
232
230
|
* };
|
|
233
231
|
* validatePolymorphicRelationship(relDef, 'attachments', scopes);
|
|
234
|
-
* // Output: {
|
|
235
|
-
* // valid: false,
|
|
236
|
-
* // error: "Polymorphic type 'unicorns' is not a registered scope"
|
|
232
|
+
* // Output: {
|
|
233
|
+
* // valid: false,
|
|
234
|
+
* // error: "Polymorphic type 'unicorns' is not a registered scope"
|
|
237
235
|
* // }
|
|
238
|
-
*
|
|
236
|
+
*
|
|
239
237
|
* @private
|
|
240
238
|
*/
|
|
241
239
|
const validatePolymorphicRelationship = (relDef, scopeName, scopes) => {
|
|
@@ -245,57 +243,57 @@ const validatePolymorphicRelationship = (relDef, scopeName, scopes) => {
|
|
|
245
243
|
// 3. Ensure types is non-empty array
|
|
246
244
|
// 4. Verify all types are registered scopes
|
|
247
245
|
// 5. Check that typeField and idField exist in the schema
|
|
248
|
-
|
|
249
|
-
const { belongsToPolymorphic } = relDef
|
|
250
|
-
|
|
246
|
+
|
|
247
|
+
const { belongsToPolymorphic } = relDef
|
|
248
|
+
|
|
251
249
|
if (!belongsToPolymorphic) {
|
|
252
|
-
return { valid: false, error: 'Missing belongsToPolymorphic definition' }
|
|
250
|
+
return { valid: false, error: 'Missing belongsToPolymorphic definition' }
|
|
253
251
|
}
|
|
254
|
-
|
|
255
|
-
const { types, typeField, idField } = belongsToPolymorphic
|
|
256
|
-
|
|
252
|
+
|
|
253
|
+
const { types, typeField, idField } = belongsToPolymorphic
|
|
254
|
+
|
|
257
255
|
if (!types || !Array.isArray(types) || types.length === 0) {
|
|
258
|
-
return {
|
|
259
|
-
valid: false,
|
|
260
|
-
error: 'belongsToPolymorphic.types must be a non-empty array'
|
|
261
|
-
}
|
|
256
|
+
return {
|
|
257
|
+
valid: false,
|
|
258
|
+
error: 'belongsToPolymorphic.types must be a non-empty array'
|
|
259
|
+
}
|
|
262
260
|
}
|
|
263
|
-
|
|
261
|
+
|
|
264
262
|
if (!typeField || typeof typeField !== 'string') {
|
|
265
|
-
return {
|
|
266
|
-
valid: false,
|
|
267
|
-
error: 'belongsToPolymorphic.typeField must be specified'
|
|
268
|
-
}
|
|
263
|
+
return {
|
|
264
|
+
valid: false,
|
|
265
|
+
error: 'belongsToPolymorphic.typeField must be specified'
|
|
266
|
+
}
|
|
269
267
|
}
|
|
270
|
-
|
|
268
|
+
|
|
271
269
|
if (!idField || typeof idField !== 'string') {
|
|
272
|
-
return {
|
|
273
|
-
valid: false,
|
|
274
|
-
error: 'belongsToPolymorphic.idField must be specified'
|
|
275
|
-
}
|
|
270
|
+
return {
|
|
271
|
+
valid: false,
|
|
272
|
+
error: 'belongsToPolymorphic.idField must be specified'
|
|
273
|
+
}
|
|
276
274
|
}
|
|
277
|
-
|
|
275
|
+
|
|
278
276
|
// Check that all types are valid scopes
|
|
279
277
|
for (const type of types) {
|
|
280
278
|
if (!scopes[type]) {
|
|
281
|
-
return {
|
|
282
|
-
valid: false,
|
|
283
|
-
error: `Polymorphic type '${type}' is not a registered scope`
|
|
284
|
-
}
|
|
279
|
+
return {
|
|
280
|
+
valid: false,
|
|
281
|
+
error: `Polymorphic type '${type}' is not a registered scope`
|
|
282
|
+
}
|
|
285
283
|
}
|
|
286
284
|
}
|
|
287
|
-
|
|
288
|
-
return { valid: true }
|
|
289
|
-
}
|
|
285
|
+
|
|
286
|
+
return { valid: true }
|
|
287
|
+
}
|
|
290
288
|
|
|
291
289
|
/**
|
|
292
290
|
* Validates a hasMany relationship definition
|
|
293
|
-
*
|
|
291
|
+
*
|
|
294
292
|
* @param {Object} relDef - Relationship definition with hasMany
|
|
295
293
|
* @param {string} relName - Relationship name
|
|
296
294
|
* @param {string} scopeName - Scope being registered
|
|
297
295
|
* @returns {Object} Validation result {valid: boolean, error?: string}
|
|
298
|
-
*
|
|
296
|
+
*
|
|
299
297
|
* @example
|
|
300
298
|
* // Input: Valid hasMany configuration
|
|
301
299
|
* const relDef = {
|
|
@@ -305,7 +303,7 @@ const validatePolymorphicRelationship = (relDef, scopeName, scopes) => {
|
|
|
305
303
|
* };
|
|
306
304
|
* validateHasManyRelationship(relDef, 'posts', 'users');
|
|
307
305
|
* // Output: { valid: true }
|
|
308
|
-
*
|
|
306
|
+
*
|
|
309
307
|
* @example
|
|
310
308
|
* // Input: Missing required foreignKey
|
|
311
309
|
* const relDef = {
|
|
@@ -314,11 +312,11 @@ const validatePolymorphicRelationship = (relDef, scopeName, scopes) => {
|
|
|
314
312
|
* // Missing: foreignKey
|
|
315
313
|
* };
|
|
316
314
|
* validateHasManyRelationship(relDef, 'posts', 'users');
|
|
317
|
-
* // Output: {
|
|
318
|
-
* // valid: false,
|
|
319
|
-
* // error: 'hasMany relationship requires foreignKey to be specified...'
|
|
315
|
+
* // Output: {
|
|
316
|
+
* // valid: false,
|
|
317
|
+
* // error: 'hasMany relationship requires foreignKey to be specified...'
|
|
320
318
|
* // }
|
|
321
|
-
*
|
|
319
|
+
*
|
|
322
320
|
* @example
|
|
323
321
|
* // Input: Polymorphic hasMany with 'via' (doesn't need foreignKey)
|
|
324
322
|
* const relDef = {
|
|
@@ -328,49 +326,49 @@ const validatePolymorphicRelationship = (relDef, scopeName, scopes) => {
|
|
|
328
326
|
* };
|
|
329
327
|
* validateHasManyRelationship(relDef, 'comments', 'posts');
|
|
330
328
|
* // Output: { valid: true }
|
|
331
|
-
*
|
|
329
|
+
*
|
|
332
330
|
* @private
|
|
333
331
|
*/
|
|
334
332
|
const validateHasManyRelationship = (relDef, relName, scopeName) => {
|
|
335
333
|
// Validate target is specified
|
|
336
334
|
if (!relDef.target) {
|
|
337
|
-
return {
|
|
338
|
-
valid: false,
|
|
339
|
-
error:
|
|
340
|
-
}
|
|
335
|
+
return {
|
|
336
|
+
valid: false,
|
|
337
|
+
error: 'hasMany relationship requires \'target\' to be specified.'
|
|
338
|
+
}
|
|
341
339
|
}
|
|
342
|
-
|
|
340
|
+
|
|
343
341
|
// Polymorphic hasMany relationships using 'via' don't need foreignKey
|
|
344
342
|
// They use the polymorphic fields (typeField, idField) from the belongsToPolymorphic relationship
|
|
345
343
|
if (relDef.via) {
|
|
346
|
-
return { valid: true }
|
|
344
|
+
return { valid: true }
|
|
347
345
|
}
|
|
348
|
-
|
|
346
|
+
|
|
349
347
|
if (!relDef.foreignKey) {
|
|
350
|
-
return {
|
|
351
|
-
valid: false,
|
|
352
|
-
error:
|
|
353
|
-
}
|
|
348
|
+
return {
|
|
349
|
+
valid: false,
|
|
350
|
+
error: 'hasMany relationship requires foreignKey to be specified. Add foreignKey: \'<field_name>\' to the relationship definition.'
|
|
351
|
+
}
|
|
354
352
|
}
|
|
355
|
-
|
|
353
|
+
|
|
356
354
|
if (typeof relDef.foreignKey !== 'string') {
|
|
357
|
-
return {
|
|
358
|
-
valid: false,
|
|
355
|
+
return {
|
|
356
|
+
valid: false,
|
|
359
357
|
error: `hasMany relationship foreignKey must be a string, got ${typeof relDef.foreignKey}`
|
|
360
|
-
}
|
|
358
|
+
}
|
|
361
359
|
}
|
|
362
|
-
|
|
363
|
-
return { valid: true }
|
|
364
|
-
}
|
|
360
|
+
|
|
361
|
+
return { valid: true }
|
|
362
|
+
}
|
|
365
363
|
|
|
366
364
|
/**
|
|
367
365
|
* Validates a hasOne relationship definition
|
|
368
|
-
*
|
|
366
|
+
*
|
|
369
367
|
* @param {Object} relDef - Relationship definition with hasOne
|
|
370
368
|
* @param {string} relName - Relationship name
|
|
371
369
|
* @param {string} scopeName - Scope being registered
|
|
372
370
|
* @returns {Object} Validation result {valid: boolean, error?: string}
|
|
373
|
-
*
|
|
371
|
+
*
|
|
374
372
|
* @example
|
|
375
373
|
* // Input: Valid hasOne configuration
|
|
376
374
|
* const relDef = {
|
|
@@ -380,43 +378,43 @@ const validateHasManyRelationship = (relDef, relName, scopeName) => {
|
|
|
380
378
|
* };
|
|
381
379
|
* validateHasOneRelationship(relDef, 'profile', 'users');
|
|
382
380
|
* // Output: { valid: true }
|
|
383
|
-
*
|
|
381
|
+
*
|
|
384
382
|
* @private
|
|
385
383
|
*/
|
|
386
384
|
const validateHasOneRelationship = (relDef, relName, scopeName) => {
|
|
387
385
|
// Validate target is specified
|
|
388
386
|
if (!relDef.target) {
|
|
389
|
-
return {
|
|
390
|
-
valid: false,
|
|
391
|
-
error:
|
|
392
|
-
}
|
|
387
|
+
return {
|
|
388
|
+
valid: false,
|
|
389
|
+
error: 'hasOne relationship requires \'target\' to be specified.'
|
|
390
|
+
}
|
|
393
391
|
}
|
|
394
|
-
|
|
392
|
+
|
|
395
393
|
if (!relDef.foreignKey) {
|
|
396
|
-
return {
|
|
397
|
-
valid: false,
|
|
398
|
-
error:
|
|
399
|
-
}
|
|
394
|
+
return {
|
|
395
|
+
valid: false,
|
|
396
|
+
error: 'hasOne relationship requires foreignKey to be specified. Add foreignKey: \'<field_name>\' to the relationship definition.'
|
|
397
|
+
}
|
|
400
398
|
}
|
|
401
|
-
|
|
399
|
+
|
|
402
400
|
if (typeof relDef.foreignKey !== 'string') {
|
|
403
|
-
return {
|
|
404
|
-
valid: false,
|
|
401
|
+
return {
|
|
402
|
+
valid: false,
|
|
405
403
|
error: `hasOne relationship foreignKey must be a string, got ${typeof relDef.foreignKey}`
|
|
406
|
-
}
|
|
404
|
+
}
|
|
407
405
|
}
|
|
408
|
-
|
|
409
|
-
return { valid: true }
|
|
410
|
-
}
|
|
406
|
+
|
|
407
|
+
return { valid: true }
|
|
408
|
+
}
|
|
411
409
|
|
|
412
410
|
/**
|
|
413
411
|
* Validates a manyToMany relationship definition
|
|
414
|
-
*
|
|
412
|
+
*
|
|
415
413
|
* @param {Object} relDef - Relationship definition with manyToMany
|
|
416
414
|
* @param {string} relName - Relationship name
|
|
417
415
|
* @param {string} scopeName - Scope being registered
|
|
418
416
|
* @returns {Object} Validation result {valid: boolean, error?: string}
|
|
419
|
-
*
|
|
417
|
+
*
|
|
420
418
|
* @example
|
|
421
419
|
* // Input: Valid manyToMany configuration
|
|
422
420
|
* const relDef = {
|
|
@@ -427,7 +425,7 @@ const validateHasOneRelationship = (relDef, relName, scopeName) => {
|
|
|
427
425
|
* };
|
|
428
426
|
* validateManyToManyRelationship(relDef, 'tags', 'articles');
|
|
429
427
|
* // Output: { valid: true }
|
|
430
|
-
*
|
|
428
|
+
*
|
|
431
429
|
* @example
|
|
432
430
|
* // Input: Missing required fields
|
|
433
431
|
* const relDef = {
|
|
@@ -437,51 +435,50 @@ const validateHasOneRelationship = (relDef, relName, scopeName) => {
|
|
|
437
435
|
* // Missing: otherKey
|
|
438
436
|
* };
|
|
439
437
|
* validateManyToManyRelationship(relDef, 'tags', 'articles');
|
|
440
|
-
* // Output: {
|
|
441
|
-
* // valid: false,
|
|
442
|
-
* // error: 'manyToMany relationship requires otherKey to be specified...'
|
|
438
|
+
* // Output: {
|
|
439
|
+
* // valid: false,
|
|
440
|
+
* // error: 'manyToMany relationship requires otherKey to be specified...'
|
|
443
441
|
* // }
|
|
444
|
-
*
|
|
442
|
+
*
|
|
445
443
|
* @private
|
|
446
444
|
*/
|
|
447
445
|
const validateManyToManyRelationship = (relDef, relName, scopeName) => {
|
|
448
|
-
const { through, foreignKey, otherKey } = relDef
|
|
449
|
-
|
|
446
|
+
const { through, foreignKey, otherKey } = relDef
|
|
447
|
+
|
|
450
448
|
if (!through) {
|
|
451
|
-
return {
|
|
452
|
-
valid: false,
|
|
453
|
-
error:
|
|
454
|
-
}
|
|
449
|
+
return {
|
|
450
|
+
valid: false,
|
|
451
|
+
error: 'manyToMany relationship requires \'through\' table to be specified'
|
|
452
|
+
}
|
|
455
453
|
}
|
|
456
|
-
|
|
454
|
+
|
|
457
455
|
if (!foreignKey) {
|
|
458
|
-
return {
|
|
459
|
-
valid: false,
|
|
460
|
-
error:
|
|
461
|
-
}
|
|
456
|
+
return {
|
|
457
|
+
valid: false,
|
|
458
|
+
error: 'manyToMany relationship requires foreignKey to be specified. Add foreignKey: \'<field_name>\' to the manyToMany configuration.'
|
|
459
|
+
}
|
|
462
460
|
}
|
|
463
|
-
|
|
461
|
+
|
|
464
462
|
if (!otherKey) {
|
|
465
|
-
return {
|
|
466
|
-
valid: false,
|
|
467
|
-
error:
|
|
468
|
-
}
|
|
463
|
+
return {
|
|
464
|
+
valid: false,
|
|
465
|
+
error: 'manyToMany relationship requires otherKey to be specified. Add otherKey: \'<field_name>\' to the manyToMany configuration.'
|
|
466
|
+
}
|
|
469
467
|
}
|
|
470
|
-
|
|
468
|
+
|
|
471
469
|
if (typeof foreignKey !== 'string') {
|
|
472
|
-
return {
|
|
473
|
-
valid: false,
|
|
470
|
+
return {
|
|
471
|
+
valid: false,
|
|
474
472
|
error: `manyToMany relationship foreignKey must be a string, got ${typeof foreignKey}`
|
|
475
|
-
}
|
|
473
|
+
}
|
|
476
474
|
}
|
|
477
|
-
|
|
475
|
+
|
|
478
476
|
if (typeof otherKey !== 'string') {
|
|
479
|
-
return {
|
|
480
|
-
valid: false,
|
|
477
|
+
return {
|
|
478
|
+
valid: false,
|
|
481
479
|
error: `manyToMany relationship otherKey must be a string, got ${typeof otherKey}`
|
|
482
|
-
}
|
|
480
|
+
}
|
|
483
481
|
}
|
|
484
|
-
|
|
485
|
-
return { valid: true };
|
|
486
|
-
};
|
|
487
482
|
|
|
483
|
+
return { valid: true }
|
|
484
|
+
}
|