@eyevinn/player-analytics-shared 0.6.0 → 0.7.1

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,143 @@
1
+ import { createClient } from '@clickhouse/client';
2
+ import { AbstractDBAdapter, ErrorType, IGetItemInput, IGetItems, IHandleErrorOutput, IPutItemInput } from '../../types/interfaces';
3
+ import winston from 'winston';
4
+
5
+ interface EventItem {
6
+ event: string;
7
+ sessionId: string;
8
+ timestamp: number;
9
+ playhead: number;
10
+ duration: number;
11
+ payload: string;
12
+ }
13
+
14
+ export class ClickHouseDBAdapter implements AbstractDBAdapter {
15
+ logger: winston.Logger;
16
+ dbClient: any;
17
+
18
+ constructor(logger: winston.Logger) {
19
+ this.logger = logger;
20
+ this.dbClient = createClient({
21
+ url: process.env.CLICKHOUSE_URL
22
+ });
23
+ }
24
+
25
+ async tableExists(name: string): Promise<boolean> {
26
+ try {
27
+ const query = `SELECT 1 FROM system.tables WHERE database = currentDatabase() AND name = '${name}'`;
28
+ const resultSet = await this.dbClient.query({
29
+ query,
30
+ format: 'JSONEachRow'
31
+ });
32
+
33
+ const rows = await resultSet.json();
34
+ if (rows.length > 0) {
35
+ return true;
36
+ } else {
37
+ // Create table if it does not exists
38
+ const createTableQuery = `
39
+ CREATE TABLE IF NOT EXISTS ${name} (
40
+ event String,
41
+ sessionId String,
42
+ timestamp DateTime64(3),
43
+ playhead Float64,
44
+ duration Float64,
45
+ live Boolean,
46
+ contentId String,
47
+ userId String,
48
+ deviceId String,
49
+ deviceModel String,
50
+ deviceType String,
51
+ payload String, /* Stored as JSON string */
52
+
53
+ /* Add derived columns for better query performance */
54
+ event_date Date DEFAULT toDate(timestamp),
55
+ event_hour DateTime DEFAULT toStartOfHour(timestamp)
56
+ )
57
+ ENGINE = MergeTree()
58
+ PARTITION BY toYYYYMM(timestamp)
59
+ ORDER BY (sessionId, timestamp)
60
+ `;
61
+
62
+ await this.dbClient.query({
63
+ query: createTableQuery
64
+ });
65
+
66
+ this.logger.info(`Table '${name}' created successfully.`);
67
+ return true;
68
+ }
69
+ } catch (err) {
70
+ this.logger.error(`Error checking if table '${name}' exists:`);
71
+ this.logger.error(err);
72
+ return false;
73
+ }
74
+ }
75
+
76
+ async putItem(params: IPutItemInput): Promise<boolean> {
77
+ const tableName = params.tableName;
78
+ const item = params.data as EventItem;
79
+
80
+ // Prepare the item for insertion
81
+ this.logger.debug(item);
82
+
83
+ // Convert payload to JSON string if it's an object
84
+ const payload = typeof item.payload === 'object'
85
+ ? JSON.stringify(item.payload)
86
+ : item.payload || '';
87
+
88
+ // Prepare the data for insertion
89
+ let parsedPayload = {};
90
+ if (payload) {
91
+ try {
92
+ parsedPayload = JSON.parse(payload);
93
+ } catch (error) {
94
+ this.logger.warn('Payload not json, skipping parsing');
95
+ }
96
+ }
97
+ const data = [{
98
+ event: item.event,
99
+ sessionId: item.sessionId,
100
+ timestamp: item.timestamp,
101
+ playhead: item.playhead || -1,
102
+ duration: item.duration || -1,
103
+ live: parsedPayload['live'] || false,
104
+ contentId: parsedPayload['contentId'] || '',
105
+ userId: parsedPayload['userId'] || '',
106
+ deviceId: parsedPayload['deviceId'] || '',
107
+ deviceModel: parsedPayload['deviceModel'] || '',
108
+ deviceType: parsedPayload['deviceType'] || '',
109
+ payload
110
+ }];
111
+
112
+ // Insert the data
113
+ await this.dbClient.insert({
114
+ table: tableName,
115
+ values: data,
116
+ format: 'JSONEachRow'
117
+ });
118
+
119
+ this.logger.debug(`Successfully inserted item into ${tableName}`);
120
+ return true;
121
+ }
122
+
123
+ async getItem(params: IGetItemInput): Promise<any> {
124
+ throw new Error('Method not implemented.');
125
+ }
126
+
127
+ async deleteItem(params: IGetItemInput): Promise<boolean> {
128
+ throw new Error('Method not implemented.');
129
+ }
130
+
131
+ async getItemsBySession(params: IGetItems): Promise<any[]> {
132
+ throw new Error('Method not implemented.');
133
+ }
134
+
135
+ handleError(errorObject: any): IHandleErrorOutput {
136
+ this.logger.error(errorObject);
137
+ const errorOutput: IHandleErrorOutput = {
138
+ errorType: ErrorType.ABORT,
139
+ error: errorObject,
140
+ };
141
+ return errorOutput;
142
+ }
143
+ }
@@ -7,6 +7,8 @@ import {
7
7
  DeleteMessageCommandInput,
8
8
  Message,
9
9
  DeleteMessageCommand,
10
+ paginateListQueues,
11
+ CreateQueueCommand,
10
12
  } from '@aws-sdk/client-sqs';
11
13
  import { AbstractQueueAdapter } from '../../types/interfaces';
12
14
  import winston from 'winston';
@@ -14,6 +16,8 @@ import winston from 'winston';
14
16
  export class SqsQueueAdapter implements AbstractQueueAdapter {
15
17
  logger: winston.Logger;
16
18
  client: SQSClient;
19
+ queueUrl: string;
20
+ queueExists: boolean = false;
17
21
 
18
22
  constructor(logger: winston.Logger) {
19
23
  this.logger = logger;
@@ -23,14 +27,45 @@ export class SqsQueueAdapter implements AbstractQueueAdapter {
23
27
  } else {
24
28
  region = process.env.AWS_REGION;
25
29
  }
30
+ this.queueUrl = process.env.SQS_QUEUE_URL!;
26
31
  this.logger.info(`SQS Region: ${region}`);
27
32
  this.client = new SQSClient({ region: region, endpoint: process.env.SQS_ENDPOINT });
28
33
  }
29
34
 
35
+ private async checkQueueExists(): Promise<boolean> {
36
+ const paginatedQueues = paginateListQueues({ client: this.client }, {});
37
+ const queues: string[] = [];
38
+
39
+ for await (const page of paginatedQueues) {
40
+ if (page.QueueUrls?.length) {
41
+ queues.push(...page.QueueUrls);
42
+ }
43
+ }
44
+ return queues.find((queue) => queue === this.queueUrl) ? true : false;
45
+ }
46
+
47
+ private async createQueue() {
48
+ const command = new CreateQueueCommand({
49
+ QueueName: new URL(this.queueUrl).pathname.split('/').pop()
50
+ });
51
+ const response = await this.client.send(command);
52
+ this.logger.info(`Queue created: ${response.QueueUrl} (expected ${this.queueUrl})`);
53
+ }
54
+
30
55
  async pushToQueue(event: Object): Promise<any> {
31
- if (process.env.SQS_QUEUE_URL === 'undefined') {
56
+ if (this.queueUrl === 'undefined') {
32
57
  return { message: 'SQS_QUEUE_URL is undefined' };
33
58
  }
59
+ if (!this.queueExists) {
60
+ this.logger.info('Checking if queue exists');
61
+ if (!(await this.checkQueueExists())) {
62
+ this.logger.error('Queue does not exist, creating queue');
63
+ await this.createQueue();
64
+ this.queueExists = true;
65
+ } else {
66
+ this.queueExists = true;
67
+ }
68
+ }
34
69
  const params: SendMessageCommandInput = {
35
70
  MessageAttributes: {
36
71
  Event: {
@@ -0,0 +1,13 @@
1
+ import { AbstractDBAdapter, IGetItemInput, IGetItems, IHandleErrorOutput, IPutItemInput } from '../../types/interfaces';
2
+ import winston from 'winston';
3
+ export declare class ClickHouseDBAdapter implements AbstractDBAdapter {
4
+ logger: winston.Logger;
5
+ dbClient: any;
6
+ constructor(logger: winston.Logger);
7
+ tableExists(name: string): Promise<boolean>;
8
+ putItem(params: IPutItemInput): Promise<boolean>;
9
+ getItem(params: IGetItemInput): Promise<any>;
10
+ deleteItem(params: IGetItemInput): Promise<boolean>;
11
+ getItemsBySession(params: IGetItems): Promise<any[]>;
12
+ handleError(errorObject: any): IHandleErrorOutput;
13
+ }
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ClickHouseDBAdapter = void 0;
13
+ const client_1 = require("@clickhouse/client");
14
+ const interfaces_1 = require("../../types/interfaces");
15
+ class ClickHouseDBAdapter {
16
+ constructor(logger) {
17
+ this.logger = logger;
18
+ this.dbClient = (0, client_1.createClient)({
19
+ url: process.env.CLICKHOUSE_URL
20
+ });
21
+ }
22
+ tableExists(name) {
23
+ return __awaiter(this, void 0, void 0, function* () {
24
+ try {
25
+ const query = `SELECT 1 FROM system.tables WHERE database = currentDatabase() AND name = '${name}'`;
26
+ const resultSet = yield this.dbClient.query({
27
+ query,
28
+ format: 'JSONEachRow'
29
+ });
30
+ const rows = yield resultSet.json();
31
+ if (rows.length > 0) {
32
+ return true;
33
+ }
34
+ else {
35
+ // Create table if it does not exists
36
+ const createTableQuery = `
37
+ CREATE TABLE IF NOT EXISTS ${name} (
38
+ event String,
39
+ sessionId String,
40
+ timestamp DateTime64(3),
41
+ playhead Float64,
42
+ duration Float64,
43
+ live Boolean,
44
+ contentId String,
45
+ userId String,
46
+ deviceId String,
47
+ deviceModel String,
48
+ deviceType String,
49
+ payload String, /* Stored as JSON string */
50
+
51
+ /* Add derived columns for better query performance */
52
+ event_date Date DEFAULT toDate(timestamp),
53
+ event_hour DateTime DEFAULT toStartOfHour(timestamp)
54
+ )
55
+ ENGINE = MergeTree()
56
+ PARTITION BY toYYYYMM(timestamp)
57
+ ORDER BY (sessionId, timestamp)
58
+ `;
59
+ yield this.dbClient.query({
60
+ query: createTableQuery
61
+ });
62
+ this.logger.info(`Table '${name}' created successfully.`);
63
+ return true;
64
+ }
65
+ }
66
+ catch (err) {
67
+ this.logger.error(`Error checking if table '${name}' exists:`);
68
+ this.logger.error(err);
69
+ return false;
70
+ }
71
+ });
72
+ }
73
+ putItem(params) {
74
+ return __awaiter(this, void 0, void 0, function* () {
75
+ const tableName = params.tableName;
76
+ const item = params.data;
77
+ // Prepare the item for insertion
78
+ this.logger.debug(item);
79
+ // Convert payload to JSON string if it's an object
80
+ const payload = typeof item.payload === 'object'
81
+ ? JSON.stringify(item.payload)
82
+ : item.payload || '';
83
+ // Prepare the data for insertion
84
+ let parsedPayload = {};
85
+ if (payload) {
86
+ try {
87
+ parsedPayload = JSON.parse(payload);
88
+ }
89
+ catch (error) {
90
+ this.logger.warn('Payload not json, skipping parsing');
91
+ }
92
+ }
93
+ const data = [{
94
+ event: item.event,
95
+ sessionId: item.sessionId,
96
+ timestamp: item.timestamp,
97
+ playhead: item.playhead || -1,
98
+ duration: item.duration || -1,
99
+ live: parsedPayload['live'] || false,
100
+ contentId: parsedPayload['contentId'] || '',
101
+ userId: parsedPayload['userId'] || '',
102
+ deviceId: parsedPayload['deviceId'] || '',
103
+ deviceModel: parsedPayload['deviceModel'] || '',
104
+ deviceType: parsedPayload['deviceType'] || '',
105
+ payload
106
+ }];
107
+ // Insert the data
108
+ yield this.dbClient.insert({
109
+ table: tableName,
110
+ values: data,
111
+ format: 'JSONEachRow'
112
+ });
113
+ this.logger.debug(`Successfully inserted item into ${tableName}`);
114
+ return true;
115
+ });
116
+ }
117
+ getItem(params) {
118
+ return __awaiter(this, void 0, void 0, function* () {
119
+ throw new Error('Method not implemented.');
120
+ });
121
+ }
122
+ deleteItem(params) {
123
+ return __awaiter(this, void 0, void 0, function* () {
124
+ throw new Error('Method not implemented.');
125
+ });
126
+ }
127
+ getItemsBySession(params) {
128
+ return __awaiter(this, void 0, void 0, function* () {
129
+ throw new Error('Method not implemented.');
130
+ });
131
+ }
132
+ handleError(errorObject) {
133
+ this.logger.error(errorObject);
134
+ const errorOutput = {
135
+ errorType: interfaces_1.ErrorType.ABORT,
136
+ error: errorObject,
137
+ };
138
+ return errorOutput;
139
+ }
140
+ }
141
+ exports.ClickHouseDBAdapter = ClickHouseDBAdapter;
142
+ //# sourceMappingURL=ClickHouseDBAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClickHouseDBAdapter.js","sourceRoot":"","sources":["../../../adapters/db/ClickHouseDBAdapter.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,11 @@ import winston from 'winston';
4
4
  export declare class SqsQueueAdapter implements AbstractQueueAdapter {
5
5
  logger: winston.Logger;
6
6
  client: SQSClient;
7
+ queueUrl: string;
8
+ queueExists: boolean;
7
9
  constructor(logger: winston.Logger);
10
+ private checkQueueExists;
11
+ private createQueue;
8
12
  pushToQueue(event: Object): Promise<any>;
9
13
  pullFromQueue(): Promise<any>;
10
14
  removeFromQueue(queueMsg: Message): Promise<any>;
@@ -8,11 +8,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
12
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
13
+ var m = o[Symbol.asyncIterator], i;
14
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
15
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
16
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
17
+ };
11
18
  Object.defineProperty(exports, "__esModule", { value: true });
12
19
  exports.SqsQueueAdapter = void 0;
13
20
  const client_sqs_1 = require("@aws-sdk/client-sqs");
14
21
  class SqsQueueAdapter {
15
22
  constructor(logger) {
23
+ this.queueExists = false;
16
24
  this.logger = logger;
17
25
  let region;
18
26
  if ('QUEUE_REGION' in process.env) {
@@ -21,14 +29,59 @@ class SqsQueueAdapter {
21
29
  else {
22
30
  region = process.env.AWS_REGION;
23
31
  }
32
+ this.queueUrl = process.env.SQS_QUEUE_URL;
24
33
  this.logger.info(`SQS Region: ${region}`);
25
34
  this.client = new client_sqs_1.SQSClient({ region: region, endpoint: process.env.SQS_ENDPOINT });
26
35
  }
36
+ checkQueueExists() {
37
+ var e_1, _a;
38
+ var _b;
39
+ return __awaiter(this, void 0, void 0, function* () {
40
+ const paginatedQueues = (0, client_sqs_1.paginateListQueues)({ client: this.client }, {});
41
+ const queues = [];
42
+ try {
43
+ for (var paginatedQueues_1 = __asyncValues(paginatedQueues), paginatedQueues_1_1; paginatedQueues_1_1 = yield paginatedQueues_1.next(), !paginatedQueues_1_1.done;) {
44
+ const page = paginatedQueues_1_1.value;
45
+ if ((_b = page.QueueUrls) === null || _b === void 0 ? void 0 : _b.length) {
46
+ queues.push(...page.QueueUrls);
47
+ }
48
+ }
49
+ }
50
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
51
+ finally {
52
+ try {
53
+ if (paginatedQueues_1_1 && !paginatedQueues_1_1.done && (_a = paginatedQueues_1.return)) yield _a.call(paginatedQueues_1);
54
+ }
55
+ finally { if (e_1) throw e_1.error; }
56
+ }
57
+ return queues.find((queue) => queue === this.queueUrl) ? true : false;
58
+ });
59
+ }
60
+ createQueue() {
61
+ return __awaiter(this, void 0, void 0, function* () {
62
+ const command = new client_sqs_1.CreateQueueCommand({
63
+ QueueName: new URL(this.queueUrl).pathname.split('/').pop()
64
+ });
65
+ const response = yield this.client.send(command);
66
+ this.logger.info(`Queue created: ${response.QueueUrl} (expected ${this.queueUrl})`);
67
+ });
68
+ }
27
69
  pushToQueue(event) {
28
70
  return __awaiter(this, void 0, void 0, function* () {
29
- if (process.env.SQS_QUEUE_URL === 'undefined') {
71
+ if (this.queueUrl === 'undefined') {
30
72
  return { message: 'SQS_QUEUE_URL is undefined' };
31
73
  }
74
+ if (!this.queueExists) {
75
+ this.logger.info('Checking if queue exists');
76
+ if (!(yield this.checkQueueExists())) {
77
+ this.logger.error('Queue does not exist, creating queue');
78
+ yield this.createQueue();
79
+ this.queueExists = true;
80
+ }
81
+ else {
82
+ this.queueExists = true;
83
+ }
84
+ }
32
85
  const params = {
33
86
  MessageAttributes: {
34
87
  Event: {
@@ -1 +1 @@
1
- {"version":3,"file":"SqsQueueAdapter.js","sourceRoot":"","sources":["../../../adapters/queue/SqsQueueAdapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SqsQueueAdapter.js","sourceRoot":"","sources":["../../../adapters/queue/SqsQueueAdapter.ts"],"names":[],"mappings":"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"}
package/build/index.d.ts CHANGED
@@ -3,4 +3,5 @@ export * from './adapters/queue/BeanstalkdAdapter';
3
3
  export * from './adapters/queue/RedisAdapter';
4
4
  export * from './adapters/db/DynamoDBAdapter';
5
5
  export * from './adapters/db/MongoDBAdapter';
6
+ export * from './adapters/db/ClickHouseDBAdapter';
6
7
  export * from './util/constants';
package/build/index.js CHANGED
@@ -15,5 +15,6 @@ __exportStar(require("./adapters/queue/BeanstalkdAdapter"), exports);
15
15
  __exportStar(require("./adapters/queue/RedisAdapter"), exports);
16
16
  __exportStar(require("./adapters/db/DynamoDBAdapter"), exports);
17
17
  __exportStar(require("./adapters/db/MongoDBAdapter"), exports);
18
+ __exportStar(require("./adapters/db/ClickHouseDBAdapter"), exports);
18
19
  __exportStar(require("./util/constants"), exports);
19
20
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,mEAAiD;AACjD,qEAAmD;AACnD,gEAA8C;AAE9C,gEAA8C;AAC9C,+DAA6C;AAE7C,mDAAiC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,mEAAiD;AACjD,qEAAmD;AACnD,gEAA8C;AAE9C,gEAA8C;AAC9C,+DAA6C;AAC7C,oEAAkD;AAElD,mDAAiC"}
package/index.ts CHANGED
@@ -4,5 +4,6 @@ export * from './adapters/queue/RedisAdapter';
4
4
 
5
5
  export * from './adapters/db/DynamoDBAdapter';
6
6
  export * from './adapters/db/MongoDBAdapter';
7
+ export * from './adapters/db/ClickHouseDBAdapter';
7
8
 
8
9
  export * from './util/constants';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eyevinn/player-analytics-shared",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Shared modules &amp; adapters for EPAS components",
5
5
  "source": "index.ts",
6
6
  "main": "./build/index.js",
@@ -29,6 +29,7 @@
29
29
  "@aws-sdk/client-dynamodb": "^3.751.0",
30
30
  "@aws-sdk/client-sqs": "^3.750.0",
31
31
  "@aws-sdk/util-dynamodb": "^3.44.0",
32
+ "@clickhouse/client": "^1.10.1",
32
33
  "@types/jasmine": "^3.10.2",
33
34
  "@types/node": "^16.6.1",
34
35
  "aws-sdk-client-mock": "^4.1.0",
@@ -36,6 +36,8 @@ describe('SQS Queue Adapter', () => {
36
36
  playhead: 0,
37
37
  duration: 0,
38
38
  };
39
+ // Using any type assertion to bypass TypeScript's type checking on spyOn
40
+ spyOn(adapter as any, 'checkQueueExists').and.returnValue(true);
39
41
  sqsMock.on(SendMessageCommand).resolves(sqsResp);
40
42
  const result = await adapter.pushToQueue(event);
41
43
  expect(result).toEqual(sqsResp);
@@ -52,6 +54,8 @@ describe('SQS Queue Adapter', () => {
52
54
  playhead: 0,
53
55
  duration: 0,
54
56
  };
57
+ // Using any type assertion to bypass TypeScript's type checking on spyOn
58
+ spyOn(adapter as any, 'checkQueueExists').and.returnValue(true);
55
59
  sqsMock.on(SendMessageCommand).resolves(sqsResp);
56
60
  const result = await adapter.pushToQueue(event);
57
61
  expect(result).toEqual(sqsResp);
@@ -139,6 +143,10 @@ describe('SQS Queue Adapter', () => {
139
143
  sqsMock.on(ReceiveMessageCommand).rejects(errMsg);
140
144
  sqsMock.on(DeleteMessageCommand).rejects(errMsg);
141
145
  const queueAdapter = new SqsQueueAdapter(Logger);
146
+
147
+ // Using any type assertion to bypass TypeScript's type checking on spyOn
148
+ spyOn(queueAdapter as any, 'checkQueueExists').and.returnValue(true);
149
+
142
150
  let pushResult = await queueAdapter.pushToQueue(mockEvent);
143
151
  let readResult = await queueAdapter.pullFromQueue();
144
152
  let removeResult = await queueAdapter.removeFromQueue(mockSQSMessage);