@boostercloud/framework-provider-azure 2.17.0 → 2.18.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,4 @@
1
- import { CosmosClient } from '@azure/cosmos';
1
+ import { CosmosClient, ItemDefinition } from '@azure/cosmos';
2
2
  import { BoosterConfig, FilterFor, ProjectionFor, ReadModelListResult, SortFor } from '@boostercloud/framework-types';
3
+ export declare function replaceOrDeleteItem(cosmosDb: CosmosClient, container: string, config: BoosterConfig, id: string, partitionKey: string, newValue?: ItemDefinition): Promise<void>;
3
4
  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>>;
@@ -1,8 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.search = void 0;
3
+ exports.search = exports.replaceOrDeleteItem = void 0;
4
4
  const framework_types_1 = require("@boostercloud/framework-types");
5
5
  const framework_common_helpers_1 = require("@boostercloud/framework-common-helpers");
6
+ async function replaceOrDeleteItem(cosmosDb, container, config, id, partitionKey, newValue) {
7
+ if (newValue) {
8
+ await cosmosDb
9
+ .database(config.resourceNames.applicationStack)
10
+ .container(container)
11
+ .item(id, partitionKey)
12
+ .replace(newValue);
13
+ }
14
+ else {
15
+ await cosmosDb.database(config.resourceNames.applicationStack).container(container).item(id, partitionKey).delete();
16
+ }
17
+ }
18
+ exports.replaceOrDeleteItem = replaceOrDeleteItem;
6
19
  async function search(cosmosDb, config, containerName, filters, limit, afterCursor, paginatedVersion = false, order, projections = '*') {
7
20
  const logger = (0, framework_common_helpers_1.getLogger)(config, 'query-helper#search');
8
21
  const filterExpression = buildFilterExpression(filters);
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ const events_stream_producer_adapter_1 = require("./library/events-stream-produc
19
19
  const event_hubs_1 = require("@azure/event-hubs");
20
20
  const events_stream_consumer_adapter_1 = require("./library/events-stream-consumer-adapter");
21
21
  const health_adapter_1 = require("./library/health-adapter");
22
+ const event_delete_adapter_1 = require("./library/event-delete-adapter");
22
23
  const events_store_adapter_1 = require("./library/events-store-adapter");
23
24
  let cosmosClient;
24
25
  if (typeof process.env[constants_1.environmentVarNames.cosmosDbConnectionString] === 'undefined') {
@@ -75,6 +76,10 @@ const Provider = (rockets) => ({
75
76
  search: events_searcher_adapter_1.searchEvents.bind(null, cosmosClient),
76
77
  searchEntitiesIDs: events_searcher_adapter_1.searchEntitiesIds.bind(null, cosmosClient),
77
78
  storeDispatched: events_adapter_1.storeDispatchedEvent.bind(null, cosmosClient),
79
+ findDeletableEvent: event_delete_adapter_1.findDeletableEvent.bind(null, cosmosClient),
80
+ findDeletableSnapshot: event_delete_adapter_1.findDeletableSnapshot.bind(null, cosmosClient),
81
+ deleteEvent: event_delete_adapter_1.deleteEvent.bind(null, cosmosClient),
82
+ deleteSnapshot: event_delete_adapter_1.deleteSnapshot.bind(null, cosmosClient),
78
83
  },
79
84
  // ProviderReadModelsLibrary
80
85
  readModels: {
@@ -0,0 +1,6 @@
1
+ import { BoosterConfig, EventDeleteParameters, SnapshotDeleteParameters, EventEnvelopeFromDatabase, EntitySnapshotEnvelopeFromDatabase } from '@boostercloud/framework-types';
2
+ import { CosmosClient } from '@azure/cosmos';
3
+ export declare function findDeletableEvent(cosmosDb: CosmosClient, config: BoosterConfig, parameters: EventDeleteParameters): Promise<Array<EventEnvelopeFromDatabase>>;
4
+ export declare function findDeletableSnapshot(cosmosDb: CosmosClient, config: BoosterConfig, parameters: SnapshotDeleteParameters): Promise<Array<EntitySnapshotEnvelopeFromDatabase>>;
5
+ export declare function deleteEvent(cosmosDb: CosmosClient, config: BoosterConfig, events: Array<EventEnvelopeFromDatabase>): Promise<void>;
6
+ export declare function deleteSnapshot(cosmosDb: CosmosClient, config: BoosterConfig, snapshots: Array<EntitySnapshotEnvelopeFromDatabase>): Promise<void>;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deleteSnapshot = exports.deleteEvent = exports.findDeletableSnapshot = exports.findDeletableEvent = void 0;
4
+ const framework_common_helpers_1 = require("@boostercloud/framework-common-helpers");
5
+ const query_helper_1 = require("../helpers/query-helper");
6
+ async function findDeletableEvent(cosmosDb, config, parameters) {
7
+ const logger = (0, framework_common_helpers_1.getLogger)(config, 'events-delete-adapter#findDeletableEvent');
8
+ const stringifyParameters = JSON.stringify(parameters);
9
+ logger.debug(`Initiating a deletable event search for ${stringifyParameters}`);
10
+ const eventStore = config.resourceNames.eventsStore;
11
+ const filter = buildDeleteEventFilter(parameters.entityTypeName, parameters.entityID, parameters.createdAt);
12
+ const result = (await (0, query_helper_1.search)(cosmosDb, config, eventStore, filter));
13
+ logger.debug(`Finished deletable event search for ${stringifyParameters}`);
14
+ return result;
15
+ }
16
+ exports.findDeletableEvent = findDeletableEvent;
17
+ async function findDeletableSnapshot(cosmosDb, config, parameters) {
18
+ const logger = (0, framework_common_helpers_1.getLogger)(config, 'events-delete-adapter#findDeletableSnapshot');
19
+ const stringifyParameters = JSON.stringify(parameters);
20
+ logger.debug(`Initiating a deletable snapshot search for ${stringifyParameters}`);
21
+ const eventStore = config.resourceNames.eventsStore;
22
+ const filter = buildDeleteEntityFilter(parameters.entityTypeName, parameters.entityID, parameters.createdAt);
23
+ const result = (await (0, query_helper_1.search)(cosmosDb, config, eventStore, filter));
24
+ logger.debug(`Finished deletable snapshot search for ${stringifyParameters}`);
25
+ return result;
26
+ }
27
+ exports.findDeletableSnapshot = findDeletableSnapshot;
28
+ async function deleteEvent(cosmosDb, config, events) {
29
+ const logger = (0, framework_common_helpers_1.getLogger)(config, 'events-delete-adapter#deleteEvent');
30
+ const stringifyParameters = JSON.stringify(events);
31
+ logger.debug(`Initiating an event delete for ${stringifyParameters}`);
32
+ const eventStore = config.resourceNames.eventsStore;
33
+ if (!events || events.length === 0) {
34
+ logger.warn('Could not find events to delete');
35
+ return;
36
+ }
37
+ for (const event of events) {
38
+ const newEvent = buildNewEvent(event);
39
+ const partitionKey = partitionKeyBuilder(event.entityTypeName, event.entityID, 'event');
40
+ await (0, query_helper_1.replaceOrDeleteItem)(cosmosDb, eventStore, config, event.id, partitionKey, newEvent);
41
+ }
42
+ logger.debug(`Finished event delete for ${stringifyParameters}`);
43
+ }
44
+ exports.deleteEvent = deleteEvent;
45
+ async function deleteSnapshot(cosmosDb, config, snapshots) {
46
+ const logger = (0, framework_common_helpers_1.getLogger)(config, 'events-delete-adapter#deleteSnapshot');
47
+ const stringifyParameters = JSON.stringify(snapshots);
48
+ logger.debug(`Initiating a snapshot delete for ${stringifyParameters}`);
49
+ const eventStore = config.resourceNames.eventsStore;
50
+ if (!snapshots || snapshots.length === 0) {
51
+ logger.warn('Could not find snapshot to delete');
52
+ return;
53
+ }
54
+ for (const snapshot of snapshots) {
55
+ const partitionKey = partitionKeyBuilder(snapshot.entityTypeName, snapshot.entityID, 'snapshot');
56
+ await (0, query_helper_1.replaceOrDeleteItem)(cosmosDb, eventStore, config, snapshot.id, partitionKey);
57
+ }
58
+ logger.debug(`Finished snapshot delete for ${stringifyParameters}`);
59
+ }
60
+ exports.deleteSnapshot = deleteSnapshot;
61
+ function buildNewEvent(existingEvent) {
62
+ return {
63
+ ...existingEvent,
64
+ deletedAt: new Date().toISOString(),
65
+ value: {},
66
+ };
67
+ }
68
+ function buildDeleteEventFilter(entityTypeName, entityId, createdAt) {
69
+ const value = `${entityTypeName}-${entityId}-event`;
70
+ return {
71
+ entityTypeName_entityID_kind: { eq: value },
72
+ createdAt: { eq: createdAt },
73
+ kind: { eq: 'event' },
74
+ deletedAt: { isDefined: false },
75
+ };
76
+ }
77
+ function buildDeleteEntityFilter(entityTypeName, entityId, createdAt) {
78
+ const value = `${entityTypeName}-${entityId}-snapshot`;
79
+ return {
80
+ entityTypeName_entityID_kind: { eq: value },
81
+ kind: { eq: 'snapshot' },
82
+ createdAt: { eq: createdAt },
83
+ deletedAt: { isDefined: false },
84
+ };
85
+ }
86
+ function partitionKeyBuilder(entityTypeName, entityID, kind) {
87
+ return `${entityTypeName}-${entityID}-${kind}`;
88
+ }
@@ -10,10 +10,13 @@ function rawEventsToEnvelopes(context) {
10
10
  }
11
11
  exports.rawEventsToEnvelopes = rawEventsToEnvelopes;
12
12
  async function readEntityEventsSince(cosmosDb, config, entityTypeName, entityID, since) {
13
+ const logger = (0, framework_common_helpers_1.getLogger)(config, 'events-adapter#readEntityEventsSince');
13
14
  const fromTime = since ? since : originOfTime;
14
15
  const querySpec = {
15
16
  query: `SELECT * FROM c WHERE c["${constants_1.eventsStoreAttributes.partitionKey}"] = @partitionKey ` +
16
- `AND c["${constants_1.eventsStoreAttributes.sortKey}"] > @fromTime ORDER BY c["${constants_1.eventsStoreAttributes.sortKey}"] ASC`,
17
+ `AND c["${constants_1.eventsStoreAttributes.sortKey}"] > @fromTime ` +
18
+ 'AND NOT IS_DEFINED(c["deletedAt"]) ' +
19
+ `ORDER BY c["${constants_1.eventsStoreAttributes.sortKey}"] ASC`,
17
20
  parameters: [
18
21
  {
19
22
  name: '@partitionKey',
@@ -30,6 +33,7 @@ async function readEntityEventsSince(cosmosDb, config, entityTypeName, entityID,
30
33
  .container(config.resourceNames.eventsStore)
31
34
  .items.query(querySpec)
32
35
  .fetchAll();
36
+ logger.debug(`Loaded events for entity ${entityTypeName} with ID ${entityID} with result:`, resources);
33
37
  return resources;
34
38
  }
35
39
  exports.readEntityEventsSince = readEntityEventsSince;
@@ -22,7 +22,11 @@ exports.searchEvents = searchEvents;
22
22
  async function searchEntitiesIds(cosmosDb, config, limit, afterCursor, entityTypeName) {
23
23
  const logger = (0, framework_common_helpers_1.getLogger)(config, 'events-searcher-adapter#searchEntitiesIds');
24
24
  logger.debug(`Initiating a paginated events search. limit: ${limit}, afterCursor: ${JSON.stringify(afterCursor)}, entityTypeName: ${entityTypeName}`);
25
- const filterQuery = { kind: { eq: 'event' }, entityTypeName: { eq: entityTypeName } };
25
+ const filterQuery = {
26
+ kind: { eq: 'event' },
27
+ entityTypeName: { eq: entityTypeName },
28
+ deletedAt: { isDefined: false },
29
+ };
26
30
  const eventStore = config.resourceNames.eventsStore;
27
31
  const result = (await (0, query_helper_1.search)(cosmosDb, config, eventStore, filterQuery, limit, afterCursor, true, undefined, 'DISTINCT c.entityID'));
28
32
  logger.debug('Unique events search result', result);
@@ -47,6 +47,7 @@ function resultToEventSearchResponse(result) {
47
47
  user: item.currentUser,
48
48
  createdAt: item.createdAt,
49
49
  value: item.value,
50
+ deletedAt: item.deletedAt,
50
51
  };
51
52
  });
52
53
  return eventSearchResult !== null && eventSearchResult !== void 0 ? eventSearchResult : [];
@@ -86,13 +86,18 @@ async function storeReadModel(db, config, readModelName, readModel) {
86
86
  exports.storeReadModel = storeReadModel;
87
87
  async function deleteReadModel(db, config, readModelName, readModel) {
88
88
  const logger = (0, framework_common_helpers_1.getLogger)(config, 'read-model-adapter#deleteReadModel');
89
- logger.debug(`[ReadModelAdapter#deleteReadModel] Entering to Read model deleted. ID = ${readModel.id}`);
90
- await db
91
- .database(config.resourceNames.applicationStack)
92
- .container(config.resourceNames.forReadModel(readModelName))
93
- .item(readModel.id, readModel.id)
94
- .delete();
95
- logger.debug(`[ReadModelAdapter#deleteReadModel] Read model deleted. ID = ${readModel.id}`);
89
+ logger.debug(`Entering to Read model deleted. ${readModelName} ID = ${readModel.id}`);
90
+ try {
91
+ await db
92
+ .database(config.resourceNames.applicationStack)
93
+ .container(config.resourceNames.forReadModel(readModelName))
94
+ .item(readModel.id, readModel.id)
95
+ .delete();
96
+ logger.debug(`Read model deleted. ${readModelName} ID = ${readModel.id}`);
97
+ }
98
+ catch (e) {
99
+ logger.warn(`Read model to delete ${readModelName} ID = ${readModel.id} not found`);
100
+ }
96
101
  }
97
102
  exports.deleteReadModel = deleteReadModel;
98
103
  async function rawReadModelEventsToEnvelopes(config, rawEvents) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boostercloud/framework-provider-azure",
3
- "version": "2.17.0",
3
+ "version": "2.18.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.17.0",
31
- "@boostercloud/framework-types": "^2.17.0",
30
+ "@boostercloud/framework-common-helpers": "^2.18.0",
31
+ "@boostercloud/framework-types": "^2.18.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.17.0",
37
+ "@boostercloud/eslint-config": "^2.18.0",
38
38
  "@types/chai": "4.2.18",
39
39
  "@types/chai-as-promised": "7.1.4",
40
40
  "@types/faker": "5.1.5",