@quatrain/messaging 1.0.3 → 1.0.5
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.
- package/LICENSE.md +15 -0
- package/dist/Messaging.test.d.ts +1 -0
- package/dist/Messaging.test.js +258 -0
- package/dist/MockMessagingAdapter.d.ts +32 -0
- package/dist/MockMessagingAdapter.js +131 -0
- package/package.json +11 -13
- package/src/AbstractMessagingAdapter.ts +9 -0
- package/src/MessageFormatter.ts +11 -0
- package/src/Messaging.test.ts +318 -0
- package/src/Messaging.ts +44 -0
- package/src/MockMessagingAdapter.ts +163 -0
- package/src/index.ts +20 -0
- package/src/types/EmailCapableAdapter.ts +14 -0
- package/src/types/MessageType.ts +6 -0
- package/src/types/MessagingRecipient.ts +6 -0
- package/src/types/MessagingTemplate.ts +3 -0
- package/src/types/NotificationCapableAdapter.ts +14 -0
- package/src/types/NotificationMessage.ts +3 -0
- package/src/types/TextCapableAdapter.ts +14 -0
- package/lib/types/AbstractMessage.d.ts +0 -6
- package/lib/types/AbstractMessage.js +0 -2
- package/lib/types/EmailCapableAdapter copy.d.ts +0 -6
- package/lib/types/EmailCapableAdapter copy.js +0 -2
- package/lib/types/MessagingInterface.d.ts +0 -6
- package/lib/types/MessagingInterface.js +0 -2
- /package/{lib → dist}/AbstractMessagingAdapter.d.ts +0 -0
- /package/{lib → dist}/AbstractMessagingAdapter.js +0 -0
- /package/{lib → dist}/MessageFormatter.d.ts +0 -0
- /package/{lib → dist}/MessageFormatter.js +0 -0
- /package/{lib → dist}/Messaging.d.ts +0 -0
- /package/{lib → dist}/Messaging.js +0 -0
- /package/{lib → dist}/index.d.ts +0 -0
- /package/{lib → dist}/index.js +0 -0
- /package/{lib → dist}/types/EmailCapableAdapter.d.ts +0 -0
- /package/{lib → dist}/types/EmailCapableAdapter.js +0 -0
- /package/{lib → dist}/types/MessageType.d.ts +0 -0
- /package/{lib → dist}/types/MessageType.js +0 -0
- /package/{lib → dist}/types/MessagingRecipient.d.ts +0 -0
- /package/{lib → dist}/types/MessagingRecipient.js +0 -0
- /package/{lib → dist}/types/MessagingTemplate.d.ts +0 -0
- /package/{lib → dist}/types/MessagingTemplate.js +0 -0
- /package/{lib → dist}/types/NotificationCapableAdapter.d.ts +0 -0
- /package/{lib → dist}/types/NotificationCapableAdapter.js +0 -0
- /package/{lib → dist}/types/NotificationMessage.d.ts +0 -0
- /package/{lib → dist}/types/NotificationMessage.js +0 -0
- /package/{lib → dist}/types/TextCapableAdapter.d.ts +0 -0
- /package/{lib → dist}/types/TextCapableAdapter.js +0 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# LICENSE UPDATE NOTICE
|
|
2
|
+
|
|
3
|
+
As of 01/01/2026, Quatrain Core is licensed under the **GNU Affero General Public License v3.0 (AGPL v3)**.
|
|
4
|
+
Previous versions remain under the MIT License.
|
|
5
|
+
|
|
6
|
+
## Why AGPL?
|
|
7
|
+
|
|
8
|
+
We believe in open collaboration for the development ecosystem. The AGPL ensures that any modification or deployment of this BaaS stack, including over a network, benefits the entire community.
|
|
9
|
+
|
|
10
|
+
## Commercial Services & Enterprise Usage
|
|
11
|
+
|
|
12
|
+
We provide official deployment services, technical training, and certification for Quatrain Core.
|
|
13
|
+
For organizations requiring a non-copyleft license (commercial license) or custom proprietary integrations, please contact the copyright holder: **Quatrain Technologies**.
|
|
14
|
+
|
|
15
|
+
Copyright © 2024-2026 Quatrain Technologies. All Rights Reserved.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,258 @@
|
|
|
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 Messaging_1 = require("./Messaging");
|
|
13
|
+
const MockMessagingAdapter_1 = require("./MockMessagingAdapter");
|
|
14
|
+
describe('Messaging', () => {
|
|
15
|
+
let mockAdapter1;
|
|
16
|
+
let mockAdapter2;
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
// Reset messagers before each test
|
|
19
|
+
;
|
|
20
|
+
Messaging_1.Messaging._messagers = {};
|
|
21
|
+
Messaging_1.Messaging.defaultMessager = 'default';
|
|
22
|
+
// Create fresh mock adapters
|
|
23
|
+
mockAdapter1 = new MockMessagingAdapter_1.MockMessagingAdapter({ debug: false });
|
|
24
|
+
mockAdapter2 = new MockMessagingAdapter_1.MockMessagingAdapter({ debug: false });
|
|
25
|
+
});
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
// Clean up
|
|
28
|
+
mockAdapter1 === null || mockAdapter1 === void 0 ? void 0 : mockAdapter1.clearMessages();
|
|
29
|
+
mockAdapter2 === null || mockAdapter2 === void 0 ? void 0 : mockAdapter2.clearMessages();
|
|
30
|
+
});
|
|
31
|
+
describe('addMessager', () => {
|
|
32
|
+
it('should add a messager with an alias', () => {
|
|
33
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'test-messager');
|
|
34
|
+
const messager = Messaging_1.Messaging.getMessager('test-messager');
|
|
35
|
+
expect(messager).toBe(mockAdapter1);
|
|
36
|
+
});
|
|
37
|
+
it('should add multiple messagers with different aliases', () => {
|
|
38
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'messager1');
|
|
39
|
+
Messaging_1.Messaging.addMessager(mockAdapter2, 'messager2');
|
|
40
|
+
const messager1 = Messaging_1.Messaging.getMessager('messager1');
|
|
41
|
+
const messager2 = Messaging_1.Messaging.getMessager('messager2');
|
|
42
|
+
expect(messager1).toBe(mockAdapter1);
|
|
43
|
+
expect(messager2).toBe(mockAdapter2);
|
|
44
|
+
});
|
|
45
|
+
it('should set default messager when setDefault is true', () => {
|
|
46
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'custom-messager', true);
|
|
47
|
+
expect(Messaging_1.Messaging.defaultMessager).toBe('custom-messager');
|
|
48
|
+
const messager = Messaging_1.Messaging.getMessager(); // Should use default
|
|
49
|
+
expect(messager).toBe(mockAdapter1);
|
|
50
|
+
});
|
|
51
|
+
it('should not change default messager when setDefault is false', () => {
|
|
52
|
+
const originalDefault = Messaging_1.Messaging.defaultMessager;
|
|
53
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'custom-messager', false);
|
|
54
|
+
expect(Messaging_1.Messaging.defaultMessager).toBe(originalDefault);
|
|
55
|
+
});
|
|
56
|
+
it('should allow overriding an existing messager', () => {
|
|
57
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'messager');
|
|
58
|
+
Messaging_1.Messaging.addMessager(mockAdapter2, 'messager');
|
|
59
|
+
const messager = Messaging_1.Messaging.getMessager('messager');
|
|
60
|
+
expect(messager).toBe(mockAdapter2);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('getMessager', () => {
|
|
64
|
+
it('should return the correct messager by alias', () => {
|
|
65
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'test-messager');
|
|
66
|
+
const messager = Messaging_1.Messaging.getMessager('test-messager');
|
|
67
|
+
expect(messager).toBe(mockAdapter1);
|
|
68
|
+
});
|
|
69
|
+
it('should return the default messager when no alias is specified', () => {
|
|
70
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'default', true);
|
|
71
|
+
const messager = Messaging_1.Messaging.getMessager();
|
|
72
|
+
expect(messager).toBe(mockAdapter1);
|
|
73
|
+
});
|
|
74
|
+
it('should throw error when messager alias does not exist', () => {
|
|
75
|
+
expect(() => {
|
|
76
|
+
Messaging_1.Messaging.getMessager('non-existent');
|
|
77
|
+
}).toThrow("Unknown messager alias: 'non-existent'");
|
|
78
|
+
});
|
|
79
|
+
it('should throw error when getting default messager that does not exist', () => {
|
|
80
|
+
expect(() => {
|
|
81
|
+
Messaging_1.Messaging.getMessager();
|
|
82
|
+
}).toThrow("Unknown messager alias: 'default'");
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
describe('defaultMessager property', () => {
|
|
86
|
+
it('should have a default value of "default"', () => {
|
|
87
|
+
expect(Messaging_1.Messaging.defaultMessager).toBe('default');
|
|
88
|
+
});
|
|
89
|
+
it('should allow changing the default messager', () => {
|
|
90
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'new-default', true);
|
|
91
|
+
expect(Messaging_1.Messaging.defaultMessager).toBe('new-default');
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
describe('Integration with MockMessagingAdapter - Notifications', () => {
|
|
95
|
+
const recipient = {
|
|
96
|
+
firstname: 'John',
|
|
97
|
+
lastname: 'Doe',
|
|
98
|
+
email: 'john@example.com',
|
|
99
|
+
messageToken: 'test-token-123',
|
|
100
|
+
};
|
|
101
|
+
const message = {
|
|
102
|
+
title: 'Test Notification',
|
|
103
|
+
body: 'This is a test',
|
|
104
|
+
};
|
|
105
|
+
it('should send single notification', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
106
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'test');
|
|
107
|
+
const messager = Messaging_1.Messaging.getMessager('test');
|
|
108
|
+
const result = yield messager.sendNotification(recipient, message);
|
|
109
|
+
expect(result.success).toBe(true);
|
|
110
|
+
expect(result.messageId).toMatch(/^notif-/);
|
|
111
|
+
expect(messager.getMessageCount('notification')).toBe(1);
|
|
112
|
+
}));
|
|
113
|
+
it('should send batch notifications', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
114
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'test');
|
|
115
|
+
const messager = Messaging_1.Messaging.getMessager('test');
|
|
116
|
+
const recipients = [
|
|
117
|
+
recipient,
|
|
118
|
+
Object.assign(Object.assign({}, recipient), { email: 'jane@example.com' }),
|
|
119
|
+
];
|
|
120
|
+
const result = yield messager.sendNotifications(recipients, message);
|
|
121
|
+
expect(result.success).toBe(true);
|
|
122
|
+
expect(result.count).toBe(2);
|
|
123
|
+
expect(messager.getMessageCount('notification')).toBe(1); // One batch
|
|
124
|
+
}));
|
|
125
|
+
});
|
|
126
|
+
describe('Integration with MockMessagingAdapter - Emails', () => {
|
|
127
|
+
const recipient = {
|
|
128
|
+
firstname: 'John',
|
|
129
|
+
lastname: 'Doe',
|
|
130
|
+
email: 'john@example.com',
|
|
131
|
+
messageToken: 'test-token-123',
|
|
132
|
+
};
|
|
133
|
+
const message = {
|
|
134
|
+
title: 'Test Email',
|
|
135
|
+
body: 'This is a test email',
|
|
136
|
+
};
|
|
137
|
+
it('should send single email', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
138
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'test');
|
|
139
|
+
const messager = Messaging_1.Messaging.getMessager('test');
|
|
140
|
+
const result = yield messager.sendEmail(recipient, message);
|
|
141
|
+
expect(result.success).toBe(true);
|
|
142
|
+
expect(result.messageId).toMatch(/^email-/);
|
|
143
|
+
expect(messager.getMessageCount('email')).toBe(1);
|
|
144
|
+
}));
|
|
145
|
+
it('should send batch emails', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
146
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'test');
|
|
147
|
+
const messager = Messaging_1.Messaging.getMessager('test');
|
|
148
|
+
const recipients = [
|
|
149
|
+
recipient,
|
|
150
|
+
Object.assign(Object.assign({}, recipient), { email: 'jane@example.com' }),
|
|
151
|
+
];
|
|
152
|
+
const result = yield messager.sendEmails(recipients, message);
|
|
153
|
+
expect(result.success).toBe(true);
|
|
154
|
+
expect(result.count).toBe(2);
|
|
155
|
+
expect(messager.getMessageCount('email')).toBe(1); // One batch
|
|
156
|
+
}));
|
|
157
|
+
});
|
|
158
|
+
describe('Integration with MockMessagingAdapter - Text Messages', () => {
|
|
159
|
+
const recipient = {
|
|
160
|
+
firstname: 'John',
|
|
161
|
+
lastname: 'Doe',
|
|
162
|
+
email: 'john@example.com',
|
|
163
|
+
messageToken: 'test-token-123',
|
|
164
|
+
};
|
|
165
|
+
const message = {
|
|
166
|
+
title: 'Test SMS',
|
|
167
|
+
body: 'This is a test SMS',
|
|
168
|
+
};
|
|
169
|
+
it('should send single text message', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
170
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'test');
|
|
171
|
+
const messager = Messaging_1.Messaging.getMessager('test');
|
|
172
|
+
const result = yield messager.sendTextMessage(recipient, message);
|
|
173
|
+
expect(result.success).toBe(true);
|
|
174
|
+
expect(result.messageId).toMatch(/^text-/);
|
|
175
|
+
expect(messager.getMessageCount('text')).toBe(1);
|
|
176
|
+
}));
|
|
177
|
+
it('should send batch text messages', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
178
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'test');
|
|
179
|
+
const messager = Messaging_1.Messaging.getMessager('test');
|
|
180
|
+
const recipients = [
|
|
181
|
+
recipient,
|
|
182
|
+
Object.assign(Object.assign({}, recipient), { email: 'jane@example.com' }),
|
|
183
|
+
];
|
|
184
|
+
const result = yield messager.sendTextMessages(recipients, message);
|
|
185
|
+
expect(result.success).toBe(true);
|
|
186
|
+
expect(result.count).toBe(2);
|
|
187
|
+
expect(messager.getMessageCount('text')).toBe(1); // One batch
|
|
188
|
+
}));
|
|
189
|
+
});
|
|
190
|
+
describe('Integration - Multiple message types', () => {
|
|
191
|
+
it('should maintain separate state between messagers', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
192
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'messager1');
|
|
193
|
+
Messaging_1.Messaging.addMessager(mockAdapter2, 'messager2');
|
|
194
|
+
const adapter1 = Messaging_1.Messaging.getMessager('messager1');
|
|
195
|
+
const adapter2 = Messaging_1.Messaging.getMessager('messager2');
|
|
196
|
+
const recipient = {
|
|
197
|
+
firstname: 'Test',
|
|
198
|
+
lastname: 'User',
|
|
199
|
+
email: 'test@example.com',
|
|
200
|
+
messageToken: 'test-token-123',
|
|
201
|
+
};
|
|
202
|
+
const message = {
|
|
203
|
+
title: 'Test',
|
|
204
|
+
body: 'Test message',
|
|
205
|
+
};
|
|
206
|
+
yield adapter1.sendNotification(recipient, message);
|
|
207
|
+
yield adapter2.sendEmail(recipient, message);
|
|
208
|
+
expect(adapter1.getMessageCount('notification')).toBe(1);
|
|
209
|
+
expect(adapter1.getMessageCount('email')).toBe(0);
|
|
210
|
+
expect(adapter2.getMessageCount('email')).toBe(1);
|
|
211
|
+
expect(adapter2.getMessageCount('notification')).toBe(0);
|
|
212
|
+
}));
|
|
213
|
+
it('should track different message types', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
214
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'test');
|
|
215
|
+
const messager = Messaging_1.Messaging.getMessager('test');
|
|
216
|
+
const recipient = {
|
|
217
|
+
firstname: 'Test',
|
|
218
|
+
lastname: 'User',
|
|
219
|
+
email: 'test@example.com',
|
|
220
|
+
messageToken: 'test-token-123',
|
|
221
|
+
};
|
|
222
|
+
const message = {
|
|
223
|
+
title: 'Test',
|
|
224
|
+
body: 'Test message',
|
|
225
|
+
};
|
|
226
|
+
yield messager.sendNotification(recipient, message);
|
|
227
|
+
yield messager.sendEmail(recipient, message);
|
|
228
|
+
yield messager.sendTextMessage(recipient, message);
|
|
229
|
+
expect(messager.getMessageCount()).toBe(3);
|
|
230
|
+
expect(messager.getMessageCount('notification')).toBe(1);
|
|
231
|
+
expect(messager.getMessageCount('email')).toBe(1);
|
|
232
|
+
expect(messager.getMessageCount('text')).toBe(1);
|
|
233
|
+
}));
|
|
234
|
+
});
|
|
235
|
+
describe('Messaging registry behavior', () => {
|
|
236
|
+
it('should maintain messagers across multiple operations', () => {
|
|
237
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'persistent');
|
|
238
|
+
// Multiple get operations
|
|
239
|
+
const messager1 = Messaging_1.Messaging.getMessager('persistent');
|
|
240
|
+
const messager2 = Messaging_1.Messaging.getMessager('persistent');
|
|
241
|
+
expect(messager1).toBe(messager2);
|
|
242
|
+
expect(messager1).toBe(mockAdapter1);
|
|
243
|
+
});
|
|
244
|
+
it('should handle switching default messager multiple times', () => {
|
|
245
|
+
Messaging_1.Messaging.addMessager(mockAdapter1, 'messager1', true);
|
|
246
|
+
expect(Messaging_1.Messaging.defaultMessager).toBe('messager1');
|
|
247
|
+
Messaging_1.Messaging.addMessager(mockAdapter2, 'messager2', true);
|
|
248
|
+
expect(Messaging_1.Messaging.defaultMessager).toBe('messager2');
|
|
249
|
+
const defaultMessager = Messaging_1.Messaging.getMessager();
|
|
250
|
+
expect(defaultMessager).toBe(mockAdapter2);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
describe('logger', () => {
|
|
254
|
+
it('should have a logger instance', () => {
|
|
255
|
+
expect(Messaging_1.Messaging.logger).toBeDefined();
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { AbstractMessagingAdapter } from './AbstractMessagingAdapter';
|
|
2
|
+
import { NotificationCapableAdapter } from './types/NotificationCapableAdapter';
|
|
3
|
+
import { EmailCapableAdapter } from './types/EmailCapableAdapter';
|
|
4
|
+
import { TextCapableAdapter } from './types/TextCapableAdapter';
|
|
5
|
+
import { MessagingRecipient } from './types/MessagingRecipient';
|
|
6
|
+
import { NotificationMessage } from './types/NotificationMessage';
|
|
7
|
+
import { MessagingParameters } from './Messaging';
|
|
8
|
+
interface SentMessage {
|
|
9
|
+
type: 'notification' | 'email' | 'text';
|
|
10
|
+
recipient: MessagingRecipient | MessagingRecipient[];
|
|
11
|
+
message: NotificationMessage;
|
|
12
|
+
timestamp: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Mock Messaging Adapter for testing purposes
|
|
16
|
+
* Implements all three capability interfaces
|
|
17
|
+
*/
|
|
18
|
+
export declare class MockMessagingAdapter extends AbstractMessagingAdapter implements NotificationCapableAdapter, EmailCapableAdapter, TextCapableAdapter {
|
|
19
|
+
private sentMessages;
|
|
20
|
+
constructor(params?: MessagingParameters);
|
|
21
|
+
sendNotification(recipient: MessagingRecipient, message: NotificationMessage): Promise<any>;
|
|
22
|
+
sendNotifications(recipients: MessagingRecipient[], message: NotificationMessage): Promise<any>;
|
|
23
|
+
sendEmail(recipient: MessagingRecipient, message: NotificationMessage): Promise<any>;
|
|
24
|
+
sendEmails(recipients: MessagingRecipient[], message: NotificationMessage): Promise<any>;
|
|
25
|
+
sendTextMessage(recipient: MessagingRecipient, message: NotificationMessage): Promise<any>;
|
|
26
|
+
sendTextMessages(recipients: MessagingRecipient[], message: NotificationMessage): Promise<any>;
|
|
27
|
+
getSentMessages(type?: 'notification' | 'email' | 'text'): SentMessage[];
|
|
28
|
+
clearMessages(): void;
|
|
29
|
+
getMessageCount(type?: 'notification' | 'email' | 'text'): number;
|
|
30
|
+
getLastMessage(type?: 'notification' | 'email' | 'text'): SentMessage | undefined;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,131 @@
|
|
|
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.MockMessagingAdapter = void 0;
|
|
13
|
+
const AbstractMessagingAdapter_1 = require("./AbstractMessagingAdapter");
|
|
14
|
+
/**
|
|
15
|
+
* Mock Messaging Adapter for testing purposes
|
|
16
|
+
* Implements all three capability interfaces
|
|
17
|
+
*/
|
|
18
|
+
class MockMessagingAdapter extends AbstractMessagingAdapter_1.AbstractMessagingAdapter {
|
|
19
|
+
constructor(params = {}) {
|
|
20
|
+
super(params);
|
|
21
|
+
this.sentMessages = [];
|
|
22
|
+
}
|
|
23
|
+
sendNotification(recipient, message) {
|
|
24
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
this.sentMessages.push({
|
|
26
|
+
type: 'notification',
|
|
27
|
+
recipient,
|
|
28
|
+
message,
|
|
29
|
+
timestamp: Date.now(),
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
success: true,
|
|
33
|
+
messageId: `notif-${Date.now()}`,
|
|
34
|
+
recipient: recipient.email,
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
sendNotifications(recipients, message) {
|
|
39
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
40
|
+
this.sentMessages.push({
|
|
41
|
+
type: 'notification',
|
|
42
|
+
recipient: recipients,
|
|
43
|
+
message,
|
|
44
|
+
timestamp: Date.now(),
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
success: true,
|
|
48
|
+
count: recipients.length,
|
|
49
|
+
messageIds: recipients.map((r, i) => `notif-batch-${Date.now()}-${i}`),
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
sendEmail(recipient, message) {
|
|
54
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
55
|
+
this.sentMessages.push({
|
|
56
|
+
type: 'email',
|
|
57
|
+
recipient,
|
|
58
|
+
message,
|
|
59
|
+
timestamp: Date.now(),
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
success: true,
|
|
63
|
+
messageId: `email-${Date.now()}`,
|
|
64
|
+
recipient: recipient.email,
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
sendEmails(recipients, message) {
|
|
69
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
70
|
+
this.sentMessages.push({
|
|
71
|
+
type: 'email',
|
|
72
|
+
recipient: recipients,
|
|
73
|
+
message,
|
|
74
|
+
timestamp: Date.now(),
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
success: true,
|
|
78
|
+
count: recipients.length,
|
|
79
|
+
messageIds: recipients.map((r, i) => `email-batch-${Date.now()}-${i}`),
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
sendTextMessage(recipient, message) {
|
|
84
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
85
|
+
this.sentMessages.push({
|
|
86
|
+
type: 'text',
|
|
87
|
+
recipient,
|
|
88
|
+
message,
|
|
89
|
+
timestamp: Date.now(),
|
|
90
|
+
});
|
|
91
|
+
return {
|
|
92
|
+
success: true,
|
|
93
|
+
messageId: `text-${Date.now()}`,
|
|
94
|
+
recipient: recipient.email,
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
sendTextMessages(recipients, message) {
|
|
99
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
100
|
+
this.sentMessages.push({
|
|
101
|
+
type: 'text',
|
|
102
|
+
recipient: recipients,
|
|
103
|
+
message,
|
|
104
|
+
timestamp: Date.now(),
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
success: true,
|
|
108
|
+
count: recipients.length,
|
|
109
|
+
messageIds: recipients.map((r, i) => `text-batch-${Date.now()}-${i}`),
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// Helper methods for testing
|
|
114
|
+
getSentMessages(type) {
|
|
115
|
+
if (type) {
|
|
116
|
+
return this.sentMessages.filter((msg) => msg.type === type);
|
|
117
|
+
}
|
|
118
|
+
return this.sentMessages;
|
|
119
|
+
}
|
|
120
|
+
clearMessages() {
|
|
121
|
+
this.sentMessages = [];
|
|
122
|
+
}
|
|
123
|
+
getMessageCount(type) {
|
|
124
|
+
return this.getSentMessages(type).length;
|
|
125
|
+
}
|
|
126
|
+
getLastMessage(type) {
|
|
127
|
+
const messages = this.getSentMessages(type);
|
|
128
|
+
return messages[messages.length - 1];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
exports.MockMessagingAdapter = MockMessagingAdapter;
|
package/package.json
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quatrain/messaging",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"license": "
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"license": "AGPL-3.0-only",
|
|
5
5
|
"description": "Messaging adapters commons",
|
|
6
|
-
"main": "
|
|
7
|
-
"types": "
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bun": "src/index.ts",
|
|
8
9
|
"files": [
|
|
9
|
-
"
|
|
10
|
+
"LICENSE.md",
|
|
11
|
+
"src/",
|
|
12
|
+
"dist/",
|
|
10
13
|
"README.md"
|
|
11
14
|
],
|
|
12
15
|
"author": "Quatrain Développement SAS <developers@quatrain.com>",
|
|
13
16
|
"dependencies": {
|
|
14
|
-
"@quatrain/core": "^1.1.
|
|
17
|
+
"@quatrain/core": "^1.1.45",
|
|
15
18
|
"mustache": "^4.2.0"
|
|
16
19
|
},
|
|
17
20
|
"devDependencies": {
|
|
@@ -19,7 +22,7 @@
|
|
|
19
22
|
"@types/jest": "^27.0.3",
|
|
20
23
|
"@types/mustache": "^4",
|
|
21
24
|
"@types/node": "^22.10.1",
|
|
22
|
-
"jest": "^
|
|
25
|
+
"jest": "^30.2.0",
|
|
23
26
|
"jest-node-exports-resolver": "^1.1.6",
|
|
24
27
|
"jest-serial-runner": "^1.2.1",
|
|
25
28
|
"trace-unhandled": "^2.0.1",
|
|
@@ -31,11 +34,6 @@
|
|
|
31
34
|
"test-ci": "jest --runInBand",
|
|
32
35
|
"build": "tsc",
|
|
33
36
|
"wbuild": "tsc --watch",
|
|
34
|
-
"bump-to": "yarn version"
|
|
35
|
-
"hash": "node ../../bin/hashFolder.js",
|
|
36
|
-
"hash:persist": "yarn hash > .hash_latest.txt",
|
|
37
|
-
"hash:compare": "yarn hash > .hash_newest.txt && cmp -s .hash_latest.txt .hash_newest.txt",
|
|
38
|
-
"publish": "yarn hash:compare || yarn publish:process",
|
|
39
|
-
"publish:process": "yarn version patch && yarn build && yarn npm publish --access public && yarn hash:persist"
|
|
37
|
+
"bump-to": "yarn version"
|
|
40
38
|
}
|
|
41
39
|
}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { Messaging, MessagingParameters } from './Messaging'
|
|
2
|
+
import { MockMessagingAdapter } from './MockMessagingAdapter'
|
|
3
|
+
import { MessagingRecipient } from './types/MessagingRecipient'
|
|
4
|
+
import { NotificationMessage } from './types/NotificationMessage'
|
|
5
|
+
|
|
6
|
+
describe('Messaging', () => {
|
|
7
|
+
let mockAdapter1: MockMessagingAdapter
|
|
8
|
+
let mockAdapter2: MockMessagingAdapter
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
// Reset messagers before each test
|
|
12
|
+
;(Messaging as any)._messagers = {}
|
|
13
|
+
Messaging.defaultMessager = 'default'
|
|
14
|
+
|
|
15
|
+
// Create fresh mock adapters
|
|
16
|
+
mockAdapter1 = new MockMessagingAdapter({ debug: false })
|
|
17
|
+
mockAdapter2 = new MockMessagingAdapter({ debug: false })
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
// Clean up
|
|
22
|
+
mockAdapter1?.clearMessages()
|
|
23
|
+
mockAdapter2?.clearMessages()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
describe('addMessager', () => {
|
|
27
|
+
it('should add a messager with an alias', () => {
|
|
28
|
+
Messaging.addMessager(mockAdapter1, 'test-messager')
|
|
29
|
+
|
|
30
|
+
const messager = Messaging.getMessager('test-messager')
|
|
31
|
+
expect(messager).toBe(mockAdapter1)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should add multiple messagers with different aliases', () => {
|
|
35
|
+
Messaging.addMessager(mockAdapter1, 'messager1')
|
|
36
|
+
Messaging.addMessager(mockAdapter2, 'messager2')
|
|
37
|
+
|
|
38
|
+
const messager1 = Messaging.getMessager('messager1')
|
|
39
|
+
const messager2 = Messaging.getMessager('messager2')
|
|
40
|
+
|
|
41
|
+
expect(messager1).toBe(mockAdapter1)
|
|
42
|
+
expect(messager2).toBe(mockAdapter2)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('should set default messager when setDefault is true', () => {
|
|
46
|
+
Messaging.addMessager(mockAdapter1, 'custom-messager', true)
|
|
47
|
+
|
|
48
|
+
expect(Messaging.defaultMessager).toBe('custom-messager')
|
|
49
|
+
const messager = Messaging.getMessager() // Should use default
|
|
50
|
+
expect(messager).toBe(mockAdapter1)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('should not change default messager when setDefault is false', () => {
|
|
54
|
+
const originalDefault = Messaging.defaultMessager
|
|
55
|
+
Messaging.addMessager(mockAdapter1, 'custom-messager', false)
|
|
56
|
+
|
|
57
|
+
expect(Messaging.defaultMessager).toBe(originalDefault)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('should allow overriding an existing messager', () => {
|
|
61
|
+
Messaging.addMessager(mockAdapter1, 'messager')
|
|
62
|
+
Messaging.addMessager(mockAdapter2, 'messager')
|
|
63
|
+
|
|
64
|
+
const messager = Messaging.getMessager('messager')
|
|
65
|
+
expect(messager).toBe(mockAdapter2)
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe('getMessager', () => {
|
|
70
|
+
it('should return the correct messager by alias', () => {
|
|
71
|
+
Messaging.addMessager(mockAdapter1, 'test-messager')
|
|
72
|
+
|
|
73
|
+
const messager = Messaging.getMessager('test-messager')
|
|
74
|
+
expect(messager).toBe(mockAdapter1)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should return the default messager when no alias is specified', () => {
|
|
78
|
+
Messaging.addMessager(mockAdapter1, 'default', true)
|
|
79
|
+
|
|
80
|
+
const messager = Messaging.getMessager()
|
|
81
|
+
expect(messager).toBe(mockAdapter1)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('should throw error when messager alias does not exist', () => {
|
|
85
|
+
expect(() => {
|
|
86
|
+
Messaging.getMessager('non-existent')
|
|
87
|
+
}).toThrow("Unknown messager alias: 'non-existent'")
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('should throw error when getting default messager that does not exist', () => {
|
|
91
|
+
expect(() => {
|
|
92
|
+
Messaging.getMessager()
|
|
93
|
+
}).toThrow("Unknown messager alias: 'default'")
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
describe('defaultMessager property', () => {
|
|
98
|
+
it('should have a default value of "default"', () => {
|
|
99
|
+
expect(Messaging.defaultMessager).toBe('default')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('should allow changing the default messager', () => {
|
|
103
|
+
Messaging.addMessager(mockAdapter1, 'new-default', true)
|
|
104
|
+
expect(Messaging.defaultMessager).toBe('new-default')
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
describe('Integration with MockMessagingAdapter - Notifications', () => {
|
|
109
|
+
const recipient: MessagingRecipient = {
|
|
110
|
+
firstname: 'John',
|
|
111
|
+
lastname: 'Doe',
|
|
112
|
+
email: 'john@example.com',
|
|
113
|
+
messageToken: 'test-token-123',
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const message: NotificationMessage = {
|
|
117
|
+
title: 'Test Notification',
|
|
118
|
+
body: 'This is a test',
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
it('should send single notification', async () => {
|
|
122
|
+
Messaging.addMessager(mockAdapter1, 'test')
|
|
123
|
+
const messager = Messaging.getMessager('test') as MockMessagingAdapter
|
|
124
|
+
|
|
125
|
+
const result = await messager.sendNotification(recipient, message)
|
|
126
|
+
|
|
127
|
+
expect(result.success).toBe(true)
|
|
128
|
+
expect(result.messageId).toMatch(/^notif-/)
|
|
129
|
+
expect(messager.getMessageCount('notification')).toBe(1)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('should send batch notifications', async () => {
|
|
133
|
+
Messaging.addMessager(mockAdapter1, 'test')
|
|
134
|
+
const messager = Messaging.getMessager('test') as MockMessagingAdapter
|
|
135
|
+
|
|
136
|
+
const recipients = [
|
|
137
|
+
recipient,
|
|
138
|
+
{ ...recipient, email: 'jane@example.com' },
|
|
139
|
+
]
|
|
140
|
+
const result = await messager.sendNotifications(recipients, message)
|
|
141
|
+
|
|
142
|
+
expect(result.success).toBe(true)
|
|
143
|
+
expect(result.count).toBe(2)
|
|
144
|
+
expect(messager.getMessageCount('notification')).toBe(1) // One batch
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
describe('Integration with MockMessagingAdapter - Emails', () => {
|
|
149
|
+
const recipient: MessagingRecipient = {
|
|
150
|
+
firstname: 'John',
|
|
151
|
+
lastname: 'Doe',
|
|
152
|
+
email: 'john@example.com',
|
|
153
|
+
messageToken: 'test-token-123',
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const message: NotificationMessage = {
|
|
157
|
+
title: 'Test Email',
|
|
158
|
+
body: 'This is a test email',
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
it('should send single email', async () => {
|
|
162
|
+
Messaging.addMessager(mockAdapter1, 'test')
|
|
163
|
+
const messager = Messaging.getMessager('test') as MockMessagingAdapter
|
|
164
|
+
|
|
165
|
+
const result = await messager.sendEmail(recipient, message)
|
|
166
|
+
|
|
167
|
+
expect(result.success).toBe(true)
|
|
168
|
+
expect(result.messageId).toMatch(/^email-/)
|
|
169
|
+
expect(messager.getMessageCount('email')).toBe(1)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('should send batch emails', async () => {
|
|
173
|
+
Messaging.addMessager(mockAdapter1, 'test')
|
|
174
|
+
const messager = Messaging.getMessager('test') as MockMessagingAdapter
|
|
175
|
+
|
|
176
|
+
const recipients = [
|
|
177
|
+
recipient,
|
|
178
|
+
{ ...recipient, email: 'jane@example.com' },
|
|
179
|
+
]
|
|
180
|
+
const result = await messager.sendEmails(recipients, message)
|
|
181
|
+
|
|
182
|
+
expect(result.success).toBe(true)
|
|
183
|
+
expect(result.count).toBe(2)
|
|
184
|
+
expect(messager.getMessageCount('email')).toBe(1) // One batch
|
|
185
|
+
})
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
describe('Integration with MockMessagingAdapter - Text Messages', () => {
|
|
189
|
+
const recipient: MessagingRecipient = {
|
|
190
|
+
firstname: 'John',
|
|
191
|
+
lastname: 'Doe',
|
|
192
|
+
email: 'john@example.com',
|
|
193
|
+
messageToken: 'test-token-123',
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const message: NotificationMessage = {
|
|
197
|
+
title: 'Test SMS',
|
|
198
|
+
body: 'This is a test SMS',
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
it('should send single text message', async () => {
|
|
202
|
+
Messaging.addMessager(mockAdapter1, 'test')
|
|
203
|
+
const messager = Messaging.getMessager('test') as MockMessagingAdapter
|
|
204
|
+
|
|
205
|
+
const result = await messager.sendTextMessage(recipient, message)
|
|
206
|
+
|
|
207
|
+
expect(result.success).toBe(true)
|
|
208
|
+
expect(result.messageId).toMatch(/^text-/)
|
|
209
|
+
expect(messager.getMessageCount('text')).toBe(1)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('should send batch text messages', async () => {
|
|
213
|
+
Messaging.addMessager(mockAdapter1, 'test')
|
|
214
|
+
const messager = Messaging.getMessager('test') as MockMessagingAdapter
|
|
215
|
+
|
|
216
|
+
const recipients = [
|
|
217
|
+
recipient,
|
|
218
|
+
{ ...recipient, email: 'jane@example.com' },
|
|
219
|
+
]
|
|
220
|
+
const result = await messager.sendTextMessages(recipients, message)
|
|
221
|
+
|
|
222
|
+
expect(result.success).toBe(true)
|
|
223
|
+
expect(result.count).toBe(2)
|
|
224
|
+
expect(messager.getMessageCount('text')).toBe(1) // One batch
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
describe('Integration - Multiple message types', () => {
|
|
229
|
+
it('should maintain separate state between messagers', async () => {
|
|
230
|
+
Messaging.addMessager(mockAdapter1, 'messager1')
|
|
231
|
+
Messaging.addMessager(mockAdapter2, 'messager2')
|
|
232
|
+
|
|
233
|
+
const adapter1 = Messaging.getMessager(
|
|
234
|
+
'messager1'
|
|
235
|
+
) as MockMessagingAdapter
|
|
236
|
+
const adapter2 = Messaging.getMessager(
|
|
237
|
+
'messager2'
|
|
238
|
+
) as MockMessagingAdapter
|
|
239
|
+
|
|
240
|
+
const recipient: MessagingRecipient = {
|
|
241
|
+
firstname: 'Test',
|
|
242
|
+
lastname: 'User',
|
|
243
|
+
email: 'test@example.com',
|
|
244
|
+
messageToken: 'test-token-123',
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const message: NotificationMessage = {
|
|
248
|
+
title: 'Test',
|
|
249
|
+
body: 'Test message',
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
await adapter1.sendNotification(recipient, message)
|
|
253
|
+
await adapter2.sendEmail(recipient, message)
|
|
254
|
+
|
|
255
|
+
expect(adapter1.getMessageCount('notification')).toBe(1)
|
|
256
|
+
expect(adapter1.getMessageCount('email')).toBe(0)
|
|
257
|
+
|
|
258
|
+
expect(adapter2.getMessageCount('email')).toBe(1)
|
|
259
|
+
expect(adapter2.getMessageCount('notification')).toBe(0)
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('should track different message types', async () => {
|
|
263
|
+
Messaging.addMessager(mockAdapter1, 'test')
|
|
264
|
+
const messager = Messaging.getMessager('test') as MockMessagingAdapter
|
|
265
|
+
|
|
266
|
+
const recipient: MessagingRecipient = {
|
|
267
|
+
firstname: 'Test',
|
|
268
|
+
lastname: 'User',
|
|
269
|
+
email: 'test@example.com',
|
|
270
|
+
messageToken: 'test-token-123',
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const message: NotificationMessage = {
|
|
274
|
+
title: 'Test',
|
|
275
|
+
body: 'Test message',
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
await messager.sendNotification(recipient, message)
|
|
279
|
+
await messager.sendEmail(recipient, message)
|
|
280
|
+
await messager.sendTextMessage(recipient, message)
|
|
281
|
+
|
|
282
|
+
expect(messager.getMessageCount()).toBe(3)
|
|
283
|
+
expect(messager.getMessageCount('notification')).toBe(1)
|
|
284
|
+
expect(messager.getMessageCount('email')).toBe(1)
|
|
285
|
+
expect(messager.getMessageCount('text')).toBe(1)
|
|
286
|
+
})
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
describe('Messaging registry behavior', () => {
|
|
290
|
+
it('should maintain messagers across multiple operations', () => {
|
|
291
|
+
Messaging.addMessager(mockAdapter1, 'persistent')
|
|
292
|
+
|
|
293
|
+
// Multiple get operations
|
|
294
|
+
const messager1 = Messaging.getMessager('persistent')
|
|
295
|
+
const messager2 = Messaging.getMessager('persistent')
|
|
296
|
+
|
|
297
|
+
expect(messager1).toBe(messager2)
|
|
298
|
+
expect(messager1).toBe(mockAdapter1)
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
it('should handle switching default messager multiple times', () => {
|
|
302
|
+
Messaging.addMessager(mockAdapter1, 'messager1', true)
|
|
303
|
+
expect(Messaging.defaultMessager).toBe('messager1')
|
|
304
|
+
|
|
305
|
+
Messaging.addMessager(mockAdapter2, 'messager2', true)
|
|
306
|
+
expect(Messaging.defaultMessager).toBe('messager2')
|
|
307
|
+
|
|
308
|
+
const defaultMessager = Messaging.getMessager()
|
|
309
|
+
expect(defaultMessager).toBe(mockAdapter2)
|
|
310
|
+
})
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
describe('logger', () => {
|
|
314
|
+
it('should have a logger instance', () => {
|
|
315
|
+
expect(Messaging.logger).toBeDefined()
|
|
316
|
+
})
|
|
317
|
+
})
|
|
318
|
+
})
|
package/src/Messaging.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Core } from '@quatrain/core'
|
|
2
|
+
import { NotificationCapableAdapter } from './types/NotificationCapableAdapter'
|
|
3
|
+
import { EmailCapableAdapter } from './types/EmailCapableAdapter'
|
|
4
|
+
import { TextCapableAdapter } from './types/TextCapableAdapter'
|
|
5
|
+
|
|
6
|
+
export type MessagingRegistry<T extends NotificationCapableAdapter
|
|
7
|
+
| EmailCapableAdapter
|
|
8
|
+
| TextCapableAdapter> = {
|
|
9
|
+
[x: string]: T
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface MessagingParameters {
|
|
13
|
+
config?: any
|
|
14
|
+
debug?: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class Messaging extends Core {
|
|
18
|
+
static defaultMessager = 'default'
|
|
19
|
+
static logger = this.addLogger('Messaging')
|
|
20
|
+
|
|
21
|
+
protected static _messagers: MessagingRegistry<any> = {}
|
|
22
|
+
|
|
23
|
+
static addMessager(
|
|
24
|
+
messager:
|
|
25
|
+
| NotificationCapableAdapter
|
|
26
|
+
| EmailCapableAdapter
|
|
27
|
+
| TextCapableAdapter,
|
|
28
|
+
alias: string,
|
|
29
|
+
setDefault: boolean = false
|
|
30
|
+
) {
|
|
31
|
+
this._messagers[alias] = messager
|
|
32
|
+
if (setDefault) {
|
|
33
|
+
this.defaultMessager = alias
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static getMessager(alias: string = this.defaultMessager) {
|
|
38
|
+
if (this._messagers[alias]) {
|
|
39
|
+
return this._messagers[alias]
|
|
40
|
+
} else {
|
|
41
|
+
throw new Error(`Unknown messager alias: '${alias}'`)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { AbstractMessagingAdapter } from './AbstractMessagingAdapter'
|
|
2
|
+
import { NotificationCapableAdapter } from './types/NotificationCapableAdapter'
|
|
3
|
+
import { EmailCapableAdapter } from './types/EmailCapableAdapter'
|
|
4
|
+
import { TextCapableAdapter } from './types/TextCapableAdapter'
|
|
5
|
+
import { MessagingRecipient } from './types/MessagingRecipient'
|
|
6
|
+
import { NotificationMessage } from './types/NotificationMessage'
|
|
7
|
+
import { MessagingParameters } from './Messaging'
|
|
8
|
+
|
|
9
|
+
interface SentMessage {
|
|
10
|
+
type: 'notification' | 'email' | 'text'
|
|
11
|
+
recipient: MessagingRecipient | MessagingRecipient[]
|
|
12
|
+
message: NotificationMessage
|
|
13
|
+
timestamp: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Mock Messaging Adapter for testing purposes
|
|
18
|
+
* Implements all three capability interfaces
|
|
19
|
+
*/
|
|
20
|
+
export class MockMessagingAdapter
|
|
21
|
+
extends AbstractMessagingAdapter
|
|
22
|
+
implements
|
|
23
|
+
NotificationCapableAdapter,
|
|
24
|
+
EmailCapableAdapter,
|
|
25
|
+
TextCapableAdapter
|
|
26
|
+
{
|
|
27
|
+
private sentMessages: SentMessage[] = []
|
|
28
|
+
|
|
29
|
+
constructor(params: MessagingParameters = {}) {
|
|
30
|
+
super(params)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async sendNotification(
|
|
34
|
+
recipient: MessagingRecipient,
|
|
35
|
+
message: NotificationMessage
|
|
36
|
+
): Promise<any> {
|
|
37
|
+
this.sentMessages.push({
|
|
38
|
+
type: 'notification',
|
|
39
|
+
recipient,
|
|
40
|
+
message,
|
|
41
|
+
timestamp: Date.now(),
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
success: true,
|
|
46
|
+
messageId: `notif-${Date.now()}`,
|
|
47
|
+
recipient: recipient.email,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async sendNotifications(
|
|
52
|
+
recipients: MessagingRecipient[],
|
|
53
|
+
message: NotificationMessage
|
|
54
|
+
): Promise<any> {
|
|
55
|
+
this.sentMessages.push({
|
|
56
|
+
type: 'notification',
|
|
57
|
+
recipient: recipients,
|
|
58
|
+
message,
|
|
59
|
+
timestamp: Date.now(),
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
count: recipients.length,
|
|
65
|
+
messageIds: recipients.map((r, i) => `notif-batch-${Date.now()}-${i}`),
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async sendEmail(
|
|
70
|
+
recipient: MessagingRecipient,
|
|
71
|
+
message: NotificationMessage
|
|
72
|
+
): Promise<any> {
|
|
73
|
+
this.sentMessages.push({
|
|
74
|
+
type: 'email',
|
|
75
|
+
recipient,
|
|
76
|
+
message,
|
|
77
|
+
timestamp: Date.now(),
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
success: true,
|
|
82
|
+
messageId: `email-${Date.now()}`,
|
|
83
|
+
recipient: recipient.email,
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async sendEmails(
|
|
88
|
+
recipients: MessagingRecipient[],
|
|
89
|
+
message: NotificationMessage
|
|
90
|
+
): Promise<any> {
|
|
91
|
+
this.sentMessages.push({
|
|
92
|
+
type: 'email',
|
|
93
|
+
recipient: recipients,
|
|
94
|
+
message,
|
|
95
|
+
timestamp: Date.now(),
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
success: true,
|
|
100
|
+
count: recipients.length,
|
|
101
|
+
messageIds: recipients.map((r, i) => `email-batch-${Date.now()}-${i}`),
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async sendTextMessage(
|
|
106
|
+
recipient: MessagingRecipient,
|
|
107
|
+
message: NotificationMessage
|
|
108
|
+
): Promise<any> {
|
|
109
|
+
this.sentMessages.push({
|
|
110
|
+
type: 'text',
|
|
111
|
+
recipient,
|
|
112
|
+
message,
|
|
113
|
+
timestamp: Date.now(),
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
success: true,
|
|
118
|
+
messageId: `text-${Date.now()}`,
|
|
119
|
+
recipient: recipient.email,
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async sendTextMessages(
|
|
124
|
+
recipients: MessagingRecipient[],
|
|
125
|
+
message: NotificationMessage
|
|
126
|
+
): Promise<any> {
|
|
127
|
+
this.sentMessages.push({
|
|
128
|
+
type: 'text',
|
|
129
|
+
recipient: recipients,
|
|
130
|
+
message,
|
|
131
|
+
timestamp: Date.now(),
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
success: true,
|
|
136
|
+
count: recipients.length,
|
|
137
|
+
messageIds: recipients.map((r, i) => `text-batch-${Date.now()}-${i}`),
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Helper methods for testing
|
|
142
|
+
getSentMessages(type?: 'notification' | 'email' | 'text'): SentMessage[] {
|
|
143
|
+
if (type) {
|
|
144
|
+
return this.sentMessages.filter((msg) => msg.type === type)
|
|
145
|
+
}
|
|
146
|
+
return this.sentMessages
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
clearMessages(): void {
|
|
150
|
+
this.sentMessages = []
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
getMessageCount(type?: 'notification' | 'email' | 'text'): number {
|
|
154
|
+
return this.getSentMessages(type).length
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
getLastMessage(
|
|
158
|
+
type?: 'notification' | 'email' | 'text'
|
|
159
|
+
): SentMessage | undefined {
|
|
160
|
+
const messages = this.getSentMessages(type)
|
|
161
|
+
return messages[messages.length - 1]
|
|
162
|
+
}
|
|
163
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Messaging, MessagingParameters } from './Messaging'
|
|
2
|
+
import { AbstractMessagingAdapter } from './AbstractMessagingAdapter'
|
|
3
|
+
import { MessagingRecipient } from './types/MessagingRecipient'
|
|
4
|
+
import { NotificationMessage } from './types/NotificationMessage'
|
|
5
|
+
import { MessageType } from './types/MessageType'
|
|
6
|
+
import { EmailCapableAdapter } from './types/EmailCapableAdapter'
|
|
7
|
+
import { NotificationCapableAdapter } from './types/NotificationCapableAdapter'
|
|
8
|
+
import { MessageFormatter } from './MessageFormatter'
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
Messaging,
|
|
12
|
+
MessagingParameters,
|
|
13
|
+
AbstractMessagingAdapter,
|
|
14
|
+
MessagingRecipient,
|
|
15
|
+
NotificationMessage,
|
|
16
|
+
MessageType,
|
|
17
|
+
EmailCapableAdapter,
|
|
18
|
+
NotificationCapableAdapter,
|
|
19
|
+
MessageFormatter,
|
|
20
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MessagingRecipient } from './MessagingRecipient'
|
|
2
|
+
import { NotificationMessage } from './NotificationMessage'
|
|
3
|
+
|
|
4
|
+
export interface EmailCapableAdapter {
|
|
5
|
+
sendEmail(
|
|
6
|
+
recipient: MessagingRecipient,
|
|
7
|
+
message: NotificationMessage
|
|
8
|
+
): Promise<any>
|
|
9
|
+
|
|
10
|
+
sendEmails(
|
|
11
|
+
recipients: MessagingRecipient[],
|
|
12
|
+
message: NotificationMessage
|
|
13
|
+
): Promise<any>
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MessagingRecipient } from './MessagingRecipient'
|
|
2
|
+
import { NotificationMessage } from './NotificationMessage'
|
|
3
|
+
|
|
4
|
+
export interface NotificationCapableAdapter {
|
|
5
|
+
sendNotification(
|
|
6
|
+
recipient: MessagingRecipient,
|
|
7
|
+
message: NotificationMessage
|
|
8
|
+
): Promise<any>
|
|
9
|
+
|
|
10
|
+
sendNotifications(
|
|
11
|
+
recipients: MessagingRecipient[],
|
|
12
|
+
message: NotificationMessage
|
|
13
|
+
): Promise<any>
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MessagingRecipient } from './MessagingRecipient'
|
|
2
|
+
import { NotificationMessage } from './NotificationMessage'
|
|
3
|
+
|
|
4
|
+
export interface TextCapableAdapter {
|
|
5
|
+
sendTextMessage(
|
|
6
|
+
recipient: MessagingRecipient,
|
|
7
|
+
message: NotificationMessage
|
|
8
|
+
): Promise<any>
|
|
9
|
+
|
|
10
|
+
sendTextMessages(
|
|
11
|
+
recipients: MessagingRecipient[],
|
|
12
|
+
message: NotificationMessage
|
|
13
|
+
): Promise<any>
|
|
14
|
+
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { MessagingRecipient } from './MessagingRecipient';
|
|
2
|
-
import { NotificationMessage } from './NotificationMessage';
|
|
3
|
-
export interface EmailCapableAdapter {
|
|
4
|
-
sendEmail(recipient: MessagingRecipient, message: NotificationMessage): Promise<any>;
|
|
5
|
-
sendEmails(recipients: MessagingRecipient[], message: NotificationMessage): Promise<any>;
|
|
6
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { AbstractMessage } from './AbstractMessage';
|
|
2
|
-
import { MessagingRecipient } from './MessagingRecipient';
|
|
3
|
-
export interface MessagingInterface {
|
|
4
|
-
sendNotification(recipient: MessagingRecipient, message: AbstractMessage): Promise<any>;
|
|
5
|
-
sendNotifications(recipients: MessagingRecipient[], message: AbstractMessage): Promise<any>;
|
|
6
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/{lib → dist}/index.d.ts
RENAMED
|
File without changes
|
/package/{lib → dist}/index.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|