@fjell/lib-sequelize 4.4.53 → 4.4.55
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/dist/Coordinate.d.ts +2 -3
- package/dist/Coordinate.d.ts.map +1 -1
- package/dist/Definition.d.ts +1 -1
- package/dist/Definition.d.ts.map +1 -1
- package/dist/Operations.d.ts +1 -2
- package/dist/Operations.d.ts.map +1 -1
- package/dist/SequelizeLibrary.d.ts +1 -2
- package/dist/SequelizeLibrary.d.ts.map +1 -1
- package/dist/SequelizeLibraryFactory.d.ts.map +1 -1
- package/dist/errors/sequelizeErrorHandler.d.ts +17 -0
- package/dist/errors/sequelizeErrorHandler.d.ts.map +1 -0
- package/dist/index.js +735 -553
- package/dist/index.js.map +4 -4
- package/dist/ops/all.d.ts +3 -2
- package/dist/ops/all.d.ts.map +1 -1
- package/dist/ops/create.d.ts +2 -8
- package/dist/ops/create.d.ts.map +1 -1
- package/dist/ops/find.d.ts +2 -2
- package/dist/ops/find.d.ts.map +1 -1
- package/dist/ops/get.d.ts +2 -2
- package/dist/ops/get.d.ts.map +1 -1
- package/dist/ops/one.d.ts +2 -2
- package/dist/ops/one.d.ts.map +1 -1
- package/dist/ops/remove.d.ts +2 -3
- package/dist/ops/remove.d.ts.map +1 -1
- package/dist/ops/update.d.ts +2 -2
- package/dist/ops/update.d.ts.map +1 -1
- package/dist/ops/upsert.d.ts +2 -2
- package/dist/ops/upsert.d.ts.map +1 -1
- package/dist/processing/ReferenceBuilder.d.ts +8 -0
- package/dist/processing/ReferenceBuilder.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/validation/LocationKeyValidator.d.ts +0 -13
- package/dist/validation/LocationKeyValidator.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -26,7 +26,7 @@ var LibLogger = Logging.getLogger("@fjell/lib-sequelize");
|
|
|
26
26
|
var logger_default = LibLogger;
|
|
27
27
|
|
|
28
28
|
// src/Coordinate.ts
|
|
29
|
-
import { createCoordinate as createBaseCoordinate } from "@fjell/
|
|
29
|
+
import { createCoordinate as createBaseCoordinate } from "@fjell/core";
|
|
30
30
|
var logger = logger_default.get("Coordinate");
|
|
31
31
|
var SCOPE_SEQUELIZE = "sequelize";
|
|
32
32
|
var createCoordinate = (kta, scopes) => {
|
|
@@ -48,10 +48,14 @@ var createDefinition = (kta, scopes, libOptions) => {
|
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
// src/SequelizeLibrary.ts
|
|
51
|
+
import * as Library3 from "@fjell/lib";
|
|
52
|
+
|
|
53
|
+
// src/Operations.ts
|
|
51
54
|
import * as Library2 from "@fjell/lib";
|
|
52
55
|
|
|
53
56
|
// src/ops/all.ts
|
|
54
|
-
import {
|
|
57
|
+
import { createAllWrapper } from "@fjell/core";
|
|
58
|
+
import { validateKeys } from "@fjell/core/validation";
|
|
55
59
|
|
|
56
60
|
// src/QueryBuilder.ts
|
|
57
61
|
import {
|
|
@@ -376,52 +380,6 @@ var buildQuery = (itemQuery, model) => {
|
|
|
376
380
|
return options;
|
|
377
381
|
};
|
|
378
382
|
|
|
379
|
-
// src/validation/LocationKeyValidator.ts
|
|
380
|
-
var logger4 = logger_default.get("LocationKeyValidator");
|
|
381
|
-
var validateLocations = (locations, coordinate, operation) => {
|
|
382
|
-
if (!locations || locations.length === 0) {
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
const keyTypeArray = coordinate.kta;
|
|
386
|
-
const expectedLocationTypes = keyTypeArray.slice(1);
|
|
387
|
-
const actualLocationTypes = locations.map((loc) => loc.kt);
|
|
388
|
-
logger4.debug(`Validating locations for ${operation}`, {
|
|
389
|
-
expected: expectedLocationTypes,
|
|
390
|
-
actual: actualLocationTypes,
|
|
391
|
-
coordinate: keyTypeArray
|
|
392
|
-
});
|
|
393
|
-
if (actualLocationTypes.length > expectedLocationTypes.length) {
|
|
394
|
-
logger4.error("Location key array has too many elements", {
|
|
395
|
-
expected: expectedLocationTypes.length,
|
|
396
|
-
actual: actualLocationTypes.length,
|
|
397
|
-
expectedTypes: expectedLocationTypes,
|
|
398
|
-
actualTypes: actualLocationTypes,
|
|
399
|
-
coordinate,
|
|
400
|
-
operation
|
|
401
|
-
});
|
|
402
|
-
throw new Error(
|
|
403
|
-
`Invalid location key array for ${operation}: Expected at most ${expectedLocationTypes.length} location keys (hierarchy: [${expectedLocationTypes.join(", ")}]), but received ${actualLocationTypes.length} (types: [${actualLocationTypes.join(", ")}])`
|
|
404
|
-
);
|
|
405
|
-
}
|
|
406
|
-
for (let i = 0; i < actualLocationTypes.length; i++) {
|
|
407
|
-
if (expectedLocationTypes[i] !== actualLocationTypes[i]) {
|
|
408
|
-
logger4.error("Location key array order mismatch", {
|
|
409
|
-
position: i,
|
|
410
|
-
expected: expectedLocationTypes[i],
|
|
411
|
-
actual: actualLocationTypes[i],
|
|
412
|
-
expectedHierarchy: expectedLocationTypes,
|
|
413
|
-
actualOrder: actualLocationTypes,
|
|
414
|
-
coordinate,
|
|
415
|
-
operation
|
|
416
|
-
});
|
|
417
|
-
throw new Error(
|
|
418
|
-
`Invalid location key array order for ${operation}: At position ${i}, expected key type "${expectedLocationTypes[i]}" but received "${actualLocationTypes[i]}". Location keys must be ordered according to the hierarchy: [${expectedLocationTypes.join(", ")}]. Received order: [${actualLocationTypes.join(", ")}]`
|
|
419
|
-
);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
logger4.debug(`Location key array validation passed for ${operation}`, { locations });
|
|
423
|
-
};
|
|
424
|
-
|
|
425
383
|
// src/util/relationshipUtils.ts
|
|
426
384
|
var buildRelationshipChain = (targetModel, kta, currentIndex, targetIndex) => {
|
|
427
385
|
const associationParts = [];
|
|
@@ -502,9 +460,9 @@ var buildRelationshipPath = (targetModel, locatorType, kta, includeIsDirect = fa
|
|
|
502
460
|
};
|
|
503
461
|
|
|
504
462
|
// src/KeyMaster.ts
|
|
505
|
-
var
|
|
463
|
+
var logger4 = logger_default.get("sequelize", "KeyMaster");
|
|
506
464
|
var extractLocationKeyValue = (model, item, locatorType, kta) => {
|
|
507
|
-
|
|
465
|
+
logger4.default("Extracting location key value", { locatorType, kta });
|
|
508
466
|
const relationshipInfo = buildRelationshipPath(model, locatorType, kta, true);
|
|
509
467
|
if (!relationshipInfo.found) {
|
|
510
468
|
throw new Error(`Location key '${locatorType}' cannot be resolved on model '${model.name}' or through its relationships. Key type array: [${kta.join(", ")}]. Available associations: [${Object.keys(model.associations || {}).join(", ")}]`);
|
|
@@ -548,12 +506,12 @@ var extractLocationKeyValue = (model, item, locatorType, kta) => {
|
|
|
548
506
|
}
|
|
549
507
|
};
|
|
550
508
|
var removeKey = (item) => {
|
|
551
|
-
|
|
509
|
+
logger4.default("Removing Key", { item });
|
|
552
510
|
delete item.key;
|
|
553
511
|
return item;
|
|
554
512
|
};
|
|
555
513
|
var addKey = (model, item, keyTypes) => {
|
|
556
|
-
|
|
514
|
+
logger4.default("Adding Key", { item });
|
|
557
515
|
const key = {};
|
|
558
516
|
const modelClass = model.constructor;
|
|
559
517
|
const primaryKeyAttr = modelClass.primaryKeyAttribute;
|
|
@@ -568,7 +526,7 @@ var addKey = (model, item, keyTypes) => {
|
|
|
568
526
|
locationKeys.push({ kt: locatorType, lk });
|
|
569
527
|
} catch (error) {
|
|
570
528
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
571
|
-
|
|
529
|
+
logger4.error(`Failed to extract location key for '${locatorType}'`, { error: errorMessage, item, keyTypes });
|
|
572
530
|
throw error;
|
|
573
531
|
}
|
|
574
532
|
}
|
|
@@ -589,7 +547,7 @@ import {
|
|
|
589
547
|
|
|
590
548
|
// src/EventCoordinator.ts
|
|
591
549
|
import deepmerge from "deepmerge";
|
|
592
|
-
var
|
|
550
|
+
var logger5 = logger_default.get("sequelize", "EventCoordinator");
|
|
593
551
|
var populateEvents = (item) => {
|
|
594
552
|
const events = {
|
|
595
553
|
created: { at: item.createdAt || null },
|
|
@@ -600,7 +558,7 @@ var populateEvents = (item) => {
|
|
|
600
558
|
return item;
|
|
601
559
|
};
|
|
602
560
|
var extractEvents = (item) => {
|
|
603
|
-
|
|
561
|
+
logger5.default("Extracting Events to database fields", { item });
|
|
604
562
|
if (item.events) {
|
|
605
563
|
if (item.events.created?.at) {
|
|
606
564
|
item.createdAt = item.events.created.at;
|
|
@@ -615,7 +573,7 @@ var extractEvents = (item) => {
|
|
|
615
573
|
return item;
|
|
616
574
|
};
|
|
617
575
|
var removeEvents = (item) => {
|
|
618
|
-
|
|
576
|
+
logger5.default("Removing Events", { item });
|
|
619
577
|
delete item.events;
|
|
620
578
|
return item;
|
|
621
579
|
};
|
|
@@ -623,10 +581,11 @@ var removeEvents = (item) => {
|
|
|
623
581
|
// src/processing/ReferenceBuilder.ts
|
|
624
582
|
var buildSequelizeReference = async (item, referenceDefinition, registry, context) => {
|
|
625
583
|
const libLogger = logger_default.get("processing", "ReferenceBuilder");
|
|
584
|
+
const isCompositeItem = referenceDefinition.kta.length > 1;
|
|
626
585
|
const primaryKeyType = referenceDefinition.kta[0];
|
|
627
|
-
if (
|
|
586
|
+
if (isCompositeItem) {
|
|
628
587
|
libLogger.debug(
|
|
629
|
-
"
|
|
588
|
+
"Detected composite item reference - will use ComKey with empty loc array",
|
|
630
589
|
{
|
|
631
590
|
kta: referenceDefinition.kta,
|
|
632
591
|
primaryKeyType,
|
|
@@ -634,10 +593,6 @@ var buildSequelizeReference = async (item, referenceDefinition, registry, contex
|
|
|
634
593
|
column: referenceDefinition.column
|
|
635
594
|
}
|
|
636
595
|
);
|
|
637
|
-
libLogger.debug(
|
|
638
|
-
'ASSUMPTION: The primary key for key type "%s" is unique and can be used to retrieve composite items',
|
|
639
|
-
primaryKeyType
|
|
640
|
-
);
|
|
641
596
|
}
|
|
642
597
|
if (!registry) {
|
|
643
598
|
throw new Error(
|
|
@@ -655,38 +610,91 @@ var buildSequelizeReference = async (item, referenceDefinition, registry, contex
|
|
|
655
610
|
item[referenceDefinition.property] = null;
|
|
656
611
|
return item;
|
|
657
612
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
613
|
+
let itemKey;
|
|
614
|
+
if (!isCompositeItem) {
|
|
615
|
+
itemKey = {
|
|
616
|
+
kt: primaryKeyType,
|
|
617
|
+
pk: columnValue
|
|
618
|
+
};
|
|
619
|
+
} else if (referenceDefinition.locationColumns && referenceDefinition.locationColumns.length > 0) {
|
|
620
|
+
const locationTypes = referenceDefinition.kta.slice(1);
|
|
621
|
+
const loc = [];
|
|
622
|
+
for (let i = 0; i < referenceDefinition.locationColumns.length; i++) {
|
|
623
|
+
const columnName = referenceDefinition.locationColumns[i];
|
|
624
|
+
const locValue = item[columnName];
|
|
625
|
+
if (locValue == null) {
|
|
626
|
+
libLogger.warning(
|
|
627
|
+
`Location column '${columnName}' is null/undefined for reference '${referenceDefinition.property}'. Falling back to empty loc array search.`
|
|
628
|
+
);
|
|
629
|
+
itemKey = {
|
|
630
|
+
kt: primaryKeyType,
|
|
631
|
+
pk: columnValue,
|
|
632
|
+
loc: []
|
|
633
|
+
};
|
|
634
|
+
break;
|
|
635
|
+
}
|
|
636
|
+
loc.push({
|
|
637
|
+
kt: locationTypes[i],
|
|
638
|
+
lk: locValue
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
if (!itemKey) {
|
|
642
|
+
itemKey = {
|
|
643
|
+
kt: primaryKeyType,
|
|
644
|
+
pk: columnValue,
|
|
645
|
+
loc
|
|
646
|
+
};
|
|
647
|
+
libLogger.debug("Built full ComKey with location context", {
|
|
648
|
+
itemKey,
|
|
649
|
+
locationColumns: referenceDefinition.locationColumns,
|
|
650
|
+
property: referenceDefinition.property
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
} else {
|
|
654
|
+
itemKey = {
|
|
655
|
+
kt: primaryKeyType,
|
|
656
|
+
pk: columnValue,
|
|
657
|
+
loc: []
|
|
658
|
+
};
|
|
659
|
+
libLogger.debug("Using empty loc array for composite item reference", {
|
|
660
|
+
kta: referenceDefinition.kta,
|
|
661
|
+
property: referenceDefinition.property
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
libLogger.debug("Created reference key", {
|
|
665
|
+
itemKey,
|
|
666
|
+
isCompositeItem,
|
|
667
|
+
hasLocationColumns: !!referenceDefinition.locationColumns,
|
|
668
|
+
property: referenceDefinition.property
|
|
669
|
+
});
|
|
662
670
|
let referencedItem;
|
|
663
671
|
if (context) {
|
|
664
|
-
if (context.isCached(
|
|
665
|
-
libLogger.debug("Using cached reference", {
|
|
666
|
-
referencedItem = context.getCached(
|
|
667
|
-
} else if (context.isInProgress(
|
|
672
|
+
if (context.isCached(itemKey)) {
|
|
673
|
+
libLogger.debug("Using cached reference", { itemKey, property: referenceDefinition.property });
|
|
674
|
+
referencedItem = context.getCached(itemKey);
|
|
675
|
+
} else if (context.isInProgress(itemKey)) {
|
|
668
676
|
libLogger.debug("Circular dependency detected, creating reference placeholder", {
|
|
669
|
-
|
|
677
|
+
itemKey,
|
|
670
678
|
property: referenceDefinition.property
|
|
671
679
|
});
|
|
672
680
|
referencedItem = {
|
|
673
|
-
key:
|
|
681
|
+
key: itemKey
|
|
674
682
|
// Add any other minimal properties that might be needed
|
|
675
683
|
// This prevents infinite loops while still providing the key for identification
|
|
676
684
|
};
|
|
677
685
|
} else {
|
|
678
|
-
context.markInProgress(
|
|
686
|
+
context.markInProgress(itemKey);
|
|
679
687
|
try {
|
|
680
|
-
referencedItem = await library.operations.get(
|
|
681
|
-
context.setCached(
|
|
688
|
+
referencedItem = await library.operations.get(itemKey);
|
|
689
|
+
context.setCached(itemKey, referencedItem);
|
|
682
690
|
} catch (error) {
|
|
683
691
|
throw error;
|
|
684
692
|
} finally {
|
|
685
|
-
context.markComplete(
|
|
693
|
+
context.markComplete(itemKey);
|
|
686
694
|
}
|
|
687
695
|
}
|
|
688
696
|
} else {
|
|
689
|
-
referencedItem = await library.operations.get(
|
|
697
|
+
referencedItem = await library.operations.get(itemKey);
|
|
690
698
|
}
|
|
691
699
|
item[referenceDefinition.property] = referencedItem;
|
|
692
700
|
return item;
|
|
@@ -700,27 +708,27 @@ var stripSequelizeReferenceItems = (item, referenceDefinitions) => {
|
|
|
700
708
|
};
|
|
701
709
|
|
|
702
710
|
// src/RowProcessor.ts
|
|
703
|
-
var
|
|
711
|
+
var logger6 = logger_default.get("sequelize", "RowProcessor");
|
|
704
712
|
var processRow = async (row, keyTypes, referenceDefinitions, aggregationDefinitions, registry, context) => {
|
|
705
|
-
|
|
713
|
+
logger6.default("Processing Row", { row });
|
|
706
714
|
const operationContext = context || createOperationContext();
|
|
707
715
|
return contextManager.withContext(operationContext, async () => {
|
|
708
716
|
let item = row.get({ plain: true });
|
|
709
|
-
|
|
717
|
+
logger6.default("Adding Key to Item with Key Types: %s", stringifyJSON(keyTypes));
|
|
710
718
|
item = addKey(row, item, keyTypes);
|
|
711
719
|
item = populateEvents(item);
|
|
712
|
-
|
|
720
|
+
logger6.default("Key Added to Item: %s", stringifyJSON(item.key));
|
|
713
721
|
operationContext.markInProgress(item.key);
|
|
714
722
|
try {
|
|
715
723
|
if (referenceDefinitions && referenceDefinitions.length > 0) {
|
|
716
724
|
for (const referenceDefinition of referenceDefinitions) {
|
|
717
|
-
|
|
725
|
+
logger6.default("Processing Reference for %s to %s", item.key.kt, stringifyJSON(referenceDefinition.kta));
|
|
718
726
|
item = await buildSequelizeReference(item, referenceDefinition, registry, operationContext);
|
|
719
727
|
}
|
|
720
728
|
}
|
|
721
729
|
if (aggregationDefinitions && aggregationDefinitions.length > 0) {
|
|
722
730
|
for (const aggregationDefinition of aggregationDefinitions) {
|
|
723
|
-
|
|
731
|
+
logger6.default("Processing Aggregation for %s from %s", item.key.kt, stringifyJSON(aggregationDefinition.kta));
|
|
724
732
|
item = await buildAggregation(item, aggregationDefinition, registry, operationContext);
|
|
725
733
|
}
|
|
726
734
|
}
|
|
@@ -728,14 +736,205 @@ var processRow = async (row, keyTypes, referenceDefinitions, aggregationDefiniti
|
|
|
728
736
|
} finally {
|
|
729
737
|
operationContext.markComplete(item.key);
|
|
730
738
|
}
|
|
731
|
-
|
|
739
|
+
logger6.default("Processed Row: %j", stringifyJSON(item));
|
|
732
740
|
return item;
|
|
733
741
|
});
|
|
734
742
|
};
|
|
735
743
|
|
|
736
744
|
// src/ops/all.ts
|
|
737
745
|
import { Op as Op2 } from "sequelize";
|
|
738
|
-
|
|
746
|
+
|
|
747
|
+
// src/errors/sequelizeErrorHandler.ts
|
|
748
|
+
import {
|
|
749
|
+
BusinessLogicError,
|
|
750
|
+
DuplicateError,
|
|
751
|
+
ValidationError
|
|
752
|
+
} from "@fjell/core";
|
|
753
|
+
function transformSequelizeError(error, itemType, key, modelName, itemData) {
|
|
754
|
+
if (error.code === "23505" || error.original?.code === "23505") {
|
|
755
|
+
const constraint = error.original?.constraint;
|
|
756
|
+
const detail = error.original?.detail;
|
|
757
|
+
let message = "";
|
|
758
|
+
if (constraint && detail) {
|
|
759
|
+
message = `Duplicate value violates unique constraint '${constraint}'. ${detail}`;
|
|
760
|
+
} else if (detail) {
|
|
761
|
+
message = `Duplicate value detected. This record already exists. ${detail}`;
|
|
762
|
+
} else {
|
|
763
|
+
message = `${itemType} already exists`;
|
|
764
|
+
}
|
|
765
|
+
const field = error.fields ? Object.keys(error.fields)[0] : "unique constraint";
|
|
766
|
+
return new DuplicateError(message, key, field);
|
|
767
|
+
}
|
|
768
|
+
if (error.name === "SequelizeUniqueConstraintError" || error.code === "ER_DUP_ENTRY") {
|
|
769
|
+
const field = error.fields ? Object.keys(error.fields)[0] : "unique constraint";
|
|
770
|
+
return new DuplicateError(
|
|
771
|
+
`${itemType} already exists`,
|
|
772
|
+
key,
|
|
773
|
+
field
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
if (error.name === "SequelizeValidationError") {
|
|
777
|
+
const fields = error.errors?.map((e) => e.path) || [];
|
|
778
|
+
const message = error.errors?.map((e) => e.message).join(", ") || `Validation failed for ${itemType}`;
|
|
779
|
+
return new ValidationError(
|
|
780
|
+
message,
|
|
781
|
+
fields,
|
|
782
|
+
"Fix validation errors and try again"
|
|
783
|
+
);
|
|
784
|
+
}
|
|
785
|
+
if (error.code === "23503" || error.original?.code === "23503") {
|
|
786
|
+
const constraint = error.original?.constraint;
|
|
787
|
+
const detail = error.original?.detail;
|
|
788
|
+
let message = "";
|
|
789
|
+
if (constraint && detail) {
|
|
790
|
+
message = `Foreign key constraint '${constraint}' violated. Referenced record does not exist. ${detail}`;
|
|
791
|
+
} else if (constraint) {
|
|
792
|
+
message = `Foreign key constraint '${constraint}' violated. Referenced record does not exist.`;
|
|
793
|
+
} else if (detail) {
|
|
794
|
+
message = `Referenced record does not exist. Check that all related records are valid. ${detail}`;
|
|
795
|
+
} else {
|
|
796
|
+
message = "Referenced item does not exist";
|
|
797
|
+
}
|
|
798
|
+
return new ValidationError(
|
|
799
|
+
message,
|
|
800
|
+
void 0,
|
|
801
|
+
"Ensure all referenced items exist before creating this relationship"
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
if (error.name === "SequelizeForeignKeyConstraintError" || error.code === "ER_NO_REFERENCED_ROW") {
|
|
805
|
+
return new ValidationError(
|
|
806
|
+
"Referenced item does not exist",
|
|
807
|
+
void 0,
|
|
808
|
+
"Ensure all referenced items exist before creating this relationship"
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
if (error.code === "23502" || error.original?.code === "23502") {
|
|
812
|
+
const column = error.original?.column;
|
|
813
|
+
const fields = column ? [column] : void 0;
|
|
814
|
+
return new ValidationError(
|
|
815
|
+
column ? `Required field '${column}' cannot be null` : "Required field is missing or null",
|
|
816
|
+
fields,
|
|
817
|
+
"Provide all required fields"
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
if (error.code === "23514" || error.original?.code === "23514") {
|
|
821
|
+
const constraint = error.original?.constraint;
|
|
822
|
+
const detail = error.original?.detail;
|
|
823
|
+
let message = "";
|
|
824
|
+
if (constraint && detail) {
|
|
825
|
+
message = `Check constraint '${constraint}' violated. ${detail}`;
|
|
826
|
+
} else if (detail) {
|
|
827
|
+
message = `Data validation failed. Check constraint violated. ${detail}`;
|
|
828
|
+
} else if (constraint) {
|
|
829
|
+
message = `Check constraint '${constraint}' violated`;
|
|
830
|
+
} else {
|
|
831
|
+
message = "Data validation failed";
|
|
832
|
+
}
|
|
833
|
+
return new ValidationError(
|
|
834
|
+
message,
|
|
835
|
+
void 0,
|
|
836
|
+
"Ensure data meets all constraints"
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
if (error.name === "SequelizeDatabaseError" && error.message?.includes("Unknown column")) {
|
|
840
|
+
const match = error.message.match(/Unknown column '(.+?)'/);
|
|
841
|
+
const field = match ? match[1] : "unknown field";
|
|
842
|
+
return new ValidationError(
|
|
843
|
+
`Invalid field: ${field}`,
|
|
844
|
+
[field],
|
|
845
|
+
"Check the field names in your request"
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
if (error.code === "22001" || error.original?.code === "22001") {
|
|
849
|
+
const detail = error.original?.detail;
|
|
850
|
+
const message = detail ? `Data too long for field. Check string lengths. ${detail}` : "Data too long for field";
|
|
851
|
+
return new ValidationError(
|
|
852
|
+
message,
|
|
853
|
+
void 0,
|
|
854
|
+
"Check string lengths and try again"
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
if (error.code === "22003" || error.original?.code === "22003") {
|
|
858
|
+
const detail = error.original?.detail;
|
|
859
|
+
const message = detail ? `Numeric value out of range. Check number values. ${detail}` : "Numeric value out of range";
|
|
860
|
+
return new ValidationError(
|
|
861
|
+
message,
|
|
862
|
+
void 0,
|
|
863
|
+
"Check number values and try again"
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
if (error.code === "42703" || error.original?.code === "42703") {
|
|
867
|
+
const column = error.original?.column;
|
|
868
|
+
const fields = column ? [column] : void 0;
|
|
869
|
+
const message = column && modelName ? `Column '${column}' does not exist in table '${modelName}'` : column ? `Column '${column}' does not exist` : "Referenced column does not exist";
|
|
870
|
+
return new ValidationError(
|
|
871
|
+
message,
|
|
872
|
+
fields,
|
|
873
|
+
"Check the field names in your request"
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
if (error.code === "42P01" || error.original?.code === "42P01") {
|
|
877
|
+
const message = modelName ? `Table '${modelName}' does not exist` : "Table does not exist";
|
|
878
|
+
return new ValidationError(
|
|
879
|
+
message,
|
|
880
|
+
void 0,
|
|
881
|
+
"Check the table name and database configuration"
|
|
882
|
+
);
|
|
883
|
+
}
|
|
884
|
+
if (error.message?.includes("notNull Violation") || error.message?.includes("cannot be null")) {
|
|
885
|
+
const fieldMatches = error.message.match(/([a-zA-Z]+\.[a-zA-Z]+) cannot be null/g);
|
|
886
|
+
if (fieldMatches) {
|
|
887
|
+
const fields = fieldMatches.map((match) => {
|
|
888
|
+
const parts = match.split(".");
|
|
889
|
+
return parts[1]?.split(" ")[0];
|
|
890
|
+
}).filter(Boolean);
|
|
891
|
+
if (fields.length > 0) {
|
|
892
|
+
return new ValidationError(
|
|
893
|
+
`Required field${fields.length > 1 ? "s" : ""} ${fields.join(", ")} cannot be null`,
|
|
894
|
+
fields,
|
|
895
|
+
"Provide all required fields"
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
return new ValidationError(
|
|
900
|
+
"Required fields are missing",
|
|
901
|
+
void 0,
|
|
902
|
+
"Provide all required fields"
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
if (error.name === "SequelizeConnectionError" || error.name === "SequelizeConnectionRefusedError") {
|
|
906
|
+
return new BusinessLogicError(
|
|
907
|
+
"Database connection failed",
|
|
908
|
+
"Check database connectivity and try again",
|
|
909
|
+
true
|
|
910
|
+
// retryable
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
if (error.name === "SequelizeTimeoutError") {
|
|
914
|
+
return new BusinessLogicError(
|
|
915
|
+
"Database operation timed out",
|
|
916
|
+
"Try again or simplify the operation",
|
|
917
|
+
true
|
|
918
|
+
// retryable
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
if (!error.original && modelName && itemData && error.message) {
|
|
922
|
+
const formattedData = JSON.stringify(itemData, null, 2);
|
|
923
|
+
return new Error(
|
|
924
|
+
`Database error in ${modelName}.create(): ${error.message}. Item data: ${formattedData}`
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
if (error.original && modelName && itemData) {
|
|
928
|
+
const formattedData = JSON.stringify(itemData, null, 2);
|
|
929
|
+
return new Error(
|
|
930
|
+
`Database error in ${modelName}.create(): ${error.message}. Item data: ${formattedData}`
|
|
931
|
+
);
|
|
932
|
+
}
|
|
933
|
+
return error;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// src/ops/all.ts
|
|
937
|
+
var logger7 = logger_default.get("sequelize", "ops", "all");
|
|
739
938
|
var mergeIncludes = (existingIncludes, newIncludes) => {
|
|
740
939
|
const mergedIncludes = [...existingIncludes];
|
|
741
940
|
for (const newInclude of newIncludes) {
|
|
@@ -757,162 +956,105 @@ var mergeIncludes = (existingIncludes, newIncludes) => {
|
|
|
757
956
|
};
|
|
758
957
|
var getAllOperation = (models, definition, registry) => {
|
|
759
958
|
const { coordinate, options: { references, aggregations } } = definition;
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
const
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
logger8.error(`Location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`, { locKey, locations: loc });
|
|
787
|
-
throw new Error(`Location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`);
|
|
788
|
-
}
|
|
789
|
-
const foreignKeyField = locKey.kt + "Id";
|
|
790
|
-
if (options.where[foreignKeyField]) {
|
|
791
|
-
logger8.debug(`[ALL] Field ${foreignKeyField} already constrained by itemQuery, skipping location constraint to avoid conflicts`);
|
|
792
|
-
continue;
|
|
793
|
-
}
|
|
794
|
-
logger8.trace(`[ALL] Setting direct location where clause: ${foreignKeyField} = ${stringifyJSON(locKey.lk)} (type: ${typeof locKey.lk})`);
|
|
795
|
-
options.where[foreignKeyField] = {
|
|
796
|
-
[Op2.eq]: locKey.lk
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
for (const locKey of hierarchicalLocations) {
|
|
800
|
-
if (locKey.lk === void 0 || locKey.lk == null || locKey.lk === "" || typeof locKey.lk === "object" && Object.keys(locKey.lk).length === 0) {
|
|
801
|
-
logger8.error(`Hierarchical location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`, { locKey, locations: loc });
|
|
802
|
-
throw new Error(`Hierarchical location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`);
|
|
803
|
-
}
|
|
804
|
-
const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta);
|
|
805
|
-
if (relationshipInfo.found && relationshipInfo.path) {
|
|
806
|
-
if (options.where[relationshipInfo.path]) {
|
|
807
|
-
logger8.debug(`[ALL] Field ${relationshipInfo.path} already constrained by itemQuery, skipping hierarchical location constraint to avoid conflicts`);
|
|
808
|
-
continue;
|
|
959
|
+
return createAllWrapper(
|
|
960
|
+
coordinate,
|
|
961
|
+
async (itemQuery, locations) => {
|
|
962
|
+
try {
|
|
963
|
+
const locs = locations ?? [];
|
|
964
|
+
logger7.debug(`ALL operation called on ${models[0].name} with ${locs.length} location filters: ${locs.map((loc2) => `${loc2.kt}=${loc2.lk}`).join(", ") || "none"}`);
|
|
965
|
+
const loc = locs;
|
|
966
|
+
const model = models[0];
|
|
967
|
+
const options = buildQuery(itemQuery ?? {}, model);
|
|
968
|
+
if (loc.length > 0) {
|
|
969
|
+
const { kta } = coordinate;
|
|
970
|
+
const directLocations = [];
|
|
971
|
+
const hierarchicalLocations = [];
|
|
972
|
+
const additionalIncludes = [];
|
|
973
|
+
for (const locKey of loc) {
|
|
974
|
+
const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta, true);
|
|
975
|
+
if (!relationshipInfo.found) {
|
|
976
|
+
const errorMessage = `Location key '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
|
|
977
|
+
logger7.error(errorMessage, { locations: loc, kta });
|
|
978
|
+
throw new Error(errorMessage);
|
|
979
|
+
}
|
|
980
|
+
if (relationshipInfo.isDirect) {
|
|
981
|
+
directLocations.push(locKey);
|
|
982
|
+
} else {
|
|
983
|
+
hierarchicalLocations.push(locKey);
|
|
984
|
+
}
|
|
809
985
|
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
986
|
+
for (const locKey of directLocations) {
|
|
987
|
+
if (locKey.lk === void 0 || locKey.lk == null || locKey.lk === "" || typeof locKey.lk === "object" && Object.keys(locKey.lk).length === 0) {
|
|
988
|
+
logger7.error(`Location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`, { locKey, locations: loc });
|
|
989
|
+
throw new Error(`Location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`);
|
|
990
|
+
}
|
|
991
|
+
const foreignKeyField = locKey.kt + "Id";
|
|
992
|
+
if (options.where[foreignKeyField]) {
|
|
993
|
+
logger7.debug(`[ALL] Field ${foreignKeyField} already constrained by itemQuery, skipping location constraint to avoid conflicts`);
|
|
994
|
+
continue;
|
|
995
|
+
}
|
|
996
|
+
logger7.trace(`[ALL] Setting direct location where clause: ${foreignKeyField} = ${stringifyJSON(locKey.lk)} (type: ${typeof locKey.lk})`);
|
|
997
|
+
options.where[foreignKeyField] = {
|
|
998
|
+
[Op2.eq]: locKey.lk
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
for (const locKey of hierarchicalLocations) {
|
|
1002
|
+
if (locKey.lk === void 0 || locKey.lk == null || locKey.lk === "" || typeof locKey.lk === "object" && Object.keys(locKey.lk).length === 0) {
|
|
1003
|
+
logger7.error(`Hierarchical location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`, { locKey, locations: loc });
|
|
1004
|
+
throw new Error(`Hierarchical location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`);
|
|
1005
|
+
}
|
|
1006
|
+
const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta);
|
|
1007
|
+
if (relationshipInfo.found && relationshipInfo.path) {
|
|
1008
|
+
if (options.where[relationshipInfo.path]) {
|
|
1009
|
+
logger7.debug(`[ALL] Field ${relationshipInfo.path} already constrained by itemQuery, skipping hierarchical location constraint to avoid conflicts`);
|
|
1010
|
+
continue;
|
|
1011
|
+
}
|
|
1012
|
+
logger7.trace(`[ALL] Setting hierarchical location where clause: ${relationshipInfo.path} = ${stringifyJSON(locKey.lk)} (type: ${typeof locKey.lk})`);
|
|
1013
|
+
options.where[relationshipInfo.path] = {
|
|
1014
|
+
[Op2.eq]: locKey.lk
|
|
1015
|
+
};
|
|
1016
|
+
if (relationshipInfo.includes) {
|
|
1017
|
+
additionalIncludes.push(...relationshipInfo.includes);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
if (additionalIncludes.length > 0) {
|
|
1022
|
+
const existingIncludes = options.include || [];
|
|
1023
|
+
options.include = mergeIncludes(existingIncludes, additionalIncludes);
|
|
816
1024
|
}
|
|
817
1025
|
}
|
|
1026
|
+
logger7.default(`All query configured for ${model.name} with where fields: ${options.where ? Object.keys(options.where).join(", ") : "none"}, includes: ${options.include?.length || 0}`);
|
|
1027
|
+
try {
|
|
1028
|
+
logger7.trace(`[ALL] Executing ${model.name}.findAll() with options: ${JSON.stringify(options, null, 2)}`);
|
|
1029
|
+
} catch {
|
|
1030
|
+
logger7.trace(`[ALL] Executing ${model.name}.findAll() with options containing non-serializable operators (${Object.keys(options.where || {}).length} where conditions)`);
|
|
1031
|
+
}
|
|
1032
|
+
const matchingItems = await model.findAll(options);
|
|
1033
|
+
const currentContext = contextManager.getCurrentContext();
|
|
1034
|
+
const results = await Promise.all(matchingItems.map(async (row) => {
|
|
1035
|
+
const processedRow = await processRow(row, coordinate.kta, references || [], aggregations || [], registry, currentContext);
|
|
1036
|
+
return validateKeys(processedRow, coordinate.kta);
|
|
1037
|
+
}));
|
|
1038
|
+
logger7.debug(`[ALL] Returning ${results.length} ${model.name} records`);
|
|
1039
|
+
return results;
|
|
1040
|
+
} catch (error) {
|
|
1041
|
+
throw transformSequelizeError(error, coordinate.kta[0]);
|
|
818
1042
|
}
|
|
819
|
-
if (additionalIncludes.length > 0) {
|
|
820
|
-
const existingIncludes = options.include || [];
|
|
821
|
-
options.include = mergeIncludes(existingIncludes, additionalIncludes);
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
logger8.default(`All query configured for ${model.name} with where fields: ${options.where ? Object.keys(options.where).join(", ") : "none"}, includes: ${options.include?.length || 0}`);
|
|
825
|
-
try {
|
|
826
|
-
logger8.trace(`[ALL] Executing ${model.name}.findAll() with options: ${JSON.stringify(options, null, 2)}`);
|
|
827
|
-
} catch {
|
|
828
|
-
logger8.trace(`[ALL] Executing ${model.name}.findAll() with options containing non-serializable operators (${Object.keys(options.where || {}).length} where conditions)`);
|
|
829
1043
|
}
|
|
830
|
-
|
|
831
|
-
const currentContext = contextManager.getCurrentContext();
|
|
832
|
-
const results = await Promise.all(matchingItems.map(async (row) => {
|
|
833
|
-
const processedRow = await processRow(row, coordinate.kta, references || [], aggregations || [], registry, currentContext);
|
|
834
|
-
return validateKeys(processedRow, coordinate.kta);
|
|
835
|
-
}));
|
|
836
|
-
logger8.debug(`[ALL] Returning ${results.length} ${model.name} records`);
|
|
837
|
-
return results;
|
|
838
|
-
};
|
|
839
|
-
return all;
|
|
1044
|
+
);
|
|
840
1045
|
};
|
|
841
1046
|
|
|
842
1047
|
// src/ops/create.ts
|
|
843
|
-
import { isComKey as isComKey2, isPriKey as isPriKey2
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
const originalMessage = error.message || "";
|
|
847
|
-
const errorCode = error.original?.code;
|
|
848
|
-
const constraint = error.original?.constraint;
|
|
849
|
-
const detail = error.original?.detail;
|
|
850
|
-
logger9.error("Database error during create operation", {
|
|
851
|
-
errorCode,
|
|
852
|
-
constraint,
|
|
853
|
-
detail,
|
|
854
|
-
originalMessage,
|
|
855
|
-
modelName,
|
|
856
|
-
itemData: JSON.stringify(itemData, null, 2)
|
|
857
|
-
});
|
|
858
|
-
switch (errorCode) {
|
|
859
|
-
case "23505":
|
|
860
|
-
if (constraint) {
|
|
861
|
-
return new Error(`Duplicate value violates unique constraint '${constraint}'. ${detail || ""}`);
|
|
862
|
-
}
|
|
863
|
-
return new Error(`Duplicate value detected. This record already exists. ${detail || ""}`);
|
|
864
|
-
case "23503":
|
|
865
|
-
if (constraint) {
|
|
866
|
-
return new Error(`Foreign key constraint '${constraint}' violated. Referenced record does not exist. ${detail || ""}`);
|
|
867
|
-
}
|
|
868
|
-
return new Error(`Referenced record does not exist. Check that all related records are valid. ${detail || ""}`);
|
|
869
|
-
case "23502":
|
|
870
|
-
const column = error.original?.column;
|
|
871
|
-
if (column) {
|
|
872
|
-
return new Error(`Required field '${column}' cannot be null`);
|
|
873
|
-
}
|
|
874
|
-
return new Error(`Required field is missing or null`);
|
|
875
|
-
case "23514":
|
|
876
|
-
if (constraint) {
|
|
877
|
-
return new Error(`Check constraint '${constraint}' violated. ${detail || ""}`);
|
|
878
|
-
}
|
|
879
|
-
return new Error(`Data validation failed. Check constraint violated. ${detail || ""}`);
|
|
880
|
-
case "22001":
|
|
881
|
-
return new Error(`Data too long for field. Check string lengths. ${detail || ""}`);
|
|
882
|
-
case "22003":
|
|
883
|
-
return new Error(`Numeric value out of range. Check number values. ${detail || ""}`);
|
|
884
|
-
case "42703":
|
|
885
|
-
const undefinedColumn = error.original?.column;
|
|
886
|
-
if (undefinedColumn) {
|
|
887
|
-
return new Error(`Column '${undefinedColumn}' does not exist in table '${modelName}'`);
|
|
888
|
-
}
|
|
889
|
-
return new Error(`Referenced column does not exist`);
|
|
890
|
-
case "42P01":
|
|
891
|
-
return new Error(`Table '${modelName}' does not exist`);
|
|
892
|
-
default:
|
|
893
|
-
if (originalMessage.includes("notNull Violation")) {
|
|
894
|
-
const fieldMatches = originalMessage.match(/([a-zA-Z]+\.[a-zA-Z]+) cannot be null/g);
|
|
895
|
-
if (fieldMatches) {
|
|
896
|
-
const fields = fieldMatches.map((match) => {
|
|
897
|
-
const parts = match.split(".");
|
|
898
|
-
return parts[1]?.split(" ")[0];
|
|
899
|
-
}).filter(Boolean);
|
|
900
|
-
if (fields.length > 0) {
|
|
901
|
-
return new Error(`Required field${fields.length > 1 ? "s" : ""} ${fields.join(", ")} cannot be null`);
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
return new Error("Required fields are missing");
|
|
905
|
-
}
|
|
906
|
-
return new Error(`Database error in ${modelName}.create(): ${originalMessage}. Item data: ${JSON.stringify(itemData, null, 2)}`);
|
|
907
|
-
}
|
|
908
|
-
}
|
|
1048
|
+
import { createCreateWrapper, isComKey as isComKey2, isPriKey as isPriKey2 } from "@fjell/core";
|
|
1049
|
+
import { validateKeys as validateKeys2 } from "@fjell/core/validation";
|
|
1050
|
+
var logger8 = logger_default.get("sequelize", "ops", "create");
|
|
909
1051
|
async function validateHierarchicalChain(models, locKey, kta) {
|
|
1052
|
+
const locatorIndex = kta.indexOf(locKey.kt);
|
|
1053
|
+
if (locatorIndex === -1) {
|
|
1054
|
+
throw new Error(`Locator type '${locKey.kt}' not found in kta array`);
|
|
1055
|
+
}
|
|
1056
|
+
const locatorModel = models[locatorIndex] || models[0];
|
|
910
1057
|
try {
|
|
911
|
-
const locatorIndex = kta.indexOf(locKey.kt);
|
|
912
|
-
if (locatorIndex === -1) {
|
|
913
|
-
throw new Error(`Locator type '${locKey.kt}' not found in kta array`);
|
|
914
|
-
}
|
|
915
|
-
const locatorModel = models[locatorIndex] || models[0];
|
|
916
1058
|
const chainResult = buildRelationshipChain(locatorModel, kta, locatorIndex, kta.length - 1);
|
|
917
1059
|
if (!chainResult.success) {
|
|
918
1060
|
const record2 = await locatorModel.findByPk(locKey.lk);
|
|
@@ -933,56 +1075,82 @@ async function validateHierarchicalChain(models, locKey, kta) {
|
|
|
933
1075
|
}
|
|
934
1076
|
} catch (error) {
|
|
935
1077
|
if (error.original) {
|
|
936
|
-
throw
|
|
1078
|
+
throw transformSequelizeError(error, locKey.kt, { locKey, kta }, locatorModel.name);
|
|
937
1079
|
}
|
|
938
1080
|
throw error;
|
|
939
1081
|
}
|
|
940
1082
|
}
|
|
941
1083
|
var getCreateOperation = (models, definition, registry) => {
|
|
942
|
-
const
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
for (const key of Object.keys(itemData)) {
|
|
961
|
-
if (!modelAttributes[key]) {
|
|
962
|
-
invalidAttributes.push(key);
|
|
1084
|
+
const { coordinate, options: { references, aggregations } } = definition;
|
|
1085
|
+
const { kta } = coordinate;
|
|
1086
|
+
return createCreateWrapper(
|
|
1087
|
+
coordinate,
|
|
1088
|
+
async (item, options) => {
|
|
1089
|
+
const constraints = options?.key ? `key: pk=${options.key.pk}, loc=[${isComKey2(options.key) ? options.key.loc.map((l) => `${l.kt}=${l.lk}`).join(", ") : ""}]` : options?.locations ? `locations: ${options.locations.map((loc) => `${loc.kt}=${loc.lk}`).join(", ")}` : "no constraints";
|
|
1090
|
+
logger8.debug(`CREATE operation called on ${models[0].name} with ${constraints}`);
|
|
1091
|
+
logger8.default(`Create configured for ${models[0].name} with ${Object.keys(item).length} item fields`);
|
|
1092
|
+
const model = models[0];
|
|
1093
|
+
const modelAttributes = model.getAttributes();
|
|
1094
|
+
let itemData = { ...item };
|
|
1095
|
+
itemData = extractEvents(itemData);
|
|
1096
|
+
itemData = removeEvents(itemData);
|
|
1097
|
+
const invalidAttributes = [];
|
|
1098
|
+
for (const key of Object.keys(itemData)) {
|
|
1099
|
+
if (!modelAttributes[key]) {
|
|
1100
|
+
invalidAttributes.push(key);
|
|
1101
|
+
}
|
|
963
1102
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1103
|
+
if (invalidAttributes.length > 0) {
|
|
1104
|
+
const availableAttributes = Object.keys(modelAttributes).join(", ");
|
|
1105
|
+
throw new Error(
|
|
1106
|
+
`Invalid attributes for model '${model.name}': [${invalidAttributes.join(", ")}]. Available attributes: [${availableAttributes}]. Item data: ${JSON.stringify(itemData, null, 2)}`
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
1109
|
+
if (options?.key) {
|
|
1110
|
+
const key = options.key;
|
|
1111
|
+
if (isPriKey2(key)) {
|
|
1112
|
+
itemData.id = key.pk;
|
|
1113
|
+
} else if (isComKey2(key)) {
|
|
1114
|
+
itemData.id = key.pk;
|
|
1115
|
+
const comKey = key;
|
|
1116
|
+
const directLocations = [];
|
|
1117
|
+
const hierarchicalLocations = [];
|
|
1118
|
+
for (const locKey of comKey.loc) {
|
|
1119
|
+
const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta, true);
|
|
1120
|
+
if (!relationshipInfo.found) {
|
|
1121
|
+
const associations = model.associations ? Object.keys(model.associations) : [];
|
|
1122
|
+
const errorMessage = `Composite key locator '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships. Available associations: [${associations.join(", ")}]. KTA: [${kta.join(", ")}]. Composite key: ${JSON.stringify(comKey, null, 2)}`;
|
|
1123
|
+
logger8.error(errorMessage, { key: comKey, kta, associations });
|
|
1124
|
+
throw new Error(errorMessage);
|
|
1125
|
+
}
|
|
1126
|
+
if (relationshipInfo.isDirect) {
|
|
1127
|
+
directLocations.push(locKey);
|
|
1128
|
+
} else {
|
|
1129
|
+
hierarchicalLocations.push(locKey);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
for (const locKey of directLocations) {
|
|
1133
|
+
if (locKey.lk == null || locKey.lk === "") {
|
|
1134
|
+
logger8.error(`Composite key location '${locKey.kt}' has undefined/null lk value`, { locKey, key: comKey });
|
|
1135
|
+
throw new Error(`Composite key location '${locKey.kt}' has undefined/null lk value`);
|
|
1136
|
+
}
|
|
1137
|
+
const foreignKeyField = locKey.kt + "Id";
|
|
1138
|
+
itemData[foreignKeyField] = locKey.lk;
|
|
1139
|
+
}
|
|
1140
|
+
for (const locKey of hierarchicalLocations) {
|
|
1141
|
+
await validateHierarchicalChain(models, locKey, kta);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
if (options?.locations) {
|
|
978
1146
|
const directLocations = [];
|
|
979
1147
|
const hierarchicalLocations = [];
|
|
980
|
-
for (const locKey of
|
|
1148
|
+
for (const locKey of options.locations) {
|
|
981
1149
|
const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta, true);
|
|
982
1150
|
if (!relationshipInfo.found) {
|
|
983
1151
|
const associations = model.associations ? Object.keys(model.associations) : [];
|
|
984
|
-
const errorMessage = `
|
|
985
|
-
|
|
1152
|
+
const errorMessage = `Location key '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships. Available associations: [${associations.join(", ")}]. KTA: [${kta.join(", ")}]. Locations: ${JSON.stringify(options.locations, null, 2)}`;
|
|
1153
|
+
logger8.error(errorMessage, { locations: options.locations, kta, associations });
|
|
986
1154
|
throw new Error(errorMessage);
|
|
987
1155
|
}
|
|
988
1156
|
if (relationshipInfo.isDirect) {
|
|
@@ -993,8 +1161,8 @@ var getCreateOperation = (models, definition, registry) => {
|
|
|
993
1161
|
}
|
|
994
1162
|
for (const locKey of directLocations) {
|
|
995
1163
|
if (locKey.lk == null || locKey.lk === "") {
|
|
996
|
-
|
|
997
|
-
throw new Error(`
|
|
1164
|
+
logger8.error(`Location option '${locKey.kt}' has undefined/null lk value`, { locKey, locations: options.locations });
|
|
1165
|
+
throw new Error(`Location option '${locKey.kt}' has undefined/null lk value`);
|
|
998
1166
|
}
|
|
999
1167
|
const foreignKeyField = locKey.kt + "Id";
|
|
1000
1168
|
itemData[foreignKeyField] = locKey.lk;
|
|
@@ -1003,99 +1171,78 @@ var getCreateOperation = (models, definition, registry) => {
|
|
|
1003
1171
|
await validateHierarchicalChain(models, locKey, kta);
|
|
1004
1172
|
}
|
|
1005
1173
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
logger9.error(errorMessage, { locations: options.locations, kta, associations });
|
|
1016
|
-
throw new Error(errorMessage);
|
|
1017
|
-
}
|
|
1018
|
-
if (relationshipInfo.isDirect) {
|
|
1019
|
-
directLocations.push(locKey);
|
|
1020
|
-
} else {
|
|
1021
|
-
hierarchicalLocations.push(locKey);
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
for (const locKey of directLocations) {
|
|
1025
|
-
if (locKey.lk == null || locKey.lk === "") {
|
|
1026
|
-
logger9.error(`Location option '${locKey.kt}' has undefined/null lk value`, { locKey, locations: options.locations });
|
|
1027
|
-
throw new Error(`Location option '${locKey.kt}' has undefined/null lk value`);
|
|
1028
|
-
}
|
|
1029
|
-
const foreignKeyField = locKey.kt + "Id";
|
|
1030
|
-
itemData[foreignKeyField] = locKey.lk;
|
|
1031
|
-
}
|
|
1032
|
-
for (const locKey of hierarchicalLocations) {
|
|
1033
|
-
await validateHierarchicalChain(models, locKey, kta);
|
|
1174
|
+
try {
|
|
1175
|
+
logger8.trace(`[CREATE] Executing ${model.name}.create() with data: ${stringifyJSON(itemData)}`);
|
|
1176
|
+
const createdRecord = await model.create(itemData);
|
|
1177
|
+
const processedRecord = await processRow(createdRecord, kta, references || [], aggregations || [], registry);
|
|
1178
|
+
const result = validateKeys2(processedRecord, kta);
|
|
1179
|
+
logger8.debug(`[CREATE] Created ${model.name} with key: ${result.key ? JSON.stringify(result.key) : `id=${createdRecord.id}`}`);
|
|
1180
|
+
return result;
|
|
1181
|
+
} catch (error) {
|
|
1182
|
+
throw transformSequelizeError(error, kta[0], options?.key, model.name, itemData);
|
|
1034
1183
|
}
|
|
1035
1184
|
}
|
|
1036
|
-
|
|
1037
|
-
logger9.trace(`[CREATE] Executing ${model.name}.create() with data: ${stringifyJSON(itemData)}`);
|
|
1038
|
-
const createdRecord = await model.create(itemData);
|
|
1039
|
-
const processedRecord = await processRow(createdRecord, kta, references || [], aggregations || [], registry);
|
|
1040
|
-
const result = validateKeys2(processedRecord, kta);
|
|
1041
|
-
logger9.debug(`[CREATE] Created ${model.name} with key: ${result.key ? JSON.stringify(result.key) : `id=${createdRecord.id}`}`);
|
|
1042
|
-
return result;
|
|
1043
|
-
} catch (error) {
|
|
1044
|
-
throw translateDatabaseError(error, itemData, model.name);
|
|
1045
|
-
}
|
|
1046
|
-
};
|
|
1047
|
-
return create;
|
|
1185
|
+
);
|
|
1048
1186
|
};
|
|
1049
1187
|
|
|
1050
1188
|
// src/ops/find.ts
|
|
1051
|
-
import {
|
|
1052
|
-
|
|
1189
|
+
import { createFindWrapper } from "@fjell/core";
|
|
1190
|
+
import { validateKeys as validateKeys3 } from "@fjell/core/validation";
|
|
1191
|
+
var logger9 = logger_default.get("sequelize", "ops", "find");
|
|
1053
1192
|
var getFindOperation = (models, definition, registry) => {
|
|
1054
1193
|
const { options: { finders, references, aggregations } } = definition;
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1194
|
+
return createFindWrapper(
|
|
1195
|
+
definition.coordinate,
|
|
1196
|
+
async (finder, finderParams, locations) => {
|
|
1197
|
+
try {
|
|
1198
|
+
const locs = locations ?? [];
|
|
1199
|
+
const params = finderParams ?? {};
|
|
1200
|
+
const locationFilters = locs.map((loc) => `${loc.kt}=${loc.lk}`).join(", ") || "none";
|
|
1201
|
+
logger9.debug(
|
|
1202
|
+
`FIND operation called on ${models[0].name} with finder '${finder}' and ${locs.length} location filters: ${locationFilters}`
|
|
1203
|
+
);
|
|
1204
|
+
logger9.default(`Find configured for ${models[0].name} using finder '${finder}' with ${Object.keys(params).length} params`);
|
|
1205
|
+
if (finders && finders[finder]) {
|
|
1206
|
+
const finderMethod = finders[finder];
|
|
1207
|
+
if (finderMethod) {
|
|
1208
|
+
logger9.trace(`[FIND] Executing finder '${finder}' on ${models[0].name} with params: ${stringifyJSON(params)}, locations: ${stringifyJSON(locs)}`);
|
|
1209
|
+
const results = await finderMethod(params, locs);
|
|
1210
|
+
if (results && results.length > 0) {
|
|
1211
|
+
const processedResults = await Promise.all(results.map(async (row) => {
|
|
1212
|
+
const processedRow = await processRow(row, definition.coordinate.kta, references || [], aggregations || [], registry);
|
|
1213
|
+
return validateKeys3(processedRow, definition.coordinate.kta);
|
|
1214
|
+
}));
|
|
1215
|
+
logger9.debug(`[FIND] Found ${processedResults.length} ${models[0].name} records using finder '${finder}'`);
|
|
1216
|
+
return processedResults;
|
|
1217
|
+
} else {
|
|
1218
|
+
logger9.debug(`[FIND] Found 0 ${models[0].name} records using finder '${finder}'`);
|
|
1219
|
+
return [];
|
|
1220
|
+
}
|
|
1221
|
+
} else {
|
|
1222
|
+
logger9.error(`Finder %s not found`, finder);
|
|
1223
|
+
throw new Error(`Finder ${finder} not found`);
|
|
1224
|
+
}
|
|
1074
1225
|
} else {
|
|
1075
|
-
|
|
1076
|
-
|
|
1226
|
+
logger9.error(`No finders have been defined for this lib`);
|
|
1227
|
+
throw new Error(`No finders found`);
|
|
1077
1228
|
}
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
throw new Error(`Finder ${finder} not found`);
|
|
1229
|
+
} catch (error) {
|
|
1230
|
+
throw transformSequelizeError(error, definition.coordinate.kta[0]);
|
|
1081
1231
|
}
|
|
1082
|
-
} else {
|
|
1083
|
-
logger10.error(`No finders have been defined for this lib`);
|
|
1084
|
-
throw new Error(`No finders found`);
|
|
1085
1232
|
}
|
|
1086
|
-
|
|
1087
|
-
return find;
|
|
1233
|
+
);
|
|
1088
1234
|
};
|
|
1089
1235
|
|
|
1090
1236
|
// src/ops/get.ts
|
|
1091
1237
|
import {
|
|
1238
|
+
createGetWrapper,
|
|
1092
1239
|
isComKey as isComKey3,
|
|
1093
1240
|
isPriKey as isPriKey3,
|
|
1094
1241
|
isValidItemKey,
|
|
1095
1242
|
validateKeys as validateKeys4
|
|
1096
1243
|
} from "@fjell/core";
|
|
1097
|
-
import { NotFoundError } from "@fjell/
|
|
1098
|
-
var
|
|
1244
|
+
import { NotFoundError } from "@fjell/core";
|
|
1245
|
+
var logger10 = logger_default.get("sequelize", "ops", "get");
|
|
1099
1246
|
var processCompositeKey = (comKey, model, kta) => {
|
|
1100
1247
|
const where = { id: comKey.pk };
|
|
1101
1248
|
const includes = [];
|
|
@@ -1103,7 +1250,7 @@ var processCompositeKey = (comKey, model, kta) => {
|
|
|
1103
1250
|
const relationshipInfo = buildRelationshipPath(model, locator.kt, kta);
|
|
1104
1251
|
if (!relationshipInfo.found) {
|
|
1105
1252
|
const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships. Key type array: [${kta.join(", ")}], Composite key: ${stringifyJSON(comKey)}, Available associations: [${Object.keys(model.associations || {}).join(", ")}]`;
|
|
1106
|
-
|
|
1253
|
+
logger10.error(errorMessage, { key: comKey, kta });
|
|
1107
1254
|
throw new Error(errorMessage);
|
|
1108
1255
|
}
|
|
1109
1256
|
if (relationshipInfo.path) {
|
|
@@ -1125,64 +1272,82 @@ var processCompositeKey = (comKey, model, kta) => {
|
|
|
1125
1272
|
var getGetOperation = (models, definition, registry) => {
|
|
1126
1273
|
const { coordinate, options: { references, aggregations } } = definition;
|
|
1127
1274
|
const { kta } = coordinate;
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1275
|
+
return createGetWrapper(
|
|
1276
|
+
coordinate,
|
|
1277
|
+
async (key) => {
|
|
1278
|
+
try {
|
|
1279
|
+
if (!isValidItemKey(key)) {
|
|
1280
|
+
logger10.error("Key for Get is not a valid ItemKey: %j", key);
|
|
1281
|
+
throw new Error("Key for Get is not a valid ItemKey");
|
|
1282
|
+
}
|
|
1283
|
+
const keyDescription = isPriKey3(key) ? `primary key: pk=${key.pk}` : `composite key: pk=${key.pk}, loc=[${key.loc.map((l) => `${l.kt}=${l.lk}`).join(", ")}]`;
|
|
1284
|
+
logger10.debug(`GET operation called on ${models[0].name} with ${keyDescription}`);
|
|
1285
|
+
logger10.default(`Get configured for ${models[0].name} with ${isPriKey3(key) ? "primary" : "composite"} key`);
|
|
1286
|
+
const itemKey = key;
|
|
1287
|
+
const model = models[0];
|
|
1288
|
+
let item;
|
|
1289
|
+
if (isPriKey3(itemKey)) {
|
|
1290
|
+
logger10.trace(`[GET] Executing ${model.name}.findByPk() with pk: ${itemKey.pk}`);
|
|
1291
|
+
item = await model.findByPk(itemKey.pk);
|
|
1292
|
+
} else if (isComKey3(itemKey)) {
|
|
1293
|
+
const comKey = itemKey;
|
|
1294
|
+
if (comKey.loc.length === 0) {
|
|
1295
|
+
logger10.debug(`[GET] Empty loc array detected - finding by primary key across all locations: ${comKey.pk}`);
|
|
1296
|
+
logger10.trace(`[GET] Executing ${model.name}.findByPk() with pk: ${comKey.pk}`);
|
|
1297
|
+
item = await model.findByPk(comKey.pk);
|
|
1298
|
+
} else {
|
|
1299
|
+
const queryOptions = processCompositeKey(comKey, model, kta);
|
|
1300
|
+
logger10.default("Composite key query", { queryOptions });
|
|
1301
|
+
logger10.trace(`[GET] Executing ${model.name}.findOne() with options: ${stringifyJSON(queryOptions)}`);
|
|
1302
|
+
item = await model.findOne(queryOptions);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
if (!item) {
|
|
1306
|
+
throw new NotFoundError(
|
|
1307
|
+
`${kta[0]} not found`,
|
|
1308
|
+
kta[0],
|
|
1309
|
+
key
|
|
1310
|
+
);
|
|
1311
|
+
}
|
|
1312
|
+
const currentContext = contextManager.getCurrentContext();
|
|
1313
|
+
const result = validateKeys4(await processRow(item, kta, references || [], aggregations || [], registry, currentContext), kta);
|
|
1314
|
+
logger10.debug(`[GET] Retrieved ${model.name} with key: ${result.key ? JSON.stringify(result.key) : `id=${item.id}`}`);
|
|
1315
|
+
return result;
|
|
1316
|
+
} catch (error) {
|
|
1317
|
+
if (error instanceof NotFoundError) throw error;
|
|
1318
|
+
throw transformSequelizeError(error, kta[0], key);
|
|
1319
|
+
}
|
|
1156
1320
|
}
|
|
1157
|
-
|
|
1158
|
-
return get;
|
|
1321
|
+
);
|
|
1159
1322
|
};
|
|
1160
1323
|
|
|
1161
1324
|
// src/ops/one.ts
|
|
1162
|
-
|
|
1325
|
+
import { createOneWrapper } from "@fjell/core";
|
|
1326
|
+
var logger11 = logger_default.get("sequelize", "ops", "one");
|
|
1163
1327
|
var getOneOperation = (models, definition, registry) => {
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
const
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1328
|
+
return createOneWrapper(
|
|
1329
|
+
definition.coordinate,
|
|
1330
|
+
async (itemQuery, locations) => {
|
|
1331
|
+
const locs = locations ?? [];
|
|
1332
|
+
logger11.debug(`ONE operation called on ${models[0].name} with ${locs.length} location filters: ${locs.map((loc) => `${loc.kt}=${loc.lk}`).join(", ") || "none"}`);
|
|
1333
|
+
logger11.default(`One configured for ${models[0].name} delegating to all operation`);
|
|
1334
|
+
const items = await getAllOperation(models, definition, registry)(itemQuery ?? {}, locs);
|
|
1335
|
+
if (items.length > 0) {
|
|
1336
|
+
const result = items[0];
|
|
1337
|
+
logger11.debug(`[ONE] Found ${models[0].name} record with key: ${result.key ? JSON.stringify(result.key) : "unknown"}`);
|
|
1338
|
+
return result;
|
|
1339
|
+
} else {
|
|
1340
|
+
logger11.debug(`[ONE] No ${models[0].name} record found`);
|
|
1341
|
+
return null;
|
|
1342
|
+
}
|
|
1176
1343
|
}
|
|
1177
|
-
|
|
1178
|
-
return one;
|
|
1344
|
+
);
|
|
1179
1345
|
};
|
|
1180
1346
|
|
|
1181
1347
|
// src/ops/remove.ts
|
|
1182
|
-
import { isValidItemKey as isValidItemKey2 } from "@fjell/core";
|
|
1183
|
-
import {
|
|
1184
|
-
|
|
1185
|
-
var logger13 = logger_default.get("sequelize", "ops", "remove");
|
|
1348
|
+
import { abbrevIK, isComKey as isComKey4, isPriKey as isPriKey4, isValidItemKey as isValidItemKey2, createRemoveWrapper } from "@fjell/core";
|
|
1349
|
+
import { NotFoundError as NotFoundError2 } from "@fjell/core";
|
|
1350
|
+
var logger12 = logger_default.get("sequelize", "ops", "remove");
|
|
1186
1351
|
var processCompositeKey2 = (comKey, model, kta) => {
|
|
1187
1352
|
const where = { id: comKey.pk };
|
|
1188
1353
|
const includes = [];
|
|
@@ -1190,7 +1355,7 @@ var processCompositeKey2 = (comKey, model, kta) => {
|
|
|
1190
1355
|
const relationshipInfo = buildRelationshipPath(model, locator.kt, kta);
|
|
1191
1356
|
if (!relationshipInfo.found) {
|
|
1192
1357
|
const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
|
|
1193
|
-
|
|
1358
|
+
logger12.error(errorMessage, { key: comKey, kta });
|
|
1194
1359
|
throw new Error(errorMessage);
|
|
1195
1360
|
}
|
|
1196
1361
|
if (relationshipInfo.path) {
|
|
@@ -1212,66 +1377,77 @@ var processCompositeKey2 = (comKey, model, kta) => {
|
|
|
1212
1377
|
var getRemoveOperation = (models, definition, _registry) => {
|
|
1213
1378
|
const { coordinate, options } = definition;
|
|
1214
1379
|
const { kta } = coordinate;
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1380
|
+
return createRemoveWrapper(
|
|
1381
|
+
coordinate,
|
|
1382
|
+
async (key) => {
|
|
1383
|
+
try {
|
|
1384
|
+
if (!isValidItemKey2(key)) {
|
|
1385
|
+
logger12.error("Key for Remove is not a valid ItemKey: %j", key);
|
|
1386
|
+
throw new Error("Key for Remove is not a valid ItemKey");
|
|
1387
|
+
}
|
|
1388
|
+
const keyDescription = isPriKey4(key) ? `primary key: pk=${key.pk}` : `composite key: pk=${key.pk}, loc=[${key.loc.map((l) => `${l.kt}=${l.lk}`).join(", ")}]`;
|
|
1389
|
+
logger12.debug(`REMOVE operation called on ${models[0].name} with ${keyDescription}`);
|
|
1390
|
+
logger12.default(`Remove configured for ${models[0].name} with ${isPriKey4(key) ? "primary" : "composite"} key`);
|
|
1391
|
+
const model = models[0];
|
|
1392
|
+
let item;
|
|
1393
|
+
let returnItem;
|
|
1394
|
+
logger12.debug("remove: %s", abbrevIK(key));
|
|
1395
|
+
if (isPriKey4(key)) {
|
|
1396
|
+
logger12.debug(`[REMOVE] Executing ${model.name}.findByPk() with pk: ${key.pk}`);
|
|
1397
|
+
item = await model.findByPk(key.pk);
|
|
1398
|
+
} else if (isComKey4(key)) {
|
|
1399
|
+
const comKey = key;
|
|
1400
|
+
const queryOptions = processCompositeKey2(comKey, model, kta);
|
|
1401
|
+
logger12.default(`Remove composite key query for ${model.name} with where fields: ${queryOptions.where ? Object.keys(queryOptions.where).join(", ") : "none"}`);
|
|
1402
|
+
logger12.debug(`[REMOVE] Executing ${model.name}.findOne() with options: ${stringifyJSON(queryOptions)}`);
|
|
1403
|
+
item = await model.findOne(queryOptions);
|
|
1404
|
+
}
|
|
1405
|
+
if (!item) {
|
|
1406
|
+
throw new NotFoundError2(
|
|
1407
|
+
`Cannot remove: ${kta[0]} not found`,
|
|
1408
|
+
kta[0],
|
|
1409
|
+
key
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1412
|
+
const isDeletedAttribute = model.getAttributes().isDeleted;
|
|
1413
|
+
const deletedAtAttribute = model.getAttributes().deletedAt;
|
|
1414
|
+
if (isDeletedAttribute || deletedAtAttribute) {
|
|
1415
|
+
if (model.getAttributes().isDeleted) {
|
|
1416
|
+
item.isDeleted = true;
|
|
1417
|
+
}
|
|
1418
|
+
if (model.getAttributes().deletedAt) {
|
|
1419
|
+
item.deletedAt = /* @__PURE__ */ new Date();
|
|
1420
|
+
}
|
|
1421
|
+
logger12.debug(`[REMOVE] Executing ${model.name}.save() for soft delete`);
|
|
1422
|
+
await item?.save();
|
|
1423
|
+
returnItem = item?.get({ plain: true });
|
|
1424
|
+
returnItem = addKey(item, returnItem, kta);
|
|
1425
|
+
returnItem = populateEvents(returnItem);
|
|
1426
|
+
} else if (options.deleteOnRemove) {
|
|
1427
|
+
logger12.debug(`[REMOVE] Executing ${model.name}.destroy() for hard delete`);
|
|
1428
|
+
await item?.destroy();
|
|
1429
|
+
returnItem = item?.get({ plain: true });
|
|
1430
|
+
returnItem = addKey(item, returnItem, kta);
|
|
1431
|
+
returnItem = populateEvents(returnItem);
|
|
1432
|
+
} else {
|
|
1433
|
+
throw new Error("No deletedAt or isDeleted attribute found in model, and deleteOnRemove is not set");
|
|
1434
|
+
}
|
|
1435
|
+
logger12.debug(`[REMOVE] Removed ${model.name} with key: ${returnItem.key ? JSON.stringify(returnItem.key) : `id=${item.id}`}`);
|
|
1436
|
+
return returnItem;
|
|
1437
|
+
} catch (error) {
|
|
1438
|
+
if (error instanceof NotFoundError2) throw error;
|
|
1439
|
+
throw transformSequelizeError(error, kta[0], key);
|
|
1248
1440
|
}
|
|
1249
|
-
logger13.debug(`[REMOVE] Executing ${model.name}.save() for soft delete`);
|
|
1250
|
-
await item?.save();
|
|
1251
|
-
returnItem = item?.get({ plain: true });
|
|
1252
|
-
returnItem = addKey(item, returnItem, kta);
|
|
1253
|
-
returnItem = populateEvents(returnItem);
|
|
1254
|
-
} else if (options.deleteOnRemove) {
|
|
1255
|
-
logger13.debug(`[REMOVE] Executing ${model.name}.destroy() for hard delete`);
|
|
1256
|
-
await item?.destroy();
|
|
1257
|
-
returnItem = item?.get({ plain: true });
|
|
1258
|
-
returnItem = addKey(item, returnItem, kta);
|
|
1259
|
-
returnItem = populateEvents(returnItem);
|
|
1260
|
-
} else {
|
|
1261
|
-
throw new Error("No deletedAt or isDeleted attribute found in model, and deleteOnRemove is not set");
|
|
1262
1441
|
}
|
|
1263
|
-
|
|
1264
|
-
return returnItem;
|
|
1265
|
-
};
|
|
1266
|
-
return remove;
|
|
1442
|
+
);
|
|
1267
1443
|
};
|
|
1268
1444
|
|
|
1269
1445
|
// src/ops/update.ts
|
|
1270
|
-
import { abbrevIK as abbrevIK2, isComKey as isComKey5,
|
|
1271
|
-
import {
|
|
1272
|
-
import { NotFoundError as NotFoundError3 } from "@fjell/
|
|
1446
|
+
import { abbrevIK as abbrevIK2, createUpdateWrapper, isComKey as isComKey5, isPriKey as isPriKey5 } from "@fjell/core";
|
|
1447
|
+
import { validateKeys as validateKeys5 } from "@fjell/core/validation";
|
|
1448
|
+
import { NotFoundError as NotFoundError3 } from "@fjell/core";
|
|
1273
1449
|
import { Op as Op3 } from "sequelize";
|
|
1274
|
-
var
|
|
1450
|
+
var logger13 = logger_default.get("sequelize", "ops", "update");
|
|
1275
1451
|
var mergeIncludes2 = (existingIncludes, newIncludes) => {
|
|
1276
1452
|
const mergedIncludes = [...existingIncludes];
|
|
1277
1453
|
for (const newInclude of newIncludes) {
|
|
@@ -1293,130 +1469,136 @@ var mergeIncludes2 = (existingIncludes, newIncludes) => {
|
|
|
1293
1469
|
};
|
|
1294
1470
|
var getUpdateOperation = (models, definition, registry) => {
|
|
1295
1471
|
const { options: { references, aggregations } } = definition;
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
const
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1472
|
+
return createUpdateWrapper(
|
|
1473
|
+
definition.coordinate,
|
|
1474
|
+
async (key, item) => {
|
|
1475
|
+
try {
|
|
1476
|
+
const keyDescription = isPriKey5(key) ? `primary key: pk=${key.pk}` : `composite key: pk=${key.pk}, loc=[${key.loc.map((l) => `${l.kt}=${l.lk}`).join(", ")}]`;
|
|
1477
|
+
logger13.debug(`UPDATE operation called on ${models[0].name} with ${keyDescription}`);
|
|
1478
|
+
const { coordinate } = definition;
|
|
1479
|
+
const { kta } = coordinate;
|
|
1480
|
+
logger13.debug("update: %s, %j", abbrevIK2(key), item);
|
|
1481
|
+
const model = models[0];
|
|
1482
|
+
let response;
|
|
1483
|
+
if (isPriKey5(key)) {
|
|
1484
|
+
const priKey = key;
|
|
1485
|
+
logger13.trace(`[UPDATE] Executing ${model.name}.findByPk() with pk: ${priKey.pk}`);
|
|
1486
|
+
response = await model.findByPk(priKey.pk);
|
|
1487
|
+
} else if (isComKey5(key)) {
|
|
1488
|
+
const comKey = key;
|
|
1489
|
+
const where = { id: comKey.pk };
|
|
1490
|
+
const additionalIncludes = [];
|
|
1491
|
+
for (const locator of comKey.loc) {
|
|
1492
|
+
const relationshipInfo = buildRelationshipPath(model, locator.kt, kta, true);
|
|
1493
|
+
if (!relationshipInfo.found) {
|
|
1494
|
+
const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
|
|
1495
|
+
logger13.error(errorMessage, { key: comKey, kta });
|
|
1496
|
+
throw new Error(errorMessage);
|
|
1497
|
+
}
|
|
1498
|
+
if (relationshipInfo.isDirect) {
|
|
1499
|
+
const fieldName = `${locator.kt}Id`;
|
|
1500
|
+
where[fieldName] = locator.lk;
|
|
1501
|
+
} else if (relationshipInfo.path) {
|
|
1502
|
+
where[relationshipInfo.path] = {
|
|
1503
|
+
[Op3.eq]: locator.lk
|
|
1504
|
+
};
|
|
1505
|
+
if (relationshipInfo.includes) {
|
|
1506
|
+
additionalIncludes.push(...relationshipInfo.includes);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1328
1509
|
}
|
|
1510
|
+
const queryOptions = { where };
|
|
1511
|
+
if (additionalIncludes.length > 0) {
|
|
1512
|
+
queryOptions.include = mergeIncludes2([], additionalIncludes);
|
|
1513
|
+
}
|
|
1514
|
+
logger13.default(`Update composite key query for ${model.name} with where fields: ${queryOptions.where ? Object.keys(queryOptions.where).join(", ") : "none"}`);
|
|
1515
|
+
logger13.trace(`[UPDATE] Executing ${model.name}.findOne() with options: ${stringifyJSON(queryOptions)}`);
|
|
1516
|
+
response = await model.findOne(queryOptions);
|
|
1329
1517
|
}
|
|
1518
|
+
if (!response) {
|
|
1519
|
+
throw new NotFoundError3(
|
|
1520
|
+
`Cannot update: ${kta[0]} not found`,
|
|
1521
|
+
kta[0],
|
|
1522
|
+
key
|
|
1523
|
+
);
|
|
1524
|
+
}
|
|
1525
|
+
let updateProps = removeKey(item);
|
|
1526
|
+
updateProps = extractEvents(updateProps);
|
|
1527
|
+
updateProps = removeEvents(updateProps);
|
|
1528
|
+
logger13.default(`Update found ${model.name} record to modify`);
|
|
1529
|
+
logger13.default(`Update properties configured: ${Object.keys(updateProps).join(", ")}`);
|
|
1530
|
+
logger13.trace(`[UPDATE] Executing ${model.name}.update() with properties: ${stringifyJSON(updateProps)}`);
|
|
1531
|
+
response = await response.update(updateProps);
|
|
1532
|
+
const processedItem = await processRow(response, kta, references || [], aggregations || [], registry);
|
|
1533
|
+
const returnItem = validateKeys5(processedItem, kta);
|
|
1534
|
+
logger13.debug(`[UPDATE] Updated ${model.name} with key: ${returnItem.key ? JSON.stringify(returnItem.key) : `id=${response.id}`}`);
|
|
1535
|
+
return returnItem;
|
|
1536
|
+
} catch (error) {
|
|
1537
|
+
if (error instanceof NotFoundError3) throw error;
|
|
1538
|
+
throw transformSequelizeError(error, definition.coordinate.kta[0], key);
|
|
1330
1539
|
}
|
|
1331
|
-
const queryOptions = { where };
|
|
1332
|
-
if (additionalIncludes.length > 0) {
|
|
1333
|
-
queryOptions.include = mergeIncludes2([], additionalIncludes);
|
|
1334
|
-
}
|
|
1335
|
-
logger14.default(`Update composite key query for ${model.name} with where fields: ${queryOptions.where ? Object.keys(queryOptions.where).join(", ") : "none"}`);
|
|
1336
|
-
logger14.trace(`[UPDATE] Executing ${model.name}.findOne() with options: ${stringifyJSON(queryOptions)}`);
|
|
1337
|
-
response = await model.findOne(queryOptions);
|
|
1338
|
-
}
|
|
1339
|
-
if (response) {
|
|
1340
|
-
let updateProps = removeKey(item);
|
|
1341
|
-
updateProps = extractEvents(updateProps);
|
|
1342
|
-
updateProps = removeEvents(updateProps);
|
|
1343
|
-
logger14.default(`Update found ${model.name} record to modify`);
|
|
1344
|
-
logger14.default(`Update properties configured: ${Object.keys(updateProps).join(", ")}`);
|
|
1345
|
-
logger14.trace(`[UPDATE] Executing ${model.name}.update() with properties: ${stringifyJSON(updateProps)}`);
|
|
1346
|
-
response = await response.update(updateProps);
|
|
1347
|
-
const processedItem = await processRow(response, kta, references || [], aggregations || [], registry);
|
|
1348
|
-
const returnItem = validateKeys5(processedItem, kta);
|
|
1349
|
-
logger14.debug(`[UPDATE] Updated ${model.name} with key: ${returnItem.key ? JSON.stringify(returnItem.key) : `id=${response.id}`}`);
|
|
1350
|
-
return returnItem;
|
|
1351
|
-
} else {
|
|
1352
|
-
throw new NotFoundError3("update", coordinate, key);
|
|
1353
1540
|
}
|
|
1354
|
-
|
|
1355
|
-
return update;
|
|
1541
|
+
);
|
|
1356
1542
|
};
|
|
1357
1543
|
|
|
1358
1544
|
// src/ops/upsert.ts
|
|
1359
|
-
import { isValidItemKey as isValidItemKey3 } from "@fjell/core";
|
|
1360
|
-
var
|
|
1545
|
+
import { createUpsertWrapper, isValidItemKey as isValidItemKey3 } from "@fjell/core";
|
|
1546
|
+
var logger14 = logger_default.get("sequelize", "ops", "upsert");
|
|
1361
1547
|
var getUpsertOperation = (models, definition, registry) => {
|
|
1362
1548
|
const get = getGetOperation(models, definition, registry);
|
|
1363
1549
|
const update = getUpdateOperation(models, definition, registry);
|
|
1364
1550
|
const create = getCreateOperation(models, definition, registry);
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
try {
|
|
1372
|
-
const existingItem = await get(key);
|
|
1373
|
-
if (existingItem) {
|
|
1374
|
-
logger15.debug(`[UPSERT] Item exists, updating with key: ${stringifyJSON(key)}`);
|
|
1375
|
-
return await update(key, item);
|
|
1551
|
+
return createUpsertWrapper(
|
|
1552
|
+
definition.coordinate,
|
|
1553
|
+
async (key, item) => {
|
|
1554
|
+
if (!isValidItemKey3(key)) {
|
|
1555
|
+
logger14.error("Key for Upsert is not a valid ItemKey: %j", key);
|
|
1556
|
+
throw new Error(`Key for Upsert is not a valid ItemKey: ${stringifyJSON(key)}`);
|
|
1376
1557
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1558
|
+
logger14.debug(`[UPSERT] Attempting upsert with key: ${stringifyJSON(key)}`);
|
|
1559
|
+
try {
|
|
1560
|
+
const existingItem = await get(key);
|
|
1561
|
+
if (existingItem) {
|
|
1562
|
+
logger14.debug(`[UPSERT] Item exists, updating with key: ${stringifyJSON(key)}`);
|
|
1563
|
+
return await update(key, item);
|
|
1564
|
+
}
|
|
1565
|
+
} catch {
|
|
1566
|
+
logger14.debug(`[UPSERT] Item not found, creating new item with key: ${stringifyJSON(key)}`);
|
|
1567
|
+
}
|
|
1568
|
+
return await create(item, { key });
|
|
1379
1569
|
}
|
|
1380
|
-
|
|
1381
|
-
};
|
|
1382
|
-
return upsert;
|
|
1570
|
+
);
|
|
1383
1571
|
};
|
|
1384
1572
|
|
|
1385
1573
|
// src/Operations.ts
|
|
1386
1574
|
var createOperations = (models, coordinate, registry, options) => {
|
|
1387
|
-
const operations = {};
|
|
1388
1575
|
const definition = { coordinate, options };
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
};
|
|
1401
|
-
operations.action = async () => {
|
|
1576
|
+
const implOps = {
|
|
1577
|
+
all: getAllOperation(models, definition, registry),
|
|
1578
|
+
one: getOneOperation(models, definition, registry),
|
|
1579
|
+
create: getCreateOperation(models, definition, registry),
|
|
1580
|
+
update: getUpdateOperation(models, definition, registry),
|
|
1581
|
+
get: getGetOperation(models, definition, registry),
|
|
1582
|
+
remove: getRemoveOperation(models, definition, registry),
|
|
1583
|
+
find: getFindOperation(models, definition, registry),
|
|
1584
|
+
// findOne depends on find, so set it after
|
|
1585
|
+
findOne: null,
|
|
1586
|
+
upsert: getUpsertOperation(models, definition, registry)
|
|
1402
1587
|
};
|
|
1403
|
-
|
|
1588
|
+
implOps.findOne = async (finder, params, locations) => {
|
|
1589
|
+
const results = await implOps.find(finder, params || {}, locations);
|
|
1590
|
+
return results.length > 0 ? results[0] : null;
|
|
1404
1591
|
};
|
|
1405
|
-
|
|
1406
|
-
operations.actions = { ...options.actions || {} };
|
|
1407
|
-
operations.facets = { ...options.facets || {} };
|
|
1408
|
-
operations.allActions = { ...options.allActions || {} };
|
|
1409
|
-
operations.allFacets = { ...options.allFacets || {} };
|
|
1410
|
-
return operations;
|
|
1592
|
+
return Library2.wrapImplementationOperations(implOps, options);
|
|
1411
1593
|
};
|
|
1412
1594
|
|
|
1413
1595
|
// src/SequelizeLibrary.ts
|
|
1414
|
-
var
|
|
1596
|
+
var logger15 = logger_default.get("SequelizeLibrary");
|
|
1415
1597
|
var createSequelizeLibrary = (registry, coordinate, models, options) => {
|
|
1416
|
-
|
|
1598
|
+
logger15.debug("createSequelizeLibrary", { coordinate, models, registry, options });
|
|
1417
1599
|
const operations = createOperations(models, coordinate, registry, options);
|
|
1418
|
-
const wrappedOperations =
|
|
1419
|
-
const libLibrary =
|
|
1600
|
+
const wrappedOperations = Library3.wrapOperations(operations, options, coordinate, registry);
|
|
1601
|
+
const libLibrary = Library3.createLibrary(registry, coordinate, wrappedOperations, options);
|
|
1420
1602
|
return {
|
|
1421
1603
|
...libLibrary,
|
|
1422
1604
|
models
|
|
@@ -1427,10 +1609,10 @@ var isSequelizeLibrary = (library) => {
|
|
|
1427
1609
|
};
|
|
1428
1610
|
|
|
1429
1611
|
// src/SequelizeLibraryFactory.ts
|
|
1430
|
-
var
|
|
1612
|
+
var logger16 = logger_default.get("InstanceFactory");
|
|
1431
1613
|
var createSequelizeLibraryFactory = (models, options) => {
|
|
1432
1614
|
return (coordinate, context) => {
|
|
1433
|
-
|
|
1615
|
+
logger16.debug("Creating Sequelize instance", {
|
|
1434
1616
|
coordinate,
|
|
1435
1617
|
registry: context.registry,
|
|
1436
1618
|
models: models.map((m) => m.name),
|
|
@@ -1475,9 +1657,9 @@ __export(primary_exports, {
|
|
|
1475
1657
|
|
|
1476
1658
|
// src/primary/SequelizeLibrary.ts
|
|
1477
1659
|
import { Primary } from "@fjell/lib";
|
|
1478
|
-
var
|
|
1660
|
+
var logger17 = logger_default.get("lib-sequelize", "primary", "library");
|
|
1479
1661
|
function createSequelizeLibrary3(keyType, models, libOptions = {}, scopes = [], registry) {
|
|
1480
|
-
|
|
1662
|
+
logger17.debug("createSequelizeLibrary", { keyType, models, libOptions, scopes });
|
|
1481
1663
|
const coordinate = createCoordinate([keyType], scopes);
|
|
1482
1664
|
const options = createOptions2(libOptions);
|
|
1483
1665
|
const operations = createOperations(models, coordinate, registry, options);
|