core-services-sdk 1.2.3 → 1.3.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/index.js +3 -0
- package/package.json +4 -1
- package/src/index.js +3 -0
- package/src/mailer/index.js +9 -0
- package/src/mailer/mailer.service.js +28 -0
- package/src/mailer/transport.factory.js +45 -0
- package/src/mongodb/index.js +3 -1
- package/tests/mailer.integration.test.js +46 -0
- package/tests/mailer.unit.test.js +68 -0
package/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "core-services-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"main": "src/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -20,9 +20,12 @@
|
|
|
20
20
|
"description": "",
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"amqplib": "^0.10.8",
|
|
23
|
+
"aws-sdk": "^2.1692.0",
|
|
23
24
|
"http-status": "^2.1.0",
|
|
24
25
|
"mongodb": "^6.17.0",
|
|
25
26
|
"node-fetch": "^3.3.2",
|
|
27
|
+
"nodemailer": "^7.0.5",
|
|
28
|
+
"nodemailer-sendgrid-transport": "^0.2.0",
|
|
26
29
|
"uuid": "^11.1.0",
|
|
27
30
|
"xml2js": "^0.6.2"
|
|
28
31
|
},
|
package/src/index.js
CHANGED
|
@@ -4,4 +4,7 @@ export * from './mongodb/connect.js'
|
|
|
4
4
|
export * from './rabbit-mq/index.js'
|
|
5
5
|
export * as http from './http/http.js'
|
|
6
6
|
export * from './http/responseType.js'
|
|
7
|
+
export { initMailer } from './mailer/index.js'
|
|
7
8
|
export { HttpError } from './http/HttpError.js'
|
|
9
|
+
export { Mailer } from './mailer/mailer.service.js'
|
|
10
|
+
export { TransportFactory } from './mailer/transport.factory.js'
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export class Mailer {
|
|
2
|
+
/**
|
|
3
|
+
* @param {object} transporter - Nodemailer transporter instance
|
|
4
|
+
*/
|
|
5
|
+
constructor(transporter) {
|
|
6
|
+
if (!transporter || typeof transporter.sendMail !== 'function') {
|
|
7
|
+
throw new Error('Invalid transporter')
|
|
8
|
+
}
|
|
9
|
+
this.transporter = transporter
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Send an email
|
|
14
|
+
*/
|
|
15
|
+
async send({ to, subject, html, text, from, cc, bcc, replyTo, attachments }) {
|
|
16
|
+
return this.transporter.sendMail({
|
|
17
|
+
to,
|
|
18
|
+
cc,
|
|
19
|
+
bcc,
|
|
20
|
+
text,
|
|
21
|
+
from,
|
|
22
|
+
html,
|
|
23
|
+
subject,
|
|
24
|
+
replyTo,
|
|
25
|
+
attachments,
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import aws from 'aws-sdk'
|
|
2
|
+
import nodemailer from 'nodemailer'
|
|
3
|
+
import sgTransport from 'nodemailer-sendgrid-transport'
|
|
4
|
+
|
|
5
|
+
export class TransportFactory {
|
|
6
|
+
/**
|
|
7
|
+
* @param {object} config
|
|
8
|
+
* @returns {import('nodemailer').Transporter}
|
|
9
|
+
*/
|
|
10
|
+
static create(config) {
|
|
11
|
+
switch (config.type) {
|
|
12
|
+
case 'smtp':
|
|
13
|
+
return nodemailer.createTransport({
|
|
14
|
+
host: config.host,
|
|
15
|
+
port: config.port,
|
|
16
|
+
secure: config.secure,
|
|
17
|
+
auth: config.auth,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
case 'gmail':
|
|
21
|
+
return nodemailer.createTransport({
|
|
22
|
+
service: 'gmail',
|
|
23
|
+
auth: config.auth,
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
case 'sendgrid':
|
|
27
|
+
return nodemailer.createTransport(
|
|
28
|
+
sgTransport({
|
|
29
|
+
auth: { api_key: config.apiKey },
|
|
30
|
+
}),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
case 'ses':
|
|
34
|
+
const ses = new aws.SES({
|
|
35
|
+
accessKeyId: config.accessKeyId,
|
|
36
|
+
secretAccessKey: config.secretAccessKey,
|
|
37
|
+
region: config.region,
|
|
38
|
+
})
|
|
39
|
+
return nodemailer.createTransport({ SES: { ses, aws } })
|
|
40
|
+
|
|
41
|
+
default:
|
|
42
|
+
throw new Error(`Unsupported transport type: ${config.type}`)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/mongodb/index.js
CHANGED
|
@@ -48,15 +48,17 @@ export const initializeMongoDb = async ({ config, collectionNames = {} }) => {
|
|
|
48
48
|
|
|
49
49
|
const withTransaction = async (action) => {
|
|
50
50
|
const session = client.startSession()
|
|
51
|
+
let actionResponse
|
|
51
52
|
try {
|
|
52
53
|
session.startTransaction()
|
|
53
|
-
await action({ session })
|
|
54
|
+
actionResponse = await action({ session })
|
|
54
55
|
await session.commitTransaction()
|
|
55
56
|
} catch (error) {
|
|
56
57
|
await session.abortTransaction()
|
|
57
58
|
throw error
|
|
58
59
|
} finally {
|
|
59
60
|
await session.endSession()
|
|
61
|
+
return actionResponse
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import nodemailer from 'nodemailer'
|
|
3
|
+
import { Mailer } from '../src/mailer/mailer.service.js'
|
|
4
|
+
|
|
5
|
+
describe('Mailer (integration)', () => {
|
|
6
|
+
it('should send email using ethereal SMTP', async () => {
|
|
7
|
+
// Create ethereal test account
|
|
8
|
+
const testAccount = await nodemailer.createTestAccount()
|
|
9
|
+
|
|
10
|
+
// Create Nodemailer transporter for ethereal
|
|
11
|
+
const transporter = nodemailer.createTransport({
|
|
12
|
+
host: testAccount.smtp.host,
|
|
13
|
+
port: testAccount.smtp.port,
|
|
14
|
+
secure: testAccount.smtp.secure,
|
|
15
|
+
auth: {
|
|
16
|
+
user: testAccount.user,
|
|
17
|
+
pass: testAccount.pass,
|
|
18
|
+
},
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// Create Mailer instance
|
|
22
|
+
const mailer = new Mailer(transporter)
|
|
23
|
+
|
|
24
|
+
// Send test email
|
|
25
|
+
const result = await mailer.send({
|
|
26
|
+
to: 'recipient@example.com',
|
|
27
|
+
text: 'Hello from test - plain text',
|
|
28
|
+
from: '"My App" <no-reply@example.com>',
|
|
29
|
+
subject: 'core-service-sdk:Integration Test Email',
|
|
30
|
+
html: '<h1>Hello from core-service-sdk test</h1><p>This is a core-service-sdk</p>',
|
|
31
|
+
cc: '',
|
|
32
|
+
bcc: '',
|
|
33
|
+
replyTo: '',
|
|
34
|
+
attachments: '',
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// Assert response
|
|
38
|
+
expect(result.messageId).toBeDefined()
|
|
39
|
+
|
|
40
|
+
// Print preview URL (clickable in terminal)
|
|
41
|
+
const previewUrl = nodemailer.getTestMessageUrl(result)
|
|
42
|
+
console.log(`📨 Preview this email: ${previewUrl}`)
|
|
43
|
+
|
|
44
|
+
expect(previewUrl).toMatch(/^https:\/\/ethereal\.email/)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
2
|
+
|
|
3
|
+
const mockTransport = {
|
|
4
|
+
sendMail: vi.fn(),
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Dynamically mock the TransportFactory module and turn .create into a spy
|
|
8
|
+
vi.mock('../src/mailer/transport.factory.js', async () => {
|
|
9
|
+
const actual = await vi.importActual('../src/mailer/transport.factory.js')
|
|
10
|
+
return {
|
|
11
|
+
...actual,
|
|
12
|
+
TransportFactory: {
|
|
13
|
+
create: vi.fn(() => mockTransport),
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
// Mock Mailer class
|
|
19
|
+
vi.mock('../src/mailer/mailer.service.js', async () => {
|
|
20
|
+
const Mailer = vi.fn().mockImplementation((transport) => ({
|
|
21
|
+
send: vi.fn((opts) => transport.sendMail(opts)),
|
|
22
|
+
}))
|
|
23
|
+
return { Mailer }
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
// Only now import code under test
|
|
27
|
+
import { initMailer } from '../src/mailer/index.js'
|
|
28
|
+
import { TransportFactory } from '../src/mailer/transport.factory.js'
|
|
29
|
+
import { Mailer } from '../src/mailer/mailer.service.js'
|
|
30
|
+
|
|
31
|
+
describe('initMailer', () => {
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
vi.clearAllMocks()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('should create a Mailer instance with correct transport', () => {
|
|
37
|
+
const config = {
|
|
38
|
+
type: 'smtp',
|
|
39
|
+
host: 'host',
|
|
40
|
+
port: 587,
|
|
41
|
+
secure: false,
|
|
42
|
+
auth: { user: 'u', pass: 'p' },
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const mailer = initMailer(config)
|
|
46
|
+
|
|
47
|
+
// Ensure TransportFactory.create was called
|
|
48
|
+
expect(TransportFactory.create).toHaveBeenCalledWith(config)
|
|
49
|
+
|
|
50
|
+
// Ensure Mailer was created with the mock transport
|
|
51
|
+
expect(Mailer).toHaveBeenCalledWith(mockTransport)
|
|
52
|
+
|
|
53
|
+
const emailOptions = {
|
|
54
|
+
to: 'a@b.com',
|
|
55
|
+
subject: 'Test',
|
|
56
|
+
html: '<b>Test</b>',
|
|
57
|
+
from: '"My App" <no-reply@example.com>',
|
|
58
|
+
cc: '',
|
|
59
|
+
bcc: '',
|
|
60
|
+
text: '',
|
|
61
|
+
replyTo: '',
|
|
62
|
+
attachments: '',
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
mailer.send(emailOptions)
|
|
66
|
+
expect(mockTransport.sendMail).toHaveBeenCalledWith(emailOptions)
|
|
67
|
+
})
|
|
68
|
+
})
|