@fjell/lib-sequelize 4.4.4 → 4.4.8

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.
@@ -3,8 +3,30 @@ import { buildQuery } from '../QueryBuilder.js';
3
3
  import LibLogger from '../logger.js';
4
4
  import { processRow } from '../RowProcessor.js';
5
5
  import { Op } from 'sequelize';
6
+ import { buildRelationshipPath } from '../util/relationshipUtils.js';
7
+ import { contextManager } from '../OperationContext.js';
6
8
 
7
9
  const logger = LibLogger.get('sequelize', 'ops', 'all');
10
+ // Helper function to merge includes avoiding duplicates
11
+ const mergeIncludes = (existingIncludes, newIncludes)=>{
12
+ const mergedIncludes = [
13
+ ...existingIncludes
14
+ ];
15
+ for (const newInclude of newIncludes){
16
+ const existingIndex = mergedIncludes.findIndex((existing)=>existing.as === newInclude.as && existing.model === newInclude.model);
17
+ if (existingIndex === -1) {
18
+ mergedIncludes.push(newInclude);
19
+ } else if (newInclude.include && mergedIncludes[existingIndex].include) {
20
+ mergedIncludes[existingIndex].include = [
21
+ ...mergedIncludes[existingIndex].include,
22
+ ...newInclude.include
23
+ ];
24
+ } else if (newInclude.include) {
25
+ mergedIncludes[existingIndex].include = newInclude.include;
26
+ }
27
+ }
28
+ return mergedIncludes;
29
+ };
8
30
  const getAllOperation = (models, definition, registry)=>{
9
31
  const { coordinate, options: { references, aggregations } } = definition;
10
32
  //#region Query
@@ -14,29 +36,64 @@ const getAllOperation = (models, definition, registry)=>{
14
36
  locations
15
37
  });
16
38
  const loc = locations || [];
17
- // SQ Libs don't support locations
18
- if (loc.length > 1) {
19
- throw new Error('Not implemented for more than one location key');
20
- }
21
- // We have the model here?
22
39
  // @ts-ignore
23
40
  const model = models[0];
24
- // We have the model here?
41
+ // Build base query from itemQuery
25
42
  const options = buildQuery(itemQuery, model);
26
- // If this has a location array, we need to add a where clause
27
- if (loc.length === 1) {
28
- const locKeyType = loc[0].kt;
29
- if (model.associations[locKeyType]) {
30
- const association = model.associations[locKeyType];
43
+ // Handle location keys if present
44
+ if (loc.length > 0) {
45
+ const { kta } = coordinate;
46
+ const directLocations = [];
47
+ const hierarchicalLocations = [];
48
+ const additionalIncludes = [];
49
+ // Categorize location keys as direct or hierarchical
50
+ for (const locKey of loc){
51
+ const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta, true);
52
+ if (!relationshipInfo.found) {
53
+ const errorMessage = `Location key '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
54
+ logger.error(errorMessage, {
55
+ locations: loc,
56
+ kta
57
+ });
58
+ throw new Error(errorMessage);
59
+ }
60
+ if (relationshipInfo.isDirect) {
61
+ directLocations.push(locKey);
62
+ } else {
63
+ hierarchicalLocations.push(locKey);
64
+ }
65
+ }
66
+ // Handle direct location keys (simple foreign key constraints)
67
+ for (const locKey of directLocations){
68
+ const foreignKeyField = locKey.kt + 'Id';
31
69
  options.where = {
32
70
  ...options.where,
33
- [association.foreignKey]: {
34
- [Op.eq]: loc[0].lk
71
+ [foreignKeyField]: {
72
+ [Op.eq]: locKey.lk
35
73
  }
36
74
  };
37
- } else {
38
- logger.error('Location key type not found in sequelize model association for: %s', locKeyType);
39
- throw new Error('Location key type not found in model');
75
+ }
76
+ // Handle hierarchical location keys (requires relationship traversal)
77
+ for (const locKey of hierarchicalLocations){
78
+ const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta);
79
+ if (relationshipInfo.found && relationshipInfo.path) {
80
+ // Add the relationship constraint using the path
81
+ options.where = {
82
+ ...options.where,
83
+ [relationshipInfo.path]: {
84
+ [Op.eq]: locKey.lk
85
+ }
86
+ };
87
+ // Add necessary includes for the relationship traversal
88
+ if (relationshipInfo.includes) {
89
+ additionalIncludes.push(...relationshipInfo.includes);
90
+ }
91
+ }
92
+ }
93
+ // Merge additional includes with existing includes
94
+ if (additionalIncludes.length > 0) {
95
+ const existingIncludes = options.include || [];
96
+ options.include = mergeIncludes(existingIncludes, additionalIncludes);
40
97
  }
41
98
  }
42
99
  logger.default('Configured this Item Query', {
@@ -45,9 +102,11 @@ const getAllOperation = (models, definition, registry)=>{
45
102
  });
46
103
  const matchingItems = await model.findAll(options);
47
104
  // this.logger.default('Matching Items', { matchingItems });
105
+ // Get the current context from context manager
106
+ const context = contextManager.getCurrentContext();
48
107
  // TODO: Move this Up!
49
108
  return await Promise.all(matchingItems.map(async (row)=>{
50
- const processedRow = await processRow(row, coordinate.kta, references, aggregations, registry);
109
+ const processedRow = await processRow(row, coordinate.kta, references, aggregations, registry, context);
51
110
  return validateKeys(processedRow, coordinate.kta);
52
111
  }));
53
112
  };
@@ -55,4 +114,4 @@ const getAllOperation = (models, definition, registry)=>{
55
114
  };
56
115
 
57
116
  export { getAllOperation };
58
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWxsLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
117
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWxsLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9
@@ -3,6 +3,7 @@ import LibLogger from '../logger.js';
3
3
  import { processRow } from '../RowProcessor.js';
4
4
  import { NotFoundError } from '@fjell/lib';
5
5
  import { buildRelationshipPath } from '../util/relationshipUtils.js';
6
+ import { contextManager } from '../OperationContext.js';
6
7
 
7
8
  const logger = LibLogger.get('sequelize', 'ops', 'get');
8
9
  // Helper function to process composite key and build query options
@@ -71,11 +72,13 @@ const getGetOperation = (models, definition, registry)=>{
71
72
  if (!item) {
72
73
  throw new NotFoundError('get', coordinate, key);
73
74
  } else {
74
- return validateKeys(await processRow(item, kta, references, aggregations, registry), kta);
75
+ // Get the current context from context manager
76
+ const context = contextManager.getCurrentContext();
77
+ return validateKeys(await processRow(item, kta, references, aggregations, registry, context), kta);
75
78
  }
76
79
  };
77
80
  return get;
78
81
  };
79
82
 
80
83
  export { getGetOperation };
81
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2V0LmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9
84
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2V0LmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9
@@ -5,8 +5,30 @@ import LibLogger from '../logger.js';
5
5
  import { processRow } from '../RowProcessor.js';
6
6
  import { stringifyJSON } from '../util/general.js';
7
7
  import { NotFoundError } from '@fjell/lib';
8
+ import { Op } from 'sequelize';
9
+ import { buildRelationshipPath } from '../util/relationshipUtils.js';
8
10
 
9
11
  const logger = LibLogger.get('sequelize', 'ops', 'update');
12
+ // Helper function to merge includes avoiding duplicates
13
+ const mergeIncludes = (existingIncludes, newIncludes)=>{
14
+ const mergedIncludes = [
15
+ ...existingIncludes
16
+ ];
17
+ for (const newInclude of newIncludes){
18
+ const existingIndex = mergedIncludes.findIndex((existing)=>existing.as === newInclude.as && existing.model === newInclude.model);
19
+ if (existingIndex === -1) {
20
+ mergedIncludes.push(newInclude);
21
+ } else if (newInclude.include && mergedIncludes[existingIndex].include) {
22
+ mergedIncludes[existingIndex].include = [
23
+ ...mergedIncludes[existingIndex].include,
24
+ ...newInclude.include
25
+ ];
26
+ } else if (newInclude.include) {
27
+ mergedIncludes[existingIndex].include = newInclude.include;
28
+ }
29
+ }
30
+ return mergedIncludes;
31
+ };
10
32
  const getUpdateOperation = (models, definition, registry)=>{
11
33
  const { options: { references, aggregations } } = definition;
12
34
  const update = async (key, item)=>{
@@ -22,15 +44,49 @@ const getUpdateOperation = (models, definition, registry)=>{
22
44
  const priKey = key;
23
45
  response = await model.findByPk(priKey.pk);
24
46
  } else if (isComKey(key)) {
25
- var _comKey_loc_, _comKey_loc_1;
26
47
  const comKey = key;
27
- // Find the model by using both of the identifiers.
28
- response = await model.findOne({
29
- where: {
30
- [(comKey === null || comKey === void 0 ? void 0 : (_comKey_loc_ = comKey.loc[0]) === null || _comKey_loc_ === void 0 ? void 0 : _comKey_loc_.kt) + 'Id']: comKey === null || comKey === void 0 ? void 0 : (_comKey_loc_1 = comKey.loc[0]) === null || _comKey_loc_1 === void 0 ? void 0 : _comKey_loc_1.lk,
31
- id: comKey === null || comKey === void 0 ? void 0 : comKey.pk
48
+ // Build query options for composite key with multiple location keys
49
+ const where = {
50
+ id: comKey.pk
51
+ };
52
+ const additionalIncludes = [];
53
+ // Process all location keys in the composite key
54
+ for (const locator of comKey.loc){
55
+ const relationshipInfo = buildRelationshipPath(model, locator.kt, kta, true);
56
+ if (!relationshipInfo.found) {
57
+ const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
58
+ logger.error(errorMessage, {
59
+ key: comKey,
60
+ kta
61
+ });
62
+ throw new Error(errorMessage);
63
+ }
64
+ if (relationshipInfo.isDirect) {
65
+ // Direct foreign key field
66
+ const fieldName = `${locator.kt}Id`;
67
+ where[fieldName] = locator.lk;
68
+ } else if (relationshipInfo.path) {
69
+ // Hierarchical relationship requiring traversal
70
+ where[relationshipInfo.path] = {
71
+ [Op.eq]: locator.lk
72
+ };
73
+ // Add necessary includes for relationship traversal
74
+ if (relationshipInfo.includes) {
75
+ additionalIncludes.push(...relationshipInfo.includes);
76
+ }
32
77
  }
78
+ }
79
+ // Build final query options
80
+ const queryOptions = {
81
+ where
82
+ };
83
+ if (additionalIncludes.length > 0) {
84
+ queryOptions.include = mergeIncludes([], additionalIncludes);
85
+ }
86
+ logger.default('Composite key query for update', {
87
+ queryOptions
33
88
  });
89
+ response = await model.findOne(queryOptions);
34
90
  }
35
91
  if (response) {
36
92
  // Remove the key and events
@@ -54,4 +110,4 @@ const getUpdateOperation = (models, definition, registry)=>{
54
110
  };
55
111
 
56
112
  export { getUpdateOperation };
57
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBkYXRlLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9
113
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBkYXRlLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=
@@ -21,8 +21,8 @@
21
21
  modelChain.push(currentModel);
22
22
  }
23
23
  // Build the full association path for the target field
24
- const targetType = kta[targetIndex];
25
- const associationPath = `$${associationParts.join('.')}.${targetType}Id$`;
24
+ const targetPrimaryKey = currentModel.primaryKeyAttribute || 'id';
25
+ const associationPath = `$${associationParts.join('.')}.${targetPrimaryKey}$`;
26
26
  // Build nested includes structure iteratively (clearer than recursion)
27
27
  let deepestInclude = null;
28
28
  // Build from the deepest level back to the root