@boostercloud/framework-provider-azure 0.26.1 → 0.26.4

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.
@@ -0,0 +1,3 @@
1
+ import { CosmosClient } from '@azure/cosmos';
2
+ import { BoosterConfig, FilterFor, Logger, ReadModelListResult } from '@boostercloud/framework-types';
3
+ export declare function search(cosmosDb: CosmosClient, config: BoosterConfig, logger: Logger, containerName: string, filters: FilterFor<unknown>, limit?: number, afterCursor?: Record<string, string> | undefined, paginatedVersion?: boolean, order?: Record<string, string>): Promise<Array<any> | ReadModelListResult<any>>;
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.search = void 0;
4
+ const framework_types_1 = require("@boostercloud/framework-types");
5
+ async function search(cosmosDb, config, logger, containerName, filters, limit, afterCursor, paginatedVersion = false, order) {
6
+ const filterExpression = buildFilterExpression(filters);
7
+ const queryDefinition = `SELECT * FROM c ${filterExpression !== '' ? `WHERE ${filterExpression}` : filterExpression}`;
8
+ const queryWithOrder = queryDefinition + buildOrderExpression(order);
9
+ let finalQuery = queryWithOrder;
10
+ if (paginatedVersion && limit) {
11
+ finalQuery += ` OFFSET ${(afterCursor === null || afterCursor === void 0 ? void 0 : afterCursor.id) || 0} LIMIT ${limit} `;
12
+ }
13
+ else {
14
+ if (limit) {
15
+ finalQuery += ` OFFSET 0 LIMIT ${limit} `;
16
+ }
17
+ }
18
+ const querySpec = {
19
+ query: finalQuery,
20
+ parameters: buildExpressionAttributeValues(filters),
21
+ };
22
+ logger.debug('Running search with the following params: \n', querySpec);
23
+ const { resources } = await cosmosDb
24
+ .database(config.resourceNames.applicationStack)
25
+ .container(containerName)
26
+ .items.query(querySpec)
27
+ .fetchAll();
28
+ if (paginatedVersion) {
29
+ return {
30
+ items: resources !== null && resources !== void 0 ? resources : [],
31
+ count: resources.length,
32
+ cursor: { id: ((limit ? limit : 1) + ((afterCursor === null || afterCursor === void 0 ? void 0 : afterCursor.id) ? parseInt(afterCursor === null || afterCursor === void 0 ? void 0 : afterCursor.id) : 0)).toString() },
33
+ };
34
+ }
35
+ else {
36
+ return resources !== null && resources !== void 0 ? resources : [];
37
+ }
38
+ }
39
+ exports.search = search;
40
+ function buildFilterExpression(filters, usedPlaceholders = []) {
41
+ return Object.entries(filters)
42
+ .map(([propName, filter]) => {
43
+ switch (propName) {
44
+ case 'not':
45
+ return `NOT (${buildFilterExpression(filter, usedPlaceholders)})`;
46
+ case 'and':
47
+ case 'or':
48
+ return `(${filter
49
+ .map((arrayFilter) => buildFilterExpression(arrayFilter, usedPlaceholders))
50
+ .join(` ${propName} `)})`;
51
+ default:
52
+ return buildOperation(propName, filter, usedPlaceholders);
53
+ }
54
+ })
55
+ .join(' AND ');
56
+ }
57
+ function buildOperation(propName, filter = {}, usedPlaceholders, nested) {
58
+ const holder = placeholderBuilderFor(propName, usedPlaceholders);
59
+ propName = nested ? `${nested}.c["${propName}"]` : `c["${propName}"]`;
60
+ return Object.entries(filter)
61
+ .map(([operation, value], index) => {
62
+ switch (operation) {
63
+ case 'eq':
64
+ return `${propName} = ${holder(index)}`;
65
+ case 'ne':
66
+ return `${propName} <> ${holder(index)}`;
67
+ case 'lt':
68
+ return `${propName} < ${holder(index)}`;
69
+ case 'gt':
70
+ return `${propName} > ${holder(index)}`;
71
+ case 'gte':
72
+ return `${propName} >= ${holder(index)}`;
73
+ case 'lte':
74
+ return `${propName} <= ${holder(index)}`;
75
+ case 'in':
76
+ return `${propName} IN (${value.map((value, subIndex) => holder(index, subIndex)).join(',')})`;
77
+ case 'contains':
78
+ return `CONTAINS(${propName}, ${holder(index)})`;
79
+ case 'beginsWith':
80
+ return `STARTSWITH(${propName}, ${holder(index)})`;
81
+ case 'includes': {
82
+ return `ARRAY_CONTAINS(${propName}, ${holder(index)}, true)`;
83
+ }
84
+ default:
85
+ if (typeof value === 'object') {
86
+ return buildOperation(operation, value, usedPlaceholders, propName);
87
+ }
88
+ throw new framework_types_1.InvalidParameterError(`Operator "${operation}" is not supported`);
89
+ }
90
+ })
91
+ .join(' AND ');
92
+ }
93
+ function placeholderBuilderFor(propName, usedPlaceholders) {
94
+ return (valueIndex, valueSubIndex) => {
95
+ const placeholder = `@${propName}_${valueIndex}` + (typeof valueSubIndex === 'number' ? `_${valueSubIndex}` : '');
96
+ if (usedPlaceholders.includes(placeholder))
97
+ return placeholderBuilderFor(propName, usedPlaceholders)(valueIndex + 1);
98
+ usedPlaceholders.push(placeholder);
99
+ return placeholder;
100
+ };
101
+ }
102
+ function buildExpressionAttributeValues(filters, usedPlaceholders = []) {
103
+ let attributeValues = [];
104
+ Object.entries(filters).forEach(([propName]) => {
105
+ switch (propName) {
106
+ case 'not':
107
+ attributeValues = [
108
+ ...attributeValues,
109
+ ...buildExpressionAttributeValues(filters[propName], usedPlaceholders),
110
+ ];
111
+ break;
112
+ case 'and':
113
+ case 'or':
114
+ for (const filter of filters[propName]) {
115
+ attributeValues = [
116
+ ...attributeValues,
117
+ ...buildExpressionAttributeValues(filter, usedPlaceholders),
118
+ ];
119
+ }
120
+ break;
121
+ default:
122
+ attributeValues = [...attributeValues, ...buildAttributeValue(propName, filters[propName], usedPlaceholders)];
123
+ break;
124
+ }
125
+ });
126
+ return attributeValues;
127
+ }
128
+ function buildAttributeValue(propName, filter = {}, usedPlaceholders) {
129
+ let attributeValues = [];
130
+ const holder = placeholderBuilderFor(propName, usedPlaceholders);
131
+ Object.entries(filter).forEach(([key, value], index) => {
132
+ if (Array.isArray(value)) {
133
+ value.forEach((element, subIndex) => {
134
+ attributeValues.push({
135
+ name: holder(index, subIndex),
136
+ value: element,
137
+ });
138
+ });
139
+ }
140
+ else if (typeof value === 'object' && key !== 'includes') {
141
+ attributeValues = [...attributeValues, ...buildExpressionAttributeValues({ [key]: value }, usedPlaceholders)];
142
+ }
143
+ else {
144
+ attributeValues.push({
145
+ name: holder(index),
146
+ value: value,
147
+ });
148
+ }
149
+ });
150
+ return attributeValues;
151
+ }
152
+ function buildOrderExpression(order) {
153
+ if (!order || !Object.keys(order).length)
154
+ return '';
155
+ let orderQuery = ' ORDER BY';
156
+ orderQuery += Object.entries(order)
157
+ .map(([key, value]) => ` c.${key} ${value}`)
158
+ .join(',');
159
+ return orderQuery;
160
+ }
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ const constants_1 = require("./constants");
10
10
  const read_model_adapter_1 = require("./library/read-model-adapter");
11
11
  const searcher_adapter_1 = require("./library/searcher-adapter");
12
12
  const scheduled_adapter_1 = require("./library/scheduled-adapter");
13
+ const events_searcher_adapter_1 = require("./library/events-searcher-adapter");
13
14
  let cosmosClient;
14
15
  if (typeof process.env[constants_1.environmentVarNames.cosmosDbConnectionString] === 'undefined') {
15
16
  cosmosClient = {};
@@ -32,7 +33,7 @@ const Provider = (rockets) => ({
32
33
  store: events_adapter_1.storeEvents.bind(null, cosmosClient),
33
34
  forEntitySince: events_adapter_1.readEntityEventsSince.bind(null, cosmosClient),
34
35
  latestEntitySnapshot: events_adapter_1.readEntityLatestSnapshot.bind(null, cosmosClient),
35
- search: undefined,
36
+ search: events_searcher_adapter_1.searchEvents.bind(null, cosmosClient),
36
37
  },
37
38
  // ProviderReadModelsLibrary
38
39
  readModels: {
@@ -61,8 +61,8 @@ async function readEntityLatestSnapshot(cosmosDb, config, logger, entityTypeName
61
61
  exports.readEntityLatestSnapshot = readEntityLatestSnapshot;
62
62
  async function storeEvents(cosmosDb, eventEnvelopes, config, logger) {
63
63
  logger.debug('[EventsAdapter#storeEvents] Storing EventEnvelopes with eventEnvelopes:', eventEnvelopes);
64
- const events = eventEnvelopes.map((eventEnvelope) => {
65
- return cosmosDb
64
+ for (const eventEnvelope of eventEnvelopes) {
65
+ await cosmosDb
66
66
  .database(config.resourceNames.applicationStack)
67
67
  .container(config.resourceNames.eventsStore)
68
68
  .items.create({
@@ -70,8 +70,7 @@ async function storeEvents(cosmosDb, eventEnvelopes, config, logger) {
70
70
  [constants_1.eventsStoreAttributes.partitionKey]: partition_keys_1.partitionKeyForEvent(eventEnvelope.entityTypeName, eventEnvelope.entityID, eventEnvelope.kind),
71
71
  [constants_1.eventsStoreAttributes.sortKey]: new Date().toISOString(),
72
72
  });
73
- });
74
- await Promise.all(events);
73
+ }
75
74
  logger.debug('[EventsAdapter#storeEvents] EventEnvelope stored');
76
75
  }
77
76
  exports.storeEvents = storeEvents;
@@ -0,0 +1,3 @@
1
+ import { BoosterConfig, EventSearchParameters, EventSearchResponse, Logger } from '@boostercloud/framework-types';
2
+ import { CosmosClient } from '@azure/cosmos';
3
+ export declare function searchEvents(cosmosDb: CosmosClient, config: BoosterConfig, logger: Logger, parameters: EventSearchParameters): Promise<Array<EventSearchResponse>>;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.searchEvents = void 0;
4
+ const query_helper_1 = require("../helpers/query-helper");
5
+ const events_searcher_builder_1 = require("./events-searcher-builder");
6
+ async function searchEvents(cosmosDb, config, logger, parameters) {
7
+ logger.debug('Initiating an events search. Filters: ', parameters);
8
+ const eventStore = config.resourceNames.eventsStore;
9
+ const timeFilterQuery = events_searcher_builder_1.buildFiltersForByTime(parameters.from, parameters.to);
10
+ const eventFilterQuery = events_searcher_builder_1.buildFiltersForByFilters(parameters);
11
+ const filterQuery = { ...eventFilterQuery, ...timeFilterQuery, kind: { eq: 'event' } };
12
+ const result = (await query_helper_1.search(cosmosDb, config, logger, eventStore, filterQuery, parameters.limit, undefined, undefined, {
13
+ createdAt: 'DESC',
14
+ }));
15
+ const eventEnvelopes = events_searcher_builder_1.resultToEventSearchResponse(result);
16
+ logger.debug('Events search result: ', eventEnvelopes);
17
+ return eventEnvelopes;
18
+ }
19
+ exports.searchEvents = searchEvents;
@@ -0,0 +1,11 @@
1
+ import { EventParametersFilterByEntity, EventParametersFilterByType, EventSearchResponse, FilterFor } from '@boostercloud/framework-types';
2
+ interface QueryFields {
3
+ createdAt: string;
4
+ entityTypeName_entityID_kind: string;
5
+ entityTypeName: string;
6
+ typeName: string;
7
+ }
8
+ export declare function buildFiltersForByTime(fromValue?: string, toValue?: string): FilterFor<QueryFields>;
9
+ export declare function buildFiltersForByFilters(parameters: EventParametersFilterByEntity | EventParametersFilterByType): FilterFor<QueryFields>;
10
+ export declare function resultToEventSearchResponse(result: any[]): Array<EventSearchResponse>;
11
+ export {};
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resultToEventSearchResponse = exports.buildFiltersForByFilters = exports.buildFiltersForByTime = void 0;
4
+ function buildFiltersForByTime(fromValue, toValue) {
5
+ if (fromValue && toValue) {
6
+ return {
7
+ createdAt: { gte: fromValue, lte: toValue },
8
+ };
9
+ }
10
+ else if (fromValue) {
11
+ return {
12
+ createdAt: { gte: fromValue },
13
+ };
14
+ }
15
+ else if (toValue) {
16
+ return {
17
+ createdAt: { lte: toValue },
18
+ };
19
+ }
20
+ return {};
21
+ }
22
+ exports.buildFiltersForByTime = buildFiltersForByTime;
23
+ function buildFiltersForByFilters(parameters) {
24
+ if ('entity' in parameters) {
25
+ if (parameters.entityID) {
26
+ return buildByEntityAndID(parameters.entity, parameters.entityID);
27
+ }
28
+ return buildByEntity(parameters.entity);
29
+ }
30
+ else if ('type' in parameters) {
31
+ return buildByType(parameters.type);
32
+ }
33
+ else {
34
+ throw new Error('Invalid search event query. It is neither an search by "entity" nor a search by "type"');
35
+ }
36
+ }
37
+ exports.buildFiltersForByFilters = buildFiltersForByFilters;
38
+ function resultToEventSearchResponse(result) {
39
+ if (!result || result.length === 0)
40
+ return [];
41
+ const eventSearchResult = result.map((item) => {
42
+ return {
43
+ type: item.typeName,
44
+ entity: item.entityTypeName,
45
+ entityID: item.entityID,
46
+ requestID: item.requestID,
47
+ user: item.currentUser,
48
+ createdAt: item.createdAt,
49
+ value: item.value,
50
+ };
51
+ });
52
+ return eventSearchResult !== null && eventSearchResult !== void 0 ? eventSearchResult : [];
53
+ }
54
+ exports.resultToEventSearchResponse = resultToEventSearchResponse;
55
+ function buildByEntityAndID(entityValue, entityIdValue) {
56
+ const value = partitionKeyForEvent(entityValue, entityIdValue);
57
+ return {
58
+ entityTypeName_entityID_kind: { eq: value },
59
+ };
60
+ }
61
+ function buildByEntity(entityValue) {
62
+ return {
63
+ entityTypeName: { eq: entityValue },
64
+ };
65
+ }
66
+ function buildByType(typeValue) {
67
+ return {
68
+ typeName: { eq: typeValue },
69
+ };
70
+ }
71
+ function partitionKeyForEvent(entityTypeName, entityID, kind = 'event') {
72
+ return `${entityTypeName}-${entityID}-${kind}`;
73
+ }
@@ -1,2 +1,3 @@
1
1
  import { EventEnvelope, UUID } from '@boostercloud/framework-types';
2
2
  export declare function partitionKeyForEvent(entityTypeName: string, entityID: UUID, kind?: EventEnvelope['kind']): string;
3
+ export declare function partitionKeyForIndexByEntity(entityTypeName: string, kind: EventEnvelope['kind']): string;
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.partitionKeyForEvent = void 0;
3
+ exports.partitionKeyForIndexByEntity = exports.partitionKeyForEvent = void 0;
4
4
  function partitionKeyForEvent(entityTypeName, entityID, kind = 'event') {
5
5
  return `${entityTypeName}-${entityID}-${kind}`;
6
6
  }
7
7
  exports.partitionKeyForEvent = partitionKeyForEvent;
8
+ function partitionKeyForIndexByEntity(entityTypeName, kind) {
9
+ return `${entityTypeName}-${kind}`;
10
+ }
11
+ exports.partitionKeyForIndexByEntity = partitionKeyForIndexByEntity;
@@ -24,11 +24,12 @@ async function fetchReadModel(db, config, logger, readModelName, readModelID) {
24
24
  }
25
25
  exports.fetchReadModel = fetchReadModel;
26
26
  async function insertReadModel(logger, readModel, db, config, readModelName) {
27
- const itemModel = {
28
- ...readModel,
29
- id: readModel.id.toString(),
30
- };
27
+ var _a;
31
28
  try {
29
+ const itemModel = {
30
+ ...readModel,
31
+ id: (_a = readModel === null || readModel === void 0 ? void 0 : readModel.id) === null || _a === void 0 ? void 0 : _a.toString(),
32
+ };
32
33
  await db
33
34
  .database(config.resourceNames.applicationStack)
34
35
  .container(config.resourceNames.forReadModel(readModelName))
@@ -37,10 +38,11 @@ async function insertReadModel(logger, readModel, db, config, readModelName) {
37
38
  }
38
39
  catch (err) {
39
40
  // In case of conflict (The ID provided for a resource on a PUT or POST operation has been taken by an existing resource) we should retry it
40
- if (err.code == constants_1.AZURE_CONFLICT_ERROR_CODE) {
41
+ if ((err === null || err === void 0 ? void 0 : err.code) == constants_1.AZURE_CONFLICT_ERROR_CODE) {
41
42
  logger.debug('[ReadModelAdapter#insertReadModel] Read model insert failed with a conflict failure');
42
- throw new framework_types_1.OptimisticConcurrencyUnexpectedVersionError(err.message);
43
+ throw new framework_types_1.OptimisticConcurrencyUnexpectedVersionError(err === null || err === void 0 ? void 0 : err.message);
43
44
  }
45
+ logger.error('[ReadModelAdapter#insertReadModel] Read model insert failed without a conflict failure');
44
46
  throw err;
45
47
  }
46
48
  }
@@ -59,20 +61,21 @@ async function updateReadModel(readModel, db, config, readModelName, logger) {
59
61
  }
60
62
  catch (err) {
61
63
  // If there is a precondition failure then we should retry it
62
- if (err.code == constants_1.AZURE_PRECONDITION_FAILED_ERROR) {
64
+ if ((err === null || err === void 0 ? void 0 : err.code) == constants_1.AZURE_PRECONDITION_FAILED_ERROR) {
63
65
  logger.debug('[ReadModelAdapter#updateReadModel] Read model update failed with a pre-condition failure');
64
- throw new framework_types_1.OptimisticConcurrencyUnexpectedVersionError(err.message);
66
+ throw new framework_types_1.OptimisticConcurrencyUnexpectedVersionError(err === null || err === void 0 ? void 0 : err.message);
65
67
  }
68
+ logger.error('[ReadModelAdapter#updateReadModel] Read model update failed without a pre-condition failure');
66
69
  throw err;
67
70
  }
68
71
  }
69
72
  async function storeReadModel(db, config, logger, readModelName, readModel) {
70
73
  var _a, _b;
71
- const version = (_b = (_a = readModel.boosterMetadata) === null || _a === void 0 ? void 0 : _a.version) !== null && _b !== void 0 ? _b : 0;
72
- if (version === 0) {
73
- await insertReadModel(logger, readModel, db, config, readModelName);
74
+ const version = (_b = (_a = readModel.boosterMetadata) === null || _a === void 0 ? void 0 : _a.version) !== null && _b !== void 0 ? _b : 1;
75
+ if (version === 1) {
76
+ return await insertReadModel(logger, readModel, db, config, readModelName);
74
77
  }
75
- await updateReadModel(readModel, db, config, readModelName, logger);
78
+ return await updateReadModel(readModel, db, config, readModelName, logger);
76
79
  }
77
80
  exports.storeReadModel = storeReadModel;
78
81
  async function deleteReadModel(db, config, logger, readModelName, readModel) {
@@ -1,142 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.searchReadModel = void 0;
4
- const framework_types_1 = require("@boostercloud/framework-types");
4
+ const query_helper_1 = require("../helpers/query-helper");
5
5
  async function searchReadModel(cosmosDb, config, logger, readModelName, filters, limit, afterCursor, paginatedVersion = false) {
6
- const filterExpression = buildFilterExpression(filters);
7
- const queryDefinition = `SELECT * FROM c ${filterExpression !== '' ? `WHERE ${filterExpression}` : filterExpression}`;
8
- const queryWithPagination = queryDefinition + (paginatedVersion && limit ? ` OFFSET ${(afterCursor === null || afterCursor === void 0 ? void 0 : afterCursor.id) || 0} LIMIT ${limit}` : '');
9
- const querySpec = {
10
- query: queryWithPagination,
11
- parameters: buildExpressionAttributeValues(filters),
12
- };
13
- logger.debug('Running search with the following params: \n', querySpec);
14
- const { resources } = await cosmosDb
15
- .database(config.resourceNames.applicationStack)
16
- .container(config.resourceNames.forReadModel(readModelName))
17
- .items.query(querySpec)
18
- .fetchAll();
19
- if (paginatedVersion) {
20
- return {
21
- items: resources !== null && resources !== void 0 ? resources : [],
22
- count: resources.length,
23
- cursor: { id: ((limit ? limit : 1) + ((afterCursor === null || afterCursor === void 0 ? void 0 : afterCursor.id) ? parseInt(afterCursor === null || afterCursor === void 0 ? void 0 : afterCursor.id) : 0)).toString() },
24
- };
25
- }
26
- else {
27
- return resources !== null && resources !== void 0 ? resources : [];
28
- }
6
+ return await query_helper_1.search(cosmosDb, config, logger, config.resourceNames.forReadModel(readModelName), filters, limit, afterCursor, paginatedVersion);
29
7
  }
30
8
  exports.searchReadModel = searchReadModel;
31
- function buildFilterExpression(filters, usedPlaceholders = []) {
32
- return Object.entries(filters)
33
- .map(([propName, filter]) => {
34
- switch (propName) {
35
- case 'not':
36
- return `NOT (${buildFilterExpression(filter, usedPlaceholders)})`;
37
- case 'and':
38
- case 'or':
39
- return `(${filter
40
- .map((arrayFilter) => buildFilterExpression(arrayFilter, usedPlaceholders))
41
- .join(` ${propName} `)})`;
42
- default:
43
- return buildOperation(propName, filter, usedPlaceholders);
44
- }
45
- })
46
- .join(' AND ');
47
- }
48
- function buildOperation(propName, filter = {}, usedPlaceholders, nested) {
49
- const holder = placeholderBuilderFor(propName, usedPlaceholders);
50
- propName = nested ? `${nested}.c["${propName}"]` : `c["${propName}"]`;
51
- return Object.entries(filter)
52
- .map(([operation, value], index) => {
53
- switch (operation) {
54
- case 'eq':
55
- return `${propName} = ${holder(index)}`;
56
- case 'ne':
57
- return `${propName} <> ${holder(index)}`;
58
- case 'lt':
59
- return `${propName} < ${holder(index)}`;
60
- case 'gt':
61
- return `${propName} > ${holder(index)}`;
62
- case 'gte':
63
- return `${propName} >= ${holder(index)}`;
64
- case 'lte':
65
- return `${propName} <= ${holder(index)}`;
66
- case 'in':
67
- return `${propName} IN (${value.map((value, subIndex) => holder(index, subIndex)).join(',')})`;
68
- case 'contains':
69
- return `CONTAINS(${propName}, ${holder(index)})`;
70
- case 'beginsWith':
71
- return `STARTSWITH(${propName}, ${holder(index)})`;
72
- case 'includes': {
73
- return `ARRAY_CONTAINS(${propName}, ${holder(index)}, true)`;
74
- }
75
- default:
76
- if (typeof value === 'object') {
77
- return buildOperation(operation, value, usedPlaceholders, propName);
78
- }
79
- throw new framework_types_1.InvalidParameterError(`Operator "${operation}" is not supported`);
80
- }
81
- })
82
- .join(' AND ');
83
- }
84
- function placeholderBuilderFor(propName, usedPlaceholders) {
85
- return (valueIndex, valueSubIndex) => {
86
- const placeholder = `@${propName}_${valueIndex}` + (typeof valueSubIndex === 'number' ? `_${valueSubIndex}` : '');
87
- if (usedPlaceholders.includes(placeholder))
88
- return placeholderBuilderFor(propName, usedPlaceholders)(valueIndex + 1);
89
- usedPlaceholders.push(placeholder);
90
- return placeholder;
91
- };
92
- }
93
- function buildExpressionAttributeValues(filters, usedPlaceholders = []) {
94
- let attributeValues = [];
95
- Object.entries(filters).forEach(([propName]) => {
96
- switch (propName) {
97
- case 'not':
98
- attributeValues = [
99
- ...attributeValues,
100
- ...buildExpressionAttributeValues(filters[propName], usedPlaceholders),
101
- ];
102
- break;
103
- case 'and':
104
- case 'or':
105
- for (const filter of filters[propName]) {
106
- attributeValues = [
107
- ...attributeValues,
108
- ...buildExpressionAttributeValues(filter, usedPlaceholders),
109
- ];
110
- }
111
- break;
112
- default:
113
- attributeValues = [...attributeValues, ...buildAttributeValue(propName, filters[propName], usedPlaceholders)];
114
- break;
115
- }
116
- });
117
- return attributeValues;
118
- }
119
- function buildAttributeValue(propName, filter = {}, usedPlaceholders) {
120
- let attributeValues = [];
121
- const holder = placeholderBuilderFor(propName, usedPlaceholders);
122
- Object.entries(filter).forEach(([key, value], index) => {
123
- if (Array.isArray(value)) {
124
- value.forEach((element, subIndex) => {
125
- attributeValues.push({
126
- name: holder(index, subIndex),
127
- value: element,
128
- });
129
- });
130
- }
131
- else if (typeof value === 'object' && key !== 'includes') {
132
- attributeValues = [...attributeValues, ...buildExpressionAttributeValues({ [key]: value }, usedPlaceholders)];
133
- }
134
- else {
135
- attributeValues.push({
136
- name: holder(index),
137
- value: value,
138
- });
139
- }
140
- });
141
- return attributeValues;
142
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boostercloud/framework-provider-azure",
3
- "version": "0.26.1",
3
+ "version": "0.26.4",
4
4
  "description": "Handle Booster's integration with Azure",
5
5
  "keywords": [
6
6
  "framework-provider-azure"
@@ -22,7 +22,7 @@
22
22
  "dependencies": {
23
23
  "@azure/cosmos": "3.7.3",
24
24
  "@azure/functions": "^1.2.2",
25
- "@boostercloud/framework-types": "^0.26.1",
25
+ "@boostercloud/framework-types": "^0.26.4",
26
26
  "chai": "4.2.0",
27
27
  "chai-as-promised": "7.1.1",
28
28
  "mocha": "8.4.0",
@@ -41,5 +41,5 @@
41
41
  "bugs": {
42
42
  "url": "https://github.com/boostercloud/booster/issues"
43
43
  },
44
- "gitHead": "b1bb4bc8a235989e9322800ae07dc8667ebfd546"
44
+ "gitHead": "e00c0c8615f6edc8926226495993ecdd35907e9b"
45
45
  }