@boostercloud/framework-provider-azure 1.9.0 → 1.10.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,7 +1,20 @@
1
+ import { BoosterConfig } from '@boostercloud/framework-types';
1
2
  export declare const eventsStoreAttributes: {
2
3
  readonly partitionKey: "entityTypeName_entityID_kind";
3
4
  readonly sortKey: "createdAt";
4
5
  };
6
+ export declare const subscriptionsStoreAttributes: {
7
+ readonly partitionKey: "className";
8
+ readonly sortKey: "connectionID_subscriptionID";
9
+ readonly ttl: "expirationTime";
10
+ readonly indexByConnectionIDPartitionKey: "connectionID";
11
+ readonly indexByConnectionIDSortKey: "subscriptionID";
12
+ readonly indexByConnectionIDName: (config: BoosterConfig) => string;
13
+ };
14
+ export declare const connectionsStoreAttributes: {
15
+ readonly partitionKey: "connectionID";
16
+ readonly ttl: "expirationTime";
17
+ };
5
18
  export declare const environmentVarNames: {
6
19
  readonly restAPIURL: "BOOSTER_REST_API_URL";
7
20
  readonly websocketAPIURL: "BOOSTER_WEBSOCKET_API_URL";
package/dist/constants.js CHANGED
@@ -1,10 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AZURE_PRECONDITION_FAILED_ERROR = exports.AZURE_CONFLICT_ERROR_CODE = exports.environmentVarNames = exports.eventsStoreAttributes = void 0;
3
+ exports.AZURE_PRECONDITION_FAILED_ERROR = exports.AZURE_CONFLICT_ERROR_CODE = exports.environmentVarNames = exports.connectionsStoreAttributes = exports.subscriptionsStoreAttributes = exports.eventsStoreAttributes = void 0;
4
4
  exports.eventsStoreAttributes = {
5
5
  partitionKey: 'entityTypeName_entityID_kind',
6
6
  sortKey: 'createdAt',
7
7
  };
8
+ exports.subscriptionsStoreAttributes = {
9
+ partitionKey: 'className',
10
+ sortKey: 'connectionID_subscriptionID',
11
+ ttl: 'expirationTime',
12
+ indexByConnectionIDPartitionKey: 'connectionID',
13
+ indexByConnectionIDSortKey: 'subscriptionID',
14
+ indexByConnectionIDName: (config) => config.resourceNames.subscriptionsStore + '-index-by-connection',
15
+ };
16
+ exports.connectionsStoreAttributes = {
17
+ partitionKey: 'connectionID',
18
+ ttl: 'expirationTime',
19
+ };
8
20
  exports.environmentVarNames = {
9
21
  restAPIURL: 'BOOSTER_REST_API_URL',
10
22
  websocketAPIURL: 'BOOSTER_WEBSOCKET_API_URL',
package/dist/index.js CHANGED
@@ -11,6 +11,8 @@ 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
13
  const events_searcher_adapter_1 = require("./library/events-searcher-adapter");
14
+ const subscription_adapter_1 = require("./library/subscription-adapter");
15
+ const connections_adapter_1 = require("./library/connections-adapter");
14
16
  const rocket_adapter_1 = require("./library/rocket-adapter");
15
17
  let cosmosClient;
16
18
  if (typeof process.env[constants_1.environmentVarNames.cosmosDbConnectionString] === 'undefined') {
@@ -42,13 +44,13 @@ const Provider = (rockets) => ({
42
44
  readModels: {
43
45
  fetch: read_model_adapter_1.fetchReadModel.bind(null, cosmosClient),
44
46
  search: searcher_adapter_1.searchReadModel.bind(null, cosmosClient),
45
- subscribe: undefined,
46
- rawToEnvelopes: undefined,
47
- fetchSubscriptions: undefined,
47
+ rawToEnvelopes: read_model_adapter_1.rawReadModelEventsToEnvelopes,
48
48
  store: read_model_adapter_1.storeReadModel.bind(null, cosmosClient),
49
49
  delete: read_model_adapter_1.deleteReadModel.bind(null, cosmosClient),
50
- deleteSubscription: undefined,
51
- deleteAllSubscriptions: undefined,
50
+ subscribe: subscription_adapter_1.subscribeToReadModel.bind(null, cosmosClient),
51
+ fetchSubscriptions: subscription_adapter_1.fetchSubscriptions.bind(null, cosmosClient),
52
+ deleteSubscription: subscription_adapter_1.deleteSubscription.bind(null, cosmosClient),
53
+ deleteAllSubscriptions: subscription_adapter_1.deleteAllSubscriptions.bind(null, cosmosClient),
52
54
  },
53
55
  // ProviderGraphQLLibrary
54
56
  graphQL: {
@@ -61,10 +63,10 @@ const Provider = (rockets) => ({
61
63
  requestFailed: api_adapter_1.requestFailed,
62
64
  },
63
65
  connections: {
64
- storeData: notImplemented,
65
- fetchData: notImplemented,
66
- deleteData: notImplemented,
67
- sendMessage: notImplemented,
66
+ storeData: connections_adapter_1.storeConnectionData.bind(null, cosmosClient),
67
+ fetchData: connections_adapter_1.fetchConnectionData.bind(null, cosmosClient),
68
+ deleteData: connections_adapter_1.deleteConnectionData.bind(null, cosmosClient),
69
+ sendMessage: connections_adapter_1.sendMessageToConnection,
68
70
  },
69
71
  // ScheduledCommandsLibrary
70
72
  scheduled: {
@@ -89,5 +91,4 @@ const Provider = (rockets) => ({
89
91
  },
90
92
  });
91
93
  exports.Provider = Provider;
92
- function notImplemented() { }
93
94
  tslib_1.__exportStar(require("./constants"), exports);
@@ -9,5 +9,5 @@ export interface ContextResponse {
9
9
  status: number;
10
10
  cookies?: Cookie[];
11
11
  }
12
- export declare function requestSucceeded(body?: unknown, headers?: Record<string, number | string | ReadonlyArray<string>>): Promise<ContextResponse>;
12
+ export declare function requestSucceeded(body?: unknown, headers?: Record<string, number | string | ReadonlyArray<string>>): Promise<ContextResponse | void>;
13
13
  export declare function requestFailed(error: Error): Promise<ContextResponse>;
@@ -2,8 +2,16 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.requestFailed = exports.requestSucceeded = void 0;
4
4
  const framework_types_1 = require("@boostercloud/framework-types");
5
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
+ const WEB_SOCKET_PROTOCOL_HEADER = 'Sec-WebSocket-Protocol';
6
6
  async function requestSucceeded(body, headers) {
7
+ if (!body && (!headers || Object.keys(headers).length === 0)) {
8
+ return;
9
+ }
10
+ const isWebSocket = headers && Object.keys(headers).includes(WEB_SOCKET_PROTOCOL_HEADER);
11
+ let extraParams = {};
12
+ if (isWebSocket) {
13
+ extraParams = { Subprotocol: headers[WEB_SOCKET_PROTOCOL_HEADER] };
14
+ }
7
15
  return {
8
16
  headers: {
9
17
  'Access-Control-Allow-Origin': '*',
@@ -12,6 +20,7 @@ async function requestSucceeded(body, headers) {
12
20
  },
13
21
  status: 200,
14
22
  body: body ? JSON.stringify(body) : '',
23
+ ...extraParams,
15
24
  };
16
25
  }
17
26
  exports.requestSucceeded = requestSucceeded;
@@ -0,0 +1,11 @@
1
+ import { BoosterConfig, ConnectionDataEnvelope } from '@boostercloud/framework-types';
2
+ import { connectionsStoreAttributes } from '../constants';
3
+ import { CosmosClient } from '@azure/cosmos';
4
+ export interface ConnectionIndexRecord extends ConnectionDataEnvelope {
5
+ id: string;
6
+ [connectionsStoreAttributes.partitionKey]: string;
7
+ }
8
+ export declare function storeConnectionData(cosmosDb: CosmosClient, config: BoosterConfig, connectionID: string, data: ConnectionDataEnvelope): Promise<void>;
9
+ export declare function fetchConnectionData(cosmosDb: CosmosClient, config: BoosterConfig, connectionID: string): Promise<ConnectionDataEnvelope | undefined>;
10
+ export declare function deleteConnectionData(cosmosDb: CosmosClient, config: BoosterConfig, connectionID: string): Promise<void>;
11
+ export declare function sendMessageToConnection(config: BoosterConfig, connectionID: string, data: unknown): Promise<void>;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sendMessageToConnection = exports.deleteConnectionData = exports.fetchConnectionData = exports.storeConnectionData = void 0;
4
+ const constants_1 = require("../constants");
5
+ const framework_common_helpers_1 = require("@boostercloud/framework-common-helpers");
6
+ const web_pubsub_1 = require("@azure/web-pubsub");
7
+ async function storeConnectionData(cosmosDb, config, connectionID, data) {
8
+ const ttl = data[constants_1.connectionsStoreAttributes.ttl];
9
+ await cosmosDb
10
+ .database(config.resourceNames.applicationStack)
11
+ .container(config.resourceNames.connectionsStore)
12
+ .items.create({
13
+ ...data,
14
+ [constants_1.connectionsStoreAttributes.partitionKey]: connectionID,
15
+ ttl: ttl,
16
+ });
17
+ }
18
+ exports.storeConnectionData = storeConnectionData;
19
+ async function fetchConnectionData(cosmosDb, config, connectionID) {
20
+ const { resources } = await cosmosDb
21
+ .database(config.resourceNames.applicationStack)
22
+ .container(config.resourceNames.connectionsStore)
23
+ .items.query({
24
+ query: `SELECT *
25
+ FROM c
26
+ WHERE c["${constants_1.connectionsStoreAttributes.partitionKey}"] = @partitionKey`,
27
+ parameters: [
28
+ {
29
+ name: '@partitionKey',
30
+ value: connectionID,
31
+ },
32
+ ],
33
+ })
34
+ .fetchAll();
35
+ return resources[0];
36
+ }
37
+ exports.fetchConnectionData = fetchConnectionData;
38
+ async function deleteConnectionData(cosmosDb, config, connectionID) {
39
+ const logger = (0, framework_common_helpers_1.getLogger)(config, 'connections-adapter#deleteConnectionData');
40
+ const { resources } = await cosmosDb
41
+ .database(config.resourceNames.applicationStack)
42
+ .container(config.resourceNames.connectionsStore)
43
+ .items.query({
44
+ query: `SELECT *
45
+ FROM c
46
+ WHERE c["${constants_1.connectionsStoreAttributes.partitionKey}"] = @partitionKey`,
47
+ parameters: [
48
+ {
49
+ name: '@partitionKey',
50
+ value: connectionID,
51
+ },
52
+ ],
53
+ })
54
+ .fetchAll();
55
+ const foundConnections = resources;
56
+ if ((foundConnections === null || foundConnections === void 0 ? void 0 : foundConnections.length) < 1) {
57
+ logger.info(`No connections found with connectionID=${connectionID}`);
58
+ return;
59
+ }
60
+ const connectionsToDelete = foundConnections[0]; // There can't be more than one, as we used the full primary key in the query
61
+ logger.debug('Deleting connection = ', connectionsToDelete);
62
+ await cosmosDb
63
+ .database(config.resourceNames.applicationStack)
64
+ .container(config.resourceNames.connectionsStore)
65
+ .item(connectionsToDelete.id, connectionsToDelete[constants_1.connectionsStoreAttributes.partitionKey])
66
+ .delete();
67
+ }
68
+ exports.deleteConnectionData = deleteConnectionData;
69
+ async function sendMessageToConnection(config, connectionID, data) {
70
+ const logger = (0, framework_common_helpers_1.getLogger)(config, 'connection-adapter#sendMessageToConnection');
71
+ logger.debug(`Sending message ${JSON.stringify(data)} to connection ${connectionID}`);
72
+ const serviceClient = new web_pubsub_1.WebPubSubServiceClient(process.env.WebPubSubConnectionString, 'booster');
73
+ await serviceClient.sendToConnection(connectionID, JSON.stringify(data), { contentType: 'text/plain' });
74
+ }
75
+ exports.sendMessageToConnection = sendMessageToConnection;
@@ -1,3 +1,3 @@
1
- import { GraphQLRequestEnvelope, GraphQLRequestEnvelopeError, BoosterConfig } from '@boostercloud/framework-types';
1
+ import { BoosterConfig, GraphQLRequestEnvelope, GraphQLRequestEnvelopeError } from '@boostercloud/framework-types';
2
2
  import { Context } from '@azure/functions';
3
3
  export declare function rawGraphQLRequestToEnvelope(config: BoosterConfig, context: Context): Promise<GraphQLRequestEnvelope | GraphQLRequestEnvelopeError>;
@@ -3,26 +3,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.rawGraphQLRequestToEnvelope = void 0;
4
4
  const framework_common_helpers_1 = require("@boostercloud/framework-common-helpers");
5
5
  async function rawGraphQLRequestToEnvelope(config, context) {
6
- var _a, _b, _c, _d, _e, _f;
6
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
7
7
  const logger = (0, framework_common_helpers_1.getLogger)(config, 'graphql-adapter#rawGraphQLRequestToEnvelope');
8
8
  logger.debug('Received GraphQL request: ', context.req);
9
9
  const requestID = context.executionContext.invocationId;
10
- const connectionID = undefined; // TODO: Add this when sockets are supported
11
- const eventType = 'MESSAGE';
10
+ const connectionContext = (_a = context.bindingData) === null || _a === void 0 ? void 0 : _a.connectionContext;
11
+ const connectionID = connectionContext === null || connectionContext === void 0 ? void 0 : connectionContext.connectionId;
12
+ const eventType = (connectionContext === null || connectionContext === void 0 ? void 0 : connectionContext.eventType) === undefined ? 'MESSAGE' : (_b = connectionContext === null || connectionContext === void 0 ? void 0 : connectionContext.eventName) === null || _b === void 0 ? void 0 : _b.toUpperCase();
12
13
  try {
13
14
  let graphQLValue = undefined;
14
- if (context.req) {
15
- graphQLValue = context.req.body;
15
+ if (((_c = context.bindings) === null || _c === void 0 ? void 0 : _c.data) || ((_d = context.req) === null || _d === void 0 ? void 0 : _d.body)) {
16
+ graphQLValue = ((_e = context.bindings) === null || _e === void 0 ? void 0 : _e.data) || ((_f = context.req) === null || _f === void 0 ? void 0 : _f.body);
16
17
  }
17
18
  return {
18
19
  requestID,
20
+ connectionID,
19
21
  eventType,
20
- token: (_b = (_a = context.req) === null || _a === void 0 ? void 0 : _a.headers) === null || _b === void 0 ? void 0 : _b.authorization,
22
+ token: (_h = (_g = context.req) === null || _g === void 0 ? void 0 : _g.headers) === null || _h === void 0 ? void 0 : _h.authorization,
21
23
  value: graphQLValue,
22
24
  context: {
23
25
  request: {
24
- headers: (_c = context.req) === null || _c === void 0 ? void 0 : _c.headers,
25
- body: (_d = context.req) === null || _d === void 0 ? void 0 : _d.body,
26
+ headers: (_j = context.req) === null || _j === void 0 ? void 0 : _j.headers,
27
+ body: (_k = context.req) === null || _k === void 0 ? void 0 : _k.body,
26
28
  },
27
29
  rawContext: context,
28
30
  },
@@ -37,8 +39,8 @@ async function rawGraphQLRequestToEnvelope(config, context) {
37
39
  eventType,
38
40
  context: {
39
41
  request: {
40
- headers: (_e = context.req) === null || _e === void 0 ? void 0 : _e.headers,
41
- body: (_f = context.req) === null || _f === void 0 ? void 0 : _f.body,
42
+ headers: (_l = context.req) === null || _l === void 0 ? void 0 : _l.headers,
43
+ body: (_m = context.req) === null || _m === void 0 ? void 0 : _m.body,
42
44
  },
43
45
  rawContext: context,
44
46
  },
@@ -1,5 +1,6 @@
1
1
  import { CosmosClient } from '@azure/cosmos';
2
- import { BoosterConfig, ReadModelInterface, ReadOnlyNonEmptyArray, UUID } from '@boostercloud/framework-types';
2
+ import { BoosterConfig, ReadModelEnvelope, ReadModelInterface, ReadOnlyNonEmptyArray, UUID } from '@boostercloud/framework-types';
3
3
  export declare function fetchReadModel(db: CosmosClient, config: BoosterConfig, readModelName: string, readModelID: UUID): Promise<ReadOnlyNonEmptyArray<ReadModelInterface>>;
4
4
  export declare function storeReadModel(db: CosmosClient, config: BoosterConfig, readModelName: string, readModel: ReadModelInterface): Promise<void>;
5
5
  export declare function deleteReadModel(db: CosmosClient, config: BoosterConfig, readModelName: string, readModel: ReadModelInterface): Promise<void>;
6
+ export declare function rawReadModelEventsToEnvelopes(config: BoosterConfig, rawEvents: unknown): Promise<Array<ReadModelEnvelope>>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.deleteReadModel = exports.storeReadModel = exports.fetchReadModel = void 0;
3
+ exports.rawReadModelEventsToEnvelopes = exports.deleteReadModel = exports.storeReadModel = exports.fetchReadModel = 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
6
  const constants_1 = require("../constants");
@@ -95,3 +95,24 @@ async function deleteReadModel(db, config, readModelName, readModel) {
95
95
  logger.debug(`[ReadModelAdapter#deleteReadModel] Read model deleted. ID = ${readModel.id}`);
96
96
  }
97
97
  exports.deleteReadModel = deleteReadModel;
98
+ async function rawReadModelEventsToEnvelopes(config, rawEvents) {
99
+ const logger = (0, framework_common_helpers_1.getLogger)(config, 'read-model-adapter#rawReadModelEventsToEnvelopes');
100
+ logger.debug(`Parsing raw read models ${JSON.stringify(rawEvents)}`);
101
+ if (isSubscriptionContext(rawEvents)) {
102
+ const typeName = rawEvents.executionContext.functionName.replace('-subscriptions-notifier', '');
103
+ return rawEvents.bindings.rawEvent.map((rawEvent) => {
104
+ const { _rid, _self, _st, _etag, _lsn, _ts, ...rest } = rawEvent;
105
+ return {
106
+ typeName: typeName,
107
+ value: rest,
108
+ };
109
+ });
110
+ }
111
+ logger.warn(`Unexpected events to be parsed ${JSON.stringify(rawEvents)}`);
112
+ return [];
113
+ }
114
+ exports.rawReadModelEventsToEnvelopes = rawReadModelEventsToEnvelopes;
115
+ function isSubscriptionContext(rawRequest) {
116
+ return (rawRequest.bindings !== undefined &&
117
+ rawRequest.bindings.rawEvent !== undefined);
118
+ }
@@ -0,0 +1,14 @@
1
+ import { BoosterConfig, SubscriptionEnvelope } from '@boostercloud/framework-types';
2
+ import { CosmosClient } from '@azure/cosmos';
3
+ import { subscriptionsStoreAttributes } from '../constants';
4
+ export interface SubscriptionIndexRecord {
5
+ id: string;
6
+ [subscriptionsStoreAttributes.partitionKey]: string;
7
+ [subscriptionsStoreAttributes.sortKey]: string;
8
+ [subscriptionsStoreAttributes.indexByConnectionIDPartitionKey]: string;
9
+ [subscriptionsStoreAttributes.indexByConnectionIDSortKey]: string;
10
+ }
11
+ export declare function subscribeToReadModel(cosmosDb: CosmosClient, config: BoosterConfig, subscriptionEnvelope: SubscriptionEnvelope): Promise<void>;
12
+ export declare function fetchSubscriptions(cosmosDb: CosmosClient, config: BoosterConfig, subscriptionName: string): Promise<Array<SubscriptionEnvelope>>;
13
+ export declare function deleteSubscription(cosmosDb: CosmosClient, config: BoosterConfig, connectionID: string, subscriptionID: string): Promise<void>;
14
+ export declare function deleteAllSubscriptions(cosmosDb: CosmosClient, config: BoosterConfig, connectionID: string): Promise<void>;
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deleteAllSubscriptions = exports.deleteSubscription = exports.fetchSubscriptions = exports.subscribeToReadModel = void 0;
4
+ const constants_1 = require("../constants");
5
+ const framework_common_helpers_1 = require("@boostercloud/framework-common-helpers");
6
+ async function subscribeToReadModel(cosmosDb, config, subscriptionEnvelope) {
7
+ if (!subscriptionEnvelope[constants_1.subscriptionsStoreAttributes.partitionKey] ||
8
+ !subscriptionEnvelope.connectionID || // First part of subscriptionsStoreAttributes.sortKey
9
+ !subscriptionEnvelope.operation.id || // Second part of subscriptionsStoreAttributes.sortKey
10
+ !subscriptionEnvelope[constants_1.subscriptionsStoreAttributes.ttl]) {
11
+ throw new Error('Subscription envelope is missing any of the following required attributes: ' +
12
+ `"${constants_1.subscriptionsStoreAttributes.partitionKey}", "connectionID", "operation.id", ${constants_1.subscriptionsStoreAttributes.ttl}"`);
13
+ }
14
+ const ttl = subscriptionEnvelope[constants_1.subscriptionsStoreAttributes.ttl];
15
+ await cosmosDb
16
+ .database(config.resourceNames.applicationStack)
17
+ .container(config.resourceNames.subscriptionsStore)
18
+ .items.create({
19
+ ...subscriptionEnvelope,
20
+ [constants_1.subscriptionsStoreAttributes.sortKey]: sortKeyForSubscription(subscriptionEnvelope.connectionID, subscriptionEnvelope.operation.id),
21
+ [constants_1.subscriptionsStoreAttributes.indexByConnectionIDSortKey]: subscriptionEnvelope.operation.id,
22
+ ttl: ttl,
23
+ });
24
+ }
25
+ exports.subscribeToReadModel = subscribeToReadModel;
26
+ async function fetchSubscriptions(cosmosDb, config, subscriptionName) {
27
+ // TODO: filter expired ones. Or... is it needed?
28
+ const { resources } = await cosmosDb
29
+ .database(config.resourceNames.applicationStack)
30
+ .container(config.resourceNames.subscriptionsStore)
31
+ .items.query({
32
+ query: `SELECT *
33
+ FROM c
34
+ WHERE c["${constants_1.subscriptionsStoreAttributes.partitionKey}"] = @partitionKey`,
35
+ parameters: [{ name: '@partitionKey', value: subscriptionName }],
36
+ })
37
+ .fetchAll();
38
+ return resources;
39
+ }
40
+ exports.fetchSubscriptions = fetchSubscriptions;
41
+ async function deleteSubscription(cosmosDb, config, connectionID, subscriptionID) {
42
+ const logger = (0, framework_common_helpers_1.getLogger)(config, 'subscription-adapter#deleteSubscription');
43
+ // TODO: Manage query pagination
44
+ const { resources } = await cosmosDb
45
+ .database(config.resourceNames.applicationStack)
46
+ .container(config.resourceNames.subscriptionsStore)
47
+ .items.query({
48
+ query: `SELECT *
49
+ FROM c
50
+ WHERE c["${constants_1.subscriptionsStoreAttributes.indexByConnectionIDPartitionKey}"] = @partitionKey
51
+ AND c["${constants_1.subscriptionsStoreAttributes.indexByConnectionIDSortKey}"] = @sortKey`,
52
+ parameters: [
53
+ {
54
+ name: '@partitionKey',
55
+ value: connectionID,
56
+ },
57
+ {
58
+ name: '@sortKey',
59
+ value: subscriptionID,
60
+ },
61
+ ],
62
+ })
63
+ .fetchAll();
64
+ const foundSubscriptions = resources;
65
+ if ((foundSubscriptions === null || foundSubscriptions === void 0 ? void 0 : foundSubscriptions.length) < 1) {
66
+ logger.info(`[deleteSubscription] No subscriptions found with connectionID=${connectionID} and subscriptionID=${subscriptionID}`);
67
+ return;
68
+ }
69
+ const subscriptionToDelete = foundSubscriptions[0]; // There can't be more than one, as we used the full primary key in the query
70
+ logger.debug('[deleteSubscription] Deleting subscription = ', subscriptionToDelete);
71
+ await cosmosDb
72
+ .database(config.resourceNames.applicationStack)
73
+ .container(config.resourceNames.subscriptionsStore)
74
+ .item(subscriptionToDelete.id, subscriptionToDelete[constants_1.subscriptionsStoreAttributes.partitionKey])
75
+ .delete();
76
+ }
77
+ exports.deleteSubscription = deleteSubscription;
78
+ async function deleteAllSubscriptions(cosmosDb, config, connectionID) {
79
+ const logger = (0, framework_common_helpers_1.getLogger)(config, 'subscription-adapter#deleteAllSubscriptions');
80
+ // TODO: Manage query pagination and db.batchWrite limit of 25 operations at a time
81
+ const { resources } = await cosmosDb
82
+ .database(config.resourceNames.applicationStack)
83
+ .container(config.resourceNames.subscriptionsStore)
84
+ .items.query({
85
+ query: `SELECT *
86
+ FROM c
87
+ WHERE c["${constants_1.subscriptionsStoreAttributes.indexByConnectionIDPartitionKey}"] = @partitionKey`,
88
+ parameters: [
89
+ {
90
+ name: '@partitionKey',
91
+ value: connectionID,
92
+ },
93
+ ],
94
+ })
95
+ .fetchAll();
96
+ const foundSubscriptions = resources;
97
+ if ((foundSubscriptions === null || foundSubscriptions === void 0 ? void 0 : foundSubscriptions.length) < 1) {
98
+ logger.info(`No subscriptions found with connectionID=${connectionID}`);
99
+ return;
100
+ }
101
+ logger.debug(`Deleting all subscriptions for connectionID=${connectionID}, which are: `, foundSubscriptions);
102
+ const deletePromises = foundSubscriptions.map((subscriptionRecord) => cosmosDb
103
+ .database(config.resourceNames.applicationStack)
104
+ .container(config.resourceNames.subscriptionsStore)
105
+ .item(subscriptionRecord.id, subscriptionRecord[constants_1.subscriptionsStoreAttributes.partitionKey])
106
+ .delete());
107
+ await Promise.allSettled(deletePromises);
108
+ }
109
+ exports.deleteAllSubscriptions = deleteAllSubscriptions;
110
+ function sortKeyForSubscription(connectionID, subscriptionID) {
111
+ return `${connectionID}-${subscriptionID}`;
112
+ }
@@ -0,0 +1,28 @@
1
+ import { BoosterMetadata } from '@boostercloud/framework-types';
2
+ export interface SubscriptionContext {
3
+ invocationId: string;
4
+ traceContext: unknown;
5
+ executionContext: ExecutionContext;
6
+ bindings: Bindings;
7
+ bindingData: unknown;
8
+ bindingDefinitions: unknown;
9
+ }
10
+ export interface Bindings {
11
+ rawEvent: RawEvent[];
12
+ }
13
+ export interface RawEvent {
14
+ id: string;
15
+ _rid: string;
16
+ _self: string;
17
+ _ts: number;
18
+ _etag: string;
19
+ [key: string]: unknown;
20
+ boosterMetadata: BoosterMetadata;
21
+ _lsn: number;
22
+ }
23
+ export interface ExecutionContext {
24
+ invocationId: string;
25
+ functionName: string;
26
+ functionDirectory: string;
27
+ retryContext: null;
28
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boostercloud/framework-provider-azure",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "Handle Booster's integration with Azure",
5
5
  "keywords": [
6
6
  "framework-provider-azure"
@@ -23,13 +23,14 @@
23
23
  "@azure/cosmos": "^3.17.0",
24
24
  "@azure/functions": "^1.2.2",
25
25
  "@azure/identity": "~2.1.0",
26
- "@boostercloud/framework-common-helpers": "^1.9.0",
27
- "@boostercloud/framework-types": "^1.9.0",
26
+ "@boostercloud/framework-common-helpers": "^1.10.0",
27
+ "@boostercloud/framework-types": "^1.10.0",
28
28
  "tslib": "^2.4.0",
29
- "@effect-ts/core": "^0.60.4"
29
+ "@effect-ts/core": "^0.60.4",
30
+ "@azure/web-pubsub": "~1.1.0"
30
31
  },
31
32
  "devDependencies": {
32
- "@boostercloud/eslint-config": "^1.9.0",
33
+ "@boostercloud/eslint-config": "^1.10.0",
33
34
  "@types/chai": "4.2.18",
34
35
  "@types/chai-as-promised": "7.1.4",
35
36
  "@types/faker": "5.1.5",