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 ADDED
@@ -0,0 +1,3 @@
1
+ import { HttpError } from './src/index.js'
2
+
3
+ throw new HttpError({ code: '2', httpStatusCode: 22 })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "core-services-sdk",
3
- "version": "1.2.3",
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,9 @@
1
+ import { Mailer } from './mailer.service.js'
2
+ import { TransportFactory } from './transport.factory.js'
3
+
4
+ export const initMailer = (config) => {
5
+ const transport = TransportFactory.create(config)
6
+ const mailer = new Mailer(transport)
7
+
8
+ return mailer
9
+ }
@@ -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
+ }
@@ -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
+ })