@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,119 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "MailgunProvider", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return MailgunProvider;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _constants = require("@flusys/nestjs-shared/constants");
|
|
12
|
-
const _common = require("@nestjs/common");
|
|
13
|
-
function _define_property(obj, key, value) {
|
|
14
|
-
if (key in obj) {
|
|
15
|
-
Object.defineProperty(obj, key, {
|
|
16
|
-
value: value,
|
|
17
|
-
enumerable: true,
|
|
18
|
-
configurable: true,
|
|
19
|
-
writable: true
|
|
20
|
-
});
|
|
21
|
-
} else {
|
|
22
|
-
obj[key] = value;
|
|
23
|
-
}
|
|
24
|
-
return obj;
|
|
25
|
-
}
|
|
26
|
-
const MAILGUN_API_URL = 'https://api.mailgun.net';
|
|
27
|
-
const MAILGUN_EU_API_URL = 'https://api.eu.mailgun.net';
|
|
28
|
-
let MailgunProvider = class MailgunProvider {
|
|
29
|
-
async initialize(config) {
|
|
30
|
-
this.domain = config.domain;
|
|
31
|
-
try {
|
|
32
|
-
// Dynamic import for optional dependency
|
|
33
|
-
const Mailgun = (await new Function('return import("mailgun.js")')()).default;
|
|
34
|
-
const FormData = (await new Function('return import("form-data")')()).default;
|
|
35
|
-
const mailgun = new Mailgun(FormData);
|
|
36
|
-
const url = config.region === 'eu' ? MAILGUN_EU_API_URL : MAILGUN_API_URL;
|
|
37
|
-
this.client = mailgun.client({
|
|
38
|
-
username: 'api',
|
|
39
|
-
key: config.apiKey,
|
|
40
|
-
url
|
|
41
|
-
});
|
|
42
|
-
} catch {
|
|
43
|
-
throw new _common.InternalServerErrorException({
|
|
44
|
-
message: 'Mailgun initialization failed. Install: npm install mailgun.js form-data',
|
|
45
|
-
messageKey: _constants.SYSTEM_MESSAGES.SDK_NOT_INSTALLED,
|
|
46
|
-
messageVariables: {
|
|
47
|
-
sdk: 'mailgun'
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
async sendEmail(options) {
|
|
53
|
-
if (!this.client || !this.domain) {
|
|
54
|
-
return {
|
|
55
|
-
success: false,
|
|
56
|
-
error: 'Mailgun provider not initialized'
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
try {
|
|
60
|
-
const messageData = this.buildMessageData(options);
|
|
61
|
-
const result = await this.client.messages.create(this.domain, messageData);
|
|
62
|
-
return {
|
|
63
|
-
success: true,
|
|
64
|
-
messageId: result.id
|
|
65
|
-
};
|
|
66
|
-
} catch (error) {
|
|
67
|
-
return {
|
|
68
|
-
success: false,
|
|
69
|
-
error: this.extractError(error)
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
async sendBulkEmails(options) {
|
|
74
|
-
return Promise.all(options.map((opt)=>this.sendEmail(opt)));
|
|
75
|
-
}
|
|
76
|
-
async healthCheck() {
|
|
77
|
-
if (!this.client || !this.domain) return false;
|
|
78
|
-
try {
|
|
79
|
-
await this.client.domains.get(this.domain);
|
|
80
|
-
return true;
|
|
81
|
-
} catch {
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
async close() {
|
|
86
|
-
this.client = null;
|
|
87
|
-
}
|
|
88
|
-
// ─── Private Helpers ────────────────────────────────────────────────────────
|
|
89
|
-
buildMessageData(options) {
|
|
90
|
-
const data = {
|
|
91
|
-
from: options.fromName ? `${options.fromName} <${options.from}>` : options.from,
|
|
92
|
-
to: this.joinAddresses(options.to),
|
|
93
|
-
subject: options.subject
|
|
94
|
-
};
|
|
95
|
-
if (options.html) data.html = options.html;
|
|
96
|
-
if (options.text) data.text = options.text;
|
|
97
|
-
if (options.cc) data.cc = this.joinAddresses(options.cc);
|
|
98
|
-
if (options.bcc) data.bcc = this.joinAddresses(options.bcc);
|
|
99
|
-
if (options.replyTo) data['h:Reply-To'] = options.replyTo;
|
|
100
|
-
if (options.attachments?.length) {
|
|
101
|
-
data.attachment = options.attachments.map((a)=>({
|
|
102
|
-
filename: a.filename,
|
|
103
|
-
data: typeof a.content === 'string' ? Buffer.from(a.content, 'base64') : a.content,
|
|
104
|
-
contentType: a.contentType
|
|
105
|
-
}));
|
|
106
|
-
}
|
|
107
|
-
return data;
|
|
108
|
-
}
|
|
109
|
-
joinAddresses(addresses) {
|
|
110
|
-
return Array.isArray(addresses) ? addresses.join(', ') : addresses;
|
|
111
|
-
}
|
|
112
|
-
extractError(error) {
|
|
113
|
-
return error instanceof Error ? error.message : 'Unknown error';
|
|
114
|
-
}
|
|
115
|
-
constructor(){
|
|
116
|
-
_define_property(this, "client", null);
|
|
117
|
-
_define_property(this, "domain", null);
|
|
118
|
-
}
|
|
119
|
-
};
|
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
const _common = require("@nestjs/common");
|
|
6
|
-
const _mailgunprovider = require("./mailgun-provider");
|
|
7
|
-
// mailgun.js is not an installed dependency in this workspace, so the real dynamic
|
|
8
|
-
// import() inside `initialize()` fails — that's exercised directly (no mocking needed).
|
|
9
|
-
// For "happy path" behavior we bypass initialize() and inject a fake client, matching
|
|
10
|
-
// the private `client`/`domain` fields the provider mutates internally.
|
|
11
|
-
function injectClient(provider, client, domain = 'example.com') {
|
|
12
|
-
provider.client = client;
|
|
13
|
-
provider.domain = domain;
|
|
14
|
-
}
|
|
15
|
-
describe('MailgunProvider', ()=>{
|
|
16
|
-
let provider;
|
|
17
|
-
beforeEach(()=>{
|
|
18
|
-
provider = new _mailgunprovider.MailgunProvider();
|
|
19
|
-
});
|
|
20
|
-
describe('initialize', ()=>{
|
|
21
|
-
it('throws InternalServerErrorException with SDK_NOT_INSTALLED when mailgun.js is unavailable', async ()=>{
|
|
22
|
-
await expect(provider.initialize({
|
|
23
|
-
apiKey: 'key',
|
|
24
|
-
domain: 'example.com'
|
|
25
|
-
})).rejects.toThrow(_common.InternalServerErrorException);
|
|
26
|
-
});
|
|
27
|
-
it('surfaces the sdk name in the exception messageVariables', async ()=>{
|
|
28
|
-
try {
|
|
29
|
-
await provider.initialize({
|
|
30
|
-
apiKey: 'key',
|
|
31
|
-
domain: 'example.com'
|
|
32
|
-
});
|
|
33
|
-
fail('expected initialize to throw');
|
|
34
|
-
} catch (error) {
|
|
35
|
-
expect(error.getResponse()).toEqual(expect.objectContaining({
|
|
36
|
-
messageKey: 'system.sdk.not.installed',
|
|
37
|
-
messageVariables: {
|
|
38
|
-
sdk: 'mailgun'
|
|
39
|
-
}
|
|
40
|
-
}));
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
describe('sendEmail', ()=>{
|
|
45
|
-
it('returns a failure result when the provider was never initialized', async ()=>{
|
|
46
|
-
const result = await provider.sendEmail({
|
|
47
|
-
to: 'a@example.com',
|
|
48
|
-
subject: 'Hi'
|
|
49
|
-
});
|
|
50
|
-
expect(result).toEqual({
|
|
51
|
-
success: false,
|
|
52
|
-
error: 'Mailgun provider not initialized'
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
it('sends via the mailgun client and returns the message id on success', async ()=>{
|
|
56
|
-
const create = jest.fn().mockResolvedValue({
|
|
57
|
-
id: '<msg-1@mailgun>'
|
|
58
|
-
});
|
|
59
|
-
injectClient(provider, {
|
|
60
|
-
messages: {
|
|
61
|
-
create
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
const result = await provider.sendEmail({
|
|
65
|
-
to: 'a@example.com',
|
|
66
|
-
cc: [
|
|
67
|
-
'b@example.com',
|
|
68
|
-
'c@example.com'
|
|
69
|
-
],
|
|
70
|
-
bcc: 'd@example.com',
|
|
71
|
-
subject: 'Hello',
|
|
72
|
-
html: '<p>hi</p>',
|
|
73
|
-
text: 'hi',
|
|
74
|
-
from: 'sender@example.com',
|
|
75
|
-
fromName: 'Sender Name',
|
|
76
|
-
replyTo: 'reply@example.com'
|
|
77
|
-
});
|
|
78
|
-
expect(result).toEqual({
|
|
79
|
-
success: true,
|
|
80
|
-
messageId: '<msg-1@mailgun>'
|
|
81
|
-
});
|
|
82
|
-
expect(create).toHaveBeenCalledWith('example.com', expect.objectContaining({
|
|
83
|
-
from: 'Sender Name <sender@example.com>',
|
|
84
|
-
to: 'a@example.com',
|
|
85
|
-
subject: 'Hello',
|
|
86
|
-
html: '<p>hi</p>',
|
|
87
|
-
text: 'hi',
|
|
88
|
-
cc: 'b@example.com, c@example.com',
|
|
89
|
-
bcc: 'd@example.com',
|
|
90
|
-
'h:Reply-To': 'reply@example.com'
|
|
91
|
-
}));
|
|
92
|
-
});
|
|
93
|
-
it('omits optional fields (cc/bcc/replyTo/html/text) when not provided', async ()=>{
|
|
94
|
-
const create = jest.fn().mockResolvedValue({
|
|
95
|
-
id: 'id-1'
|
|
96
|
-
});
|
|
97
|
-
injectClient(provider, {
|
|
98
|
-
messages: {
|
|
99
|
-
create
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
await provider.sendEmail({
|
|
103
|
-
to: 'a@example.com',
|
|
104
|
-
subject: 'Hi',
|
|
105
|
-
from: 'x@example.com'
|
|
106
|
-
});
|
|
107
|
-
const [, messageData] = create.mock.calls[0];
|
|
108
|
-
expect(messageData).not.toHaveProperty('cc');
|
|
109
|
-
expect(messageData).not.toHaveProperty('bcc');
|
|
110
|
-
expect(messageData).not.toHaveProperty('h:Reply-To');
|
|
111
|
-
expect(messageData).not.toHaveProperty('html');
|
|
112
|
-
expect(messageData).not.toHaveProperty('text');
|
|
113
|
-
});
|
|
114
|
-
it('base64-decodes attachment content before sending', async ()=>{
|
|
115
|
-
const create = jest.fn().mockResolvedValue({
|
|
116
|
-
id: 'id-1'
|
|
117
|
-
});
|
|
118
|
-
injectClient(provider, {
|
|
119
|
-
messages: {
|
|
120
|
-
create
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
await provider.sendEmail({
|
|
124
|
-
to: 'a@example.com',
|
|
125
|
-
subject: 'Hi',
|
|
126
|
-
attachments: [
|
|
127
|
-
{
|
|
128
|
-
filename: 'doc.txt',
|
|
129
|
-
content: Buffer.from('hello').toString('base64'),
|
|
130
|
-
contentType: 'text/plain'
|
|
131
|
-
}
|
|
132
|
-
]
|
|
133
|
-
});
|
|
134
|
-
const [, messageData] = create.mock.calls[0];
|
|
135
|
-
const attachment = messageData.attachment[0];
|
|
136
|
-
expect(attachment.filename).toBe('doc.txt');
|
|
137
|
-
expect(attachment.contentType).toBe('text/plain');
|
|
138
|
-
expect(Buffer.isBuffer(attachment.data)).toBe(true);
|
|
139
|
-
expect(attachment.data.toString('utf8')).toBe('hello');
|
|
140
|
-
});
|
|
141
|
-
it('returns the error message when the mailgun client rejects with an Error', async ()=>{
|
|
142
|
-
injectClient(provider, {
|
|
143
|
-
messages: {
|
|
144
|
-
create: jest.fn().mockRejectedValue(new Error('domain not found'))
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
const result = await provider.sendEmail({
|
|
148
|
-
to: 'a@example.com',
|
|
149
|
-
subject: 'Hi'
|
|
150
|
-
});
|
|
151
|
-
expect(result).toEqual({
|
|
152
|
-
success: false,
|
|
153
|
-
error: 'domain not found'
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
it('returns "Unknown error" when the mailgun client rejects with a non-Error', async ()=>{
|
|
157
|
-
injectClient(provider, {
|
|
158
|
-
messages: {
|
|
159
|
-
create: jest.fn().mockRejectedValue('some string failure')
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
const result = await provider.sendEmail({
|
|
163
|
-
to: 'a@example.com',
|
|
164
|
-
subject: 'Hi'
|
|
165
|
-
});
|
|
166
|
-
expect(result).toEqual({
|
|
167
|
-
success: false,
|
|
168
|
-
error: 'Unknown error'
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
describe('sendBulkEmails', ()=>{
|
|
173
|
-
it('sends each message individually and aggregates the results', async ()=>{
|
|
174
|
-
const create = jest.fn().mockResolvedValueOnce({
|
|
175
|
-
id: 'id-1'
|
|
176
|
-
}).mockRejectedValueOnce(new Error('failed-2'));
|
|
177
|
-
injectClient(provider, {
|
|
178
|
-
messages: {
|
|
179
|
-
create
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
const results = await provider.sendBulkEmails([
|
|
183
|
-
{
|
|
184
|
-
to: 'a@example.com',
|
|
185
|
-
subject: 'A'
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
to: 'b@example.com',
|
|
189
|
-
subject: 'B'
|
|
190
|
-
}
|
|
191
|
-
]);
|
|
192
|
-
expect(results).toEqual([
|
|
193
|
-
{
|
|
194
|
-
success: true,
|
|
195
|
-
messageId: 'id-1'
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
success: false,
|
|
199
|
-
error: 'failed-2'
|
|
200
|
-
}
|
|
201
|
-
]);
|
|
202
|
-
expect(create).toHaveBeenCalledTimes(2);
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
describe('healthCheck', ()=>{
|
|
206
|
-
it('returns false when not initialized', async ()=>{
|
|
207
|
-
await expect(provider.healthCheck()).resolves.toBe(false);
|
|
208
|
-
});
|
|
209
|
-
it('returns true when the mailgun domain lookup succeeds', async ()=>{
|
|
210
|
-
injectClient(provider, {
|
|
211
|
-
domains: {
|
|
212
|
-
get: jest.fn().mockResolvedValue({})
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
await expect(provider.healthCheck()).resolves.toBe(true);
|
|
216
|
-
});
|
|
217
|
-
it('returns false when the mailgun domain lookup fails', async ()=>{
|
|
218
|
-
injectClient(provider, {
|
|
219
|
-
domains: {
|
|
220
|
-
get: jest.fn().mockRejectedValue(new Error('nope'))
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
await expect(provider.healthCheck()).resolves.toBe(false);
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
describe('close', ()=>{
|
|
227
|
-
it('clears the client reference', async ()=>{
|
|
228
|
-
injectClient(provider, {
|
|
229
|
-
messages: {
|
|
230
|
-
create: jest.fn()
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
await provider.close();
|
|
234
|
-
const result = await provider.sendEmail({
|
|
235
|
-
to: 'a@example.com',
|
|
236
|
-
subject: 'Hi'
|
|
237
|
-
});
|
|
238
|
-
expect(result).toEqual({
|
|
239
|
-
success: false,
|
|
240
|
-
error: 'Mailgun provider not initialized'
|
|
241
|
-
});
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
});
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "SendGridProvider", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return SendGridProvider;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _constants = require("@flusys/nestjs-shared/constants");
|
|
12
|
-
const _common = require("@nestjs/common");
|
|
13
|
-
function _define_property(obj, key, value) {
|
|
14
|
-
if (key in obj) {
|
|
15
|
-
Object.defineProperty(obj, key, {
|
|
16
|
-
value: value,
|
|
17
|
-
enumerable: true,
|
|
18
|
-
configurable: true,
|
|
19
|
-
writable: true
|
|
20
|
-
});
|
|
21
|
-
} else {
|
|
22
|
-
obj[key] = value;
|
|
23
|
-
}
|
|
24
|
-
return obj;
|
|
25
|
-
}
|
|
26
|
-
let SendGridProvider = class SendGridProvider {
|
|
27
|
-
async initialize(config) {
|
|
28
|
-
this.apiKey = config.apiKey;
|
|
29
|
-
try {
|
|
30
|
-
// Dynamic import to avoid bundling issues
|
|
31
|
-
const sgMailModule = await new Function('return import("@sendgrid/mail")')();
|
|
32
|
-
this.sgMail = sgMailModule.default || sgMailModule;
|
|
33
|
-
this.sgMail.setApiKey(config.apiKey);
|
|
34
|
-
} catch {
|
|
35
|
-
throw new _common.InternalServerErrorException({
|
|
36
|
-
message: 'SendGrid initialization failed. Install: npm install @sendgrid/mail',
|
|
37
|
-
messageKey: _constants.SYSTEM_MESSAGES.SDK_NOT_INSTALLED,
|
|
38
|
-
messageVariables: {
|
|
39
|
-
sdk: 'sendgrid'
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
async sendEmail(options) {
|
|
45
|
-
if (!this.sgMail) return {
|
|
46
|
-
success: false,
|
|
47
|
-
error: 'SendGrid provider not initialized'
|
|
48
|
-
};
|
|
49
|
-
try {
|
|
50
|
-
const msg = this.buildMessage(options, true);
|
|
51
|
-
const [response] = await this.sgMail.send(msg);
|
|
52
|
-
const messageId = response.headers['x-message-id'] || response.headers['X-Message-Id'];
|
|
53
|
-
return {
|
|
54
|
-
success: true,
|
|
55
|
-
messageId
|
|
56
|
-
};
|
|
57
|
-
} catch (error) {
|
|
58
|
-
return {
|
|
59
|
-
success: false,
|
|
60
|
-
error: this.extractError(error)
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
async sendBulkEmails(options) {
|
|
65
|
-
if (!this.sgMail) return options.map(()=>({
|
|
66
|
-
success: false,
|
|
67
|
-
error: 'SendGrid provider not initialized'
|
|
68
|
-
}));
|
|
69
|
-
try {
|
|
70
|
-
const messages = options.map((opt)=>this.buildMessage(opt, false));
|
|
71
|
-
await this.sgMail.send(messages);
|
|
72
|
-
return options.map(()=>({
|
|
73
|
-
success: true
|
|
74
|
-
}));
|
|
75
|
-
} catch (error) {
|
|
76
|
-
const errorMessage = this.extractError(error);
|
|
77
|
-
return options.map(()=>({
|
|
78
|
-
success: false,
|
|
79
|
-
error: errorMessage
|
|
80
|
-
}));
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
async healthCheck() {
|
|
84
|
-
return !!(this.sgMail && this.apiKey?.startsWith('SG.'));
|
|
85
|
-
}
|
|
86
|
-
async close() {
|
|
87
|
-
this.sgMail = null;
|
|
88
|
-
}
|
|
89
|
-
// ─── Private Helpers ────────────────────────────────────────────────────────
|
|
90
|
-
buildMessage(options, includeExtras) {
|
|
91
|
-
const msg = {
|
|
92
|
-
to: this.toArray(options.to),
|
|
93
|
-
from: options.fromName ? {
|
|
94
|
-
email: options.from,
|
|
95
|
-
name: options.fromName
|
|
96
|
-
} : options.from,
|
|
97
|
-
subject: options.subject
|
|
98
|
-
};
|
|
99
|
-
if (options.html) msg.html = options.html;
|
|
100
|
-
if (options.text) msg.text = options.text;
|
|
101
|
-
if (includeExtras) {
|
|
102
|
-
if (options.cc) msg.cc = this.toArray(options.cc);
|
|
103
|
-
if (options.bcc) msg.bcc = this.toArray(options.bcc);
|
|
104
|
-
if (options.replyTo) msg.replyTo = options.replyTo;
|
|
105
|
-
if (options.attachments?.length) {
|
|
106
|
-
msg.attachments = options.attachments.map((a)=>({
|
|
107
|
-
filename: a.filename,
|
|
108
|
-
content: typeof a.content === 'string' ? a.content : a.content.toString('base64'),
|
|
109
|
-
type: a.contentType,
|
|
110
|
-
disposition: 'attachment'
|
|
111
|
-
}));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return msg;
|
|
115
|
-
}
|
|
116
|
-
toArray(value) {
|
|
117
|
-
return Array.isArray(value) ? value : [
|
|
118
|
-
value
|
|
119
|
-
];
|
|
120
|
-
}
|
|
121
|
-
extractError(error) {
|
|
122
|
-
const sgError = error;
|
|
123
|
-
return sgError.response?.body?.errors?.[0]?.message || sgError.message || 'Unknown error';
|
|
124
|
-
}
|
|
125
|
-
constructor(){
|
|
126
|
-
_define_property(this, "sgMail", null);
|
|
127
|
-
_define_property(this, "apiKey", null);
|
|
128
|
-
}
|
|
129
|
-
};
|