From 9d8eb92d77b02fbb92e627580bdd5fa94d1309ba Mon Sep 17 00:00:00 2001 From: Rana Salem Date: Thu, 22 May 2025 21:38:27 +0100 Subject: [PATCH 01/16] version bump + convert to es6 --- ...pm-publish-alpha.yaml => publish-alpha.yaml} | 2 +- ...-publish-stable.yaml => publish-stable.yaml} | 2 +- CONTRIBUTORS-GUIDE.md | 17 ----------------- LICENSE | 2 +- app/busgres-client.js | 10 +++------- app/busgres/client.js | 0 app/index.js | 4 ++-- package.json | 6 ++---- 8 files changed, 10 insertions(+), 33 deletions(-) rename .github/workflows/{npm-publish-alpha.yaml => publish-alpha.yaml} (96%) rename .github/workflows/{npm-publish-stable.yaml => publish-stable.yaml} (95%) delete mode 100644 CONTRIBUTORS-GUIDE.md create mode 100644 app/busgres/client.js diff --git a/.github/workflows/npm-publish-alpha.yaml b/.github/workflows/publish-alpha.yaml similarity index 96% rename from .github/workflows/npm-publish-alpha.yaml rename to .github/workflows/publish-alpha.yaml index 12e92b2..a5cc9d9 100644 --- a/.github/workflows/npm-publish-alpha.yaml +++ b/.github/workflows/publish-alpha.yaml @@ -1,4 +1,4 @@ -name: Busgres NPM Publish Alpha Version +name: NPM Publish Alpha Version on: push: diff --git a/.github/workflows/npm-publish-stable.yaml b/.github/workflows/publish-stable.yaml similarity index 95% rename from .github/workflows/npm-publish-stable.yaml rename to .github/workflows/publish-stable.yaml index 588e331..5bcc5d8 100644 --- a/.github/workflows/npm-publish-stable.yaml +++ b/.github/workflows/publish-stable.yaml @@ -1,4 +1,4 @@ -name: Busgres NPM Publish Stable Version +name: NPM Publish Stable Version on: push: diff --git a/CONTRIBUTORS-GUIDE.md b/CONTRIBUTORS-GUIDE.md deleted file mode 100644 index 21d5a7b..0000000 --- a/CONTRIBUTORS-GUIDE.md +++ /dev/null @@ -1,17 +0,0 @@ -# Contributor's Guide -This guide is for any developer who wants to contribute to the Busgres Node.js package. -## Reach Out -If you have any ideas for improvements or new features, reach out. Any and all ideas are welcome to be discussed. -## Branch Naming -Branch naming is largely flexible. The only requirement is that it is relevent to the work being completed on the branch. Each branch must include one of the following prefixes: -- `feature/`: Implementation of new features. -- `bugfix/`: Repairing bugs in the codebase. -- `refactor/`: Refactoring/cleaning up the codebase. -- `docs/`: Creating or updating existing documentation. -- `chore/`: Any small miscellaneous tasks. - -Once merged into the main branch, your working branch must be deleted. -## GitHub Workflows -Two GitHub Actions have been configured to automate publishing new versions (stable and alpha) to the NPM registry: -- [npm-publish-alpha.yaml](https://github.com/rtasalem/busgres/blob/main/.github/workflows/npm-publish-alpha.yaml) -- [npm-publish-stable.yaml](https://github.com/rtasalem/busgres/blob/main/.github/workflows/npm-publish-stable.yaml) diff --git a/LICENSE b/LICENSE index 71a72cf..b114b3a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Rana Salem +Copyright (c) 2025 Rana Salem Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app/busgres-client.js b/app/busgres-client.js index 1c1d181..935e449 100644 --- a/app/busgres-client.js +++ b/app/busgres-client.js @@ -1,7 +1,7 @@ -const { ServiceBusClient } = require('@azure/service-bus') -const { Client } = require('pg') +import { ServiceBusClient } from '@azure/service-bus' +import { Client } from 'pg' -class BusgresClient { +export class BusgresClient { constructor (sbConnectionString, sbEntityName, sbEntityType, sbEntitySubscription, pgClient) { this.sbConnectionString = sbConnectionString this.sbEntityName = sbEntityName @@ -85,7 +85,3 @@ class BusgresClient { } } } - -module.exports = { - BusgresClient -} diff --git a/app/busgres/client.js b/app/busgres/client.js new file mode 100644 index 0000000..e69de29 diff --git a/app/index.js b/app/index.js index 7c4cb67..313d8ee 100644 --- a/app/index.js +++ b/app/index.js @@ -1,5 +1,5 @@ -const { BusgresClient } = require('./busgres-client') +import { BusgresClient } from './busgres-client' -module.exports = { +export { BusgresClient } diff --git a/package.json b/package.json index 3dfebe3..3d167de 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,9 @@ { "name": "busgres", - "version": "3.0.12", + "version": "3.1.0", "description": "Busgres is an NPM package for receiving messages from Service Bus and saving them into a PostgreSQL database.", "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, + "type": "module", "repository": { "type": "git", "url": "https://github.com/rtasalem/busgres" From 81316e08eca1a666f77663fe5b754da31ff0decc Mon Sep 17 00:00:00 2001 From: Rana Salem Date: Thu, 22 May 2025 21:40:19 +0100 Subject: [PATCH 02/16] update readme (still a wip --- README.md | 5 ++--- package-definition.png | Bin 92349 -> 0 bytes 2 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 package-definition.png diff --git a/README.md b/README.md index 291bcae..0568394 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # Busgres +Service BUS + PostGRES = Busgres -![A sarcastic description of the Busgres NPM module](package-definition.png) - -[Busgres](https://www.npmjs.com/package/busgres) is a Node.js package that will receieve a message from an Azure Service Bus queue or topic and save it into a PostgreSQL database. It utilises the [`@azure/service-bus`](https://www.npmjs.com/package/@azure/service-bus) package for Service Bus integration and the [`pg` (node-postgres)](https://www.npmjs.com/package/pg) package for PostgreSQL connectivity. +[Busgres](https://www.npmjs.com/package/busgres) is a Node.js package that will receieve a message from an Azure Service Bus queue or topic and save it into a PostgreSQL database. It abstracts the [`@azure/service-bus`](https://www.npmjs.com/package/@azure/service-bus) the [`pg` (node-postgres)](https://www.npmjs.com/package/pg) package for Service Bus and Postgres integration. ## Installation diff --git a/package-definition.png b/package-definition.png deleted file mode 100644 index 50aa19f1c31fc4f4848861fe65ddad73fd77399c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92349 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 From f1b838261302502c829acd932b1f2af0b709f399 Mon Sep 17 00:00:00 2001 From: Rana Salem Date: Thu, 22 May 2025 21:45:03 +0100 Subject: [PATCH 03/16] clean up .gitignore --- .gitignore | 238 +--------------------------------------------- package-lock.json | 30 +++--- package.json | 4 +- 3 files changed, 20 insertions(+), 252 deletions(-) diff --git a/.gitignore b/.gitignore index 8b63f7b..740261a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,241 +1,5 @@ -# Created by https://www.toptal.com/developers/gitignore/api/node,visualstudiocode,macos,windows,linux -# Edit at https://www.toptal.com/developers/gitignore?templates=node,visualstudiocode,macos,windows,linux - -### Linux ### -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### macOS ### -# General .DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### macOS Patch ### -# iCloud generated files -*.icloud - -### Node ### -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul coverage *.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* - -### Node Patch ### -# Serverless Webpack directories -.webpack/ - -# Optional stylelint cache - -# SvelteKit build / generate output -.svelte-kit - -### VisualStudioCode ### -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -!.vscode/*.code-snippets - -# Local History for Visual Studio Code -.history/ - -# Built Visual Studio Code Extensions -*.vsix - -### VisualStudioCode Patch ### -# Ignore all local history of files -.history -.ionide - -### Windows ### -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# Other -test-output -test -test/test.js - -# End of https://www.toptal.com/developers/gitignore/api/node,visualstudiocode,macos,windows,linux \ No newline at end of file +test-output \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 6daea3b..7133df2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "busgres", - "version": "3.0.12", + "version": "3.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "busgres", - "version": "3.0.12", + "version": "3.1.0", "license": "MIT", "dependencies": { "@azure/service-bus": "^7.9.4", @@ -394,21 +394,18 @@ } }, "node_modules/fast-xml-parser": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.6.tgz", - "integrity": "sha512-M2SovcRxD4+vC493Uc2GZVcZaj66CCJhWurC4viynVSTvrpErCShNcDz1lAho6n9REQKvL/ll4A4/fw6Y9z8nw==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" } ], + "license": "MIT", "dependencies": { - "strnum": "^1.0.5" + "strnum": "^1.1.1" }, "bin": { "fxparser": "src/cli/cli.js" @@ -842,9 +839,16 @@ } }, "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" }, "node_modules/tslib": { "version": "2.6.2", diff --git a/package.json b/package.json index 3d167de..0694f62 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ ], "license": "MIT", "dependencies": { - "@azure/service-bus": "^7.9.4", - "pg": "^8.11.5" + "@azure/service-bus": "7.9.4", + "pg": "8.11.5" } } From 1ccf763191f8364db36b8f59b3eede6909a225e2 Mon Sep 17 00:00:00 2001 From: Rana Salem Date: Thu, 22 May 2025 21:47:36 +0100 Subject: [PATCH 04/16] update github workflows --- .github/workflows/publish-alpha.yaml | 4 ++-- .github/workflows/publish-stable.yaml | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish-alpha.yaml b/.github/workflows/publish-alpha.yaml index a5cc9d9..ccd647c 100644 --- a/.github/workflows/publish-alpha.yaml +++ b/.github/workflows/publish-alpha.yaml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [20.x] + node-version: [22.x] steps: - uses: actions/checkout@v4 @@ -35,4 +35,4 @@ jobs: run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_ACCESS_TOKEN }}" > ~/.npmrc - name: Publish to NPM - run: npm publish --tag alpha + run: npm publish --tag alpha \ No newline at end of file diff --git a/.github/workflows/publish-stable.yaml b/.github/workflows/publish-stable.yaml index 5bcc5d8..29bf896 100644 --- a/.github/workflows/publish-stable.yaml +++ b/.github/workflows/publish-stable.yaml @@ -1,11 +1,10 @@ name: NPM Publish Stable Version on: - push: - branches: - - main pull_request: - branches: + types: + - closed + branches: - main jobs: From 28c8aecca9db77e8c122c75d51bc5cd030b95ed2 Mon Sep 17 00:00:00 2001 From: Rana Salem Date: Thu, 22 May 2025 21:50:18 +0100 Subject: [PATCH 05/16] add codeowners --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..9f13513 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @rtasalem \ No newline at end of file From f7d6d48610e957e4d9b826f05e59eb13f8c45fca Mon Sep 17 00:00:00 2001 From: Rana Salem Date: Thu, 22 May 2025 23:33:18 +0100 Subject: [PATCH 06/16] wip --- app/database/postgres/client.js | 43 +++++++++++++++++++++++++++++ app/messaging/service-bus/client.js | 40 +++++++++++++++++++++++++++ package-lock.json | 4 +-- 3 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 app/database/postgres/client.js create mode 100644 app/messaging/service-bus/client.js diff --git a/app/database/postgres/client.js b/app/database/postgres/client.js new file mode 100644 index 0000000..9e07a53 --- /dev/null +++ b/app/database/postgres/client.js @@ -0,0 +1,43 @@ +import { Client } from 'pg' + +export class PostgresClient { + constructor(config) { + this.client = new Client(config) + } + + async connect() { + try { + await this.client.connect() + } catch (error) { + console.error('Error connecting to Postgres database:', error) + } + } + + async persistMessage(table, columns, message) { + try { + const messageContent = message.body + + const columns = columnNames + .map((column, index) => `$${index + 1}`) + .join(', ') + + const query = `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${columns})` + + const values = columns.map((column) => messageContent[column]) + + await this.client.query(query, values) + + console.log('A new message has been saved to the database:', messageContent) + } catch (error) { + console.error('Error saving message to the database:', error) + } + } + + async disconnect() { + try { + await this.client.end() + } catch (error) { + console.error('Error disconnecting database:', error) + } + } +} \ No newline at end of file diff --git a/app/messaging/service-bus/client.js b/app/messaging/service-bus/client.js new file mode 100644 index 0000000..d090512 --- /dev/null +++ b/app/messaging/service-bus/client.js @@ -0,0 +1,40 @@ +import { ServiceBusClient } from '@azure/service-bus' + +export class ServiceBusClient { + constructor(connectionString, entity, entityType, subscription, client) { + this.connectionString = connectionString + this.entity = entity + this.entityType = entityType + this.subscription = subscription + this.client = new ServiceBusClient(connectionString) + } + + createReceiver() { + if (this.type === 'queue') { + this.receiver = this.client.createReceiver(this.entity) + } else if (this.type === 'topic' && this.subscription) { + this.receiver = this.client.createReceiver( + this.entity, + this.subscription + ) + } else { + throw new Error( + 'Invalid entity type. Must be "queue" or "topic" OR subscription is missing' + ) + } + } + + async receiveMessage(table, columns) { + this.createReceiver() + + this.receiver.subscribe({ + processMessage: async (message) => { + await processMessageFn(message) + await this.receiver.completeMessage(message) + }, + processError: async (error) => { + await processErrorFn(error) + } + }) + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7133df2..02ef21a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,8 @@ "version": "3.1.0", "license": "MIT", "dependencies": { - "@azure/service-bus": "^7.9.4", - "pg": "^8.11.5" + "@azure/service-bus": "7.9.4", + "pg": "8.11.5" } }, "node_modules/@azure/abort-controller": { From f60ac869b088d628d6b7ddcb17ea3e4a4cee497b Mon Sep 17 00:00:00 2001 From: Rana Salem Date: Fri, 23 May 2025 20:47:21 +0100 Subject: [PATCH 07/16] refactor --- app/busgres-client.js | 101 ++++++++++--------------------------- app/busgres/client.js | 0 app/index.js | 5 -- app/message-persister.js | 19 +++++++ app/postgres-handler.js | 27 ++++++++++ app/service-bus-handler.js | 28 ++++++++++ index.js | 6 +-- 7 files changed, 104 insertions(+), 82 deletions(-) delete mode 100644 app/busgres/client.js delete mode 100644 app/index.js create mode 100644 app/message-persister.js create mode 100644 app/postgres-handler.js create mode 100644 app/service-bus-handler.js diff --git a/app/busgres-client.js b/app/busgres-client.js index 935e449..322f7a7 100644 --- a/app/busgres-client.js +++ b/app/busgres-client.js @@ -1,87 +1,40 @@ -import { ServiceBusClient } from '@azure/service-bus' -import { Client } from 'pg' +import { ServiceBusHandler } from './service-bus-handler.js' +import { PostgresHandler } from './postgres-handler.js' +import { MessagePersister } from './message-persister.js' export class BusgresClient { - constructor (sbConnectionString, sbEntityName, sbEntityType, sbEntitySubscription, pgClient) { - this.sbConnectionString = sbConnectionString - this.sbEntityName = sbEntityName - this.sbEntityType = sbEntityType - this.sbEntitySubscription = sbEntitySubscription || null - this.pgClient = new Client(pgClient) + constructor(serviceBusConfig, postgresConfig) { + this.serviceBusHandler = new ServiceBusHandler(serviceBusConfig) + this.postgresHandler = new PostgresHandler(postgresConfig) + this.messagePersister = new MessagePersister(this.postgresHandler) + this.receiver = null } - async connect () { + async start(table, columnNames) { try { - await this.pgClient.connect() + await this.postgresHandler.connect() + this.receiver = this.ServiceBusHandler.getReceiver() + + this.receiver.subscribe({ + processMessage: async (message) => { + await this.messagePersister.save(table, columnNames, message.body) + await this.receiver.completeMessage(message) + }, + processError: async (error) => { + console.error(`Error receiving message from Service Bus: ${error}`) + } + }) } catch (error) { - console.error('Error connecting to the database:', error) + throw new Error(`Error starting Busgres connection: ${error}`) } } - async saveMessage (tableName, columnNames, message) { + async stop() { try { - const messageContent = message.body - const columns = columnNames - .map((column, index) => `$${index + 1}`) - .join(', ') - const query = `INSERT INTO ${tableName} (${columnNames.join( - ', ' - )}) VALUES (${columns})` - - const values = columnNames.map((column) => messageContent[column]) - - await this.pgClient.query(query, values) - console.log( - 'The following message has been saved to the database:', - messageContent - ) - } catch (error) { - console.error('Error saving message to the database:', error) - } - } - - async receiveMessage (tableName, columnNames) { - this.sbClient = new ServiceBusClient(this.sbConnectionString) - this.receiver = this.sbClient.createReceiver(this.sbEntity) - - if (this.sbEntityType === 'queue') { - this.receiver = this.sbClient.createReceiver(this.sbEntityName) - } else if (this.sbEntityType === 'topic' && this.sbEntitySubscription) { - this.receiver = this.sbClient.createReceiver( - this.sbEntityName, - this.sbEntitySubscription - ) - } else { - throw new Error( - 'Invalid entity type (must be "queue" or "topic" OR missing subscription name for topic' - ) - } - - this.receiver.subscribe({ - processMessage: async (message) => { - console.log( - 'The following message was received from Service Bus:', - message.body - ) - await this.saveMessage(tableName, columnNames, message) - await this.receiver.completeMessage(message) - }, - processError: async (error) => { - console.error( - 'Error occurred while receiving message from Service Bus:', - error - ) - } - }) - } - - async disconnect () { - try { - await this.pgClient.end() - await this.receiver.close() - await this.sbClient.close() + await this.postgresHandler.disconnect() + await this.serviceBusHandler.disconnect() } catch (error) { - console.error('Error disconnecting:', error) + throw new Error(`Error stopping Busgres connection: ${error}`) } } -} +} \ No newline at end of file diff --git a/app/busgres/client.js b/app/busgres/client.js deleted file mode 100644 index e69de29..0000000 diff --git a/app/index.js b/app/index.js deleted file mode 100644 index 313d8ee..0000000 --- a/app/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import { BusgresClient } from './busgres-client' - -export { - BusgresClient -} diff --git a/app/message-persister.js b/app/message-persister.js new file mode 100644 index 0000000..af052e8 --- /dev/null +++ b/app/message-persister.js @@ -0,0 +1,19 @@ +export class MessagePersister { + constructor(postgresHandler) { + this.db = postgresHandler + } + + async save(table, columnNames, message) { + try { + const columns = columnNames + .map((column, index) => `$${index + 1}`) + .join(', ') + + const query = `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${columns})` + const values = columns.map((column) => message[column]) + await this.database.query(query, values) + } catch (error) { + throw new Error(`Error persisting message to database: ${error}`) + } + } +} \ No newline at end of file diff --git a/app/postgres-handler.js b/app/postgres-handler.js new file mode 100644 index 0000000..7d8166c --- /dev/null +++ b/app/postgres-handler.js @@ -0,0 +1,27 @@ +import { Client } from 'pg' + +export class PostgresHandler { + constructor(config) { + this.client = new Client(config) + } + + async connect () { + try { + await this.client.connect() + } catch (error) { + throw new Error(`Error connecting to Postgres: ${error}`) + } + } + + async disconnect () { + try { + await this.client.end() + } catch (error) { + throw new Error(`Error disconnecting from Postgres: ${error}`) + } + } + + async query(query, values) { + return this.client.query(query, values) + } +} \ No newline at end of file diff --git a/app/service-bus-handler.js b/app/service-bus-handler.js new file mode 100644 index 0000000..8a27d88 --- /dev/null +++ b/app/service-bus-handler.js @@ -0,0 +1,28 @@ +import { ServiceBusClient } from '@azure/service-bus' + +export class ServiceBusHandler { + constructor(connectionString, entity, entityType, subscription) { + this.client = new ServiceBusClient(connectionString) + this.entity = entity + this.entityType = entityType + this.subscription = subscription + } + + getReceiver() { + if (this.entityType === 'queue') { + this.client.createReceiver(this.entity) + } else if (this.entityType === 'topic') { + this.client.createReceiver(this.entity, this.subscription) + } else { + throw new Error('Entity type must be "queue" or "topic"') + } + } + + async disconnect() { + try { + await this.client.close() + } catch (error) { + throw new Error(`Error closing Service Bus connection: ${error}`) + } + } +} \ No newline at end of file diff --git a/index.js b/index.js index b981a07..f7b53bc 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ -const { BusgresClient } = require('./app') +import { BusgresClient } from './app/busgres-client.js' -module.exports = { +export { BusgresClient -} +} \ No newline at end of file From 72253565971d4d6d27f7ddd4240dc82e4bdf535c Mon Sep 17 00:00:00 2001 From: Rana Salem Date: Fri, 23 May 2025 21:01:12 +0100 Subject: [PATCH 08/16] update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 740261a..e922aec 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ coverage *.lcov node_modules/ -test-output \ No newline at end of file +test-output +.vscode/ \ No newline at end of file From 003f02ae1609555e575850d68aabdc917e6183d1 Mon Sep 17 00:00:00 2001 From: Rana Salem Date: Fri, 23 May 2025 22:13:53 +0100 Subject: [PATCH 09/16] update import statement for pg --- app/postgres-handler.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/postgres-handler.js b/app/postgres-handler.js index 7d8166c..17b2e48 100644 --- a/app/postgres-handler.js +++ b/app/postgres-handler.js @@ -1,11 +1,12 @@ -import { Client } from 'pg' +import pg from 'pg' +const { Client } = pg export class PostgresHandler { constructor(config) { this.client = new Client(config) } - async connect () { + async connect() { try { await this.client.connect() } catch (error) { @@ -13,7 +14,7 @@ export class PostgresHandler { } } - async disconnect () { + async disconnect() { try { await this.client.end() } catch (error) { From 91cf3c38b560c24a9ebd18fec3c351bdb3c1b4ce Mon Sep 17 00:00:00 2001 From: Rana Salem Date: Fri, 23 May 2025 23:16:16 +0100 Subject: [PATCH 10/16] update how to instantiate ServiceBusHandler --- app/busgres-client.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/busgres-client.js b/app/busgres-client.js index 322f7a7..232072c 100644 --- a/app/busgres-client.js +++ b/app/busgres-client.js @@ -4,7 +4,15 @@ import { MessagePersister } from './message-persister.js' export class BusgresClient { constructor(serviceBusConfig, postgresConfig) { - this.serviceBusHandler = new ServiceBusHandler(serviceBusConfig) + const { connectionString, entity, entityType, subscription } = serviceBusConfig + + this.serviceBusHandler = new ServiceBusHandler( + connectionString, + entity, + entityType, + subscription + ) + this.postgresHandler = new PostgresHandler(postgresConfig) this.messagePersister = new MessagePersister(this.postgresHandler) this.receiver = null @@ -13,7 +21,7 @@ export class BusgresClient { async start(table, columnNames) { try { await this.postgresHandler.connect() - this.receiver = this.ServiceBusHandler.getReceiver() + this.receiver = this.serviceBusHandler.getReceiver() this.receiver.subscribe({ processMessage: async (message) => { From 735b5005b66dd6a5bfd27829d9b4d6ac9fd9f916 Mon Sep 17 00:00:00 2001 From: Rana Salem Date: Fri, 23 May 2025 23:19:12 +0100 Subject: [PATCH 11/16] update busgres client constructor --- app/busgres-client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/busgres-client.js b/app/busgres-client.js index 232072c..afed5d1 100644 --- a/app/busgres-client.js +++ b/app/busgres-client.js @@ -3,7 +3,7 @@ import { PostgresHandler } from './postgres-handler.js' import { MessagePersister } from './message-persister.js' export class BusgresClient { - constructor(serviceBusConfig, postgresConfig) { + constructor({serviceBusConfig, postgresConfig}) { const { connectionString, entity, entityType, subscription } = serviceBusConfig this.serviceBusHandler = new ServiceBusHandler( From f743dab549f1b284fc4d9dc29dee550ba52c09c1 Mon Sep 17 00:00:00 2001 From: Rana Salem Date: Fri, 23 May 2025 23:51:10 +0100 Subject: [PATCH 12/16] update busgres client constructor --- app/busgres-client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/busgres-client.js b/app/busgres-client.js index afed5d1..5feb538 100644 --- a/app/busgres-client.js +++ b/app/busgres-client.js @@ -3,7 +3,7 @@ import { PostgresHandler } from './postgres-handler.js' import { MessagePersister } from './message-persister.js' export class BusgresClient { - constructor({serviceBusConfig, postgresConfig}) { + constructor({serviceBus, postgres}) { const { connectionString, entity, entityType, subscription } = serviceBusConfig this.serviceBusHandler = new ServiceBusHandler( From 6d77c53ff789a5c9919eafe6ed2e062c7cc5e6eb Mon Sep 17 00:00:00 2001 From: Rana Salem Date: Fri, 23 May 2025 23:53:46 +0100 Subject: [PATCH 13/16] update busgre client contructor --- app/busgres-client.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/busgres-client.js b/app/busgres-client.js index 5feb538..2405da0 100644 --- a/app/busgres-client.js +++ b/app/busgres-client.js @@ -4,7 +4,7 @@ import { MessagePersister } from './message-persister.js' export class BusgresClient { constructor({serviceBus, postgres}) { - const { connectionString, entity, entityType, subscription } = serviceBusConfig + const { connectionString, entity, entityType, subscription } = serviceBus this.serviceBusHandler = new ServiceBusHandler( connectionString, @@ -13,7 +13,7 @@ export class BusgresClient { subscription ) - this.postgresHandler = new PostgresHandler(postgresConfig) + this.postgresHandler = new PostgresHandler(postgres) this.messagePersister = new MessagePersister(this.postgresHandler) this.receiver = null } From 7fb8bc34997d75f7edb615ab0dcdec1a4acb025a Mon Sep 17 00:00:00 2001 From: Rana Salem Date: Fri, 23 May 2025 23:57:26 +0100 Subject: [PATCH 14/16] major version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0694f62..3b44b2f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "busgres", - "version": "3.1.0", + "version": "5.0.0", "description": "Busgres is an NPM package for receiving messages from Service Bus and saving them into a PostgreSQL database.", "main": "index.js", "type": "module", From 146242946b27d2e05de395f92c0f4762fcfee33c Mon Sep 17 00:00:00 2001 From: Rana Salem Date: Sat, 24 May 2025 00:17:05 +0100 Subject: [PATCH 15/16] add return statements to ServiceBusHandler class --- app/database/postgres/client.js | 43 ----------------------------- app/messaging/service-bus/client.js | 40 --------------------------- app/service-bus-handler.js | 8 ++++-- 3 files changed, 6 insertions(+), 85 deletions(-) delete mode 100644 app/database/postgres/client.js delete mode 100644 app/messaging/service-bus/client.js diff --git a/app/database/postgres/client.js b/app/database/postgres/client.js deleted file mode 100644 index 9e07a53..0000000 --- a/app/database/postgres/client.js +++ /dev/null @@ -1,43 +0,0 @@ -import { Client } from 'pg' - -export class PostgresClient { - constructor(config) { - this.client = new Client(config) - } - - async connect() { - try { - await this.client.connect() - } catch (error) { - console.error('Error connecting to Postgres database:', error) - } - } - - async persistMessage(table, columns, message) { - try { - const messageContent = message.body - - const columns = columnNames - .map((column, index) => `$${index + 1}`) - .join(', ') - - const query = `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${columns})` - - const values = columns.map((column) => messageContent[column]) - - await this.client.query(query, values) - - console.log('A new message has been saved to the database:', messageContent) - } catch (error) { - console.error('Error saving message to the database:', error) - } - } - - async disconnect() { - try { - await this.client.end() - } catch (error) { - console.error('Error disconnecting database:', error) - } - } -} \ No newline at end of file diff --git a/app/messaging/service-bus/client.js b/app/messaging/service-bus/client.js deleted file mode 100644 index d090512..0000000 --- a/app/messaging/service-bus/client.js +++ /dev/null @@ -1,40 +0,0 @@ -import { ServiceBusClient } from '@azure/service-bus' - -export class ServiceBusClient { - constructor(connectionString, entity, entityType, subscription, client) { - this.connectionString = connectionString - this.entity = entity - this.entityType = entityType - this.subscription = subscription - this.client = new ServiceBusClient(connectionString) - } - - createReceiver() { - if (this.type === 'queue') { - this.receiver = this.client.createReceiver(this.entity) - } else if (this.type === 'topic' && this.subscription) { - this.receiver = this.client.createReceiver( - this.entity, - this.subscription - ) - } else { - throw new Error( - 'Invalid entity type. Must be "queue" or "topic" OR subscription is missing' - ) - } - } - - async receiveMessage(table, columns) { - this.createReceiver() - - this.receiver.subscribe({ - processMessage: async (message) => { - await processMessageFn(message) - await this.receiver.completeMessage(message) - }, - processError: async (error) => { - await processErrorFn(error) - } - }) - } -} \ No newline at end of file diff --git a/app/service-bus-handler.js b/app/service-bus-handler.js index 8a27d88..b9caec7 100644 --- a/app/service-bus-handler.js +++ b/app/service-bus-handler.js @@ -10,9 +10,13 @@ export class ServiceBusHandler { getReceiver() { if (this.entityType === 'queue') { - this.client.createReceiver(this.entity) + return this.client.createReceiver(this.entity) } else if (this.entityType === 'topic') { - this.client.createReceiver(this.entity, this.subscription) + if (!this.subscription) { + throw new Error('Subscription is missing') + } + + return this.client.createReceiver(this.entity, this.subscription) } else { throw new Error('Entity type must be "queue" or "topic"') } From e339be1d9c01046bb5dbe0434a5ea0492dca4dc1 Mon Sep 17 00:00:00 2001 From: Rana Salem Date: Sat, 24 May 2025 00:58:41 +0100 Subject: [PATCH 16/16] update message persister --- app/busgres-client.js | 14 +++++++------- app/message-persister.js | 15 ++++++++------- app/postgres-handler.js | 14 +++++++------- app/service-bus-handler.js | 16 ++++++++-------- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/app/busgres-client.js b/app/busgres-client.js index 2405da0..54960e5 100644 --- a/app/busgres-client.js +++ b/app/busgres-client.js @@ -3,7 +3,7 @@ import { PostgresHandler } from './postgres-handler.js' import { MessagePersister } from './message-persister.js' export class BusgresClient { - constructor({serviceBus, postgres}) { + constructor ({ serviceBus, postgres }) { const { connectionString, entity, entityType, subscription } = serviceBus this.serviceBusHandler = new ServiceBusHandler( @@ -18,7 +18,7 @@ export class BusgresClient { this.receiver = null } - async start(table, columnNames) { + async start (table, columnNames) { try { await this.postgresHandler.connect() this.receiver = this.serviceBusHandler.getReceiver() @@ -29,20 +29,20 @@ export class BusgresClient { await this.receiver.completeMessage(message) }, processError: async (error) => { - console.error(`Error receiving message from Service Bus: ${error}`) + console.error(`Error receiving message from Service Bus: ${error.message}`) } }) } catch (error) { - throw new Error(`Error starting Busgres connection: ${error}`) + console.error(`Error starting Busgres connection: ${error.message}`) } } - async stop() { + async stop () { try { await this.postgresHandler.disconnect() await this.serviceBusHandler.disconnect() } catch (error) { - throw new Error(`Error stopping Busgres connection: ${error}`) + console.error(`Error stopping Busgres connection: ${error.message}`) } } -} \ No newline at end of file +} diff --git a/app/message-persister.js b/app/message-persister.js index af052e8..a1c0cf3 100644 --- a/app/message-persister.js +++ b/app/message-persister.js @@ -1,19 +1,20 @@ export class MessagePersister { - constructor(postgresHandler) { - this.db = postgresHandler + constructor (postgresHandler) { + this.database = postgresHandler } - async save(table, columnNames, message) { + async save (table, columnNames, message) { try { const columns = columnNames .map((column, index) => `$${index + 1}`) .join(', ') - const query = `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${columns})` - const values = columns.map((column) => message[column]) + const query = `INSERT INTO ${table} (${columnNames.join(', ')}) VALUES (${columns})` + const values = columnNames.map((column) => message[column]) + await this.database.query(query, values) } catch (error) { - throw new Error(`Error persisting message to database: ${error}`) + console.error(`Error persisting message to database: ${error.message}`) } } -} \ No newline at end of file +} diff --git a/app/postgres-handler.js b/app/postgres-handler.js index 17b2e48..61d67dc 100644 --- a/app/postgres-handler.js +++ b/app/postgres-handler.js @@ -2,27 +2,27 @@ import pg from 'pg' const { Client } = pg export class PostgresHandler { - constructor(config) { + constructor (config) { this.client = new Client(config) } - async connect() { + async connect () { try { await this.client.connect() } catch (error) { - throw new Error(`Error connecting to Postgres: ${error}`) + console.error(`Error connecting to Postgres: ${error.message}`) } } - async disconnect() { + async disconnect () { try { await this.client.end() } catch (error) { - throw new Error(`Error disconnecting from Postgres: ${error}`) + console.error(`Error disconnecting from Postgres: ${error.message}`) } } - async query(query, values) { + async query (query, values) { return this.client.query(query, values) } -} \ No newline at end of file +} diff --git a/app/service-bus-handler.js b/app/service-bus-handler.js index 8a27d88..00638a6 100644 --- a/app/service-bus-handler.js +++ b/app/service-bus-handler.js @@ -1,28 +1,28 @@ import { ServiceBusClient } from '@azure/service-bus' export class ServiceBusHandler { - constructor(connectionString, entity, entityType, subscription) { + constructor (connectionString, entity, entityType, subscription) { this.client = new ServiceBusClient(connectionString) this.entity = entity this.entityType = entityType this.subscription = subscription } - getReceiver() { + getReceiver () { if (this.entityType === 'queue') { - this.client.createReceiver(this.entity) + return this.client.createReceiver(this.entity) } else if (this.entityType === 'topic') { - this.client.createReceiver(this.entity, this.subscription) + return this.client.createReceiver(this.entity, this.subscription) } else { - throw new Error('Entity type must be "queue" or "topic"') + console.error('Entity type must be "queue" or "topic"') } } - async disconnect() { + async disconnect () { try { await this.client.close() } catch (error) { - throw new Error(`Error closing Service Bus connection: ${error}`) + console.error(`Error closing Service Bus connection: ${error.message}`) } } -} \ No newline at end of file +}