@fjell/lib-sequelize 4.4.1 → 4.4.2

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.
@@ -22,6 +22,24 @@ const populateEvents = (item)=>{
22
22
  item.events = events;
23
23
  return item;
24
24
  };
25
+ const extractEvents = (item)=>{
26
+ logger.default('Extracting Events to database fields', {
27
+ item
28
+ });
29
+ if (item.events) {
30
+ var _item_events_created, _item_events_updated, _item_events_deleted;
31
+ if ((_item_events_created = item.events.created) === null || _item_events_created === void 0 ? void 0 : _item_events_created.at) {
32
+ item.createdAt = item.events.created.at;
33
+ }
34
+ if ((_item_events_updated = item.events.updated) === null || _item_events_updated === void 0 ? void 0 : _item_events_updated.at) {
35
+ item.updatedAt = item.events.updated.at;
36
+ }
37
+ if ((_item_events_deleted = item.events.deleted) === null || _item_events_deleted === void 0 ? void 0 : _item_events_deleted.at) {
38
+ item.deletedAt = item.events.deleted.at;
39
+ }
40
+ }
41
+ return item;
42
+ };
25
43
  const removeEvents = (item)=>{
26
44
  logger.default('Removing Events', {
27
45
  item
@@ -30,6 +48,7 @@ const removeEvents = (item)=>{
30
48
  return item;
31
49
  };
32
50
 
51
+ exports.extractEvents = extractEvents;
33
52
  exports.populateEvents = populateEvents;
34
53
  exports.removeEvents = removeEvents;
35
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRXZlbnRDb29yZGluYXRvci5janMiLCJzb3VyY2VzIjpbXSwic291cmNlc0NvbnRlbnQiOltdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
54
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRXZlbnRDb29yZGluYXRvci5janMiLCJzb3VyY2VzIjpbXSwic291cmNlc0NvbnRlbnQiOltdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=
@@ -3,8 +3,43 @@
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
5
  const logger$1 = require('./logger.cjs');
6
+ const relationshipUtils = require('./util/relationshipUtils.cjs');
6
7
 
7
8
  const logger = logger$1.default.get('sequelize', 'KeyMaster');
9
+ // Helper function to extract location key value from item
10
+ const extractLocationKeyValue = (model, item, locatorType, kta)=>{
11
+ logger.default('Extracting location key value', {
12
+ locatorType,
13
+ kta
14
+ });
15
+ const relationshipInfo = relationshipUtils.buildRelationshipPath(model, locatorType, kta, true);
16
+ if (!relationshipInfo.found) {
17
+ throw new Error(`Location key '${locatorType}' cannot be resolved on model '${model.name}' or through its relationships.`);
18
+ }
19
+ if (relationshipInfo.isDirect) {
20
+ // Direct foreign key field
21
+ const foreignKeyField = `${locatorType}Id`;
22
+ const value = item[foreignKeyField];
23
+ if (typeof value === 'undefined' || value === null) {
24
+ throw new Error(`Direct foreign key field '${foreignKeyField}' is missing or null in item`);
25
+ }
26
+ return value;
27
+ } else {
28
+ // Need to traverse relationship
29
+ // Try to get the value from the loaded relationship object
30
+ const relationshipObject = item[locatorType];
31
+ if (relationshipObject && typeof relationshipObject.id !== 'undefined') {
32
+ return relationshipObject.id;
33
+ }
34
+ // If the relationship object isn't loaded, we might need to look at the foreign key field
35
+ // This handles cases where we have the foreign key but not the full object
36
+ const foreignKeyField = `${locatorType}Id`;
37
+ if (typeof item[foreignKeyField] !== 'undefined' && item[foreignKeyField] !== null) {
38
+ return item[foreignKeyField];
39
+ }
40
+ throw new Error(`Unable to extract location key for '${locatorType}'. Neither the relationship object nor direct foreign key is available.`);
41
+ }
42
+ };
8
43
  const removeKey = (item)=>{
9
44
  logger.default('Removing Key', {
10
45
  item
@@ -39,11 +74,13 @@ const removeKey = (item)=>{
39
74
  // }
40
75
  // return item;
41
76
  // }
42
- const addKey = (item, keyTypes)=>{
77
+ const addKey = (model, item, keyTypes)=>{
43
78
  logger.default('Adding Key', {
44
79
  item
45
80
  });
46
81
  const key = {};
82
+ const modelClass = model.constructor;
83
+ const primaryKeyAttr = modelClass.primaryKeyAttribute;
47
84
  if (Array.isArray(keyTypes) && keyTypes.length > 1) {
48
85
  const type = [
49
86
  ...keyTypes
@@ -51,33 +88,34 @@ const addKey = (item, keyTypes)=>{
51
88
  const pkType = type.shift();
52
89
  Object.assign(key, {
53
90
  kt: pkType,
54
- pk: item.id
91
+ pk: item[primaryKeyAttr]
55
92
  });
56
- // TODO: This is really just for primary items
57
- if (type.length === 1) {
58
- // TODO: This should be looking at the model to get the primary key of the reference item or association
59
- const locKeyTypeId = type[0] + 'Id';
60
- Object.assign(key, {
61
- loc: [
62
- {
63
- kt: type[0],
64
- lk: item[locKeyTypeId]
65
- }
66
- ]
67
- });
68
- } else if (type.length === 2) {
69
- throw new Error('Not implemented');
70
- } else if (type.length === 3) {
71
- throw new Error('Not implemented');
72
- } else if (type.length === 4) {
73
- throw new Error('Not implemented');
74
- } else if (type.length === 5) {
75
- throw new Error('Not implemented');
93
+ // Build location keys for composite key
94
+ const locationKeys = [];
95
+ for (const locatorType of type){
96
+ try {
97
+ const lk = extractLocationKeyValue(modelClass, item, locatorType, keyTypes);
98
+ locationKeys.push({
99
+ kt: locatorType,
100
+ lk
101
+ });
102
+ } catch (error) {
103
+ const errorMessage = error instanceof Error ? error.message : String(error);
104
+ logger.error(`Failed to extract location key for '${locatorType}'`, {
105
+ error: errorMessage,
106
+ item,
107
+ keyTypes
108
+ });
109
+ throw error;
110
+ }
76
111
  }
112
+ Object.assign(key, {
113
+ loc: locationKeys
114
+ });
77
115
  } else {
78
116
  Object.assign(key, {
79
117
  kt: keyTypes[0],
80
- pk: item.id
118
+ pk: item[primaryKeyAttr]
81
119
  });
82
120
  }
83
121
  Object.assign(item, {
@@ -88,4 +126,4 @@ const addKey = (item, keyTypes)=>{
88
126
 
89
127
  exports.addKey = addKey;
90
128
  exports.removeKey = removeKey;
91
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiS2V5TWFzdGVyLmNqcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=
129
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiS2V5TWFzdGVyLmNqcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
@@ -14,12 +14,12 @@ const createOperations = (models, definition, registry)=>{
14
14
  const operations = {};
15
15
  operations.all = all.getAllOperation(models, definition, registry);
16
16
  operations.one = one.getOneOperation(models, definition, registry);
17
- operations.create = create.getCreateOperation();
17
+ operations.create = create.getCreateOperation(models, definition, registry);
18
18
  operations.update = update.getUpdateOperation(models, definition, registry);
19
19
  operations.get = get.getGetOperation(models, definition, registry);
20
20
  operations.remove = remove.getRemoveOperation(models, definition);
21
21
  operations.find = find.getFindOperation(models, definition, registry);
22
- operations.upsert = ()=>{
22
+ operations.upsert = async ()=>{
23
23
  throw new Error('Not implemented');
24
24
  };
25
25
  return operations;
@@ -84,7 +84,8 @@ const addReferenceQueries = (options, references, model)=>{
84
84
  return options;
85
85
  };
86
86
  const addCompoundCondition = (options, compoundCondition, model)=>{
87
- const where = {};
87
+ // Ensure options.where exists
88
+ options.where = options.where || {};
88
89
  let compoundOp;
89
90
  const compoundType = compoundCondition.compoundType;
90
91
  if (compoundType === "AND") {
@@ -100,36 +101,135 @@ const addCompoundCondition = (options, compoundCondition, model)=>{
100
101
  throw new Error('Nest Compound conditions not supported');
101
102
  }
102
103
  });
103
- where[compoundOp] = conditions;
104
- options.where = where;
104
+ // Merge with existing where conditions instead of replacing
105
+ if (Object.keys(options.where).length > 0) {
106
+ // If there are existing conditions, wrap everything in an AND
107
+ options.where = {
108
+ [sequelize.Op.and]: [
109
+ options.where,
110
+ {
111
+ [compoundOp]: conditions
112
+ }
113
+ ]
114
+ };
115
+ } else {
116
+ // If no existing conditions, just set the compound condition
117
+ options.where[compoundOp] = conditions;
118
+ }
105
119
  return options;
106
120
  };
107
- const addCondition = (conditions, condition, model)=>{
108
- let conditionOp;
121
+ const getSequelizeOperator = (operator)=>{
122
+ if (operator === '==') {
123
+ return sequelize.Op.eq;
124
+ } else if (operator === '<') {
125
+ return sequelize.Op.lt;
126
+ } else if (operator === '>') {
127
+ return sequelize.Op.gt;
128
+ } else if (operator === '<=') {
129
+ return sequelize.Op.lte;
130
+ } else if (operator === '>=') {
131
+ return sequelize.Op.gte;
132
+ } else if (operator === 'in') {
133
+ return sequelize.Op.in;
134
+ } else {
135
+ throw new Error(`Operator ${operator} not supported`);
136
+ }
137
+ };
138
+ const addAssociationCondition = (conditions, condition, model)=>{
139
+ const [associationName, attributeName] = condition.column.split('.', 2);
140
+ // Check if the association exists on the model
141
+ if (!model.associations || !model.associations[associationName]) {
142
+ throw new Error(`Association ${associationName} not found on model ${model.name}`);
143
+ }
144
+ const association = model.associations[associationName];
145
+ const associatedModel = association.target;
146
+ // Check if the attribute exists on the associated model
147
+ if (!associatedModel.getAttributes()[attributeName]) {
148
+ throw new Error(`Attribute ${attributeName} not found on associated model ${associatedModel.name} for association ${associationName}`);
149
+ }
150
+ // Use Sequelize's $association.attribute$ syntax for querying associated models
151
+ const sequelizeAssociationColumn = `$${associationName}.${attributeName}$`;
152
+ const conditionOp = getSequelizeOperator(condition.operator);
153
+ conditions[sequelizeAssociationColumn] = {
154
+ [conditionOp]: condition.value
155
+ };
156
+ return conditions;
157
+ };
158
+ const addAttributeCondition = (conditions, condition, model)=>{
109
159
  const conditionColumn = condition.column;
110
160
  if (!model.getAttributes()[conditionColumn]) {
111
161
  throw new Error(`Condition column ${conditionColumn} not found on model ${model.name}`);
112
162
  }
113
- if (condition.operator === '==') {
114
- conditionOp = sequelize.Op.eq;
115
- } else if (condition.operator === '<') {
116
- conditionOp = sequelize.Op.lt;
117
- } else if (condition.operator === '>') {
118
- conditionOp = sequelize.Op.gt;
119
- } else if (condition.operator === '<=') {
120
- conditionOp = sequelize.Op.lte;
121
- } else if (condition.operator === '>=') {
122
- conditionOp = sequelize.Op.gte;
123
- } else if (condition.operator === 'in') {
124
- conditionOp = sequelize.Op.in;
125
- } else {
126
- throw new Error(`Operator ${condition.operator} not supported`);
127
- }
163
+ const conditionOp = getSequelizeOperator(condition.operator);
128
164
  conditions[conditionColumn] = {
129
165
  [conditionOp]: condition.value
130
166
  };
131
167
  return conditions;
132
168
  };
169
+ const addCondition = (conditions, condition, model)=>{
170
+ const conditionColumn = condition.column;
171
+ // Check if this is an association query (contains a dot)
172
+ if (conditionColumn.includes('.')) {
173
+ return addAssociationCondition(conditions, condition, model);
174
+ }
175
+ // Handle regular column queries
176
+ return addAttributeCondition(conditions, condition, model);
177
+ };
178
+ const collectAssociationsFromConditions = (conditions)=>{
179
+ const associations = new Set();
180
+ const processObject = (obj)=>{
181
+ if (typeof obj === 'object' && obj !== null) {
182
+ // Check string keys
183
+ Object.keys(obj).forEach((key)=>{
184
+ // Check if this is an association reference ($association.attribute$)
185
+ if (typeof key === 'string' && key.startsWith('$') && key.endsWith('$') && key.includes('.')) {
186
+ const associationName = key.substring(1, key.indexOf('.'));
187
+ associations.add(associationName);
188
+ }
189
+ // Recursively process nested objects
190
+ if (typeof obj[key] === 'object') {
191
+ processObject(obj[key]);
192
+ }
193
+ });
194
+ // Also check Symbol keys (for compound conditions like Op.and, Op.or)
195
+ Object.getOwnPropertySymbols(obj).forEach((symbol)=>{
196
+ if (typeof obj[symbol] === 'object') {
197
+ processObject(obj[symbol]);
198
+ }
199
+ });
200
+ }
201
+ // Handle arrays (for compound conditions that might be arrays)
202
+ if (Array.isArray(obj)) {
203
+ obj.forEach((item)=>{
204
+ if (typeof item === 'object') {
205
+ processObject(item);
206
+ }
207
+ });
208
+ }
209
+ };
210
+ processObject(conditions);
211
+ return associations;
212
+ };
213
+ const addAssociationIncludes = (options, model)=>{
214
+ // Collect all association names used in conditions
215
+ const referencedAssociations = collectAssociationsFromConditions(options.where);
216
+ if (referencedAssociations.size > 0) {
217
+ options.include = options.include || [];
218
+ // Add each referenced association to the include array
219
+ referencedAssociations.forEach((associationName)=>{
220
+ // Check if this association is already included
221
+ const alreadyIncluded = options.include.some((inc)=>typeof inc === 'string' && inc === associationName || typeof inc === 'object' && inc.association === associationName);
222
+ if (!alreadyIncluded && model.associations && model.associations[associationName]) {
223
+ options.include.push({
224
+ model: model.associations[associationName].target,
225
+ as: associationName,
226
+ required: false // Use LEFT JOIN so records without associations are still returned
227
+ });
228
+ }
229
+ });
230
+ }
231
+ return options;
232
+ };
133
233
  const buildQuery = (itemQuery, model)=>{
134
234
  logger.default('build', {
135
235
  itemQuery
@@ -179,10 +279,12 @@ const buildQuery = (itemQuery, model)=>{
179
279
  ];
180
280
  });
181
281
  }
282
+ // Add includes for any associations referenced in conditions
283
+ options = addAssociationIncludes(options, model);
182
284
  return options;
183
285
  };
184
286
 
185
287
  exports.addCompoundCondition = addCompoundCondition;
186
288
  exports.addCondition = addCondition;
187
289
  exports.buildQuery = buildQuery;
188
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUXVlcnlCdWlsZGVyLmNqcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9
290
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUXVlcnlCdWlsZGVyLmNqcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9
@@ -18,7 +18,7 @@ const processRow = async (row, keyTypes, referenceDefinitions, aggregationDefini
18
18
  plain: true
19
19
  });
20
20
  logger.default('Adding Key to Item with Key Types: %s', general.stringifyJSON(keyTypes));
21
- item = KeyMaster.addKey(item, keyTypes);
21
+ item = KeyMaster.addKey(row, item, keyTypes);
22
22
  item = EventCoordinator.populateEvents(item);
23
23
  logger.default('Key Added to Item: %s', general.stringifyJSON(item.key));
24
24
  if (referenceDefinitions && referenceDefinitions.length > 0) {
@@ -2,22 +2,153 @@
2
2
 
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
+ const core = require('@fjell/core');
5
6
  const logger$1 = require('../logger.cjs');
7
+ const RowProcessor = require('../RowProcessor.cjs');
8
+ const EventCoordinator = require('../EventCoordinator.cjs');
9
+ const relationshipUtils = require('../util/relationshipUtils.cjs');
6
10
 
7
11
  const logger = logger$1.default.get('sequelize', 'ops', 'create');
8
- const getCreateOperation = (// eslint-disable-next-line @typescript-eslint/no-unused-vars
9
- models, // eslint-disable-next-line @typescript-eslint/no-unused-vars
10
- definition, // eslint-disable-next-line @typescript-eslint/no-unused-vars
11
- registry)=>{
12
+ // Helper function to validate hierarchical chain exists
13
+ async function validateHierarchicalChain(models, locKey, kta) {
14
+ // Find the direct parent model that contains this locator
15
+ const locatorIndex = kta.indexOf(locKey.kt);
16
+ if (locatorIndex === -1) {
17
+ throw new Error(`Locator type '${locKey.kt}' not found in kta array`);
18
+ }
19
+ // Get the model for this locator
20
+ const locatorModel = models[locatorIndex] || models[0]; // Fallback to primary model
21
+ // Build a query to validate the chain exists
22
+ const chainResult = relationshipUtils.buildRelationshipChain(locatorModel, kta, locatorIndex, kta.length - 1);
23
+ if (!chainResult.success) {
24
+ // If we can't build a chain, just validate the record exists
25
+ const record = await locatorModel.findByPk(locKey.lk);
26
+ if (!record) {
27
+ throw new Error(`Referenced ${locKey.kt} with id ${locKey.lk} does not exist`);
28
+ }
29
+ return;
30
+ }
31
+ // Validate that the chain exists
32
+ const queryOptions = {
33
+ where: {
34
+ id: locKey.lk
35
+ }
36
+ };
37
+ if (chainResult.includes && chainResult.includes.length > 0) {
38
+ queryOptions.include = chainResult.includes;
39
+ }
40
+ const record = await locatorModel.findOne(queryOptions);
41
+ if (!record) {
42
+ throw new Error(`Referenced ${locKey.kt} with id ${locKey.lk} does not exist or chain is invalid`);
43
+ }
44
+ }
45
+ const getCreateOperation = (models, definition, registry)=>{
12
46
  const create = async (item, options)=>{
13
47
  logger.default('Create', {
14
48
  item,
15
49
  options
16
50
  });
17
- throw new Error('Not implemented');
51
+ const { coordinate, options: { references, aggregations } } = definition;
52
+ const { kta } = coordinate;
53
+ // Get the primary model (first model in array)
54
+ const model = models[0];
55
+ const modelAttributes = model.getAttributes();
56
+ // Validate that all item attributes exist on the model
57
+ let itemData = {
58
+ ...item
59
+ };
60
+ // TODO: We need the opposite of processRow, something to step down from fjell to database.
61
+ itemData = EventCoordinator.extractEvents(itemData);
62
+ itemData = EventCoordinator.removeEvents(itemData);
63
+ for (const key of Object.keys(itemData)){
64
+ if (!modelAttributes[key]) {
65
+ throw new Error(`Attribute '${key}' does not exist on model ${model.name}`);
66
+ }
67
+ }
68
+ // Handle key options
69
+ // If a key is supplied, assume its contents are to be assigned to the appropriate ids.
70
+ // For most cases this will be null as key generation is often through autoIncrement.
71
+ // If this is a CItem then the locations will be present.
72
+ if (options === null || options === void 0 ? void 0 : options.key) {
73
+ const key = options.key;
74
+ if (core.isPriKey(key)) {
75
+ // Set the primary key
76
+ itemData.id = key.pk;
77
+ } else if (core.isComKey(key)) {
78
+ // Set primary key
79
+ itemData.id = key.pk;
80
+ // Process location keys - only set direct foreign keys, validate hierarchical chains
81
+ const comKey = key;
82
+ const directLocations = [];
83
+ const hierarchicalLocations = [];
84
+ // Categorize location keys as direct or hierarchical
85
+ for (const locKey of comKey.loc){
86
+ const relationshipInfo = relationshipUtils.buildRelationshipPath(model, locKey.kt, kta, true);
87
+ if (!relationshipInfo.found) {
88
+ const errorMessage = `Composite key locator '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
89
+ logger.error(errorMessage, {
90
+ key: comKey,
91
+ kta
92
+ });
93
+ throw new Error(errorMessage);
94
+ }
95
+ if (relationshipInfo.isDirect) {
96
+ directLocations.push(locKey);
97
+ } else {
98
+ hierarchicalLocations.push(locKey);
99
+ }
100
+ }
101
+ // Set direct foreign keys
102
+ for (const locKey of directLocations){
103
+ const foreignKeyField = locKey.kt + 'Id';
104
+ itemData[foreignKeyField] = locKey.lk;
105
+ }
106
+ // Validate hierarchical chains exist
107
+ for (const locKey of hierarchicalLocations){
108
+ await validateHierarchicalChain(models, locKey, kta);
109
+ }
110
+ }
111
+ }
112
+ // Handle locations options
113
+ // This is the most frequent way relationship ids will be set
114
+ if (options === null || options === void 0 ? void 0 : options.locations) {
115
+ const directLocations = [];
116
+ const hierarchicalLocations = [];
117
+ // Categorize location keys as direct or hierarchical
118
+ for (const locKey of options.locations){
119
+ const relationshipInfo = relationshipUtils.buildRelationshipPath(model, locKey.kt, kta, true);
120
+ if (!relationshipInfo.found) {
121
+ const errorMessage = `Location key '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
122
+ logger.error(errorMessage, {
123
+ locations: options.locations,
124
+ kta
125
+ });
126
+ throw new Error(errorMessage);
127
+ }
128
+ if (relationshipInfo.isDirect) {
129
+ directLocations.push(locKey);
130
+ } else {
131
+ hierarchicalLocations.push(locKey);
132
+ }
133
+ }
134
+ // Set direct foreign keys
135
+ for (const locKey of directLocations){
136
+ const foreignKeyField = locKey.kt + 'Id';
137
+ itemData[foreignKeyField] = locKey.lk;
138
+ }
139
+ // Validate hierarchical chains exist
140
+ for (const locKey of hierarchicalLocations){
141
+ await validateHierarchicalChain(models, locKey, kta);
142
+ }
143
+ }
144
+ // Create the record
145
+ const createdRecord = await model.create(itemData);
146
+ // Add key and events
147
+ const processedRecord = await RowProcessor.processRow(createdRecord, kta, references, aggregations, registry);
148
+ return core.validateKeys(processedRecord, kta);
18
149
  };
19
150
  return create;
20
151
  };
21
152
 
22
153
  exports.getCreateOperation = getCreateOperation;
23
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLmNqcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9
154
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLmNqcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=
@@ -6,8 +6,45 @@ const core = require('@fjell/core');
6
6
  const logger$1 = require('../logger.cjs');
7
7
  const RowProcessor = require('../RowProcessor.cjs');
8
8
  const Library = require('@fjell/lib');
9
+ const relationshipUtils = require('../util/relationshipUtils.cjs');
9
10
 
10
11
  const logger = logger$1.default.get('sequelize', 'ops', 'get');
12
+ // Helper function to process composite key and build query options
13
+ const processCompositeKey = (comKey, model, kta)=>{
14
+ const where = {
15
+ id: comKey.pk
16
+ };
17
+ const includes = [];
18
+ for (const locator of comKey.loc){
19
+ const relationshipInfo = relationshipUtils.buildRelationshipPath(model, locator.kt, kta);
20
+ if (!relationshipInfo.found) {
21
+ const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
22
+ logger.error(errorMessage, {
23
+ key: comKey,
24
+ kta
25
+ });
26
+ throw new Error(errorMessage);
27
+ }
28
+ if (relationshipInfo.path) {
29
+ // This requires a relationship traversal
30
+ where[relationshipInfo.path] = locator.lk;
31
+ if (relationshipInfo.includes) {
32
+ includes.push(...relationshipInfo.includes);
33
+ }
34
+ } else {
35
+ // This is a direct field
36
+ const fieldName = `${locator.kt}Id`;
37
+ where[fieldName] = locator.lk;
38
+ }
39
+ }
40
+ const result = {
41
+ where
42
+ };
43
+ if (includes.length > 0) {
44
+ result.include = includes;
45
+ }
46
+ return result;
47
+ };
11
48
  const getGetOperation = (models, definition, registry)=>{
12
49
  const { coordinate, options: { references, aggregations } } = definition;
13
50
  const { kta } = coordinate;
@@ -24,17 +61,16 @@ const getGetOperation = (models, definition, registry)=>{
24
61
  const model = models[0];
25
62
  let item;
26
63
  if (core.isPriKey(itemKey)) {
64
+ // This is the easy case because we can just find the item by its primary key
27
65
  item = await model.findByPk(itemKey.pk);
28
66
  } else if (core.isComKey(itemKey)) {
29
- var _comKey_loc_, _comKey_loc_1;
67
+ // This is a composite key, so we need to build a where clause based on the composite key's locators
30
68
  const comKey = itemKey;
31
- // TODO: This should probably interrogate the model?
32
- item = await model.findOne({
33
- where: {
34
- id: comKey.pk,
35
- [(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
36
- }
69
+ const queryOptions = processCompositeKey(comKey, model, kta);
70
+ logger.default('Composite key query', {
71
+ queryOptions
37
72
  });
73
+ item = await model.findOne(queryOptions);
38
74
  }
39
75
  if (!item) {
40
76
  throw new Library.NotFoundError('get', coordinate, key);
@@ -46,4 +82,4 @@ const getGetOperation = (models, definition, registry)=>{
46
82
  };
47
83
 
48
84
  exports.getGetOperation = getGetOperation;
49
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2V0LmNqcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=
85
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2V0LmNqcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=