@alanszp/eventbridge-client 7.4.1 → 7.4.3

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,7 @@
1
+ // Used by Jest
2
+ module.exports = {
3
+ presets: [
4
+ ["@babel/preset-env", { targets: { node: "current" } }],
5
+ "@babel/preset-typescript",
6
+ ],
7
+ };
@@ -13,6 +13,10 @@ exports.BasicEventbridgeClient = void 0;
13
13
  const lodash_1 = require("lodash");
14
14
  const aws_1 = require("./aws");
15
15
  const mapLaraEventToAWSEvent_1 = require("./helpers/mapLaraEventToAWSEvent");
16
+ /**
17
+ * Max batch size for the putEvents request defined by AWS.
18
+ */
19
+ const MAX_BATCH_SIZE = 10;
16
20
  /**
17
21
  * Basic client for Eventbridge.
18
22
  * Usage will be done by extending this class and implementing methods that internally call the protected sendEvents method.
@@ -28,11 +32,29 @@ class BasicEventbridgeClient {
28
32
  sendEvents(events) {
29
33
  return __awaiter(this, void 0, void 0, function* () {
30
34
  const logger = this.getLogger();
31
- const eventsToSend = {
32
- Entries: (0, lodash_1.compact)(events.map((event) => (0, mapLaraEventToAWSEvent_1.mapLaraEventToAWSEvent)(event, this.env, this.appName, this.bus, logger, this.context))),
33
- };
34
- const result = yield aws_1.eventbridgeClient.putEvents(eventsToSend).promise();
35
- const { Entries, FailedEntryCount: failedCount } = result;
35
+ const eventsToSend = (0, lodash_1.chain)(events)
36
+ .map((event) => (0, mapLaraEventToAWSEvent_1.mapLaraEventToAWSEvent)(event, this.env, this.appName, this.bus, logger, this.context))
37
+ .compact()
38
+ .chunk(MAX_BATCH_SIZE)
39
+ .map((mappedEventsChunk) => ({
40
+ Entries: mappedEventsChunk,
41
+ }))
42
+ .value();
43
+ const results = yield Promise.all(eventsToSend.map((events) => aws_1.eventbridgeClient
44
+ .putEvents(events)
45
+ .promise()));
46
+ const aggregatedResult = results.reduce((prev, act) => {
47
+ const { Entries: NewEntries, FailedEntryCount: newFailedCount } = act;
48
+ const { Entries, FailedEntryCount } = prev;
49
+ return {
50
+ Entries: [...(Entries || []), ...(NewEntries || [])],
51
+ FailedEntryCount: (FailedEntryCount || 0) + (newFailedCount || 0),
52
+ };
53
+ }, {
54
+ Entries: [],
55
+ FailedEntryCount: 0,
56
+ });
57
+ const { Entries, FailedEntryCount: failedCount } = aggregatedResult;
36
58
  const [successful, failed] = (0, lodash_1.partition)(Entries, (entry) => entry.EventId);
37
59
  logger.info("eventbridge.client.sendEvents.end", {
38
60
  successful,
@@ -1 +1 @@
1
- {"version":3,"file":"basicEventbridgeClient.js","sourceRoot":"","sources":["../src/basicEventbridgeClient.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEA,mCAA4C;AAC5C,+BAA+E;AAC/E,6EAA0E;AAoB1E;;;GAGG;AACH,MAAa,sBAAsB;IAQjC,YACE,OAAe,EACf,GAAW,EACX,SAAwB,EACxB,OAAsB,EACtB,GAAW;QAEX,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAEe,UAAU,CACxB,MAAmB;;YAEnB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;YAEhC,MAAM,YAAY,GAAiB;gBACjC,OAAO,EAAE,IAAA,gBAAO,EACd,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACnB,IAAA,+CAAsB,EACpB,KAAK,EACL,IAAI,CAAC,GAAG,EACR,IAAI,CAAC,OAAO,EACZ,IAAI,CAAC,GAAG,EACR,MAAM,EACN,IAAI,CAAC,OAAO,CACb,CACF,CACF;aACF,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,uBAAiB,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC;YACzE,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;YAC1D,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,IAAA,kBAAS,EAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAE1E,MAAM,CAAC,IAAI,CAAC,mCAAmC,EAAE;gBAC/C,UAAU;gBACV,MAAM;aACP,CAAC,CAAC;YAEH,OAAO;gBACL,UAAU;gBACV,MAAM;gBACN,WAAW;aACZ,CAAC;QACJ,CAAC;KAAA;CACF;AAzDD,wDAyDC"}
1
+ {"version":3,"file":"basicEventbridgeClient.js","sourceRoot":"","sources":["../src/basicEventbridgeClient.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEA,mCAA0D;AAC1D,+BAA+E;AAC/E,6EAA0E;AAoB1E;;GAEG;AACH,MAAM,cAAc,GAAG,EAAE,CAAC;AAE1B;;;GAGG;AACH,MAAa,sBAAsB;IAQjC,YACE,OAAe,EACf,GAAW,EACX,SAAwB,EACxB,OAAsB,EACtB,GAAW;QAEX,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAEe,UAAU,CACxB,MAAmB;;YAEnB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;YAEhC,MAAM,YAAY,GAAG,IAAA,cAAK,EAAC,MAAM,CAAC;iBAC/B,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACb,IAAA,+CAAsB,EACpB,KAAK,EACL,IAAI,CAAC,GAAG,EACR,IAAI,CAAC,OAAO,EACZ,IAAI,CAAC,GAAG,EACR,MAAM,EACN,IAAI,CAAC,OAAO,CACb,CACF;iBACA,OAAO,EAAE;iBACT,KAAK,CAAC,cAAc,CAAC;iBACrB,GAAG,CAAC,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;gBAC3B,OAAO,EAAE,iBAAiB;aAC3B,CAAC,CAAC;iBACF,KAAK,EAAE,CAAC;YAEX,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,YAAY,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAC1B,uBAAiB;iBACd,SAAS,CAAC,MAAM,CAAC;iBACjB,OAAO,EAAE,CACb,CACF,CAAC;YAEF,MAAM,gBAAgB,GAAG,OAAO,CAAC,MAAM,CACrC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;gBACZ,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,GAAG,GAAG,CAAC;gBACtE,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAAC;gBAE3C,OAAO;oBACL,OAAO,EAAE,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;oBACpD,gBAAgB,EAAE,CAAC,gBAAgB,IAAI,CAAC,CAAC,GAAG,CAAC,cAAc,IAAI,CAAC,CAAC;iBAClE,CAAC;YACJ,CAAC,EACD;gBACE,OAAO,EAAE,EAAE;gBACX,gBAAgB,EAAE,CAAC;aACpB,CACF,CAAC;YAEF,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,GAAG,gBAAgB,CAAC;YAEpE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,IAAA,kBAAS,EAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAE1E,MAAM,CAAC,IAAI,CAAC,mCAAmC,EAAE;gBAC/C,UAAU;gBACV,MAAM;aACP,CAAC,CAAC;YAEH,OAAO;gBACL,UAAU;gBACV,MAAM;gBACN,WAAW;aACZ,CAAC;QACJ,CAAC;KAAA;CACF;AApFD,wDAoFC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,74 @@
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
+ const logger_1 = require("@alanszp/logger");
13
+ const shared_context_1 = require("@alanszp/shared-context");
14
+ const awsEventbridgeClient_1 = require("./aws/awsEventbridgeClient");
15
+ const mockEventbridgeClient_1 = require("./mockEventbridgeClient");
16
+ const eventbridgeFixture_1 = require("./mocks/fixtures/eventbridgeFixture");
17
+ const { eventsToSend, allSuccessfulResponses, allUnsuccessfulResponses, sevenUnsuccessfulResponses, } = eventbridgeFixture_1.fifteenEventsFixture;
18
+ jest.mock("./aws/awsEventbridgeClient");
19
+ const client = new mockEventbridgeClient_1.MockEventbridgeClient("appName", "env", () => (0, logger_1.createMockLogger)({}), new shared_context_1.SharedContext(), "busName");
20
+ describe("BasicEventbridgeClient", () => {
21
+ describe("sendEvents", () => {
22
+ describe("when there are more than 10 events", () => {
23
+ describe("when they're all successful", () => {
24
+ it("should send two requests, aggregate results, and show no failures", () => __awaiter(void 0, void 0, void 0, function* () {
25
+ awsEventbridgeClient_1.eventbridgeClient.putEvents
26
+ .mockImplementationOnce((_) => ({
27
+ promise: () => Promise.resolve(allSuccessfulResponses.firstResponse),
28
+ }))
29
+ .mockImplementationOnce((_) => ({
30
+ promise: () => Promise.resolve(allSuccessfulResponses.secondResponse),
31
+ }));
32
+ const response = yield client.mockSendEvents(eventsToSend);
33
+ expect(awsEventbridgeClient_1.eventbridgeClient.putEvents).toHaveBeenCalledTimes(2);
34
+ expect(response.failedCount).toBe(0);
35
+ expect(response.successful.length).toBe(15);
36
+ expect(response.failed.length).toBe(0);
37
+ }));
38
+ });
39
+ describe("when they're all successful", () => {
40
+ it("should send two requests, aggregate results, and show fifteen failures", () => __awaiter(void 0, void 0, void 0, function* () {
41
+ awsEventbridgeClient_1.eventbridgeClient.putEvents
42
+ .mockImplementationOnce((_) => ({
43
+ promise: () => Promise.resolve(allUnsuccessfulResponses.firstResponse),
44
+ }))
45
+ .mockImplementationOnce((_) => ({
46
+ promise: () => Promise.resolve(allUnsuccessfulResponses.secondResponse),
47
+ }));
48
+ const response = yield client.mockSendEvents(eventsToSend);
49
+ expect(awsEventbridgeClient_1.eventbridgeClient.putEvents).toHaveBeenCalledTimes(2);
50
+ expect(response.failedCount).toBe(15);
51
+ expect(response.successful.length).toBe(0);
52
+ expect(response.failed.length).toBe(15);
53
+ }));
54
+ });
55
+ describe("when eight are successful", () => {
56
+ it("should send two requests, aggregate results, and show seven failures", () => __awaiter(void 0, void 0, void 0, function* () {
57
+ awsEventbridgeClient_1.eventbridgeClient.putEvents
58
+ .mockImplementationOnce((_) => ({
59
+ promise: () => Promise.resolve(sevenUnsuccessfulResponses.firstResponse),
60
+ }))
61
+ .mockImplementationOnce((_) => ({
62
+ promise: () => Promise.resolve(sevenUnsuccessfulResponses.secondResponse),
63
+ }));
64
+ const response = yield client.mockSendEvents(eventsToSend);
65
+ expect(awsEventbridgeClient_1.eventbridgeClient.putEvents).toHaveBeenCalledTimes(2);
66
+ expect(response.failedCount).toBe(7);
67
+ expect(response.successful.length).toBe(8);
68
+ expect(response.failed.length).toBe(7);
69
+ }));
70
+ });
71
+ });
72
+ });
73
+ });
74
+ //# sourceMappingURL=basicEventbridgeClient.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"basicEventbridgeClient.test.js","sourceRoot":"","sources":["../src/basicEventbridgeClient.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { BasicEventbridgeClient, EventDispatchResult, LaraEvent } from "./basicEventbridgeClient";
2
+ export declare class MockEventbridgeClient extends BasicEventbridgeClient {
3
+ mockSendEvents(events: LaraEvent[]): Promise<EventDispatchResult>;
4
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MockEventbridgeClient = void 0;
4
+ const basicEventbridgeClient_1 = require("./basicEventbridgeClient");
5
+ class MockEventbridgeClient extends basicEventbridgeClient_1.BasicEventbridgeClient {
6
+ mockSendEvents(events) {
7
+ return this.sendEvents(events);
8
+ }
9
+ }
10
+ exports.MockEventbridgeClient = MockEventbridgeClient;
11
+ //# sourceMappingURL=mockEventbridgeClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mockEventbridgeClient.js","sourceRoot":"","sources":["../src/mockEventbridgeClient.ts"],"names":[],"mappings":";;;AAAA,qEAIkC;AAElC,MAAa,qBAAsB,SAAQ,+CAAsB;IACxD,cAAc,CAAC,MAAmB;QACvC,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;CACF;AAJD,sDAIC"}
@@ -0,0 +1,54 @@
1
+ export declare const fifteenEventsFixture: {
2
+ eventsToSend: {
3
+ topic: string;
4
+ body: {
5
+ property: boolean;
6
+ };
7
+ }[];
8
+ allSuccessfulResponses: {
9
+ firstResponse: {
10
+ Entries: {
11
+ EventId: number;
12
+ }[];
13
+ FailedEntryCount: number;
14
+ };
15
+ secondResponse: {
16
+ Entries: {
17
+ EventId: number;
18
+ }[];
19
+ FailedEntryCount: number;
20
+ };
21
+ };
22
+ allUnsuccessfulResponses: {
23
+ firstResponse: {
24
+ Entries: {
25
+ EventId: null;
26
+ }[];
27
+ FailedEntryCount: number;
28
+ };
29
+ secondResponse: {
30
+ Entries: {
31
+ EventId: null;
32
+ }[];
33
+ FailedEntryCount: number;
34
+ };
35
+ };
36
+ sevenUnsuccessfulResponses: {
37
+ firstResponse: {
38
+ Entries: ({
39
+ EventId: number;
40
+ } | {
41
+ EventId: null;
42
+ })[];
43
+ FailedEntryCount: number;
44
+ };
45
+ secondResponse: {
46
+ Entries: ({
47
+ EventId: number;
48
+ } | {
49
+ EventId: null;
50
+ })[];
51
+ FailedEntryCount: number;
52
+ };
53
+ };
54
+ };
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fifteenEventsFixture = void 0;
4
+ exports.fifteenEventsFixture = {
5
+ eventsToSend: [
6
+ { topic: "topic", body: { property: true } },
7
+ { topic: "topic", body: { property: true } },
8
+ { topic: "topic", body: { property: true } },
9
+ { topic: "topic", body: { property: true } },
10
+ { topic: "topic", body: { property: true } },
11
+ { topic: "topic", body: { property: true } },
12
+ { topic: "topic", body: { property: true } },
13
+ { topic: "topic", body: { property: true } },
14
+ { topic: "topic", body: { property: true } },
15
+ { topic: "topic", body: { property: true } },
16
+ { topic: "topic", body: { property: true } },
17
+ { topic: "topic", body: { property: true } },
18
+ { topic: "topic", body: { property: true } },
19
+ { topic: "topic", body: { property: true } },
20
+ { topic: "topic", body: { property: true } },
21
+ ],
22
+ allSuccessfulResponses: {
23
+ firstResponse: {
24
+ Entries: [
25
+ { EventId: 1 },
26
+ { EventId: 2 },
27
+ { EventId: 3 },
28
+ { EventId: 4 },
29
+ { EventId: 5 },
30
+ { EventId: 6 },
31
+ { EventId: 7 },
32
+ { EventId: 8 },
33
+ { EventId: 9 },
34
+ { EventId: 10 },
35
+ ],
36
+ FailedEntryCount: 0,
37
+ },
38
+ secondResponse: {
39
+ Entries: [
40
+ { EventId: 11 },
41
+ { EventId: 12 },
42
+ { EventId: 13 },
43
+ { EventId: 14 },
44
+ { EventId: 15 },
45
+ ],
46
+ FailedEntryCount: 0,
47
+ },
48
+ },
49
+ allUnsuccessfulResponses: {
50
+ firstResponse: {
51
+ Entries: [
52
+ { EventId: null },
53
+ { EventId: null },
54
+ { EventId: null },
55
+ { EventId: null },
56
+ { EventId: null },
57
+ { EventId: null },
58
+ { EventId: null },
59
+ { EventId: null },
60
+ { EventId: null },
61
+ { EventId: null },
62
+ ],
63
+ FailedEntryCount: 10,
64
+ },
65
+ secondResponse: {
66
+ Entries: [
67
+ { EventId: null },
68
+ { EventId: null },
69
+ { EventId: null },
70
+ { EventId: null },
71
+ { EventId: null },
72
+ ],
73
+ FailedEntryCount: 5,
74
+ },
75
+ },
76
+ sevenUnsuccessfulResponses: {
77
+ firstResponse: {
78
+ Entries: [
79
+ { EventId: 1 },
80
+ { EventId: 2 },
81
+ { EventId: 3 },
82
+ { EventId: 4 },
83
+ { EventId: null },
84
+ { EventId: null },
85
+ { EventId: null },
86
+ { EventId: null },
87
+ { EventId: null },
88
+ { EventId: null },
89
+ ],
90
+ FailedEntryCount: 6,
91
+ },
92
+ secondResponse: {
93
+ Entries: [
94
+ { EventId: 5 },
95
+ { EventId: 6 },
96
+ { EventId: 8 },
97
+ { EventId: null },
98
+ { EventId: 7 },
99
+ ],
100
+ FailedEntryCount: 1,
101
+ },
102
+ },
103
+ };
104
+ //# sourceMappingURL=eventbridgeFixture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eventbridgeFixture.js","sourceRoot":"","sources":["../../../src/mocks/fixtures/eventbridgeFixture.ts"],"names":[],"mappings":"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"}
package/jest.config.js ADDED
@@ -0,0 +1,10 @@
1
+ module.exports = {
2
+ collectCoverageFrom: ["src/**/*.{js,jsx,ts,tsx}"],
3
+ roots: ["<rootDir>/src/"],
4
+ clearMocks: true,
5
+ resetMocks: true,
6
+ testEnvironment: "node",
7
+ moduleNameMapper: {
8
+ "@/(.*)": "<rootDir>/src/$1",
9
+ },
10
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alanszp/eventbridge-client",
3
- "version": "7.4.1",
3
+ "version": "7.4.3",
4
4
  "description": "Alan's basic eventbridge client.",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -15,11 +15,16 @@
15
15
  "compile": "rm -rf ./dist && tsc --declaration",
16
16
  "compile-watch": "tsc -w",
17
17
  "build": "yarn run compile",
18
+ "test": "TZ=Etc/UTC jest",
18
19
  "prepack": "yarn run build",
19
20
  "yalc-publish": "yarn run yalc publish"
20
21
  },
21
22
  "devDependencies": {
23
+ "@types/jest": "^28.1.8",
22
24
  "@types/node": "^15.12.3",
25
+ "babel-jest": "^29.0.0",
26
+ "eslint-plugin-jest": "^26.8.7",
27
+ "jest": "^29.0.0",
23
28
  "ts-node": "^10.0.0",
24
29
  "typescript": "^4.3.4"
25
30
  },
@@ -30,5 +35,5 @@
30
35
  "cuid": "^2.1.8",
31
36
  "lodash": "^4.17.21"
32
37
  },
33
- "gitHead": "84e3f67bf87c2e574df3899a8f9a95303739d423"
38
+ "gitHead": "3d54c1c62cb0c48b79abf79498e00eafe7843269"
34
39
  }
@@ -0,0 +1,89 @@
1
+ import { createMockLogger } from "@alanszp/logger";
2
+ import { SharedContext } from "@alanszp/shared-context";
3
+ import { eventbridgeClient } from "./aws/awsEventbridgeClient";
4
+ import { MockEventbridgeClient } from "./mockEventbridgeClient";
5
+ import { fifteenEventsFixture } from "./mocks/fixtures/eventbridgeFixture";
6
+
7
+ const {
8
+ eventsToSend,
9
+ allSuccessfulResponses,
10
+ allUnsuccessfulResponses,
11
+ sevenUnsuccessfulResponses,
12
+ } = fifteenEventsFixture;
13
+
14
+ jest.mock("./aws/awsEventbridgeClient");
15
+
16
+ const client = new MockEventbridgeClient(
17
+ "appName",
18
+ "env",
19
+ () => createMockLogger({}),
20
+ new SharedContext(),
21
+ "busName"
22
+ );
23
+
24
+ describe("BasicEventbridgeClient", () => {
25
+ describe("sendEvents", () => {
26
+ describe("when there are more than 10 events", () => {
27
+ describe("when they're all successful", () => {
28
+ it("should send two requests, aggregate results, and show no failures", async () => {
29
+ (eventbridgeClient.putEvents as jest.Mock)
30
+ .mockImplementationOnce((_) => ({
31
+ promise: () =>
32
+ Promise.resolve(allSuccessfulResponses.firstResponse),
33
+ }))
34
+ .mockImplementationOnce((_) => ({
35
+ promise: () =>
36
+ Promise.resolve(allSuccessfulResponses.secondResponse),
37
+ }));
38
+
39
+ const response = await client.mockSendEvents(eventsToSend);
40
+
41
+ expect(eventbridgeClient.putEvents).toHaveBeenCalledTimes(2);
42
+ expect(response.failedCount).toBe(0);
43
+ expect(response.successful.length).toBe(15);
44
+ expect(response.failed.length).toBe(0);
45
+ });
46
+ });
47
+ describe("when they're all successful", () => {
48
+ it("should send two requests, aggregate results, and show fifteen failures", async () => {
49
+ (eventbridgeClient.putEvents as jest.Mock)
50
+ .mockImplementationOnce((_) => ({
51
+ promise: () =>
52
+ Promise.resolve(allUnsuccessfulResponses.firstResponse),
53
+ }))
54
+ .mockImplementationOnce((_) => ({
55
+ promise: () =>
56
+ Promise.resolve(allUnsuccessfulResponses.secondResponse),
57
+ }));
58
+
59
+ const response = await client.mockSendEvents(eventsToSend);
60
+
61
+ expect(eventbridgeClient.putEvents).toHaveBeenCalledTimes(2);
62
+ expect(response.failedCount).toBe(15);
63
+ expect(response.successful.length).toBe(0);
64
+ expect(response.failed.length).toBe(15);
65
+ });
66
+ });
67
+ describe("when eight are successful", () => {
68
+ it("should send two requests, aggregate results, and show seven failures", async () => {
69
+ (eventbridgeClient.putEvents as jest.Mock)
70
+ .mockImplementationOnce((_) => ({
71
+ promise: () =>
72
+ Promise.resolve(sevenUnsuccessfulResponses.firstResponse),
73
+ }))
74
+ .mockImplementationOnce((_) => ({
75
+ promise: () =>
76
+ Promise.resolve(sevenUnsuccessfulResponses.secondResponse),
77
+ }));
78
+
79
+ const response = await client.mockSendEvents(eventsToSend);
80
+
81
+ expect(eventbridgeClient.putEvents).toHaveBeenCalledTimes(2);
82
+ expect(response.failedCount).toBe(7);
83
+ expect(response.successful.length).toBe(8);
84
+ expect(response.failed.length).toBe(7);
85
+ });
86
+ });
87
+ });
88
+ });
89
+ });
@@ -1,6 +1,6 @@
1
1
  import { ILogger } from "@alanszp/logger";
2
2
  import { SharedContext } from "@alanszp/shared-context";
3
- import { compact, partition } from "lodash";
3
+ import { chain, chunk, compact, partition } from "lodash";
4
4
  import { eventbridgeClient, EventRequest, PutEventEntryResponse } from "./aws";
5
5
  import { mapLaraEventToAWSEvent } from "./helpers/mapLaraEventToAWSEvent";
6
6
 
@@ -22,6 +22,11 @@ export interface EventDispatchResult {
22
22
  failedCount: number | undefined;
23
23
  }
24
24
 
25
+ /**
26
+ * Max batch size for the putEvents request defined by AWS.
27
+ */
28
+ const MAX_BATCH_SIZE = 10;
29
+
25
30
  /**
26
31
  * Basic client for Eventbridge.
27
32
  * Usage will be done by extending this class and implementing methods that internally call the protected sendEvents method.
@@ -53,23 +58,50 @@ export class BasicEventbridgeClient {
53
58
  ): Promise<EventDispatchResult> {
54
59
  const logger = this.getLogger();
55
60
 
56
- const eventsToSend: EventRequest = {
57
- Entries: compact(
58
- events.map((event) =>
59
- mapLaraEventToAWSEvent(
60
- event,
61
- this.env,
62
- this.appName,
63
- this.bus,
64
- logger,
65
- this.context
66
- )
61
+ const eventsToSend = chain(events)
62
+ .map((event) =>
63
+ mapLaraEventToAWSEvent(
64
+ event,
65
+ this.env,
66
+ this.appName,
67
+ this.bus,
68
+ logger,
69
+ this.context
67
70
  )
68
- ),
69
- };
71
+ )
72
+ .compact()
73
+ .chunk(MAX_BATCH_SIZE)
74
+ .map((mappedEventsChunk) => ({
75
+ Entries: mappedEventsChunk,
76
+ }))
77
+ .value();
78
+
79
+ const results = await Promise.all(
80
+ eventsToSend.map((events) =>
81
+ eventbridgeClient
82
+ .putEvents(events)
83
+ .promise()
84
+ )
85
+ );
86
+
87
+ const aggregatedResult = results.reduce(
88
+ (prev, act) => {
89
+ const { Entries: NewEntries, FailedEntryCount: newFailedCount } = act;
90
+ const { Entries, FailedEntryCount } = prev;
91
+
92
+ return {
93
+ Entries: [...(Entries || []), ...(NewEntries || [])],
94
+ FailedEntryCount: (FailedEntryCount || 0) + (newFailedCount || 0),
95
+ };
96
+ },
97
+ {
98
+ Entries: [],
99
+ FailedEntryCount: 0,
100
+ }
101
+ );
102
+
103
+ const { Entries, FailedEntryCount: failedCount } = aggregatedResult;
70
104
 
71
- const result = await eventbridgeClient.putEvents(eventsToSend).promise();
72
- const { Entries, FailedEntryCount: failedCount } = result;
73
105
  const [successful, failed] = partition(Entries, (entry) => entry.EventId);
74
106
 
75
107
  logger.info("eventbridge.client.sendEvents.end", {
@@ -0,0 +1,11 @@
1
+ import {
2
+ BasicEventbridgeClient,
3
+ EventDispatchResult,
4
+ LaraEvent,
5
+ } from "./basicEventbridgeClient";
6
+
7
+ export class MockEventbridgeClient extends BasicEventbridgeClient {
8
+ public mockSendEvents(events: LaraEvent[]): Promise<EventDispatchResult> {
9
+ return this.sendEvents(events);
10
+ }
11
+ }
@@ -0,0 +1,105 @@
1
+ export const fifteenEventsFixture = {
2
+ eventsToSend: [
3
+ { topic: "topic", body: { property: true } },
4
+ { topic: "topic", body: { property: true } },
5
+ { topic: "topic", body: { property: true } },
6
+ { topic: "topic", body: { property: true } },
7
+ { topic: "topic", body: { property: true } },
8
+ { topic: "topic", body: { property: true } },
9
+ { topic: "topic", body: { property: true } },
10
+ { topic: "topic", body: { property: true } },
11
+ { topic: "topic", body: { property: true } },
12
+ { topic: "topic", body: { property: true } },
13
+ { topic: "topic", body: { property: true } },
14
+ { topic: "topic", body: { property: true } },
15
+ { topic: "topic", body: { property: true } },
16
+ { topic: "topic", body: { property: true } },
17
+ { topic: "topic", body: { property: true } },
18
+ ],
19
+
20
+ allSuccessfulResponses: {
21
+ firstResponse: {
22
+ Entries: [
23
+ { EventId: 1 },
24
+ { EventId: 2 },
25
+ { EventId: 3 },
26
+ { EventId: 4 },
27
+ { EventId: 5 },
28
+ { EventId: 6 },
29
+ { EventId: 7 },
30
+ { EventId: 8 },
31
+ { EventId: 9 },
32
+ { EventId: 10 },
33
+ ],
34
+ FailedEntryCount: 0,
35
+ },
36
+
37
+ secondResponse: {
38
+ Entries: [
39
+ { EventId: 11 },
40
+ { EventId: 12 },
41
+ { EventId: 13 },
42
+ { EventId: 14 },
43
+ { EventId: 15 },
44
+ ],
45
+ FailedEntryCount: 0,
46
+ },
47
+ },
48
+
49
+ allUnsuccessfulResponses: {
50
+ firstResponse: {
51
+ Entries: [
52
+ { EventId: null },
53
+ { EventId: null },
54
+ { EventId: null },
55
+ { EventId: null },
56
+ { EventId: null },
57
+ { EventId: null },
58
+ { EventId: null },
59
+ { EventId: null },
60
+ { EventId: null },
61
+ { EventId: null },
62
+ ],
63
+ FailedEntryCount: 10,
64
+ },
65
+ secondResponse: {
66
+ Entries: [
67
+ { EventId: null },
68
+ { EventId: null },
69
+ { EventId: null },
70
+ { EventId: null },
71
+ { EventId: null },
72
+ ],
73
+ FailedEntryCount: 5,
74
+ },
75
+ },
76
+
77
+ sevenUnsuccessfulResponses: {
78
+ firstResponse: {
79
+ Entries: [
80
+ { EventId: 1 },
81
+ { EventId: 2 },
82
+ { EventId: 3 },
83
+ { EventId: 4 },
84
+ { EventId: null },
85
+ { EventId: null },
86
+ { EventId: null },
87
+ { EventId: null },
88
+ { EventId: null },
89
+ { EventId: null },
90
+ ],
91
+ FailedEntryCount: 6,
92
+ },
93
+
94
+ secondResponse: {
95
+ Entries: [
96
+ { EventId: 5 },
97
+ { EventId: 6 },
98
+ { EventId: 8 },
99
+ { EventId: null },
100
+ { EventId: 7 },
101
+ ],
102
+ FailedEntryCount: 1,
103
+ },
104
+ },
105
+ };
package/tsconfig.json CHANGED
@@ -4,12 +4,15 @@
4
4
  "outDir": "dist",
5
5
  "module": "commonjs",
6
6
  "target": "es6",
7
- "types": ["node"],
7
+ "types": ["jest", "node"],
8
8
  "esModuleInterop": true,
9
9
  "sourceMap": true,
10
10
 
11
11
  "alwaysStrict": true,
12
12
  "strictNullChecks": true
13
13
  },
14
- "exclude": ["node_modules"]
14
+ "exclude": ["node_modules"],
15
+ "paths": {
16
+ "@/*": ["src/*"]
17
+ }
15
18
  }