@halix/action-sdk 1.0.17 → 1.0.19

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/README.md CHANGED
@@ -39,29 +39,29 @@ export const handler = async (event) => {
39
39
  // Refresh the object from the server
40
40
  let fullObj;
41
41
  try {
42
- fullObj = await hx.getObject("exampleType", obj.objKey);
42
+ fullObj = await hx.getObject('exampleType', obj.objKey);
43
43
  } catch (err) {
44
- console.error("Error fetching object", err);
45
- return hx.prepareErrorResponse("Failed to retrieve data");
44
+ console.error('Error fetching object', err);
45
+ return hx.prepareErrorResponse('Failed to retrieve data');
46
46
  }
47
47
 
48
48
  // Perform updates or logic
49
- fullObj.status = "Updated";
49
+ fullObj.status = 'Updated';
50
50
 
51
51
  // Save the updated object
52
52
  let saved;
53
53
  try {
54
- saved = await hx.saveRelatedObject("parentType", fullObj.parentKey, "exampleType", fullObj);
54
+ saved = await hx.saveRelatedObject('parentType', fullObj.parentKey, 'exampleType', fullObj);
55
55
  } catch (err) {
56
- console.error("Error saving object", err);
57
- return hx.prepareErrorResponse("Failed to save data");
56
+ console.error('Error saving object', err);
57
+ return hx.prepareErrorResponse('Failed to save data');
58
58
  }
59
59
 
60
60
  // Return a success response
61
61
  return hx.prepareSuccessResponse({
62
- responseType: "formTemplateAction",
62
+ responseType: 'formTemplateAction',
63
63
  updatedSubject: saved,
64
- successMessage: "Object saved successfully",
64
+ successMessage: 'Object saved successfully',
65
65
  isError: false
66
66
  });
67
67
  };
@@ -86,14 +86,40 @@ This action pattern is typical for use in Halix’s Lambda-style runtime environ
86
86
  | Function | Description |
87
87
  |----------|-------------|
88
88
  | `initialize(event)` | Initializes the SDK with event context |
89
- | `getObject(...)`, `getRelatedObjects(...)` | Retrieve objects from the Halix data layer |
90
- | `saveRelatedObject(...)` | Save objects and establish relationships |
89
+ | `getObject(...)` / `getObjectAsObservable(...)` | Retrieve a single object |
90
+ | `getRelatedObjects(...)` / `getRelatedObjectsAsObservable(...)` | Retrieve related objects |
91
+ | `saveRelatedObject(...)` / `saveRelatedObjectAsObservable(...)` | Save objects and relationships |
92
+ | `deleteRelatedObject(...)` / `deleteRelatedObjectAsObservable(...)` | Delete a single related object |
93
+ | `deleteRelatedObjects(...)` / `deleteRelatedObjectsAsObservable(...)` | Delete multiple related objects (uses `keys` query param) |
91
94
  | `prepareSuccessResponse(...)` | Create a success response |
92
95
  | `prepareErrorResponse(...)` | Create an error response |
93
- | `sortObjectArray(...)` | Utility to sort object arrays |
94
- | `getValueFromObject(...)` | Access nested or relationship-based attributes |
95
96
 
96
- See [Full Documentation](https://mmastrangelo.github.io/halixsdk/) for more information.
97
+ Notes:
98
+ - Functions that interact with services require `initialize(event)` to have been called; they depend on `userContext`, `sandboxKey`, and `serviceAddress`.
99
+
100
+ ---
101
+
102
+ ## 📦 Content Resource Helpers
103
+
104
+ These helpers simplify working with content resources and file uploads.
105
+
106
+ | Function | Description |
107
+ |----------|-------------|
108
+ | `getOrCreateResource(...)` / `getOrCreateResourceAsObservable(...)` | Retrieve an existing content resource by key or create a new one |
109
+ | `saveResource(...)` / `saveResourceAsObservable(...)` | Persist a content resource |
110
+ | `sendFileContents(resourceKey, file, publicFlag)` / `sendFileContentsAsObservable(...)` | Upload file contents (multipart/form-data) to a content resource |
111
+ | `createOrUpdateResource(resourceKey?, file, publicFlag, resourceType, tags)` / `createOrUpdateResourceAsObservable(...)` | Create or update a resource and upload the file in one call |
112
+
113
+ ---
114
+
115
+ ## 🧪 Utilities
116
+
117
+ | Function | Description |
118
+ |----------|-------------|
119
+ | `getValueFromObject(object, attribute)` | Access nested or relationship-based attributes |
120
+ | `debounceFn(fn, wait?)` | Debounce utility for throttling calls |
121
+ | `compareValues(a, b, descending, caseInsensitive)` | Utility comparer for sorting |
122
+ | `sortObjectArray(array, sort)` | Utility to sort object arrays |
97
123
 
98
124
  ---
99
125
 
package/lib/cjs/index.js CHANGED
@@ -21,18 +21,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
21
21
  };
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
23
23
  exports.useBody = exports.params = exports.userContext = exports.actionSubject = exports.serviceAddress = exports.sandboxKey = exports.getAuthToken = void 0;
24
- exports.getRelatedObjects = getRelatedObjects;
25
- exports.getRelatedObjectsAsObservable = getRelatedObjectsAsObservable;
24
+ exports.initialize = initialize;
25
+ exports.prepareSuccessResponse = prepareSuccessResponse;
26
+ exports.prepareErrorResponse = prepareErrorResponse;
26
27
  exports.getObject = getObject;
27
28
  exports.getObjectAsObservable = getObjectAsObservable;
29
+ exports.getRelatedObjects = getRelatedObjects;
30
+ exports.getRelatedObjectsAsObservable = getRelatedObjectsAsObservable;
28
31
  exports.saveRelatedObject = saveRelatedObject;
29
32
  exports.saveRelatedObjectAsObservable = saveRelatedObjectAsObservable;
30
- exports.sortObjectArray = sortObjectArray;
31
- exports.compareValues = compareValues;
32
- exports.getValueFromObject = getValueFromObject;
33
- exports.prepareSuccessResponse = prepareSuccessResponse;
34
- exports.prepareErrorResponse = prepareErrorResponse;
35
- exports.initialize = initialize;
33
+ exports.deleteRelatedObject = deleteRelatedObject;
34
+ exports.deleteRelatedObjectAsObservable = deleteRelatedObjectAsObservable;
35
+ exports.deleteRelatedObjects = deleteRelatedObjects;
36
+ exports.deleteRelatedObjectsAsObservable = deleteRelatedObjectsAsObservable;
36
37
  exports.getOrCreateResource = getOrCreateResource;
37
38
  exports.getOrCreateResourceAsObservable = getOrCreateResourceAsObservable;
38
39
  exports.saveResource = saveResource;
@@ -41,6 +42,9 @@ exports.sendFileContents = sendFileContents;
41
42
  exports.sendFileContentsAsObservable = sendFileContentsAsObservable;
42
43
  exports.createOrUpdateResource = createOrUpdateResource;
43
44
  exports.createOrUpdateResourceAsObservable = createOrUpdateResourceAsObservable;
45
+ exports.sortObjectArray = sortObjectArray;
46
+ exports.compareValues = compareValues;
47
+ exports.getValueFromObject = getValueFromObject;
44
48
  exports.debounceFn = debounceFn;
45
49
  /**
46
50
  * @module @halix/action-sdk
@@ -51,6 +55,115 @@ exports.debounceFn = debounceFn;
51
55
  */
52
56
  const axios_1 = __importDefault(require("axios"));
53
57
  const rxjs_1 = require("rxjs");
58
+ /**
59
+ * initialize initializes the SDK with event data. This should be called at the beginning of the
60
+ * action handler to set up the SDK with incoming information, including context information, input
61
+ * parameters, and authentication information needed to make API requests to the Halix service.
62
+ *
63
+ * @param event - The event object containing authentication and context information
64
+ */
65
+ function initialize(event) {
66
+ let body = event;
67
+ if (event.body) {
68
+ body = event.body;
69
+ exports.useBody = true;
70
+ }
71
+ if (body) {
72
+ ({ sandboxKey: exports.sandboxKey, serviceAddress: exports.serviceAddress, actionSubject: exports.actionSubject, userContext: exports.userContext, params: exports.params } = body);
73
+ if (body.authToken) {
74
+ exports.getAuthToken = () => (0, rxjs_1.of)(body.authToken);
75
+ }
76
+ else if (body.authTokenRetriever) {
77
+ exports.getAuthToken = body.authTokenRetriever;
78
+ }
79
+ }
80
+ }
81
+ // ================================================================================
82
+ // RESPONSE HELPER FUNCTIONS
83
+ // ================================================================================
84
+ /**
85
+ * prepareSuccessResponse prepares a success response in the appropriate format. The action handler
86
+ * should return an ActionResponse response when the action is successful. If useBody is true, the
87
+ * response will be returned as an object with the HTTP response code and the ActionResponse in the
88
+ * body field. If useBody is false, the ActionResponse will be returned directly.
89
+ *
90
+ * @param successResponse - The value to return
91
+ *
92
+ * @returns Formatted success response; an ActionResponse unless useBody is true
93
+ */
94
+ function prepareSuccessResponse(successResponse) {
95
+ if (exports.useBody) {
96
+ return {
97
+ statusCode: 200,
98
+ body: JSON.stringify(successResponse)
99
+ };
100
+ }
101
+ return successResponse;
102
+ }
103
+ /**
104
+ * prepareErrorResponse prepares an error response in the appropriate format. The action handler
105
+ * should return an ErrorResponse response when the action is not successful. If useBody is true,
106
+ * the response will be returned as an object with the HTTP response code and the ErrorResponse in
107
+ * the body field. If useBody is false, the ErrorResponse will be returned directly.
108
+ *
109
+ * @param errorMessage - The error message
110
+ *
111
+ * @returns Formatted error response; an ErrorResponse unless useBody is true
112
+ */
113
+ function prepareErrorResponse(errorMessage) {
114
+ if (exports.useBody) {
115
+ return {
116
+ statusCode: 400,
117
+ body: JSON.stringify({ errorMessage })
118
+ };
119
+ }
120
+ return { errorMessage, responseType: "error" };
121
+ }
122
+ // ================================================================================
123
+ // DATA RETRIEVAL FUNCTIONS
124
+ // ================================================================================
125
+ /**
126
+ * getObject retrieves a single object from the database by its data element ID and key.
127
+ *
128
+ * @param dataElementId - The ID of the data element
129
+ * @param key - The key of the object
130
+ * @param fetchedRelationships - Optional array of relationships to fetch; if provided, the returned
131
+ * object will include the specified related objects as nested objects
132
+ * @returns Promise resolving to the object data
133
+ */
134
+ function getObject(dataElementId, key, fetchedRelationships) {
135
+ return __awaiter(this, void 0, void 0, function* () {
136
+ let params;
137
+ if (fetchedRelationships) {
138
+ let p = {};
139
+ if (fetchedRelationships) {
140
+ p.fetchedRelationships = fetchedRelationships.join(",");
141
+ }
142
+ params = new URLSearchParams(p);
143
+ }
144
+ let url = `${exports.serviceAddress}/schema/sandboxes/${exports.sandboxKey}/${dataElementId}/${key}`;
145
+ let authToken = yield (0, rxjs_1.lastValueFrom)((0, exports.getAuthToken)());
146
+ console.log("Sending GET request to " + url + " with token " + authToken);
147
+ let response = yield axios_1.default.get(url, {
148
+ headers: { "Authorization": `Bearer ${authToken}` },
149
+ params: params,
150
+ });
151
+ return response.data;
152
+ });
153
+ }
154
+ /**
155
+ * getObjectAsObservable retrieves a single object from the database by its data element ID and key.
156
+ *
157
+ * @param dataElementId - The ID of the data element
158
+ * @param key - The key of the object
159
+ * @param fetchedRelationships - Optional array of relationships to fetch; if provided, the returned
160
+ * object will include the specified related objects as nested objects
161
+ *
162
+ * @returns Observable resolving to the object data
163
+ */
164
+ function getObjectAsObservable(dataElementId, key, fetchedRelationships) {
165
+ return (0, rxjs_1.from)(getObject(dataElementId, key, fetchedRelationships));
166
+ }
54
167
  /**
55
168
  * getRelatedObjects retrieves an array of objects from the the database. The objects returned are
56
169
  * related to a parent through a defined relationship in the schema. In a typical setup, action's
@@ -123,48 +236,9 @@ function getRelatedObjects(parentElementId, parentKey, elementId, filter, fetche
123
236
  function getRelatedObjectsAsObservable(parentElementId, parentKey, elementId, filter, fetchedRelationships) {
124
237
  return (0, rxjs_1.from)(getRelatedObjects(parentElementId, parentKey, elementId, filter, fetchedRelationships));
125
238
  }
126
- /**
127
- * getObject retrieves a single object from the database by its data element ID and key.
128
- *
129
- * @param dataElementId - The ID of the data element
130
- * @param key - The key of the object
131
- * @param fetchedRelationships - Optional array of relationships to fetch; if provided, the returned
132
- * object will include the specified related objects as nested objects
133
- * @returns Promise resolving to the object data
134
- */
135
- function getObject(dataElementId, key, fetchedRelationships) {
136
- return __awaiter(this, void 0, void 0, function* () {
137
- let params;
138
- if (fetchedRelationships) {
139
- let p = {};
140
- if (fetchedRelationships) {
141
- p.fetchedRelationships = fetchedRelationships.join(",");
142
- }
143
- params = new URLSearchParams(p);
144
- }
145
- let url = `${exports.serviceAddress}/schema/sandboxes/${exports.sandboxKey}/${dataElementId}/${key}`;
146
- let authToken = yield (0, rxjs_1.lastValueFrom)((0, exports.getAuthToken)());
147
- console.log("Sending GET request to " + url + " with token " + authToken);
148
- let response = yield axios_1.default.get(url, {
149
- headers: { "Authorization": `Bearer ${authToken}` },
150
- params: params,
151
- });
152
- return response.data;
153
- });
154
- }
155
- /**
156
- * getObjectAsObservable retrieves a single object from the database by its data element ID and key.
157
- *
158
- * @param dataElementId - The ID of the data element
159
- * @param key - The key of the object
160
- * @param fetchedRelationships - Optional array of relationships to fetch; if provided, the returned
161
- * object will include the specified related objects as nested objects
162
- *
163
- * @returns Observable resolving to the object data
164
- */
165
- function getObjectAsObservable(dataElementId, key, fetchedRelationships) {
166
- return (0, rxjs_1.from)(getObject(dataElementId, key, fetchedRelationships));
167
- }
239
+ // ================================================================================
240
+ // DATA SAVE FUNCTIONS
241
+ // ================================================================================
168
242
  /**
169
243
  * saveRelatedObject saves a related object to the database. The objectToSave is saved, and its
170
244
  * relationship to the parent object is established based on the relationship specified in the
@@ -217,165 +291,87 @@ function saveRelatedObject(parentElementId, parentKey, elementId, objectToSave,
217
291
  function saveRelatedObjectAsObservable(parentElementId, parentKey, elementId, objectToSave, opts) {
218
292
  return (0, rxjs_1.from)(saveRelatedObject(parentElementId, parentKey, elementId, objectToSave, opts));
219
293
  }
294
+ // ================================================================================
295
+ // DATA DELETE FUNCTIONS
296
+ // ================================================================================
220
297
  /**
221
- * sortObjectArray is a helper function that sorts the passed array in place by the given
222
- * attributes. Sorting by nested attributes in the form of a delimited attribute string are
223
- * supported (e.g., "attribute.nestedAttribute").
298
+ * deleteRelatedObject deletes a single object related to a specific parent.
224
299
  *
225
- * @param array - The array to sort
226
- * @param sort - Array of sort field specifications
227
- * @returns The sorted array
300
+ * @param parentElementId - The ID of the parent element
301
+ * @param parentKey - The key of the parent object
302
+ * @param childElementId - The ID of the child element to delete
303
+ * @param childKey - The key of the child object to delete
304
+ *
305
+ * @returns Promise resolving to true if deletion was successful
228
306
  */
229
- function sortObjectArray(array, sort) {
230
- return array.sort((a, b) => {
231
- let comparison = 0;
232
- for (let s of sort) {
233
- let valueA = getValueFromObject(a, s.attributeId);
234
- let valueB = getValueFromObject(b, s.attributeId);
235
- comparison = compareValues(valueA, valueB, !!s.descending, !!s.caseInsensitive);
236
- if (comparison !== 0) {
237
- break;
238
- }
307
+ function deleteRelatedObject(parentElementId, parentKey, childElementId, childKey) {
308
+ return __awaiter(this, void 0, void 0, function* () {
309
+ if (!exports.userContext) {
310
+ throw new Error("userContext is required but not available; check that the initialize function has been called");
239
311
  }
240
- return comparison;
312
+ let url = `${exports.serviceAddress}/schema/sandboxes/${exports.sandboxKey}/${parentElementId}/${parentKey}/${childElementId}/${childKey}`;
313
+ let authToken = yield (0, rxjs_1.lastValueFrom)((0, exports.getAuthToken)());
314
+ console.log("Sending DELETE request to " + url + " with token " + authToken);
315
+ let response = yield axios_1.default.delete(url, {
316
+ headers: { "Authorization": `Bearer ${authToken}` },
317
+ });
318
+ return response.status === 204;
241
319
  });
242
320
  }
243
321
  /**
244
- * compareValues is a helper function that compares two values for sorting purposes. If the values
245
- * are strings, the comparison is case-insensitive. If the values are numbers, the comparison is
246
- * performed numerically.
322
+ * deleteRelatedObjectAsObservable deletes a single object related to a specific parent.
247
323
  *
248
- * @param valueA - First value to compare
249
- * @param valueB - Second value to compare
250
- * @param descending - Whether to sort in descending order
251
- * @param caseInsensitive - Whether to perform case-insensitive comparison for strings
324
+ * @param parentElementId - The ID of the parent element
325
+ * @param parentKey - The key of the parent object
326
+ * @param childElementId - The ID of the child element to delete
327
+ * @param childKey - The key of the child object to delete
252
328
  *
253
- * @returns Comparison result (-1, 0, or 1)
329
+ * @returns Observable resolving to true if deletion was successful
254
330
  */
255
- function compareValues(valueA, valueB, descending, caseInsensitive) {
256
- if (caseInsensitive && (typeof valueA === 'string' || valueA instanceof String)) {
257
- if (valueA && valueB) {
258
- let comp = valueA.toLowerCase().localeCompare(valueB.toLowerCase());
259
- if (descending) {
260
- comp = comp * -1;
261
- }
262
- return comp;
263
- }
264
- else if (valueA && !valueB) {
265
- return -1;
266
- }
267
- else if (!valueA && valueB) {
268
- return 1;
269
- }
270
- else {
271
- return 0;
272
- }
273
- }
274
- else {
275
- if (valueA < valueB) {
276
- return (descending ? 1 : -1);
277
- }
278
- if (valueA > valueB) {
279
- return (descending ? -1 : 1);
280
- }
281
- }
282
- return 0;
331
+ function deleteRelatedObjectAsObservable(parentElementId, parentKey, childElementId, childKey) {
332
+ return (0, rxjs_1.from)(deleteRelatedObject(parentElementId, parentKey, childElementId, childKey));
283
333
  }
284
334
  /**
285
- * getValueFromObject is a helper function that extracts a value from an object using a dot-notation
286
- * path. The path can include relationships. Relationship IDs may include a colon delimiter (e.g.,
287
- * "accountMember:ownerAccountMemberKey") to specify the key of the related object. This is useful
288
- * when an element has more than one relationship to the same object type. Otherwise, if only one
289
- * relationship to the same object type exists, the key may be specified without the relationship ID
290
- * (e.g., simply, "accountMember").
335
+ * deleteRelatedObjects deletes multiple objects related to a specific parent.
291
336
  *
292
- * @param object - The object to extract value from
293
- * @param attribute - The attribute path (e.g., "user.address.city")
337
+ * @param parentElementId - The ID of the parent element
338
+ * @param parentKey - The key of the parent object
339
+ * @param childElementId - The ID of the child element to delete
340
+ * @param childKeys - Array of keys of the child objects to delete
294
341
  *
295
- * @returns The extracted value
342
+ * @returns Promise resolving to true if deletion was successful
296
343
  */
297
- function getValueFromObject(object, attribute) {
298
- let components = attribute.split(".");
299
- let value = object;
300
- for (let component of components) {
301
- if (value) {
302
- // If a relationship specifies a key, it will be in the format [datatype]:[key]. Otherwise the colon
303
- // delimiter will not be present.
304
- // The related value will be in a field named after the key. For example: accountMember:ownerAccountMemberKey
305
- // the related owner account member will be in a field called "ownerAccountMember".
306
- let compSplit = component.split(":");
307
- if (compSplit.length > 1) {
308
- let keyField = compSplit[1];
309
- value = value[keyField.replace("Key", "")];
310
- }
311
- else {
312
- value = value[component];
313
- }
344
+ function deleteRelatedObjects(parentElementId, parentKey, childElementId, childKeys) {
345
+ return __awaiter(this, void 0, void 0, function* () {
346
+ if (!exports.userContext) {
347
+ throw new Error("userContext is required but not available; check that the initialize function has been called");
314
348
  }
315
- }
316
- return value;
317
- }
318
- /**
319
- * prepareSuccessResponse prepares a success response in the appropriate format. The action handler
320
- * should return an ActionResponse response when the action is successful. If useBody is true, the
321
- * response will be returned as an object with the HTTP response code and the ActionResponse in the
322
- * body field. If useBody is false, the ActionResponse will be returned directly.
323
- *
324
- * @param successResponse - The value to return
325
- *
326
- * @returns Formatted success response; an ActionResponse unless useBody is true
327
- */
328
- function prepareSuccessResponse(successResponse) {
329
- if (exports.useBody) {
330
- return {
331
- statusCode: 200,
332
- body: JSON.stringify(successResponse)
333
- };
334
- }
335
- return successResponse;
349
+ let url = `${exports.serviceAddress}/schema/sandboxes/${exports.sandboxKey}/${parentElementId}/${parentKey}/${childElementId}`;
350
+ let authToken = yield (0, rxjs_1.lastValueFrom)((0, exports.getAuthToken)());
351
+ console.log("Sending DELETE request to " + url + " with token " + authToken);
352
+ let response = yield axios_1.default.delete(url, {
353
+ headers: { "Authorization": `Bearer ${authToken}` },
354
+ params: { keys: childKeys.join(",") },
355
+ });
356
+ return response.status === 204;
357
+ });
336
358
  }
337
359
  /**
338
- * prepareErrorResponse prepares an error response in the appropriate format. The action handler
339
- * should return an ErrorResponse response when the action is not successful. If useBody is true,
340
- * the response will be returned as an object with the HTTP response code and the ErrorResponse in
341
- * the body field. If useBody is false, the ErrorResponse will be returned directly.
342
- *
343
- * @param errorMessage - The error message
360
+ * deleteRelatedObjectsAsObservable deletes multiple objects related to a specific parent.
344
361
  *
345
- * @returns Formatted error response; an ErrorResponse unless useBody is true
346
- */
347
- function prepareErrorResponse(errorMessage) {
348
- if (exports.useBody) {
349
- return {
350
- statusCode: 400,
351
- body: JSON.stringify({ errorMessage })
352
- };
353
- }
354
- return { errorMessage, responseType: "error" };
355
- }
356
- /**
357
- * initialize initializes the SDK with event data. This should be called at the beginning of the
358
- * action handler to set up the SDK with incoming information, including context information, input
359
- * parameters, and authentication information needed to make API requests to the Halix service.
362
+ * @param parentElementId - The ID of the parent element
363
+ * @param parentKey - The key of the parent object
364
+ * @param childElementId - The ID of the child element to delete
365
+ * @param childKeys - Array of keys of the child objects to delete
360
366
  *
361
- * @param event - The event object containing authentication and context information
367
+ * @returns Observable resolving to true if deletion was successful
362
368
  */
363
- function initialize(event) {
364
- let body = event;
365
- if (event.body) {
366
- body = event.body;
367
- exports.useBody = true;
368
- }
369
- if (body) {
370
- ({ sandboxKey: exports.sandboxKey, serviceAddress: exports.serviceAddress, actionSubject: exports.actionSubject, userContext: exports.userContext, params: exports.params } = body);
371
- if (body.authToken) {
372
- exports.getAuthToken = () => (0, rxjs_1.of)(body.authToken);
373
- }
374
- else if (body.authTokenRetriever) {
375
- exports.getAuthToken = body.authTokenRetriever;
376
- }
377
- }
369
+ function deleteRelatedObjectsAsObservable(parentElementId, parentKey, childElementId, childKeys) {
370
+ return (0, rxjs_1.from)(deleteRelatedObjects(parentElementId, parentKey, childElementId, childKeys));
378
371
  }
372
+ // ================================================================================
373
+ // CONTENT RESOURCE FUNCTIONS
374
+ // ================================================================================
379
375
  /**
380
376
  * getOrCreateResource retrieves an existing content resource by its key, or creates a new one
381
377
  * if the key is not provided. If a resource key is provided, it attempts to fetch the existing
@@ -588,6 +584,107 @@ function createOrUpdateResource(resourceKey, fileToUpload, publicFlag, resourceT
588
584
  function createOrUpdateResourceAsObservable(resourceKey, fileToUpload, publicFlag, resourceType, tags) {
589
585
  return (0, rxjs_1.from)(createOrUpdateResource(resourceKey, fileToUpload, publicFlag, resourceType, tags));
590
586
  }
587
+ // ================================================================================
588
+ // UTILITY FUNCTIONS
589
+ // ================================================================================
590
+ /**
591
+ * sortObjectArray is a helper function that sorts the passed array in place by the given
592
+ * attributes. Sorting by nested attributes in the form of a delimited attribute string are
593
+ * supported (e.g., "attribute.nestedAttribute").
594
+ *
595
+ * @param array - The array to sort
596
+ * @param sort - Array of sort field specifications
597
+ * @returns The sorted array
598
+ */
599
+ function sortObjectArray(array, sort) {
600
+ return array.sort((a, b) => {
601
+ let comparison = 0;
602
+ for (let s of sort) {
603
+ let valueA = getValueFromObject(a, s.attributeId);
604
+ let valueB = getValueFromObject(b, s.attributeId);
605
+ comparison = compareValues(valueA, valueB, !!s.descending, !!s.caseInsensitive);
606
+ if (comparison !== 0) {
607
+ break;
608
+ }
609
+ }
610
+ return comparison;
611
+ });
612
+ }
613
+ /**
614
+ * compareValues is a helper function that compares two values for sorting purposes. If the values
615
+ * are strings, the comparison is case-insensitive. If the values are numbers, the comparison is
616
+ * performed numerically.
617
+ *
618
+ * @param valueA - First value to compare
619
+ * @param valueB - Second value to compare
620
+ * @param descending - Whether to sort in descending order
621
+ * @param caseInsensitive - Whether to perform case-insensitive comparison for strings
622
+ *
623
+ * @returns Comparison result (-1, 0, or 1)
624
+ */
625
+ function compareValues(valueA, valueB, descending, caseInsensitive) {
626
+ if (caseInsensitive && (typeof valueA === 'string' || valueA instanceof String)) {
627
+ if (valueA && valueB) {
628
+ let comp = valueA.toLowerCase().localeCompare(valueB.toLowerCase());
629
+ if (descending) {
630
+ comp = comp * -1;
631
+ }
632
+ return comp;
633
+ }
634
+ else if (valueA && !valueB) {
635
+ return -1;
636
+ }
637
+ else if (!valueA && valueB) {
638
+ return 1;
639
+ }
640
+ else {
641
+ return 0;
642
+ }
643
+ }
644
+ else {
645
+ if (valueA < valueB) {
646
+ return (descending ? 1 : -1);
647
+ }
648
+ if (valueA > valueB) {
649
+ return (descending ? -1 : 1);
650
+ }
651
+ }
652
+ return 0;
653
+ }
654
+ /**
655
+ * getValueFromObject is a helper function that extracts a value from an object using a dot-notation
656
+ * path. The path can include relationships. Relationship IDs may include a colon delimiter (e.g.,
657
+ * "accountMember:ownerAccountMemberKey") to specify the key of the related object. This is useful
658
+ * when an element has more than one relationship to the same object type. Otherwise, if only one
659
+ * relationship to the same object type exists, the key may be specified without the relationship ID
660
+ * (e.g., simply, "accountMember").
661
+ *
662
+ * @param object - The object to extract value from
663
+ * @param attribute - The attribute path (e.g., "user.address.city")
664
+ *
665
+ * @returns The extracted value
666
+ */
667
+ function getValueFromObject(object, attribute) {
668
+ let components = attribute.split(".");
669
+ let value = object;
670
+ for (let component of components) {
671
+ if (value) {
672
+ // If a relationship specifies a key, it will be in the format [datatype]:[key]. Otherwise the colon
673
+ // delimiter will not be present.
674
+ // The related value will be in a field named after the key. For example: accountMember:ownerAccountMemberKey
675
+ // the related owner account member will be in a field called "ownerAccountMember".
676
+ let compSplit = component.split(":");
677
+ if (compSplit.length > 1) {
678
+ let keyField = compSplit[1];
679
+ value = value[keyField.replace("Key", "")];
680
+ }
681
+ else {
682
+ value = value[component];
683
+ }
684
+ }
685
+ }
686
+ return value;
687
+ }
591
688
  /**
592
689
  * debounceFn is a utility function that debounces a function call. It is used to prevent multiple
593
690
  * calls to the same function within a short period of time.