@flusys/nestjs-email 7.1.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/index.d.ts +3 -3
- package/controllers/email-config.controller.d.ts +2 -2
- package/controllers/email-send.controller.d.ts +3 -3
- package/controllers/email-template.controller.d.ts +4 -4
- package/controllers/index.d.ts +3 -3
- package/docs/email-swagger.config.d.ts +2 -2
- package/docs/index.d.ts +1 -1
- package/dtos/email-config.dto.d.ts +1 -1
- package/dtos/email-send.dto.d.ts +1 -1
- package/dtos/index.d.ts +3 -3
- package/entities/email-config-with-company.entity.d.ts +1 -1
- package/entities/email-template-with-company.entity.d.ts +1 -1
- package/entities/index.d.ts +10 -10
- package/enums/index.d.ts +1 -1
- package/fesm/261.js +799 -0
- package/fesm/493.js +1723 -0
- package/fesm/chunks/99.js +12 -0
- package/fesm/config/index.js +104 -3
- package/fesm/controllers/index.js +396 -3
- package/fesm/docs/index.js +134 -1
- package/fesm/dtos/index.js +22 -3
- package/fesm/entities/index.js +409 -13
- package/fesm/enums/index.js +37 -1
- package/fesm/index.js +762 -10
- package/fesm/interfaces/index.js +18 -4
- package/fesm/modules/index.js +541 -1
- package/fesm/providers/index.js +523 -5
- package/fesm/runtime.js +134 -0
- package/fesm/services/index.js +17 -5
- package/index.d.ts +10 -10
- package/interfaces/email-config.interface.d.ts +2 -2
- package/interfaces/email-module-options.interface.d.ts +3 -3
- package/interfaces/email-template.interface.d.ts +1 -1
- package/interfaces/index.d.ts +4 -4
- package/modules/email.module.d.ts +2 -2
- package/modules/index.d.ts +1 -1
- package/package.json +27 -38
- package/providers/email-factory.service.d.ts +2 -2
- package/providers/email-provider.registry.d.ts +2 -2
- package/providers/index.d.ts +5 -5
- package/providers/mailgun-provider.d.ts +1 -1
- package/providers/sendgrid-provider.d.ts +1 -1
- package/providers/smtp-provider.d.ts +1 -1
- package/services/email-config.service.d.ts +3 -3
- package/services/email-datasource.provider.d.ts +3 -3
- package/services/email-provider-config.service.d.ts +7 -7
- package/services/email-send.service.d.ts +7 -7
- package/services/email-template.service.d.ts +6 -6
- package/services/index.d.ts +5 -5
- package/utils/index.d.ts +1 -1
- package/cjs/config/email.constants.js +0 -22
- package/cjs/config/event-actions.js +0 -34
- package/cjs/config/index.js +0 -20
- package/cjs/config/message-keys.js +0 -39
- package/cjs/controllers/email-config.controller.js +0 -111
- package/cjs/controllers/email-config.controller.spec.js +0 -85
- package/cjs/controllers/email-send.controller.js +0 -128
- package/cjs/controllers/email-send.controller.spec.js +0 -139
- package/cjs/controllers/email-template.controller.js +0 -142
- package/cjs/controllers/email-template.controller.spec.js +0 -89
- package/cjs/controllers/index.js +0 -20
- package/cjs/docs/email-swagger.config.js +0 -116
- package/cjs/docs/email-swagger.config.spec.js +0 -62
- package/cjs/docs/index.js +0 -11
- package/cjs/dtos/email-config.dto.js +0 -212
- package/cjs/dtos/email-send.dto.js +0 -354
- package/cjs/dtos/email-template.dto.js +0 -252
- package/cjs/dtos/index.js +0 -20
- package/cjs/entities/email-config-with-company.entity.js +0 -63
- package/cjs/entities/email-config.entity.js +0 -114
- package/cjs/entities/email-template-with-company.entity.js +0 -65
- package/cjs/entities/email-template.entity.js +0 -136
- package/cjs/entities/index.js +0 -56
- package/cjs/entities/index.spec.js +0 -18
- package/cjs/enums/email-provider-type.enum.js +0 -18
- package/cjs/enums/index.js +0 -18
- package/cjs/index.js +0 -28
- package/cjs/interfaces/email-config.interface.js +0 -4
- package/cjs/interfaces/email-module-options.interface.js +0 -4
- package/cjs/interfaces/email-provider.interface.js +0 -4
- package/cjs/interfaces/email-template.interface.js +0 -4
- package/cjs/interfaces/index.js +0 -21
- package/cjs/modules/email.module.js +0 -122
- package/cjs/modules/email.module.spec.js +0 -142
- package/cjs/modules/index.js +0 -18
- package/cjs/providers/email-factory.service.js +0 -113
- package/cjs/providers/email-factory.service.spec.js +0 -242
- package/cjs/providers/email-provider.registry.js +0 -41
- package/cjs/providers/email-provider.registry.spec.js +0 -58
- package/cjs/providers/index.js +0 -22
- package/cjs/providers/mailgun-provider.js +0 -119
- package/cjs/providers/mailgun-provider.spec.js +0 -244
- package/cjs/providers/sendgrid-provider.js +0 -129
- package/cjs/providers/sendgrid-provider.spec.js +0 -346
- package/cjs/providers/smtp-provider.js +0 -164
- package/cjs/providers/smtp-provider.spec.js +0 -327
- package/cjs/services/email-config.service.js +0 -71
- package/cjs/services/email-config.service.spec.js +0 -69
- package/cjs/services/email-datasource.provider.js +0 -84
- package/cjs/services/email-datasource.provider.spec.js +0 -135
- package/cjs/services/email-provider-config.service.js +0 -159
- package/cjs/services/email-provider-config.service.spec.js +0 -269
- package/cjs/services/email-send.service.js +0 -315
- package/cjs/services/email-send.service.spec.js +0 -614
- package/cjs/services/email-template.service.js +0 -144
- package/cjs/services/email-template.service.spec.js +0 -261
- package/cjs/services/index.js +0 -22
- package/cjs/utils/email-templates.util.js +0 -64
- package/cjs/utils/email-templates.util.spec.js +0 -61
- package/cjs/utils/index.js +0 -18
- package/fesm/config/email.constants.js +0 -4
- package/fesm/config/event-actions.js +0 -13
- package/fesm/config/message-keys.js +0 -18
- package/fesm/controllers/email-config.controller.js +0 -101
- package/fesm/controllers/email-config.controller.spec.js +0 -81
- package/fesm/controllers/email-send.controller.js +0 -118
- package/fesm/controllers/email-send.controller.spec.js +0 -135
- package/fesm/controllers/email-template.controller.js +0 -132
- package/fesm/controllers/email-template.controller.spec.js +0 -85
- package/fesm/docs/email-swagger.config.js +0 -109
- package/fesm/docs/email-swagger.config.spec.js +0 -58
- package/fesm/dtos/email-config.dto.js +0 -191
- package/fesm/dtos/email-send.dto.js +0 -324
- package/fesm/dtos/email-template.dto.js +0 -231
- package/fesm/entities/email-config-with-company.entity.js +0 -53
- package/fesm/entities/email-config.entity.js +0 -104
- package/fesm/entities/email-template-with-company.entity.js +0 -55
- package/fesm/entities/email-template.entity.js +0 -126
- package/fesm/entities/index.spec.js +0 -14
- package/fesm/enums/email-provider-type.enum.js +0 -8
- package/fesm/interfaces/email-config.interface.js +0 -1
- package/fesm/interfaces/email-module-options.interface.js +0 -1
- package/fesm/interfaces/email-provider.interface.js +0 -1
- package/fesm/interfaces/email-template.interface.js +0 -1
- package/fesm/modules/email.module.js +0 -112
- package/fesm/modules/email.module.spec.js +0 -138
- package/fesm/providers/email-factory.service.js +0 -103
- package/fesm/providers/email-factory.service.spec.js +0 -238
- package/fesm/providers/email-provider.registry.js +0 -31
- package/fesm/providers/email-provider.registry.spec.js +0 -54
- package/fesm/providers/mailgun-provider.js +0 -109
- package/fesm/providers/mailgun-provider.spec.js +0 -240
- package/fesm/providers/sendgrid-provider.js +0 -119
- package/fesm/providers/sendgrid-provider.spec.js +0 -342
- package/fesm/providers/smtp-provider.js +0 -113
- package/fesm/providers/smtp-provider.spec.js +0 -282
- package/fesm/services/email-config.service.js +0 -61
- package/fesm/services/email-config.service.spec.js +0 -65
- package/fesm/services/email-datasource.provider.js +0 -74
- package/fesm/services/email-datasource.provider.spec.js +0 -131
- package/fesm/services/email-provider-config.service.js +0 -149
- package/fesm/services/email-provider-config.service.spec.js +0 -265
- package/fesm/services/email-send.service.js +0 -305
- package/fesm/services/email-send.service.spec.js +0 -610
- package/fesm/services/email-template.service.js +0 -134
- package/fesm/services/email-template.service.spec.js +0 -257
- package/fesm/utils/email-templates.util.js +0 -47
- package/fesm/utils/email-templates.util.spec.js +0 -57
- package/fesm/utils/index.js +0 -1
|
@@ -1,327 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
const _nodemailer = /*#__PURE__*/ _interop_require_wildcard(require("nodemailer"));
|
|
6
|
-
const _smtpprovider = require("./smtp-provider");
|
|
7
|
-
function _getRequireWildcardCache(nodeInterop) {
|
|
8
|
-
if (typeof WeakMap !== "function") return null;
|
|
9
|
-
var cacheBabelInterop = new WeakMap();
|
|
10
|
-
var cacheNodeInterop = new WeakMap();
|
|
11
|
-
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
12
|
-
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
13
|
-
})(nodeInterop);
|
|
14
|
-
}
|
|
15
|
-
function _interop_require_wildcard(obj, nodeInterop) {
|
|
16
|
-
if (!nodeInterop && obj && obj.__esModule) {
|
|
17
|
-
return obj;
|
|
18
|
-
}
|
|
19
|
-
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
20
|
-
return {
|
|
21
|
-
default: obj
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
var cache = _getRequireWildcardCache(nodeInterop);
|
|
25
|
-
if (cache && cache.has(obj)) {
|
|
26
|
-
return cache.get(obj);
|
|
27
|
-
}
|
|
28
|
-
var newObj = {
|
|
29
|
-
__proto__: null
|
|
30
|
-
};
|
|
31
|
-
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
32
|
-
for(var key in obj){
|
|
33
|
-
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
34
|
-
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
35
|
-
if (desc && (desc.get || desc.set)) {
|
|
36
|
-
Object.defineProperty(newObj, key, desc);
|
|
37
|
-
} else {
|
|
38
|
-
newObj[key] = obj[key];
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
newObj.default = obj;
|
|
43
|
-
if (cache) {
|
|
44
|
-
cache.set(obj, newObj);
|
|
45
|
-
}
|
|
46
|
-
return newObj;
|
|
47
|
-
}
|
|
48
|
-
const mockTransporter = {
|
|
49
|
-
verify: jest.fn(),
|
|
50
|
-
sendMail: jest.fn(),
|
|
51
|
-
close: jest.fn()
|
|
52
|
-
};
|
|
53
|
-
jest.mock('nodemailer', ()=>({
|
|
54
|
-
createTransport: jest.fn()
|
|
55
|
-
}));
|
|
56
|
-
const createTransport = _nodemailer.createTransport;
|
|
57
|
-
createTransport.mockReturnValue(mockTransporter);
|
|
58
|
-
function injectTransporter(provider, transporter) {
|
|
59
|
-
provider.transporter = transporter;
|
|
60
|
-
}
|
|
61
|
-
describe('SmtpProvider', ()=>{
|
|
62
|
-
let provider;
|
|
63
|
-
beforeEach(()=>{
|
|
64
|
-
provider = new _smtpprovider.SmtpProvider();
|
|
65
|
-
jest.clearAllMocks();
|
|
66
|
-
});
|
|
67
|
-
describe('initialize', ()=>{
|
|
68
|
-
it('creates a nodemailer transport with the given config and verifies the connection', async ()=>{
|
|
69
|
-
mockTransporter.verify.mockResolvedValue(true);
|
|
70
|
-
await provider.initialize({
|
|
71
|
-
host: 'smtp.example.com',
|
|
72
|
-
port: 587,
|
|
73
|
-
auth: {
|
|
74
|
-
user: 'user',
|
|
75
|
-
pass: 'pass'
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
expect(createTransport).toHaveBeenCalledWith(expect.objectContaining({
|
|
79
|
-
host: 'smtp.example.com',
|
|
80
|
-
port: 587,
|
|
81
|
-
secure: false,
|
|
82
|
-
auth: {
|
|
83
|
-
user: 'user',
|
|
84
|
-
pass: 'pass'
|
|
85
|
-
},
|
|
86
|
-
tls: expect.objectContaining({
|
|
87
|
-
rejectUnauthorized: true,
|
|
88
|
-
minVersion: 'TLSv1.2'
|
|
89
|
-
})
|
|
90
|
-
}));
|
|
91
|
-
expect(mockTransporter.verify).toHaveBeenCalled();
|
|
92
|
-
});
|
|
93
|
-
it('defaults secure=true when port is 465 and secure is not explicitly set', async ()=>{
|
|
94
|
-
mockTransporter.verify.mockResolvedValue(true);
|
|
95
|
-
await provider.initialize({
|
|
96
|
-
host: 'smtp.example.com',
|
|
97
|
-
port: 465
|
|
98
|
-
});
|
|
99
|
-
expect(createTransport).toHaveBeenCalledWith(expect.objectContaining({
|
|
100
|
-
secure: true
|
|
101
|
-
}));
|
|
102
|
-
});
|
|
103
|
-
it('honors an explicit secure flag over the port-based default', async ()=>{
|
|
104
|
-
mockTransporter.verify.mockResolvedValue(true);
|
|
105
|
-
await provider.initialize({
|
|
106
|
-
host: 'smtp.example.com',
|
|
107
|
-
port: 587,
|
|
108
|
-
secure: true
|
|
109
|
-
});
|
|
110
|
-
expect(createTransport).toHaveBeenCalledWith(expect.objectContaining({
|
|
111
|
-
secure: true
|
|
112
|
-
}));
|
|
113
|
-
});
|
|
114
|
-
it('allows disabling TLS certificate validation via config', async ()=>{
|
|
115
|
-
mockTransporter.verify.mockResolvedValue(true);
|
|
116
|
-
await provider.initialize({
|
|
117
|
-
host: 'smtp.example.com',
|
|
118
|
-
port: 587,
|
|
119
|
-
tls: {
|
|
120
|
-
rejectUnauthorized: false
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
expect(createTransport).toHaveBeenCalledWith(expect.objectContaining({
|
|
124
|
-
tls: expect.objectContaining({
|
|
125
|
-
rejectUnauthorized: false
|
|
126
|
-
})
|
|
127
|
-
}));
|
|
128
|
-
});
|
|
129
|
-
it('propagates a connection verification failure', async ()=>{
|
|
130
|
-
mockTransporter.verify.mockRejectedValue(new Error('ECONNREFUSED'));
|
|
131
|
-
await expect(provider.initialize({
|
|
132
|
-
host: 'smtp.example.com',
|
|
133
|
-
port: 587
|
|
134
|
-
})).rejects.toThrow('ECONNREFUSED');
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
describe('sendEmail', ()=>{
|
|
138
|
-
it('returns a failure result when the provider was never initialized', async ()=>{
|
|
139
|
-
const result = await provider.sendEmail({
|
|
140
|
-
to: 'a@example.com',
|
|
141
|
-
subject: 'Hi'
|
|
142
|
-
});
|
|
143
|
-
expect(result).toEqual({
|
|
144
|
-
success: false,
|
|
145
|
-
error: 'SMTP provider not initialized'
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
it('sends mail via the transporter and returns the message id', async ()=>{
|
|
149
|
-
injectTransporter(provider, mockTransporter);
|
|
150
|
-
mockTransporter.sendMail.mockResolvedValue({
|
|
151
|
-
messageId: 'msg-1'
|
|
152
|
-
});
|
|
153
|
-
const result = await provider.sendEmail({
|
|
154
|
-
to: [
|
|
155
|
-
'a@example.com',
|
|
156
|
-
'b@example.com'
|
|
157
|
-
],
|
|
158
|
-
cc: 'c@example.com',
|
|
159
|
-
bcc: [
|
|
160
|
-
'd@example.com'
|
|
161
|
-
],
|
|
162
|
-
subject: 'Hello',
|
|
163
|
-
html: '<p>hi</p>',
|
|
164
|
-
text: 'hi',
|
|
165
|
-
from: 'sender@example.com',
|
|
166
|
-
fromName: 'Sender Name',
|
|
167
|
-
replyTo: 'reply@example.com'
|
|
168
|
-
});
|
|
169
|
-
expect(result).toEqual({
|
|
170
|
-
success: true,
|
|
171
|
-
messageId: 'msg-1'
|
|
172
|
-
});
|
|
173
|
-
expect(mockTransporter.sendMail).toHaveBeenCalledWith(expect.objectContaining({
|
|
174
|
-
from: '"Sender Name" <sender@example.com>',
|
|
175
|
-
to: 'a@example.com, b@example.com',
|
|
176
|
-
cc: 'c@example.com',
|
|
177
|
-
bcc: 'd@example.com',
|
|
178
|
-
subject: 'Hello',
|
|
179
|
-
html: '<p>hi</p>',
|
|
180
|
-
text: 'hi',
|
|
181
|
-
replyTo: 'reply@example.com'
|
|
182
|
-
}));
|
|
183
|
-
});
|
|
184
|
-
it('sends the bare "from" address when fromName is not provided', async ()=>{
|
|
185
|
-
injectTransporter(provider, mockTransporter);
|
|
186
|
-
mockTransporter.sendMail.mockResolvedValue({
|
|
187
|
-
messageId: 'msg-1'
|
|
188
|
-
});
|
|
189
|
-
await provider.sendEmail({
|
|
190
|
-
to: 'a@example.com',
|
|
191
|
-
subject: 'Hi',
|
|
192
|
-
from: 'sender@example.com'
|
|
193
|
-
});
|
|
194
|
-
expect(mockTransporter.sendMail).toHaveBeenCalledWith(expect.objectContaining({
|
|
195
|
-
from: 'sender@example.com'
|
|
196
|
-
}));
|
|
197
|
-
});
|
|
198
|
-
it('maps attachments through to nodemailer mail options', async ()=>{
|
|
199
|
-
injectTransporter(provider, mockTransporter);
|
|
200
|
-
mockTransporter.sendMail.mockResolvedValue({
|
|
201
|
-
messageId: 'msg-1'
|
|
202
|
-
});
|
|
203
|
-
await provider.sendEmail({
|
|
204
|
-
to: 'a@example.com',
|
|
205
|
-
subject: 'Hi',
|
|
206
|
-
attachments: [
|
|
207
|
-
{
|
|
208
|
-
filename: 'doc.txt',
|
|
209
|
-
content: Buffer.from('hi'),
|
|
210
|
-
contentType: 'text/plain'
|
|
211
|
-
}
|
|
212
|
-
]
|
|
213
|
-
});
|
|
214
|
-
const [mailOptions] = mockTransporter.sendMail.mock.calls[0];
|
|
215
|
-
expect(mailOptions.attachments).toEqual([
|
|
216
|
-
{
|
|
217
|
-
filename: 'doc.txt',
|
|
218
|
-
content: Buffer.from('hi'),
|
|
219
|
-
contentType: 'text/plain',
|
|
220
|
-
encoding: undefined
|
|
221
|
-
}
|
|
222
|
-
]);
|
|
223
|
-
});
|
|
224
|
-
it('returns the error message when sendMail rejects with an Error', async ()=>{
|
|
225
|
-
injectTransporter(provider, mockTransporter);
|
|
226
|
-
mockTransporter.sendMail.mockRejectedValue(new Error('Connection refused'));
|
|
227
|
-
const result = await provider.sendEmail({
|
|
228
|
-
to: 'a@example.com',
|
|
229
|
-
subject: 'Hi'
|
|
230
|
-
});
|
|
231
|
-
expect(result).toEqual({
|
|
232
|
-
success: false,
|
|
233
|
-
error: 'Connection refused'
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
it('returns "Unknown error" when sendMail rejects with a non-Error', async ()=>{
|
|
237
|
-
injectTransporter(provider, mockTransporter);
|
|
238
|
-
mockTransporter.sendMail.mockRejectedValue('raw string failure');
|
|
239
|
-
const result = await provider.sendEmail({
|
|
240
|
-
to: 'a@example.com',
|
|
241
|
-
subject: 'Hi'
|
|
242
|
-
});
|
|
243
|
-
expect(result).toEqual({
|
|
244
|
-
success: false,
|
|
245
|
-
error: 'Unknown error'
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
it('surfaces a timeout error if sendMail never settles before the send timeout', async ()=>{
|
|
249
|
-
jest.useFakeTimers();
|
|
250
|
-
injectTransporter(provider, mockTransporter);
|
|
251
|
-
mockTransporter.sendMail.mockReturnValue(new Promise(()=>{}));
|
|
252
|
-
const pending = provider.sendEmail({
|
|
253
|
-
to: 'a@example.com',
|
|
254
|
-
subject: 'Hi'
|
|
255
|
-
});
|
|
256
|
-
jest.advanceTimersByTime(30_000);
|
|
257
|
-
const result = await pending;
|
|
258
|
-
expect(result).toEqual({
|
|
259
|
-
success: false,
|
|
260
|
-
error: 'SMTP send timeout'
|
|
261
|
-
});
|
|
262
|
-
jest.useRealTimers();
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
describe('sendBulkEmails', ()=>{
|
|
266
|
-
it('sends each message individually and aggregates the results', async ()=>{
|
|
267
|
-
injectTransporter(provider, mockTransporter);
|
|
268
|
-
mockTransporter.sendMail.mockResolvedValueOnce({
|
|
269
|
-
messageId: 'id-1'
|
|
270
|
-
}).mockRejectedValueOnce(new Error('failed-2'));
|
|
271
|
-
const results = await provider.sendBulkEmails([
|
|
272
|
-
{
|
|
273
|
-
to: 'a@example.com',
|
|
274
|
-
subject: 'A'
|
|
275
|
-
},
|
|
276
|
-
{
|
|
277
|
-
to: 'b@example.com',
|
|
278
|
-
subject: 'B'
|
|
279
|
-
}
|
|
280
|
-
]);
|
|
281
|
-
expect(results).toEqual([
|
|
282
|
-
{
|
|
283
|
-
success: true,
|
|
284
|
-
messageId: 'id-1'
|
|
285
|
-
},
|
|
286
|
-
{
|
|
287
|
-
success: false,
|
|
288
|
-
error: 'failed-2'
|
|
289
|
-
}
|
|
290
|
-
]);
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
describe('healthCheck', ()=>{
|
|
294
|
-
it('returns false when not initialized', async ()=>{
|
|
295
|
-
await expect(provider.healthCheck()).resolves.toBe(false);
|
|
296
|
-
});
|
|
297
|
-
it('returns true when transporter.verify() resolves', async ()=>{
|
|
298
|
-
injectTransporter(provider, mockTransporter);
|
|
299
|
-
mockTransporter.verify.mockResolvedValue(true);
|
|
300
|
-
await expect(provider.healthCheck()).resolves.toBe(true);
|
|
301
|
-
});
|
|
302
|
-
it('returns false when transporter.verify() rejects', async ()=>{
|
|
303
|
-
injectTransporter(provider, mockTransporter);
|
|
304
|
-
mockTransporter.verify.mockRejectedValue(new Error('down'));
|
|
305
|
-
await expect(provider.healthCheck()).resolves.toBe(false);
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
|
-
describe('close', ()=>{
|
|
309
|
-
it('closes and clears the transporter', async ()=>{
|
|
310
|
-
injectTransporter(provider, mockTransporter);
|
|
311
|
-
await provider.close();
|
|
312
|
-
expect(mockTransporter.close).toHaveBeenCalled();
|
|
313
|
-
const result = await provider.sendEmail({
|
|
314
|
-
to: 'a@example.com',
|
|
315
|
-
subject: 'Hi'
|
|
316
|
-
});
|
|
317
|
-
expect(result).toEqual({
|
|
318
|
-
success: false,
|
|
319
|
-
error: 'SMTP provider not initialized'
|
|
320
|
-
});
|
|
321
|
-
});
|
|
322
|
-
it('is a no-op when no transporter exists', async ()=>{
|
|
323
|
-
await expect(provider.close()).resolves.toBeUndefined();
|
|
324
|
-
expect(mockTransporter.close).not.toHaveBeenCalled();
|
|
325
|
-
});
|
|
326
|
-
});
|
|
327
|
-
});
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "EmailConfigService", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return EmailConfigService;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _common = require("@nestjs/common");
|
|
12
|
-
const _emailconstants = require("../config/email.constants");
|
|
13
|
-
const _interfaces = require("../interfaces");
|
|
14
|
-
function _define_property(obj, key, value) {
|
|
15
|
-
if (key in obj) {
|
|
16
|
-
Object.defineProperty(obj, key, {
|
|
17
|
-
value: value,
|
|
18
|
-
enumerable: true,
|
|
19
|
-
configurable: true,
|
|
20
|
-
writable: true
|
|
21
|
-
});
|
|
22
|
-
} else {
|
|
23
|
-
obj[key] = value;
|
|
24
|
-
}
|
|
25
|
-
return obj;
|
|
26
|
-
}
|
|
27
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
28
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
29
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
30
|
-
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
31
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
32
|
-
}
|
|
33
|
-
function _ts_metadata(k, v) {
|
|
34
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
35
|
-
}
|
|
36
|
-
function _ts_param(paramIndex, decorator) {
|
|
37
|
-
return function(target, key) {
|
|
38
|
-
decorator(target, key, paramIndex);
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
let EmailConfigService = class EmailConfigService {
|
|
42
|
-
// ─── IModuleConfigService Implementation ────────────────────────────────────
|
|
43
|
-
isCompanyFeatureEnabled() {
|
|
44
|
-
return this.options.bootstrapAppConfig?.enableCompanyFeature ?? false;
|
|
45
|
-
}
|
|
46
|
-
getDatabaseMode() {
|
|
47
|
-
return this.options.bootstrapAppConfig?.databaseMode ?? 'single';
|
|
48
|
-
}
|
|
49
|
-
isMultiTenant() {
|
|
50
|
-
return this.getDatabaseMode() === 'multi-tenant';
|
|
51
|
-
}
|
|
52
|
-
// ─── Config Getters ─────────────────────────────────────────────────────────
|
|
53
|
-
getDefaultFromName() {
|
|
54
|
-
return _emailconstants.DEFAULT_FROM_NAME;
|
|
55
|
-
}
|
|
56
|
-
getOptions() {
|
|
57
|
-
return this.options;
|
|
58
|
-
}
|
|
59
|
-
constructor(options){
|
|
60
|
-
_define_property(this, "options", void 0);
|
|
61
|
-
this.options = options;
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
EmailConfigService = _ts_decorate([
|
|
65
|
-
(0, _common.Injectable)(),
|
|
66
|
-
_ts_param(0, (0, _common.Inject)(_emailconstants.EMAIL_MODULE_OPTIONS)),
|
|
67
|
-
_ts_metadata("design:type", Function),
|
|
68
|
-
_ts_metadata("design:paramtypes", [
|
|
69
|
-
typeof _interfaces.EmailModuleOptions === "undefined" ? Object : _interfaces.EmailModuleOptions
|
|
70
|
-
])
|
|
71
|
-
], EmailConfigService);
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
const _emailconstants = require("../config/email.constants");
|
|
6
|
-
const _emailconfigservice = require("./email-config.service");
|
|
7
|
-
function buildOptions(overrides = {}) {
|
|
8
|
-
return {
|
|
9
|
-
bootstrapAppConfig: undefined,
|
|
10
|
-
config: undefined,
|
|
11
|
-
...overrides
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
describe('EmailConfigService', ()=>{
|
|
15
|
-
describe('isCompanyFeatureEnabled', ()=>{
|
|
16
|
-
it('returns false when bootstrapAppConfig is not provided', ()=>{
|
|
17
|
-
const service = new _emailconfigservice.EmailConfigService(buildOptions());
|
|
18
|
-
expect(service.isCompanyFeatureEnabled()).toBe(false);
|
|
19
|
-
});
|
|
20
|
-
it('returns false when enableCompanyFeature is explicitly false', ()=>{
|
|
21
|
-
const service = new _emailconfigservice.EmailConfigService(buildOptions({
|
|
22
|
-
bootstrapAppConfig: {
|
|
23
|
-
enableCompanyFeature: false
|
|
24
|
-
}
|
|
25
|
-
}));
|
|
26
|
-
expect(service.isCompanyFeatureEnabled()).toBe(false);
|
|
27
|
-
});
|
|
28
|
-
it('returns true when enableCompanyFeature is true', ()=>{
|
|
29
|
-
const service = new _emailconfigservice.EmailConfigService(buildOptions({
|
|
30
|
-
bootstrapAppConfig: {
|
|
31
|
-
enableCompanyFeature: true
|
|
32
|
-
}
|
|
33
|
-
}));
|
|
34
|
-
expect(service.isCompanyFeatureEnabled()).toBe(true);
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
describe('getDatabaseMode / isMultiTenant', ()=>{
|
|
38
|
-
it('defaults to "single" when bootstrapAppConfig is missing', ()=>{
|
|
39
|
-
const service = new _emailconfigservice.EmailConfigService(buildOptions());
|
|
40
|
-
expect(service.getDatabaseMode()).toBe('single');
|
|
41
|
-
expect(service.isMultiTenant()).toBe(false);
|
|
42
|
-
});
|
|
43
|
-
it('reflects "multi-tenant" mode from bootstrapAppConfig', ()=>{
|
|
44
|
-
const service = new _emailconfigservice.EmailConfigService(buildOptions({
|
|
45
|
-
bootstrapAppConfig: {
|
|
46
|
-
databaseMode: 'multi-tenant'
|
|
47
|
-
}
|
|
48
|
-
}));
|
|
49
|
-
expect(service.getDatabaseMode()).toBe('multi-tenant');
|
|
50
|
-
expect(service.isMultiTenant()).toBe(true);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
describe('getDefaultFromName', ()=>{
|
|
54
|
-
it('always returns the module-level DEFAULT_FROM_NAME constant', ()=>{
|
|
55
|
-
const service = new _emailconfigservice.EmailConfigService(buildOptions());
|
|
56
|
-
expect(service.getDefaultFromName()).toBe(_emailconstants.DEFAULT_FROM_NAME);
|
|
57
|
-
expect(service.getDefaultFromName()).toBe('FLUSYS');
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
describe('getOptions', ()=>{
|
|
61
|
-
it('returns the exact options object injected via the constructor', ()=>{
|
|
62
|
-
const options = buildOptions({
|
|
63
|
-
defaultProvider: 'smtp'
|
|
64
|
-
});
|
|
65
|
-
const service = new _emailconfigservice.EmailConfigService(options);
|
|
66
|
-
expect(service.getOptions()).toBe(options);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
});
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "EmailDataSourceProvider", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return EmailDataSourceProvider;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _modules = require("@flusys/nestjs-shared/modules");
|
|
12
|
-
const _common = require("@nestjs/common");
|
|
13
|
-
const _core = require("@nestjs/core");
|
|
14
|
-
const _express = require("express");
|
|
15
|
-
const _entities = require("../entities");
|
|
16
|
-
const _emailconfigservice = require("./email-config.service");
|
|
17
|
-
function _define_property(obj, key, value) {
|
|
18
|
-
if (key in obj) {
|
|
19
|
-
Object.defineProperty(obj, key, {
|
|
20
|
-
value: value,
|
|
21
|
-
enumerable: true,
|
|
22
|
-
configurable: true,
|
|
23
|
-
writable: true
|
|
24
|
-
});
|
|
25
|
-
} else {
|
|
26
|
-
obj[key] = value;
|
|
27
|
-
}
|
|
28
|
-
return obj;
|
|
29
|
-
}
|
|
30
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
31
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
32
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
33
|
-
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
34
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
35
|
-
}
|
|
36
|
-
function _ts_metadata(k, v) {
|
|
37
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
38
|
-
}
|
|
39
|
-
function _ts_param(paramIndex, decorator) {
|
|
40
|
-
return function(target, key) {
|
|
41
|
-
decorator(target, key, paramIndex);
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
let EmailDataSourceProvider = class EmailDataSourceProvider extends _modules.MultiTenantDataSourceService {
|
|
45
|
-
static buildParentOptions(options) {
|
|
46
|
-
return {
|
|
47
|
-
bootstrapAppConfig: options.bootstrapAppConfig,
|
|
48
|
-
defaultDatabaseConfig: options.config?.defaultDatabaseConfig,
|
|
49
|
-
tenantDefaultDatabaseConfig: options.config?.tenantDefaultDatabaseConfig,
|
|
50
|
-
tenants: options.config?.tenants
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
getEnableCompanyFeatureForTenant(tenant) {
|
|
54
|
-
return tenant?.enableCompanyFeature ?? this.configService.isCompanyFeatureEnabled();
|
|
55
|
-
}
|
|
56
|
-
async createDataSourceFromConfig(config) {
|
|
57
|
-
const currentTenant = this.getCurrentTenant();
|
|
58
|
-
const enableCompanyFeature = this.getEnableCompanyFeatureForTenant(currentTenant ?? undefined);
|
|
59
|
-
const entities = (0, _entities.getEmailEntitiesByConfig)(enableCompanyFeature);
|
|
60
|
-
return super.createDataSourceFromConfig(config, entities);
|
|
61
|
-
}
|
|
62
|
-
constructor(configService, request){
|
|
63
|
-
super(EmailDataSourceProvider.buildParentOptions(configService.getOptions()), request), _define_property(this, "configService", void 0), this.configService = configService;
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
_define_property(EmailDataSourceProvider, "tenantConnections", new Map());
|
|
67
|
-
_define_property(EmailDataSourceProvider, "singleDataSource", null);
|
|
68
|
-
_define_property(EmailDataSourceProvider, "tenantsRegistry", new Map());
|
|
69
|
-
_define_property(EmailDataSourceProvider, "initialized", false);
|
|
70
|
-
_define_property(EmailDataSourceProvider, "connectionLocks", new Map());
|
|
71
|
-
_define_property(EmailDataSourceProvider, "singleConnectionLock", null);
|
|
72
|
-
EmailDataSourceProvider = _ts_decorate([
|
|
73
|
-
(0, _common.Injectable)({
|
|
74
|
-
scope: _common.Scope.REQUEST
|
|
75
|
-
}),
|
|
76
|
-
_ts_param(0, (0, _common.Inject)(_emailconfigservice.EmailConfigService)),
|
|
77
|
-
_ts_param(1, (0, _common.Optional)()),
|
|
78
|
-
_ts_param(1, (0, _common.Inject)(_core.REQUEST)),
|
|
79
|
-
_ts_metadata("design:type", Function),
|
|
80
|
-
_ts_metadata("design:paramtypes", [
|
|
81
|
-
typeof _emailconfigservice.EmailConfigService === "undefined" ? Object : _emailconfigservice.EmailConfigService,
|
|
82
|
-
typeof _express.Request === "undefined" ? Object : _express.Request
|
|
83
|
-
])
|
|
84
|
-
], EmailDataSourceProvider);
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
const _modules = require("@flusys/nestjs-shared/modules");
|
|
6
|
-
const _entities = require("../entities");
|
|
7
|
-
const _emaildatasourceprovider = require("./email-datasource.provider");
|
|
8
|
-
function buildConfigService(overrides = {}) {
|
|
9
|
-
const options = {
|
|
10
|
-
bootstrapAppConfig: {
|
|
11
|
-
databaseMode: 'single',
|
|
12
|
-
enableCompanyFeature: false
|
|
13
|
-
},
|
|
14
|
-
config: {
|
|
15
|
-
defaultDatabaseConfig: {
|
|
16
|
-
type: 'mysql',
|
|
17
|
-
host: 'localhost',
|
|
18
|
-
database: 'flusys'
|
|
19
|
-
},
|
|
20
|
-
tenants: []
|
|
21
|
-
},
|
|
22
|
-
...overrides
|
|
23
|
-
};
|
|
24
|
-
return {
|
|
25
|
-
isCompanyFeatureEnabled: jest.fn().mockReturnValue(options.bootstrapAppConfig?.enableCompanyFeature ?? false),
|
|
26
|
-
getDatabaseMode: jest.fn().mockReturnValue(options.bootstrapAppConfig?.databaseMode ?? 'single'),
|
|
27
|
-
isMultiTenant: jest.fn().mockReturnValue(options.bootstrapAppConfig?.databaseMode === 'multi-tenant'),
|
|
28
|
-
getDefaultFromName: jest.fn().mockReturnValue('FLUSYS'),
|
|
29
|
-
getOptions: jest.fn().mockReturnValue(options)
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
describe('EmailDataSourceProvider', ()=>{
|
|
33
|
-
afterEach(()=>{
|
|
34
|
-
jest.restoreAllMocks();
|
|
35
|
-
// Reset static state isolated to this subclass between tests.
|
|
36
|
-
_emaildatasourceprovider.EmailDataSourceProvider.singleDataSource = null;
|
|
37
|
-
_emaildatasourceprovider.EmailDataSourceProvider.initialized = false;
|
|
38
|
-
_emaildatasourceprovider.EmailDataSourceProvider.tenantsRegistry.clear();
|
|
39
|
-
});
|
|
40
|
-
it('propagates bootstrapAppConfig from EmailConfigService into the base data source service', ()=>{
|
|
41
|
-
const configService = buildConfigService({
|
|
42
|
-
bootstrapAppConfig: {
|
|
43
|
-
databaseMode: 'multi-tenant',
|
|
44
|
-
enableCompanyFeature: true
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
const provider = new _emaildatasourceprovider.EmailDataSourceProvider(configService);
|
|
48
|
-
expect(provider.isMultiTenant()).toBe(true);
|
|
49
|
-
expect(provider.getDatabaseMode()).toBe('multi-tenant');
|
|
50
|
-
});
|
|
51
|
-
it('defaults to single-database mode when bootstrapAppConfig is absent', ()=>{
|
|
52
|
-
const configService = buildConfigService({
|
|
53
|
-
bootstrapAppConfig: undefined
|
|
54
|
-
});
|
|
55
|
-
const provider = new _emaildatasourceprovider.EmailDataSourceProvider(configService);
|
|
56
|
-
expect(provider.isMultiTenant()).toBe(false);
|
|
57
|
-
expect(provider.getDatabaseMode()).toBe('single');
|
|
58
|
-
});
|
|
59
|
-
describe('getEnableCompanyFeatureForTenant', ()=>{
|
|
60
|
-
it('falls back to EmailConfigService.isCompanyFeatureEnabled() when no tenant is given', ()=>{
|
|
61
|
-
const configService = buildConfigService({
|
|
62
|
-
bootstrapAppConfig: {
|
|
63
|
-
databaseMode: 'single',
|
|
64
|
-
enableCompanyFeature: true
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
const provider = new _emaildatasourceprovider.EmailDataSourceProvider(configService);
|
|
68
|
-
expect(provider.getEnableCompanyFeatureForTenant(undefined)).toBe(true);
|
|
69
|
-
});
|
|
70
|
-
it('prefers the tenant-level enableCompanyFeature flag over the module default', ()=>{
|
|
71
|
-
const configService = buildConfigService({
|
|
72
|
-
bootstrapAppConfig: {
|
|
73
|
-
databaseMode: 'multi-tenant',
|
|
74
|
-
enableCompanyFeature: true
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
const provider = new _emaildatasourceprovider.EmailDataSourceProvider(configService);
|
|
78
|
-
expect(provider.getEnableCompanyFeatureForTenant({
|
|
79
|
-
id: 'tenant-1',
|
|
80
|
-
database: 'tenant_db',
|
|
81
|
-
enableCompanyFeature: false
|
|
82
|
-
})).toBe(false);
|
|
83
|
-
});
|
|
84
|
-
it('uses the module default when the tenant does not specify enableCompanyFeature', ()=>{
|
|
85
|
-
const configService = buildConfigService({
|
|
86
|
-
bootstrapAppConfig: {
|
|
87
|
-
databaseMode: 'multi-tenant',
|
|
88
|
-
enableCompanyFeature: true
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
const provider = new _emaildatasourceprovider.EmailDataSourceProvider(configService);
|
|
92
|
-
expect(provider.getEnableCompanyFeatureForTenant({
|
|
93
|
-
id: 'tenant-1',
|
|
94
|
-
database: 'tenant_db'
|
|
95
|
-
})).toBe(true);
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
describe('createDataSourceFromConfig', ()=>{
|
|
99
|
-
it('resolves core (non-company) entities when the company feature is disabled', async ()=>{
|
|
100
|
-
const configService = buildConfigService({
|
|
101
|
-
bootstrapAppConfig: {
|
|
102
|
-
databaseMode: 'single',
|
|
103
|
-
enableCompanyFeature: false
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
const provider = new _emaildatasourceprovider.EmailDataSourceProvider(configService);
|
|
107
|
-
const fakeDataSource = {};
|
|
108
|
-
const superSpy = jest.spyOn(_modules.MultiTenantDataSourceService.prototype, 'createDataSourceFromConfig').mockResolvedValue(fakeDataSource);
|
|
109
|
-
const config = {
|
|
110
|
-
type: 'mysql',
|
|
111
|
-
database: 'flusys'
|
|
112
|
-
};
|
|
113
|
-
const result = await provider.createDataSourceFromConfig(config);
|
|
114
|
-
expect(result).toBe(fakeDataSource);
|
|
115
|
-
expect(superSpy).toHaveBeenCalledWith(config, _entities.EmailCoreEntities);
|
|
116
|
-
});
|
|
117
|
-
it('resolves company-aware entities when the company feature is enabled', async ()=>{
|
|
118
|
-
const configService = buildConfigService({
|
|
119
|
-
bootstrapAppConfig: {
|
|
120
|
-
databaseMode: 'single',
|
|
121
|
-
enableCompanyFeature: true
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
const provider = new _emaildatasourceprovider.EmailDataSourceProvider(configService);
|
|
125
|
-
const fakeDataSource = {};
|
|
126
|
-
const superSpy = jest.spyOn(_modules.MultiTenantDataSourceService.prototype, 'createDataSourceFromConfig').mockResolvedValue(fakeDataSource);
|
|
127
|
-
const config = {
|
|
128
|
-
type: 'mysql',
|
|
129
|
-
database: 'flusys'
|
|
130
|
-
};
|
|
131
|
-
await provider.createDataSourceFromConfig(config);
|
|
132
|
-
expect(superSpy).toHaveBeenCalledWith(config, _entities.EmailCompanyEntities);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
});
|