From 74eeb283a775bb30324efda274778b2cda689281 Mon Sep 17 00:00:00 2001 From: Shane Froebel Date: Mon, 11 Dec 2023 20:40:11 -0500 Subject: [PATCH] feat: client work x2 - fully working - some lint errors, to solve later #15 [ci skip] --- __tests__/hl7.build.test.ts | 92 ++++++++---------------- __tests__/hl7.client.test.ts | 32 ++++----- src/builder/message.ts | 6 +- src/client/client.ts | 5 +- src/client/hl7Outbound.ts | 133 +++++++++++++++++------------------ src/index.ts | 1 - src/specification/2.7.ts | 25 ++++--- src/specification/generic.ts | 17 ----- 8 files changed, 128 insertions(+), 183 deletions(-) delete mode 100644 src/specification/generic.ts diff --git a/__tests__/hl7.build.test.ts b/__tests__/hl7.build.test.ts index f1f56c9..6527454 100644 --- a/__tests__/hl7.build.test.ts +++ b/__tests__/hl7.build.test.ts @@ -35,15 +35,13 @@ describe('node hl7 client - builder tests', () => { test("error - Message Object - msh 9.1 is empty ", async () => { try { new Message({ + // @ts-expect-error 9.1 should be not empty messageHeader: { - // @ts-expect-error 9.1 should be not empty - msh_9: { - msh_9_1: "", - }, + msh_9_1: "", } }) } catch (err) { - expect(err).toEqual(new Error('MSH.9.1 & MSH 9.2 & MSH 9.3 must be defined.')) + expect(err).toEqual(new Error('MSH.9.1 & MSH 9.2 must be defined.')) } }) @@ -52,10 +50,8 @@ describe('node hl7 client - builder tests', () => { new Message({ // @ts-expect-error 9.2 should be not empty messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "", - }, + msh_9_1: "ADT", + msh_9_2: "", } }) } catch (err) { @@ -67,10 +63,8 @@ describe('node hl7 client - builder tests', () => { try { new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADTY", - msh_9_2: "A01" - }, + msh_9_1: "ADTY", + msh_9_2: "A01", msh_10: "123456" } }) @@ -83,10 +77,8 @@ describe('node hl7 client - builder tests', () => { try { new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "A01Y" - }, + msh_9_1: "ADT", + msh_9_2: "A01Y", msh_10: "123456" } }) @@ -99,10 +91,8 @@ describe('node hl7 client - builder tests', () => { try { new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "A01" - }, + msh_9_1: "ADT", + msh_9_2: "A01", msh_10: "AaasdfjlsdjflskdfsdflkjjsdlkflkasdflsjdflkjasdlfjsldkjlskjdfksajflksjdfAaasdfjlsdjflskdfsdflkjjsdlkflkasdflsjdflkjasdlfjsldkjlskjdfksajflksjdfAaasdfjlsdjflskdfsdflkjjsdlkflkasdflsjdflkjasdlfjsldkjlskjdfksajflksjdf" } }) @@ -115,10 +105,8 @@ describe('node hl7 client - builder tests', () => { try { new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "A01" - }, + msh_9_1: "ADT", + msh_9_2: "A01", msh_10: "" } }) @@ -137,10 +125,8 @@ describe('node hl7 client - builder tests', () => { beforeEach(async () => { message = new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "A01" - }, + msh_9_1: "ADT", + msh_9_2: "A01", msh_10: randomControlID } }) @@ -194,10 +180,8 @@ describe('node hl7 client - builder tests', () => { beforeEach(async () => { message = new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "A01" - }, + msh_9_1: "ADT", + msh_9_2: "A01", msh_10: "12345" } }) @@ -367,10 +351,8 @@ describe('node hl7 client - builder tests', () => { message = new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "A01" - }, + msh_9_1: "ADT", + msh_9_2: "A01", msh_10: 'CONTROL_ID' } }) @@ -385,10 +367,8 @@ describe('node hl7 client - builder tests', () => { message = new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "A01" - }, + msh_9_1: "ADT", + msh_9_2: "A01", msh_10: 'CONTROL_ID' } }) @@ -405,10 +385,8 @@ describe('node hl7 client - builder tests', () => { message = new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "A01" - }, + msh_9_1: "ADT", + msh_9_2: "A01", msh_10: 'CONTROL_ID' } }) @@ -426,10 +404,8 @@ describe('node hl7 client - builder tests', () => { message = new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "A01" - }, + msh_9_1: "ADT", + msh_9_2: "A01", msh_10: 'CONTROL_ID' } }) @@ -481,10 +457,8 @@ describe('node hl7 client - builder tests', () => { let message = new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "A01" - }, + msh_9_1: "ADT", + msh_9_2: "A01", msh_10: 'CONTROL_ID' } }) @@ -509,10 +483,8 @@ describe('node hl7 client - builder tests', () => { const message = new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "A01" - }, + msh_9_1: "ADT", + msh_9_2: "A01", msh_10: 'CONTROL_ID' } }) @@ -524,10 +496,8 @@ describe('node hl7 client - builder tests', () => { const message = new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "A01" - }, + msh_9_1: "ADT", + msh_9_2: "A01", msh_10: 'CONTROL_ID' } }) diff --git a/__tests__/hl7.client.test.ts b/__tests__/hl7.client.test.ts index 8fcf4e1..5a4e669 100644 --- a/__tests__/hl7.client.test.ts +++ b/__tests__/hl7.client.test.ts @@ -118,10 +118,10 @@ describe('node hl7 client', () => { stopPort: 65353 }) - const server = new Server({ bindAddress: 'localhost'}) + const server = new Server({ bindAddress: '0.0.0.0'}) const listener = server.createInbound({port: LISTEN_PORT}, async () => {}) - const client = new Client({ host: 'localhost'}) + const client = new Client({ host: '0.0.0.0'}) const outGoing = client.createOutbound({ port: LISTEN_PORT }, async () => {}) await expectEvent(listener, 'client.connect') @@ -151,10 +151,10 @@ describe('node hl7 client', () => { stopPort: 65353 }) - server = new Server({bindAddress: 'localhost'}) + server = new Server({bindAddress: '0.0.0.0'}) listener = server.createInbound({port: LISTEN_PORT}, async () => {}) - client = new Client({host: 'localhost'}) + client = new Client({host: '0.0.0.0'}) outGoing = client.createOutbound({port: LISTEN_PORT, waitAck: waitAck !== 2}, async () => {}) }) @@ -170,10 +170,8 @@ describe('node hl7 client', () => { let message = new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "A01" - }, + msh_9_1: "ADT", + msh_9_2: "A01", msh_10: 'CONTROL_ID' } }) @@ -187,10 +185,8 @@ describe('node hl7 client', () => { try { let message = new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "A01" - }, + msh_9_1: "ADT", + msh_9_2: "A01", msh_10: 'CONTROL_ID' } }) @@ -209,10 +205,8 @@ describe('node hl7 client', () => { let message = new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "A01" - }, + msh_9_1: "ADT", + msh_9_2: "A01", msh_10: 'CONTROL_ID' } }) @@ -221,10 +215,8 @@ describe('node hl7 client', () => { message = new Message({ messageHeader: { - msh_9: { - msh_9_1: "ADT", - msh_9_2: "A01" - }, + msh_9_1: "ADT", + msh_9_2: "A01", msh_10: 'CONTROL_ID' } }) diff --git a/src/builder/message.ts b/src/builder/message.ts index c67186d..2739c8c 100644 --- a/src/builder/message.ts +++ b/src/builder/message.ts @@ -42,9 +42,9 @@ export class Message extends RootBase { if (typeof this._opt.messageHeader !== 'undefined') { if (this._opt.specification.checkMSH(this._opt.messageHeader) === true) { this.set('MSH.7', Util.createHL7Date(new Date())) - this.set('MSH.9.1', this._opt.messageHeader.msh_9.msh_9_1.toString()) - this.set('MSH.9.2', this._opt.messageHeader.msh_9.msh_9_2.toString()) - this.set('MSH.9.3', `${this._opt.messageHeader.msh_9.msh_9_1.toString()}_${this._opt.messageHeader.msh_9.msh_9_2.toString()}`) + this.set('MSH.9.1', this._opt.messageHeader.msh_9_1.toString()) + this.set('MSH.9.2', this._opt.messageHeader.msh_9_2.toString()) + this.set('MSH.9.3', `${this._opt.messageHeader.msh_9_1.toString()}_${this._opt.messageHeader.msh_9_2.toString()}`) this.set('MSH.10', this._opt.messageHeader.msh_10.toString()) this.set('MSH.12', this._opt.specification.name.toString()) } diff --git a/src/client/client.ts b/src/client/client.ts index e744159..9972512 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -1,6 +1,6 @@ import EventEmitter from 'events' import { normalizeClientOptions, ClientListenerOptions, ClientOptions } from '../utils/normalizedClient.js' -import {HL7Outbound, OutboundHandler} from "./hl7Outbound"; +import { HL7Outbound, OutboundHandler } from './hl7Outbound' /** * Client Class @@ -20,8 +20,7 @@ export class Client extends EventEmitter { return new HL7Outbound(this, props, cb) } - getHost(): string { + getHost (): string { return this._opt.host } - } diff --git a/src/client/hl7Outbound.ts b/src/client/hl7Outbound.ts index 15f9730..99935ef 100644 --- a/src/client/hl7Outbound.ts +++ b/src/client/hl7Outbound.ts @@ -1,12 +1,12 @@ import EventEmitter from 'events' import * as net from 'net' import * as tls from 'tls' -import {Batch} from '../builder/batch.js' -import {Message} from '../builder/message.js' -import {randomString} from "../utils"; -import {HL7FatalError} from '../utils/exception' -import {ClientListenerOptions, normalizeClientListenerOptions} from '../utils/normalizedClient.js' -import {Client} from './client.js' +import { Batch } from '../builder/batch.js' +import { Message } from '../builder/message.js' +import { randomString } from '../utils' +import { HL7FatalError } from '../utils/exception' +import { ClientListenerOptions, normalizeClientListenerOptions } from '../utils/normalizedClient.js' +import { Client } from './client.js' export type OutboundHandler = (res: Buffer) => Promise @@ -18,15 +18,15 @@ export class HL7Outbound extends EventEmitter { /** @internal */ private readonly _handler: (res: Buffer) => void /** @internal */ - private _main: Client + private readonly _main: Client /** @internal */ private readonly _nodeId: string /** @internal */ - private _opt: ReturnType + private readonly _opt: ReturnType /** @internal */ - private _server: net.Socket | tls.TLSSocket + private readonly _server: net.Socket | tls.TLSSocket /** @internal */ - private _sockets: Map + private readonly _sockets: Map constructor (client: Client, props: ClientListenerOptions, handler: OutboundHandler) { super() @@ -41,11 +41,11 @@ export class HL7Outbound extends EventEmitter { this._server = this._connect() } - getHost(): string { + getHost (): string { return this._main._opt.host } - getPort(): string { + getPort (): string { return this._opt.port.toString() } @@ -55,7 +55,7 @@ export class HL7Outbound extends EventEmitter { async sendMessage (message: Message | Batch): Promise { // if we are waiting for an ack before we can send something else, and we are in that process. if (this._opt.waitAck && this._awaitingResponse) { - throw new HL7FatalError(500, `Can't send message while we are waiting for a response.`) + throw new HL7FatalError(500, 'Can\'t send message while we are waiting for a response.') } // ok, if our options are to wait for an acknowledgement, set the var to "true" @@ -63,70 +63,66 @@ export class HL7Outbound extends EventEmitter { this._awaitingResponse = true } - let messageToSend = Buffer.from(message.toString()) + const messageToSend = Buffer.from(message.toString()) - const header = Buffer.alloc(6); - header.writeInt32BE(messageToSend.length + 6, 1); - header.writeInt8(2, 5); - header[0] = header[1] ^ header[2] ^ header[3] ^ header[4] ^ header[5]; + const header = Buffer.alloc(6) + header.writeInt32BE(messageToSend.length + 6, 1) + header.writeInt8(2, 5) + header[0] = header[1] ^ header[2] ^ header[3] ^ header[4] ^ header[5] - const payload = Buffer.concat([header, messageToSend]); + const payload = Buffer.concat([header, messageToSend]) return this._server.write(payload, this._opt.encoding, () => { // FOR DEBUGGING ONLY: console.log(toSendData) }) - } /** @internal */ private _connect (): net.Socket | tls.TLSSocket { let server: net.Socket | tls.TLSSocket - let host = this._main._opt.host - let port = this._opt.port - let _opt_tls = this._main._opt.tls - - if (typeof _opt_tls !== 'undefined') { - // @TODO this needs to be expanded on for TLS options - server = tls.connect({host, port}) - } else { - server = net.createConnection({host, port}, () => { - - // set no delay - server.setNoDelay(true) - - // add socket - this._addSocket(this._nodeId, server, true) - - // check to make sure we do not max out on connections, we shouldn't... - if (this._sockets.size > this._opt.maxConnections) { - this._manageConnections() - } - }) - } - - server.on('connect', () => { - this.emit('connect') + const host = this._main._opt.host + const port = this._opt.port + const _optTls = this._main._opt.tls + + if (typeof _optTls !== 'undefined') { + // @TODO this needs to be expanded on for TLS options + server = tls.connect({ host, port }) + } else { + server = net.createConnection({ host, port }, () => { + // set no delay + server.setNoDelay(true) + + // add socket + this._addSocket(this._nodeId, server, true) + + // check to make sure we do not max out on connections, we shouldn't... + if (this._sockets.size > this._opt.maxConnections) { + this._manageConnections() + } }) + } - server.on('data', (buffer: Buffer) => { - this._handler(buffer) - }) + server.on('connect', () => { + this.emit('connect') + }) - server.on('error', err => { - this._removeSocket(this._nodeId) - this.emit('client.error', err, this._nodeId) - throw new HL7FatalError(500, 'Unable to connect to remote host.') - }) + server.on('data', (buffer: Buffer) => { + this._handler(buffer) + }) - server.on('end', () => { - this._removeSocket(this._nodeId) - this.emit('client.end') - }) + server.on('error', err => { + this._removeSocket(this._nodeId) + this.emit('client.error', err, this._nodeId) + }) - server.unref() + server.on('end', () => { + this._removeSocket(this._nodeId) + this.emit('client.end') + }) - return server + server.unref() + return server } /** Close Client Listener Instance. @@ -163,21 +159,20 @@ export class HL7Outbound extends EventEmitter { this._sockets.delete(nodeId) } - /**@internal */ - private _manageConnections() { - let count = this._sockets.size - this._opt.maxConnections; + /** @internal */ + private _manageConnections (): void { + let count = this._sockets.size - this._opt.maxConnections if (count <= 0) { return } - const list: { nodeID: any; lastUsed: any }[] = []; - this._sockets.forEach((socket, nodeID) => list.push({ nodeID, lastUsed: socket.lastUsed })); - list.sort((a, b) => a.lastUsed - b.lastUsed); - - count = Math.min(count, list.length - 1); - const removable = list.slice(0, count); + const list: Array<{ nodeID: any, lastUsed: any }> = [] + this._sockets.forEach((socket, nodeID) => list.push({ nodeID, lastUsed: socket.lastUsed })) + list.sort((a, b) => a.lastUsed - b.lastUsed) - removable.forEach(({ nodeID }) => this._removeSocket(nodeID)); + count = Math.min(count, list.length - 1) + const removable = list.slice(0, count) + removable.forEach(({ nodeID }) => this._removeSocket(nodeID)) } } diff --git a/src/index.ts b/src/index.ts index 1ff615c..921d64c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,7 +19,6 @@ export { assertNumber, isBatch, isFile, createHL7Date, validIPv6, validIPv4 } fr /** HL7 Specs **/ export type { MSH } from './specification/specification.js' -export type { HL7_MSH_MESSAGE_TYPE } from './specification/generic.js' export type { HL7_2_7_MSH } from './specification/2.7.js' /** HL7 Class **/ export { HL7_SPEC, HL7_SPEC_BASE, HL7_2_7 } diff --git a/src/specification/2.7.ts b/src/specification/2.7.ts index 6c5eae4..a6920f3 100644 --- a/src/specification/2.7.ts +++ b/src/specification/2.7.ts @@ -1,4 +1,3 @@ -import { HL7_MSH_MESSAGE_TYPE } from './generic.js' import { HL7_SPEC_BASE } from './specification.js' /** @@ -6,10 +5,18 @@ import { HL7_SPEC_BASE } from './specification.js' * @since 1.0.0 */ export interface HL7_2_7_MSH { - /** Message Type - * @description The message type of the Hl7 message we are sending. + /** Message Code * @since 1.0.0 */ - msh_9: HL7_MSH_MESSAGE_TYPE + msh_9_1: string + /** Trigger Event + * @since 1.0.0 */ + msh_9_2: string + /** Processing ID + * @since 1.0.0 */ + msh_11_1?: 'D' | 'P' | 'T' + /** Processing Mode + * @since 1.0.0 */ + msh_11_2?: 'A' | 'I' | 'R' | 'T' | '' /** Message Control ID * @description The message control ID that should ID this actual message. * @since 1.0.0 */ @@ -50,16 +57,16 @@ export class HL7_2_7 extends HL7_SPEC_BASE { * @return boolean */ checkMSH (msh: HL7_2_7_MSH): boolean { - if (typeof msh.msh_9.msh_9_1 === 'undefined' || - typeof msh.msh_9.msh_9_2 === 'undefined') { - throw new Error('MSH.9.1 & MSH 9.2 & MSH 9.3 must be defined.') + if (typeof msh.msh_9_1 === 'undefined' || + typeof msh.msh_9_2 === 'undefined') { + throw new Error('MSH.9.1 & MSH 9.2 must be defined.') } - if (msh.msh_9.msh_9_1.length !== 3) { + if (msh.msh_9_1.length !== 3) { throw new Error('MSH.9.1 must be 3 characters in length.') } - if (msh.msh_9.msh_9_2.length !== 3) { + if (msh.msh_9_2.length !== 3) { throw new Error('MSH.9.2 must be 3 characters in length.') } diff --git a/src/specification/generic.ts b/src/specification/generic.ts deleted file mode 100644 index 76e5fb6..0000000 --- a/src/specification/generic.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** @internal */ -export interface HL7_MSH_MESSAGE_TYPE { - /** Message Code */ - msh_9_1: string - /** Trigger Event */ - msh_9_2: string -} - -/** @internal */ -export interface HL7_MSH_PROCESSING_TYPE { - /** Processing ID - * @since 1.0.0 */ - msh_11_1?: 'D' | 'P' | 'T' - /** Processing Mode - * @since 1.0.0 */ - msh_11_2?: 'A' | 'I' | 'R' | 'T' | '' -}