core-services-sdk 1.1.1 → 1.2.1
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/.vscode/launch.json +14 -0
- package/package.json +2 -1
- package/src/index.js +2 -0
- package/src/mongodb/connect.js +23 -0
- package/src/mongodb/index.js +71 -0
- package/src/rabbit-mq/index.js +3 -3
- package/tests/core-util.js +24 -0
- package/tests/mongodb.test.js +70 -0
- package/vitest.config.js +6 -0
- package/src/rabbit-mq/use-how-to.js +0 -22
package/.vscode/launch.json
CHANGED
|
@@ -15,6 +15,20 @@
|
|
|
15
15
|
"smartStep": true,
|
|
16
16
|
"skipFiles": ["<node_internals>/**"],
|
|
17
17
|
"console": "integratedTerminal"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"type": "node",
|
|
21
|
+
"request": "launch",
|
|
22
|
+
"name": "Debug Vitest Current File",
|
|
23
|
+
"autoAttachChildProcesses": true,
|
|
24
|
+
"program": "${workspaceFolder}/node_modules/vitest/vitest.mjs",
|
|
25
|
+
"args": [
|
|
26
|
+
"run",
|
|
27
|
+
"${relativeFile}" // ← no `--test`
|
|
28
|
+
],
|
|
29
|
+
"smartStep": true,
|
|
30
|
+
"skipFiles": ["<node_internals>/**"],
|
|
31
|
+
"console": "integratedTerminal"
|
|
18
32
|
}
|
|
19
33
|
]
|
|
20
34
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "core-services-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"main": "src/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"amqplib": "^0.10.8",
|
|
23
23
|
"http-status": "^2.1.0",
|
|
24
|
+
"mongodb": "^6.17.0",
|
|
24
25
|
"node-fetch": "^3.3.2",
|
|
25
26
|
"uuid": "^11.1.0",
|
|
26
27
|
"xml2js": "^0.6.2"
|
package/src/index.js
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { MongoClient, ServerApiVersion } from 'mongodb'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Connects to MongoDB.
|
|
5
|
+
*
|
|
6
|
+
* @param {Object} options
|
|
7
|
+
* @param {string} options.uri - MongoDB connection URI.
|
|
8
|
+
* @param {object} [options.serverApi] - Optional serverApi configuration.
|
|
9
|
+
* @returns {Promise<import('mongodb').MongoClient>}
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export const mongoConnect = async ({ uri, serverApi }) => {
|
|
13
|
+
const client = await MongoClient.connect(uri, {
|
|
14
|
+
serverApi: {
|
|
15
|
+
version: ServerApiVersion.v1,
|
|
16
|
+
strict: true,
|
|
17
|
+
deprecationErrors: true,
|
|
18
|
+
...serverApi,
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
return client
|
|
23
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import { mongoConnect } from './connect.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Initializes MongoDB collections and provides a transaction wrapper and read-only client accessor.
|
|
6
|
+
*
|
|
7
|
+
* @param {Object} options
|
|
8
|
+
* @param {{ uri: string, options: { dbName: string } }} options.config - MongoDB connection config
|
|
9
|
+
* @param {Record<string, string>} options.collectionNames - Map of collection keys to MongoDB collection names
|
|
10
|
+
*
|
|
11
|
+
* @returns {Promise<
|
|
12
|
+
* Record<string, import('mongodb').Collection> & {
|
|
13
|
+
* withTransaction: (action: ({ session: import('mongodb').ClientSession }) => Promise<void>) => Promise<void>,
|
|
14
|
+
* readonly client: import('mongodb').MongoClient
|
|
15
|
+
* }
|
|
16
|
+
* >}
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* const { users, logs, withTransaction, client } = await initializeMongoDb({
|
|
20
|
+
* config: {
|
|
21
|
+
* uri: 'mongodb://localhost:27017',
|
|
22
|
+
* options: { dbName: 'mydb' },
|
|
23
|
+
* },
|
|
24
|
+
* collectionNames: {
|
|
25
|
+
* users: 'users',
|
|
26
|
+
* logs: 'system_logs',
|
|
27
|
+
* },
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* await withTransaction(async ({ session }) => {
|
|
31
|
+
* await users.insertOne({ name: 'Alice' }, { session });
|
|
32
|
+
* await logs.insertOne({ event: 'UserCreated', user: 'Alice' }, { session });
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* await client.close(); // Close connection manually
|
|
36
|
+
*/
|
|
37
|
+
export const initializeMongoDb = async ({ config, collectionNames }) => {
|
|
38
|
+
const client = await mongoConnect(config)
|
|
39
|
+
const db = client.db(config.options.dbName)
|
|
40
|
+
|
|
41
|
+
const collectionRefs = Object.entries(collectionNames).reduce(
|
|
42
|
+
(collections, [key, collectionName]) => ({
|
|
43
|
+
...collections,
|
|
44
|
+
[key]: db.collection(collectionName),
|
|
45
|
+
}),
|
|
46
|
+
{},
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
const withTransaction = async (action) => {
|
|
50
|
+
const session = client.startSession()
|
|
51
|
+
try {
|
|
52
|
+
session.startTransaction()
|
|
53
|
+
await action({ session })
|
|
54
|
+
await session.commitTransaction()
|
|
55
|
+
} catch (error) {
|
|
56
|
+
await session.abortTransaction()
|
|
57
|
+
throw error
|
|
58
|
+
} finally {
|
|
59
|
+
await session.endSession()
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
...collectionRefs,
|
|
65
|
+
withTransaction,
|
|
66
|
+
/** @type {import('mongodb').MongoClient} */
|
|
67
|
+
get client() {
|
|
68
|
+
return client
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
}
|
package/src/rabbit-mq/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { v4 as uuidv4 } from 'uuid'
|
|
|
10
10
|
/**
|
|
11
11
|
* Connects to RabbitMQ server.
|
|
12
12
|
* @param {{ host: string }} options
|
|
13
|
-
* @returns {Promise<
|
|
13
|
+
* @returns {Promise<import('amqplib').Connection>}
|
|
14
14
|
*/
|
|
15
15
|
export const connectQueueService = async ({ host }) => {
|
|
16
16
|
try {
|
|
@@ -57,10 +57,10 @@ const parseMessage = (msgInfo) => {
|
|
|
57
57
|
* @returns {Promise<void>}
|
|
58
58
|
*/
|
|
59
59
|
export const subscribeToQueue = async ({
|
|
60
|
-
|
|
60
|
+
log,
|
|
61
61
|
queue,
|
|
62
|
+
channel,
|
|
62
63
|
onReceive,
|
|
63
|
-
log,
|
|
64
64
|
nackOnError = false,
|
|
65
65
|
}) => {
|
|
66
66
|
try {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { exec } from 'child_process'
|
|
2
|
+
/**
|
|
3
|
+
* Starts a MongoDB Docker container on the specified port.
|
|
4
|
+
* @param {string} command - Command to run, like: 'docker run -d --name mongo-test -p 2730:27017 mongo'.
|
|
5
|
+
* @returns {Promise<void>}
|
|
6
|
+
*/
|
|
7
|
+
export const runInTerminal = async (command) => {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
exec(command, (error, stdout, stderr) => {
|
|
10
|
+
if (error) {
|
|
11
|
+
console.error('Error starting command:', error.message)
|
|
12
|
+
return reject(error)
|
|
13
|
+
}
|
|
14
|
+
if (stderr) {
|
|
15
|
+
console.warn('stderr:', stderr)
|
|
16
|
+
}
|
|
17
|
+
console.log('Command started:', stdout.trim())
|
|
18
|
+
resolve()
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const sleep = async (milliseconds) =>
|
|
24
|
+
new Promise((res) => setTimeout(res, milliseconds))
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
|
2
|
+
import { runInTerminal, sleep } from './core-util.js'
|
|
3
|
+
import { initializeMongoDb } from '../src/mongodb/index.js'
|
|
4
|
+
|
|
5
|
+
const port = 2730
|
|
6
|
+
const dbName = 'testdb'
|
|
7
|
+
const host = 'localhost'
|
|
8
|
+
const mongoUri = `mongodb://${host}:${port}/?replicaSet=rs0`
|
|
9
|
+
const dockerStopCommand = `docker stop ${dbName} && docker rm ${dbName}`
|
|
10
|
+
const dockerCreateCommant = `docker run -d --name ${dbName} -p ${port}:27017 mongo --replSet rs0`
|
|
11
|
+
const dockerReplicaSetCommand = `docker exec -i ${dbName} mongosh --eval "rs.initiate()"`
|
|
12
|
+
|
|
13
|
+
describe('MongoDB Init & Transaction SDK', () => {
|
|
14
|
+
let collections
|
|
15
|
+
|
|
16
|
+
beforeAll(async () => {
|
|
17
|
+
try {
|
|
18
|
+
await runInTerminal(dockerStopCommand)
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.log('No existing container to stop.')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
await runInTerminal(dockerCreateCommant)
|
|
24
|
+
await sleep(5000)
|
|
25
|
+
await runInTerminal(dockerReplicaSetCommand)
|
|
26
|
+
|
|
27
|
+
collections = await initializeMongoDb({
|
|
28
|
+
config: {
|
|
29
|
+
uri: mongoUri,
|
|
30
|
+
options: { dbName },
|
|
31
|
+
},
|
|
32
|
+
collectionNames: {
|
|
33
|
+
users: 'users',
|
|
34
|
+
logs: 'logs',
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
await collections.users.deleteMany({})
|
|
39
|
+
await collections.logs.deleteMany({})
|
|
40
|
+
}, 60000)
|
|
41
|
+
|
|
42
|
+
afterAll(async () => {
|
|
43
|
+
if (collections?.client) {
|
|
44
|
+
await collections.client.db(dbName).dropDatabase()
|
|
45
|
+
await collections.client.close()
|
|
46
|
+
}
|
|
47
|
+
}, 20000)
|
|
48
|
+
|
|
49
|
+
it.skip('should insert into multiple collections within a transaction', async () => {
|
|
50
|
+
if (!collections) throw new Error('collections not initialized')
|
|
51
|
+
|
|
52
|
+
await collections.withTransaction(async ({ session }) => {
|
|
53
|
+
const userInsert = collections.users.insertOne(
|
|
54
|
+
{ name: 'Alice' },
|
|
55
|
+
{ session },
|
|
56
|
+
)
|
|
57
|
+
const logInsert = collections.logs.insertOne(
|
|
58
|
+
{ action: 'UserCreated', user: 'Alice' },
|
|
59
|
+
{ session },
|
|
60
|
+
)
|
|
61
|
+
await Promise.all([userInsert, logInsert])
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const insertedUser = await collections.users.findOne({ name: 'Alice' })
|
|
65
|
+
const insertedLog = await collections.logs.findOne({ user: 'Alice' })
|
|
66
|
+
|
|
67
|
+
expect(insertedUser).not.toBeNull()
|
|
68
|
+
expect(insertedLog).not.toBeNull()
|
|
69
|
+
}, 20000)
|
|
70
|
+
}, 60000)
|
package/vitest.config.js
ADDED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { initializeQueue, rabbitUriFromEnv } from './rabbit.js'
|
|
2
|
-
|
|
3
|
-
const log = {
|
|
4
|
-
info: console.log,
|
|
5
|
-
error: console.error,
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const start = async () => {
|
|
9
|
-
const host = rabbitUriFromEnv(process.env)
|
|
10
|
-
const rabbit = await initializeQueue({ host, log })
|
|
11
|
-
|
|
12
|
-
await rabbit.subscribe({
|
|
13
|
-
queue: 'testQueue',
|
|
14
|
-
onReceive: async (data) => {
|
|
15
|
-
console.log('Received:', data)
|
|
16
|
-
},
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
await rabbit.publish('testQueue', { hello: 'world' })
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
start()
|