adminizer 4.6.0-build.204 → 4.6.0-build.205
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/lib/DataAccessor.js +33 -48
- package/lib/model/adapter/sequelize.js +33 -7
- package/package.json +1 -1
package/lib/DataAccessor.js
CHANGED
|
@@ -351,20 +351,11 @@ export class DataAccessor {
|
|
|
351
351
|
throw new Error(`Unsupported or invalid via field "${via}" in intermediate model "${intermediateRelation.model}". ` +
|
|
352
352
|
`Currently, only relations to "userap" are supported`);
|
|
353
353
|
}
|
|
354
|
-
// Fetch
|
|
355
|
-
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
360
|
-
// Ensure there is only one associated intermediate record
|
|
361
|
-
const intermediateRecordCount = await intermediateModel.count({ [via]: this.user.id }, this);
|
|
362
|
-
if (intermediateRecordCount > 1) {
|
|
363
|
-
throw new Error(`Multiple intermediate records found in model "${intermediateRelation.model}" associated with user ID "${this.user.id}". ` +
|
|
364
|
-
`Expected only one`);
|
|
365
|
-
}
|
|
366
|
-
// Add the intermediate record ID to the criteria
|
|
367
|
-
sanitizedCriteria = { ...sanitizedCriteria, [field]: intermediateRecord.id };
|
|
354
|
+
// Fetch all intermediate records associated with the user
|
|
355
|
+
const intermediateRecords = await intermediateModel["_find"]({ [via]: this.user.id });
|
|
356
|
+
const intermediateIds = (intermediateRecords || []).map((r) => r.id);
|
|
357
|
+
// Filter main model by all matching intermediate record IDs
|
|
358
|
+
sanitizedCriteria = { ...sanitizedCriteria, [field]: { in: intermediateIds } };
|
|
368
359
|
}
|
|
369
360
|
}
|
|
370
361
|
// TODO fix types when deleting waterline (temporary decision here)
|
|
@@ -412,42 +403,36 @@ export class DataAccessor {
|
|
|
412
403
|
else if (typeof userAccessRelation === 'object' && userAccessRelation !== null) {
|
|
413
404
|
// If userAccessRelation is an object
|
|
414
405
|
const { field, via } = userAccessRelation;
|
|
415
|
-
//
|
|
406
|
+
// For non-admins: validate that the chosen intermediate record belongs to the user
|
|
416
407
|
if (updatedRecord[field] && !this.user.isAdministrator) {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
const intermediateRecordCount = await intermediateModel.count({ [via]: this.user.id }, this);
|
|
445
|
-
if (intermediateRecordCount > 1) {
|
|
446
|
-
throw new Error(`Multiple intermediate records found in model "${intermediateRelation.model}" associated with user ID "${this.user.id}". ` +
|
|
447
|
-
`Expected only one`);
|
|
408
|
+
if (!via) {
|
|
409
|
+
throw new Error(`Invalid userAccessRelation configuration: "via" is required`);
|
|
410
|
+
}
|
|
411
|
+
const modelAttributes = this.entity.model.attributes;
|
|
412
|
+
const intermediateRelation = modelAttributes[field];
|
|
413
|
+
if (!intermediateRelation || !intermediateRelation.model) {
|
|
414
|
+
throw new Error(`Invalid intermediate relation configuration for field "${field}" in model ${this.entity.model.modelname}`);
|
|
415
|
+
}
|
|
416
|
+
const intermediateModel = this.adminizer.modelHandler.model.get(intermediateRelation.model.toLowerCase());
|
|
417
|
+
if (!intermediateModel) {
|
|
418
|
+
throw new Error(`Intermediate model "${intermediateRelation.model}" not found`);
|
|
419
|
+
}
|
|
420
|
+
// Validate the `via` field in the intermediate model
|
|
421
|
+
const intermediateAttributes = intermediateModel.attributes;
|
|
422
|
+
const viaRelation = intermediateAttributes[via];
|
|
423
|
+
if (!viaRelation || !viaRelation.model || viaRelation.model.toLowerCase() !== 'userap') {
|
|
424
|
+
throw new Error(`Unsupported or invalid via field "${via}" in intermediate model "${intermediateRelation.model}". ` +
|
|
425
|
+
`Currently, only relations to "userap" are supported`);
|
|
426
|
+
}
|
|
427
|
+
const intermediatePk = (intermediateModel.primaryKey ?? 'id');
|
|
428
|
+
const chosenId = typeof updatedRecord[field] === 'object'
|
|
429
|
+
? updatedRecord[field][intermediatePk]
|
|
430
|
+
: updatedRecord[field];
|
|
431
|
+
const record = await intermediateModel["_findOne"]({ [intermediatePk]: chosenId, [via]: this.user.id });
|
|
432
|
+
if (!record) {
|
|
433
|
+
throw new Error(`Access denied: "${field}" does not belong to the current user`);
|
|
434
|
+
}
|
|
448
435
|
}
|
|
449
|
-
// Set the ID of the intermediate record to the main record's field
|
|
450
|
-
updatedRecord[field] = intermediateRecord.id;
|
|
451
436
|
}
|
|
452
437
|
}
|
|
453
438
|
return updatedRecord;
|
|
@@ -275,18 +275,26 @@ export class SequelizeModel extends AbstractModel {
|
|
|
275
275
|
const assocNames = Object.keys(this.model.associations);
|
|
276
276
|
const plainData = {};
|
|
277
277
|
const assocData = {};
|
|
278
|
-
// console.debug(">> _create: input data:", data);
|
|
279
|
-
// console.debug(">> Available associations:", assocNames);
|
|
280
278
|
for (const [key, val] of Object.entries(data)) {
|
|
281
279
|
if (assocNames.includes(key)) {
|
|
282
|
-
|
|
280
|
+
const assoc = this.model.associations[key];
|
|
281
|
+
// For BelongsTo, set the FK directly in plainData so NOT NULL constraints are satisfied
|
|
282
|
+
if (assoc && assoc.associationType === 'BelongsTo') {
|
|
283
|
+
const fk = assoc.foreignKey;
|
|
284
|
+
if (val !== null && val !== undefined) {
|
|
285
|
+
plainData[fk] = typeof val === 'object' ? val[assoc.target.primaryKeyAttribute] : val;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
assocData[key] = val;
|
|
290
|
+
}
|
|
283
291
|
}
|
|
284
292
|
else {
|
|
285
293
|
plainData[key] = val;
|
|
286
294
|
}
|
|
287
295
|
}
|
|
288
|
-
|
|
289
|
-
|
|
296
|
+
console.debug(">> Normal fields for create():", JSON.stringify(plainData));
|
|
297
|
+
console.debug(">> Association data:", JSON.stringify(assocData));
|
|
290
298
|
let instance;
|
|
291
299
|
try {
|
|
292
300
|
instance = await this.model.create(plainData);
|
|
@@ -415,7 +423,16 @@ export class SequelizeModel extends AbstractModel {
|
|
|
415
423
|
const assocData = {};
|
|
416
424
|
for (const [key, val] of Object.entries(data)) {
|
|
417
425
|
if (assocNames.includes(key)) {
|
|
418
|
-
|
|
426
|
+
const assoc = this.model.associations[key];
|
|
427
|
+
if (assoc && assoc.associationType === 'BelongsTo') {
|
|
428
|
+
const fk = assoc.foreignKey;
|
|
429
|
+
if (val !== null && val !== undefined) {
|
|
430
|
+
plainData[fk] = typeof val === 'object' ? val[assoc.target.primaryKeyAttribute] : val;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
assocData[key] = val;
|
|
435
|
+
}
|
|
419
436
|
}
|
|
420
437
|
else {
|
|
421
438
|
plainData[key] = val;
|
|
@@ -434,7 +451,16 @@ export class SequelizeModel extends AbstractModel {
|
|
|
434
451
|
const assocData = {};
|
|
435
452
|
for (const [key, val] of Object.entries(data)) {
|
|
436
453
|
if (assocNames.includes(key)) {
|
|
437
|
-
|
|
454
|
+
const assoc = this.model.associations[key];
|
|
455
|
+
if (assoc && assoc.associationType === 'BelongsTo') {
|
|
456
|
+
const fk = assoc.foreignKey;
|
|
457
|
+
if (val !== null && val !== undefined) {
|
|
458
|
+
plainData[fk] = typeof val === 'object' ? val[assoc.target.primaryKeyAttribute] : val;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
assocData[key] = val;
|
|
463
|
+
}
|
|
438
464
|
}
|
|
439
465
|
else {
|
|
440
466
|
plainData[key] = val;
|