@boostercloud/framework-provider-azure 2.10.1 → 2.12.0

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.
@@ -1,3 +1,3 @@
1
1
  import { CosmosClient } from '@azure/cosmos';
2
- import { BoosterConfig, FilterFor, ReadModelListResult, SortFor } from '@boostercloud/framework-types';
3
- export declare function search<TResult>(cosmosDb: CosmosClient, config: BoosterConfig, containerName: string, filters: FilterFor<unknown>, limit?: number | undefined, afterCursor?: Record<string, string> | undefined, paginatedVersion?: boolean, order?: SortFor<unknown>, projections?: string): Promise<Array<TResult> | ReadModelListResult<TResult>>;
2
+ import { BoosterConfig, FilterFor, ProjectionFor, ReadModelListResult, SortFor } from '@boostercloud/framework-types';
3
+ export declare function search<TResult>(cosmosDb: CosmosClient, config: BoosterConfig, containerName: string, filters: FilterFor<unknown>, limit?: number | undefined, afterCursor?: Record<string, string> | undefined, paginatedVersion?: boolean, order?: SortFor<unknown>, projections?: ProjectionFor<unknown> | string): Promise<Array<TResult> | ReadModelListResult<TResult>>;
@@ -6,7 +6,8 @@ const framework_common_helpers_1 = require("@boostercloud/framework-common-helpe
6
6
  async function search(cosmosDb, config, containerName, filters, limit, afterCursor, paginatedVersion = false, order, projections = '*') {
7
7
  const logger = (0, framework_common_helpers_1.getLogger)(config, 'query-helper#search');
8
8
  const filterExpression = buildFilterExpression(filters);
9
- const queryDefinition = `SELECT ${projections} FROM c ${filterExpression !== '' ? `WHERE ${filterExpression}` : filterExpression}`;
9
+ const projectionsExpression = buildProjections(projections);
10
+ const queryDefinition = `SELECT ${projectionsExpression} FROM c ${filterExpression !== '' ? `WHERE ${filterExpression}` : filterExpression}`;
10
11
  const queryWithOrder = queryDefinition + buildOrderExpression(order);
11
12
  let finalQuery = queryWithOrder;
12
13
  if (paginatedVersion && limit) {
@@ -22,11 +23,12 @@ async function search(cosmosDb, config, containerName, filters, limit, afterCurs
22
23
  parameters: buildExpressionAttributeValues(filters),
23
24
  };
24
25
  logger.debug('Running search with the following params: \n', querySpec);
25
- const { resources } = await cosmosDb
26
+ let { resources } = await cosmosDb
26
27
  .database(config.resourceNames.applicationStack)
27
28
  .container(containerName)
28
29
  .items.query(querySpec)
29
30
  .fetchAll();
31
+ resources = nestProperties(resources);
30
32
  if (paginatedVersion) {
31
33
  return {
32
34
  items: resources !== null && resources !== void 0 ? resources : [],
@@ -178,3 +180,164 @@ function toLocalSortFor(sortBy, parentKey = '', sortedList = []) {
178
180
  });
179
181
  return sortedList;
180
182
  }
183
+ function buildProjections(projections = '*') {
184
+ if (typeof projections !== 'object') {
185
+ return projections;
186
+ }
187
+ // Helper function to convert dot notation to square-bracket notation
188
+ const toSquareBracketsNotation = (path) => {
189
+ return path
190
+ .split('.')
191
+ .map((part) => `["${part}"]`)
192
+ .join('');
193
+ };
194
+ // Group fields by the root property
195
+ const groupedFields = {};
196
+ Object.values(projections).forEach((field) => {
197
+ const root = field.split('.')[0];
198
+ if (!groupedFields[root]) {
199
+ groupedFields[root] = [];
200
+ }
201
+ groupedFields[root].push(field);
202
+ });
203
+ return Object.keys(groupedFields)
204
+ .map((root) => {
205
+ const fields = groupedFields[root];
206
+ if (root.endsWith('[]')) {
207
+ const arrayRoot = root.slice(0, -2);
208
+ const subFields = fields
209
+ .map((f) => f.replace(`${root}.`, ''))
210
+ .map(toSquareBracketsNotation)
211
+ .map((f) => `item${f}`)
212
+ .join(', ');
213
+ return `ARRAY(SELECT ${subFields} FROM item IN c["${arrayRoot}"]) AS ${arrayRoot}`;
214
+ }
215
+ else if (fields.length === 1 && !fields[0].includes('.')) {
216
+ // Simple field
217
+ return `c${toSquareBracketsNotation(fields[0])}`;
218
+ }
219
+ else {
220
+ // Nested object fields
221
+ const nestedFields = {};
222
+ fields.forEach((f) => {
223
+ const parts = f.split('.').slice(1);
224
+ if (parts.length > 0) {
225
+ const nestedRoot = parts[0];
226
+ if (!nestedFields[nestedRoot]) {
227
+ nestedFields[nestedRoot] = [];
228
+ }
229
+ nestedFields[nestedRoot].push(parts.join('.'));
230
+ }
231
+ });
232
+ return Object.keys(nestedFields)
233
+ .map((nestedRoot) => {
234
+ const subFields = nestedFields[nestedRoot]
235
+ .map((f) => `c${toSquareBracketsNotation(`${root}.${f}`)} AS "${root}.${f}"`)
236
+ .join(', ');
237
+ if (nestedRoot.endsWith('[]')) {
238
+ const arrayNestedRoot = nestedRoot.slice(0, -2);
239
+ const subArrayFields = nestedFields[nestedRoot]
240
+ .map((f) => {
241
+ const subFieldParts = f.split('.').slice(1).join('.');
242
+ return `item${toSquareBracketsNotation(subFieldParts)}`;
243
+ })
244
+ .join(', ');
245
+ return `ARRAY(SELECT ${subArrayFields} FROM item IN c${toSquareBracketsNotation(`${root}.${arrayNestedRoot}`)}) AS "${root}.${arrayNestedRoot}"`;
246
+ }
247
+ return subFields;
248
+ })
249
+ .join(', ');
250
+ }
251
+ })
252
+ .join(', ');
253
+ }
254
+ /**
255
+ * Transforms the flat properties returned by Cosmos DB into a nested structure. For example, the following object:
256
+ *
257
+ * ```json
258
+ * {
259
+ * "foo.bar": "baz",
260
+ * "items": [{"qux.quux": "corge"}]
261
+ * }
262
+ * ```
263
+ *
264
+ * is transformed to this:
265
+ *
266
+ * ```json
267
+ * {
268
+ * "foo": {
269
+ * "bar": "baz"
270
+ * },
271
+ * "items": [
272
+ * {
273
+ * "qux": {
274
+ * "quux": "corge"
275
+ * }
276
+ * }
277
+ * ]
278
+ * }
279
+ * ```
280
+ *
281
+ * @param {any} obj - The object to be nested.
282
+ * @returns {any} - The nested object.
283
+ */
284
+ function nestProperties(obj) {
285
+ const result = {};
286
+ /**
287
+ * Sets a nested property on an object.
288
+ * @param {any} obj - The object on which to set the property.
289
+ * @param {string[]} path - The path to the property.
290
+ * @param {any} value - The value to set.
291
+ */
292
+ function setNestedProperty(obj, path, value) {
293
+ let current = obj;
294
+ for (let i = 0; i < path.length - 1; i++) {
295
+ if (!current[path[i]]) {
296
+ current[path[i]] = {};
297
+ }
298
+ current = current[path[i]];
299
+ }
300
+ current[path[path.length - 1]] = value;
301
+ }
302
+ /**
303
+ * Processes an object, nesting its properties.
304
+ * @param {any} input - The object to process.
305
+ * @param {any} output - The object to output.
306
+ */
307
+ function processObject(input, output) {
308
+ for (const key in input) {
309
+ if (Object.prototype.hasOwnProperty.call(input, key)) {
310
+ const value = input[key];
311
+ const keys = key.split('.');
312
+ setNestedProperty(output, keys, value);
313
+ }
314
+ }
315
+ }
316
+ /**
317
+ * Processes an array, nesting its properties.
318
+ * @param {any[]} arr - The array to process.
319
+ * @returns {any[]} - The processed array.
320
+ */
321
+ function processArray(arr) {
322
+ return arr.map((item) => {
323
+ if (Array.isArray(item)) {
324
+ return processArray(item);
325
+ }
326
+ else if (item !== null && typeof item === 'object') {
327
+ const nestedItem = {};
328
+ processObject(item, nestedItem);
329
+ return nestedItem;
330
+ }
331
+ else {
332
+ return item;
333
+ }
334
+ });
335
+ }
336
+ if (Array.isArray(obj)) {
337
+ return processArray(obj);
338
+ }
339
+ else if (obj !== null && typeof obj === 'object') {
340
+ processObject(obj, result);
341
+ }
342
+ return result;
343
+ }
@@ -1,3 +1,3 @@
1
1
  import { CosmosClient } from '@azure/cosmos';
2
- import { BoosterConfig, FilterFor, ReadModelListResult, SortFor } from '@boostercloud/framework-types';
3
- export declare function searchReadModel(cosmosDb: CosmosClient, config: BoosterConfig, readModelName: string, filters: FilterFor<unknown>, sortBy?: SortFor<unknown>, limit?: number, afterCursor?: Record<string, string> | undefined, paginatedVersion?: boolean): Promise<Array<any> | ReadModelListResult<any>>;
2
+ import { BoosterConfig, FilterFor, ProjectionFor, ReadModelListResult, SortFor } from '@boostercloud/framework-types';
3
+ export declare function searchReadModel(cosmosDb: CosmosClient, config: BoosterConfig, readModelName: string, filters: FilterFor<unknown>, sortBy?: SortFor<unknown>, limit?: number, afterCursor?: Record<string, string> | undefined, paginatedVersion?: boolean, select?: ProjectionFor<unknown>): Promise<Array<any> | ReadModelListResult<any>>;
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.searchReadModel = void 0;
4
4
  const queryHelper = require("../helpers/query-helper");
5
- async function searchReadModel(cosmosDb, config, readModelName, filters, sortBy, limit, afterCursor, paginatedVersion = false) {
6
- return await queryHelper.search(cosmosDb, config, config.resourceNames.forReadModel(readModelName), filters, limit, afterCursor, paginatedVersion, sortBy);
5
+ async function searchReadModel(cosmosDb, config, readModelName, filters, sortBy, limit, afterCursor, paginatedVersion = false, select) {
6
+ return await queryHelper.search(cosmosDb, config, config.resourceNames.forReadModel(readModelName), filters, limit, afterCursor, paginatedVersion, sortBy, select);
7
7
  }
8
8
  exports.searchReadModel = searchReadModel;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boostercloud/framework-provider-azure",
3
- "version": "2.10.1",
3
+ "version": "2.12.0",
4
4
  "description": "Handle Booster's integration with Azure",
5
5
  "keywords": [
6
6
  "framework-provider-azure"
@@ -27,14 +27,14 @@
27
27
  "@azure/functions": "^1.2.2",
28
28
  "@azure/identity": "~2.1.0",
29
29
  "@azure/event-hubs": "5.11.1",
30
- "@boostercloud/framework-common-helpers": "^2.10.1",
31
- "@boostercloud/framework-types": "^2.10.1",
30
+ "@boostercloud/framework-common-helpers": "^2.12.0",
31
+ "@boostercloud/framework-types": "^2.12.0",
32
32
  "tslib": "^2.4.0",
33
33
  "@effect-ts/core": "^0.60.4",
34
34
  "@azure/web-pubsub": "~1.1.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@boostercloud/eslint-config": "^2.10.1",
37
+ "@boostercloud/eslint-config": "^2.12.0",
38
38
  "@types/chai": "4.2.18",
39
39
  "@types/chai-as-promised": "7.1.4",
40
40
  "@types/faker": "5.1.5",