@loomcore/api 0.1.76 → 0.1.78

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.
@@ -2,22 +2,87 @@ import { Join } from '../../operations/join.operation.js';
2
2
  import { JoinMany } from '../../operations/join-many.operation.js';
3
3
  import { JoinThrough } from '../../operations/join-through.operation.js';
4
4
  import { JoinThroughMany } from '../../operations/join-through-many.operation.js';
5
+ function findNestedObject(obj, alias, path = []) {
6
+ if (obj[alias] !== undefined && obj[alias] !== null) {
7
+ return { obj: obj[alias], path: [...path, alias] };
8
+ }
9
+ for (const key in obj) {
10
+ if (obj[key] && typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
11
+ const found = findNestedObject(obj[key], alias, [...path, key]);
12
+ if (found !== null) {
13
+ return found;
14
+ }
15
+ }
16
+ else if (Array.isArray(obj[key])) {
17
+ for (const item of obj[key]) {
18
+ if (item && typeof item === 'object') {
19
+ const found = findNestedObject(item, alias, [...path, key]);
20
+ if (found !== null) {
21
+ return found;
22
+ }
23
+ }
24
+ }
25
+ }
26
+ }
27
+ return null;
28
+ }
29
+ function parseJsonValue(value) {
30
+ if (value === null || value === undefined) {
31
+ return null;
32
+ }
33
+ if (typeof value === 'string') {
34
+ try {
35
+ return JSON.parse(value);
36
+ }
37
+ catch {
38
+ return value;
39
+ }
40
+ }
41
+ return value;
42
+ }
43
+ function findEnrichmentTarget(operation, operations) {
44
+ if (!operation.localField.includes('.')) {
45
+ return null;
46
+ }
47
+ const [alias] = operation.localField.split('.');
48
+ const target = operations.find(op => (op instanceof JoinMany || op instanceof JoinThroughMany) && op.as === alias);
49
+ if (target && operations.indexOf(target) < operations.indexOf(operation)) {
50
+ return target;
51
+ }
52
+ return null;
53
+ }
54
+ function mapEnrichmentFieldName(fieldName, targetAlias) {
55
+ if (fieldName === 'policy_agents' && (targetAlias === 'client_policies' || targetAlias === 'policies')) {
56
+ return 'agents';
57
+ }
58
+ return fieldName;
59
+ }
5
60
  export function transformJoinResults(rows, operations) {
6
61
  const joinOperations = operations.filter(op => op instanceof Join);
7
62
  const joinManyOperations = operations.filter(op => op instanceof JoinMany);
8
63
  const joinThroughOperations = operations.filter(op => op instanceof JoinThrough);
9
64
  const joinThroughManyOperations = operations.filter(op => op instanceof JoinThroughMany);
10
- if (joinOperations.length === 0 && joinManyOperations.length === 0 && joinThroughOperations.length === 0 && joinThroughManyOperations.length === 0) {
65
+ if (joinOperations.length === 0 &&
66
+ joinManyOperations.length === 0 &&
67
+ joinThroughOperations.length === 0 &&
68
+ joinThroughManyOperations.length === 0) {
11
69
  return rows;
12
70
  }
71
+ const allJoinAliases = [
72
+ ...joinOperations.map(j => j.as),
73
+ ...joinManyOperations.map(j => j.as),
74
+ ...joinThroughOperations.map(j => j.as),
75
+ ...joinThroughManyOperations.map(j => j.as)
76
+ ];
77
+ const enrichmentMap = new Map();
78
+ for (const op of [...joinManyOperations, ...joinThroughManyOperations]) {
79
+ const target = findEnrichmentTarget(op, operations);
80
+ if (target) {
81
+ enrichmentMap.set(op, target);
82
+ }
83
+ }
13
84
  return rows.map(row => {
14
85
  const transformed = {};
15
- const allJoinAliases = [
16
- ...joinOperations.map(j => j.as),
17
- ...joinManyOperations.map(j => j.as),
18
- ...joinThroughOperations.map(j => j.as),
19
- ...joinThroughManyOperations.map(j => j.as)
20
- ];
21
86
  for (const key of Object.keys(row)) {
22
87
  const hasJoinPrefix = joinOperations.some(join => key.startsWith(`${join.as}__`));
23
88
  const isJoinAlias = allJoinAliases.includes(key);
@@ -26,34 +91,10 @@ export function transformJoinResults(rows, operations) {
26
91
  }
27
92
  }
28
93
  for (const operation of operations) {
29
- if (operation instanceof JoinThrough) {
30
- const jsonValue = row[operation.as];
31
- let parsedValue;
32
- if (jsonValue !== null && jsonValue !== undefined) {
33
- parsedValue = typeof jsonValue === 'string'
34
- ? JSON.parse(jsonValue)
35
- : jsonValue;
36
- }
37
- else {
38
- parsedValue = null;
39
- }
40
- if (operation.localField.includes('.')) {
41
- const [tableAlias] = operation.localField.split('.');
42
- const relatedJoin = joinOperations.find(j => j.as === tableAlias);
43
- const relatedJoinThrough = joinThroughOperations.find(j => j.as === tableAlias);
44
- if ((relatedJoin && transformed[relatedJoin.as]) || (relatedJoinThrough && transformed[relatedJoinThrough.as])) {
45
- const targetAlias = relatedJoin ? relatedJoin.as : relatedJoinThrough.as;
46
- transformed[targetAlias][operation.as] = parsedValue;
47
- }
48
- else {
49
- transformed[operation.as] = parsedValue;
50
- }
51
- }
52
- else {
53
- transformed[operation.as] = parsedValue;
54
- }
94
+ if (enrichmentMap.has(operation)) {
95
+ continue;
55
96
  }
56
- else if (operation instanceof Join) {
97
+ if (operation instanceof Join) {
57
98
  const prefix = `${operation.as}__`;
58
99
  const joinedData = {};
59
100
  let hasAnyData = false;
@@ -71,45 +112,21 @@ export function transformJoinResults(rows, operations) {
71
112
  const [tableAlias] = operation.localField.split('.');
72
113
  const relatedJoin = joinOperations.find(j => j.as === tableAlias);
73
114
  const relatedJoinThrough = joinThroughOperations.find(j => j.as === tableAlias);
74
- const findNestedObject = (obj, alias, path = []) => {
75
- if (obj[alias] !== undefined && obj[alias] !== null) {
76
- return { obj: obj[alias], path: [...path, alias] };
77
- }
78
- for (const key in obj) {
79
- if (obj[key] && typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
80
- const found = findNestedObject(obj[key], alias, [...path, key]);
81
- if (found !== null) {
82
- return found;
83
- }
84
- }
85
- }
86
- return null;
87
- };
88
115
  let targetObject = null;
89
- if (relatedJoin) {
90
- if (transformed[relatedJoin.as] !== undefined && transformed[relatedJoin.as] !== null) {
91
- targetObject = transformed[relatedJoin.as];
92
- }
93
- else {
94
- const found = findNestedObject(transformed, relatedJoin.as);
95
- if (found !== null && found.obj !== undefined && found.obj !== null) {
96
- targetObject = found.obj;
97
- }
98
- }
116
+ if (relatedJoin && transformed[relatedJoin.as]) {
117
+ targetObject = transformed[relatedJoin.as];
99
118
  }
100
- else if (relatedJoinThrough) {
101
- const found = findNestedObject(transformed, relatedJoinThrough.as);
102
- if (found !== null && found.obj !== undefined && found.obj !== null) {
119
+ else if (relatedJoinThrough && transformed[relatedJoinThrough.as]) {
120
+ targetObject = transformed[relatedJoinThrough.as];
121
+ }
122
+ else {
123
+ const found = findNestedObject(transformed, tableAlias);
124
+ if (found) {
103
125
  targetObject = found.obj;
104
126
  }
105
127
  }
106
128
  if (targetObject) {
107
- if (hasAnyData) {
108
- targetObject[operation.as] = joinedData;
109
- }
110
- else {
111
- targetObject[operation.as] = null;
112
- }
129
+ targetObject[operation.as] = hasAnyData ? joinedData : null;
113
130
  }
114
131
  else {
115
132
  transformed[operation.as] = hasAnyData ? joinedData : null;
@@ -119,89 +136,140 @@ export function transformJoinResults(rows, operations) {
119
136
  transformed[operation.as] = hasAnyData ? joinedData : null;
120
137
  }
121
138
  }
122
- }
123
- for (const joinMany of joinManyOperations) {
124
- const jsonValue = row[joinMany.as];
125
- let parsedValue;
126
- if (jsonValue !== null && jsonValue !== undefined) {
127
- parsedValue = typeof jsonValue === 'string'
128
- ? JSON.parse(jsonValue)
129
- : jsonValue;
130
- }
131
- else {
132
- parsedValue = [];
133
- }
134
- if (joinMany.localField.includes('.')) {
135
- const [tableAlias] = joinMany.localField.split('.');
136
- const relatedJoin = joinOperations.find(j => j.as === tableAlias);
137
- if (relatedJoin && transformed[relatedJoin.as]) {
138
- transformed[relatedJoin.as][joinMany.as] = parsedValue;
139
+ else if (operation instanceof JoinThrough) {
140
+ const jsonValue = parseJsonValue(row[operation.as]);
141
+ if (operation.localField.includes('.')) {
142
+ const [tableAlias] = operation.localField.split('.');
143
+ const relatedJoin = joinOperations.find(j => j.as === tableAlias);
144
+ const relatedJoinThrough = joinThroughOperations.find(j => j.as === tableAlias);
145
+ let targetObject = null;
146
+ if (relatedJoin && transformed[relatedJoin.as]) {
147
+ targetObject = transformed[relatedJoin.as];
148
+ }
149
+ else if (relatedJoinThrough) {
150
+ const found = findNestedObject(transformed, relatedJoinThrough.as);
151
+ if (found) {
152
+ targetObject = found.obj;
153
+ }
154
+ }
155
+ if (targetObject) {
156
+ targetObject[operation.as] = jsonValue;
157
+ }
158
+ else {
159
+ transformed[operation.as] = jsonValue;
160
+ }
139
161
  }
140
162
  else {
141
- transformed[joinMany.as] = parsedValue;
163
+ transformed[operation.as] = jsonValue;
142
164
  }
143
165
  }
144
- else {
145
- transformed[joinMany.as] = parsedValue;
146
- }
147
- }
148
- const replacedJoins = new Map();
149
- for (const joinThroughMany of joinThroughManyOperations) {
150
- if (joinThroughMany.localField.includes('.')) {
151
- const [tableAlias] = joinThroughMany.localField.split('.');
152
- const referencedJoin = joinThroughManyOperations.find(j => j.as === tableAlias);
153
- if (referencedJoin) {
154
- const referencedIndex = operations.indexOf(referencedJoin);
155
- const currentIndex = operations.indexOf(joinThroughMany);
156
- if (referencedIndex < currentIndex) {
157
- replacedJoins.set(tableAlias, joinThroughMany.as);
166
+ else if (operation instanceof JoinMany) {
167
+ const jsonValue = parseJsonValue(row[operation.as]);
168
+ let parsedValue = Array.isArray(jsonValue) ? jsonValue : (jsonValue ? [jsonValue] : []);
169
+ const enrichments = Array.from(enrichmentMap.entries())
170
+ .filter(([_, target]) => target === operation)
171
+ .map(([enrichOp]) => enrichOp);
172
+ if (enrichments.length > 0 && Array.isArray(parsedValue)) {
173
+ for (const item of parsedValue) {
174
+ if (item && typeof item === 'object') {
175
+ for (const enrichment of enrichments) {
176
+ if (item[enrichment.as] !== undefined) {
177
+ const mappedName = mapEnrichmentFieldName(enrichment.as, operation.as);
178
+ if (mappedName !== enrichment.as) {
179
+ item[mappedName] = item[enrichment.as];
180
+ delete item[enrichment.as];
181
+ }
182
+ }
183
+ }
184
+ }
158
185
  }
159
186
  }
160
- }
161
- }
162
- for (const joinThroughMany of joinThroughManyOperations) {
163
- if (replacedJoins.has(joinThroughMany.as)) {
164
- continue;
165
- }
166
- const originalAlias = Array.from(replacedJoins.entries()).find(([_, replacing]) => replacing === joinThroughMany.as)?.[0];
167
- const aliasToUse = originalAlias || joinThroughMany.as;
168
- const jsonValue = row[aliasToUse];
169
- let parsedValue;
170
- if (jsonValue !== null && jsonValue !== undefined) {
171
- parsedValue = typeof jsonValue === 'string'
172
- ? JSON.parse(jsonValue)
173
- : jsonValue;
174
- }
175
- else {
176
- parsedValue = [];
177
- }
178
- if (joinThroughMany.localField.includes('.')) {
179
- const [tableAlias] = joinThroughMany.localField.split('.');
180
- const relatedJoin = joinOperations.find(j => j.as === tableAlias);
181
- const relatedJoinThrough = joinThroughOperations.find(j => j.as === tableAlias);
182
- const relatedJoinThroughMany = joinThroughManyOperations.find(j => j.as === tableAlias);
183
- if ((relatedJoin && transformed[relatedJoin.as]) ||
184
- (relatedJoinThrough && transformed[relatedJoinThrough.as]) ||
185
- (relatedJoinThroughMany && transformed[relatedJoinThroughMany.as])) {
186
- const targetAlias = relatedJoin ? relatedJoin.as :
187
- (relatedJoinThrough ? relatedJoinThrough.as : relatedJoinThroughMany.as);
188
- if (replacedJoins.get(targetAlias) === joinThroughMany.as) {
189
- transformed[targetAlias] = parsedValue;
187
+ if (operation.localField.includes('.')) {
188
+ const [tableAlias] = operation.localField.split('.');
189
+ const relatedJoin = joinOperations.find(j => j.as === tableAlias);
190
+ const relatedJoinMany = joinManyOperations.find(j => j.as === tableAlias);
191
+ const relatedJoinThrough = joinThroughOperations.find(j => j.as === tableAlias);
192
+ const relatedJoinThroughMany = joinThroughManyOperations.find(j => j.as === tableAlias);
193
+ let targetObject = null;
194
+ if (relatedJoin && transformed[relatedJoin.as]) {
195
+ targetObject = transformed[relatedJoin.as];
190
196
  }
191
- else {
192
- let fieldName = joinThroughMany.as;
193
- if (fieldName === 'policy_agents' && targetAlias === 'policies') {
194
- fieldName = 'agents';
197
+ else if (relatedJoinThrough) {
198
+ const found = findNestedObject(transformed, relatedJoinThrough.as);
199
+ if (found) {
200
+ targetObject = found.obj;
195
201
  }
196
- transformed[targetAlias][fieldName] = parsedValue;
202
+ }
203
+ else if (relatedJoinMany && transformed[relatedJoinMany.as]) {
204
+ targetObject = transformed[relatedJoinMany.as];
205
+ }
206
+ else if (relatedJoinThroughMany && transformed[relatedJoinThroughMany.as]) {
207
+ targetObject = transformed[relatedJoinThroughMany.as];
208
+ }
209
+ if (targetObject) {
210
+ targetObject[operation.as] = parsedValue;
211
+ }
212
+ else {
213
+ transformed[operation.as] = parsedValue;
197
214
  }
198
215
  }
199
216
  else {
200
- transformed[aliasToUse] = parsedValue;
217
+ transformed[operation.as] = parsedValue;
201
218
  }
202
219
  }
203
- else {
204
- transformed[aliasToUse] = parsedValue;
220
+ else if (operation instanceof JoinThroughMany) {
221
+ const jsonValue = parseJsonValue(row[operation.as]);
222
+ let parsedValue = Array.isArray(jsonValue) ? jsonValue : (jsonValue ? [jsonValue] : []);
223
+ const enrichments = Array.from(enrichmentMap.entries())
224
+ .filter(([_, target]) => target === operation)
225
+ .map(([enrichOp]) => enrichOp);
226
+ if (enrichments.length > 0 && Array.isArray(parsedValue)) {
227
+ for (const item of parsedValue) {
228
+ if (item && typeof item === 'object') {
229
+ for (const enrichment of enrichments) {
230
+ if (item[enrichment.as] !== undefined) {
231
+ const mappedName = mapEnrichmentFieldName(enrichment.as, operation.as);
232
+ if (mappedName !== enrichment.as) {
233
+ item[mappedName] = item[enrichment.as];
234
+ delete item[enrichment.as];
235
+ }
236
+ }
237
+ }
238
+ }
239
+ }
240
+ }
241
+ if (operation.localField.includes('.')) {
242
+ const [tableAlias] = operation.localField.split('.');
243
+ const relatedJoin = joinOperations.find(j => j.as === tableAlias);
244
+ const relatedJoinMany = joinManyOperations.find(j => j.as === tableAlias);
245
+ const relatedJoinThrough = joinThroughOperations.find(j => j.as === tableAlias);
246
+ const relatedJoinThroughMany = joinThroughManyOperations.find(j => j.as === tableAlias);
247
+ let targetObject = null;
248
+ if (relatedJoin && transformed[relatedJoin.as]) {
249
+ targetObject = transformed[relatedJoin.as];
250
+ }
251
+ else if (relatedJoinThrough) {
252
+ const found = findNestedObject(transformed, relatedJoinThrough.as);
253
+ if (found) {
254
+ targetObject = found.obj;
255
+ }
256
+ }
257
+ else if (relatedJoinMany && transformed[relatedJoinMany.as]) {
258
+ targetObject = transformed[relatedJoinMany.as];
259
+ }
260
+ else if (relatedJoinThroughMany && transformed[relatedJoinThroughMany.as]) {
261
+ targetObject = transformed[relatedJoinThroughMany.as];
262
+ }
263
+ if (targetObject) {
264
+ targetObject[operation.as] = parsedValue;
265
+ }
266
+ else {
267
+ transformed[operation.as] = parsedValue;
268
+ }
269
+ }
270
+ else {
271
+ transformed[operation.as] = parsedValue;
272
+ }
205
273
  }
206
274
  }
207
275
  return transformed;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loomcore/api",
3
- "version": "0.1.76",
3
+ "version": "0.1.78",
4
4
  "private": false,
5
5
  "description": "Loom Core Api - An opinionated Node.js api using Typescript, Express, and MongoDb or PostgreSQL",
6
6
  "scripts": {