@boostercloud/framework-provider-azure 2.10.1 → 2.11.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,148 @@ 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
+ // Group fields by the root property
188
+ const groupedFields = {};
189
+ Object.values(projections).forEach((field) => {
190
+ const root = field.split('.')[0];
191
+ if (!groupedFields[root]) {
192
+ groupedFields[root] = [];
193
+ }
194
+ groupedFields[root].push(field);
195
+ });
196
+ return Object.keys(groupedFields)
197
+ .map((root) => {
198
+ const fields = groupedFields[root];
199
+ if (root.endsWith('[]')) {
200
+ const arrayRoot = root.slice(0, -2);
201
+ const subFields = fields.map((f) => f.replace(`${root}.`, 'item.')).join(', ');
202
+ return `ARRAY(SELECT ${subFields} FROM item IN c.${arrayRoot}) AS ${arrayRoot}`;
203
+ }
204
+ else if (fields.length === 1 && !fields[0].includes('.')) {
205
+ // Simple field
206
+ return `c.${fields[0]}`;
207
+ }
208
+ else {
209
+ // Nested object fields
210
+ const nestedFields = {};
211
+ fields.forEach((f) => {
212
+ const parts = f.split('.').slice(1);
213
+ if (parts.length > 0) {
214
+ const nestedRoot = parts[0];
215
+ if (!nestedFields[nestedRoot]) {
216
+ nestedFields[nestedRoot] = [];
217
+ }
218
+ nestedFields[nestedRoot].push(parts.join('.'));
219
+ }
220
+ });
221
+ return Object.keys(nestedFields)
222
+ .map((nestedRoot) => {
223
+ const subFields = nestedFields[nestedRoot].map((f) => `c.${root}.${f} AS "${root}.${f}"`).join(', ');
224
+ if (nestedRoot.endsWith('[]')) {
225
+ const arrayNestedRoot = nestedRoot.slice(0, -2);
226
+ const subArrayFields = nestedFields[nestedRoot]
227
+ .map((f) => `item.${f.split('.').slice(1).join('.')}`)
228
+ .join(', ');
229
+ return `ARRAY(SELECT ${subArrayFields} FROM item IN c.${root}.${arrayNestedRoot}) AS "${root}.${arrayNestedRoot}"`;
230
+ }
231
+ return subFields;
232
+ })
233
+ .join(', ');
234
+ }
235
+ })
236
+ .join(', ');
237
+ }
238
+ /**
239
+ * Transforms the flat properties returned by Cosmos DB into a nested structure. For example, the following object:
240
+ *
241
+ * ```json
242
+ * {
243
+ * "foo.bar": "baz",
244
+ * "items": [{"qux.quux": "corge"}]
245
+ * }
246
+ * ```
247
+ *
248
+ * is transformed to this:
249
+ *
250
+ * ```json
251
+ * {
252
+ * "foo": {
253
+ * "bar": "baz"
254
+ * },
255
+ * "items": [
256
+ * {
257
+ * "qux": {
258
+ * "quux": "corge"
259
+ * }
260
+ * }
261
+ * ]
262
+ * }
263
+ * ```
264
+ *
265
+ * @param {any} obj - The object to be nested.
266
+ * @returns {any} - The nested object.
267
+ */
268
+ function nestProperties(obj) {
269
+ const result = {};
270
+ /**
271
+ * Sets a nested property on an object.
272
+ * @param {any} obj - The object on which to set the property.
273
+ * @param {string[]} path - The path to the property.
274
+ * @param {any} value - The value to set.
275
+ */
276
+ function setNestedProperty(obj, path, value) {
277
+ let current = obj;
278
+ for (let i = 0; i < path.length - 1; i++) {
279
+ if (!current[path[i]]) {
280
+ current[path[i]] = {};
281
+ }
282
+ current = current[path[i]];
283
+ }
284
+ current[path[path.length - 1]] = value;
285
+ }
286
+ /**
287
+ * Processes an object, nesting its properties.
288
+ * @param {any} input - The object to process.
289
+ * @param {any} output - The object to output.
290
+ */
291
+ function processObject(input, output) {
292
+ for (const key in input) {
293
+ if (Object.prototype.hasOwnProperty.call(input, key)) {
294
+ const value = input[key];
295
+ const keys = key.split('.');
296
+ setNestedProperty(output, keys, value);
297
+ }
298
+ }
299
+ }
300
+ /**
301
+ * Processes an array, nesting its properties.
302
+ * @param {any[]} arr - The array to process.
303
+ * @returns {any[]} - The processed array.
304
+ */
305
+ function processArray(arr) {
306
+ return arr.map((item) => {
307
+ if (Array.isArray(item)) {
308
+ return processArray(item);
309
+ }
310
+ else if (item !== null && typeof item === 'object') {
311
+ const nestedItem = {};
312
+ processObject(item, nestedItem);
313
+ return nestedItem;
314
+ }
315
+ else {
316
+ return item;
317
+ }
318
+ });
319
+ }
320
+ if (Array.isArray(obj)) {
321
+ return processArray(obj);
322
+ }
323
+ else if (obj !== null && typeof obj === 'object') {
324
+ processObject(obj, result);
325
+ }
326
+ return result;
327
+ }
@@ -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.11.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.11.0",
31
+ "@boostercloud/framework-types": "^2.11.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.11.0",
38
38
  "@types/chai": "4.2.18",
39
39
  "@types/chai-as-promised": "7.1.4",
40
40
  "@types/faker": "5.1.5",