Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#21): saved message structure #118

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions src/client/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ import Message from '../builder/message.js'
import { PROTOCOL_MLLP_FOOTER, PROTOCOL_MLLP_HEADER } from '../utils/constants.js'
import { ReadyState } from '../utils/enum.js'
import { HL7FatalError } from '../utils/exception.js'
import { ClientListenerOptions, normalizeClientListenerOptions, OutboundHandler } from '../utils/normalizedClient.js'
import {
ClientListenerOptions,
normalizeClientListenerOptions,
OutboundHandler,
SavedMessageHandler
} from '../utils/normalizedClient.js'
import { createDeferred, Deferred, expBackoff } from '../utils/utils.js'
import { Client } from './client.js'
import { InboundResponse } from './module/inboundResponse.js'
import { InboundResponse } from './module'

/* eslint-disable */
export interface IConnection extends EventEmitter {
Expand Down Expand Up @@ -41,6 +46,8 @@ export class Connection extends EventEmitter implements IConnection {
/** @internal */
_handler: OutboundHandler
/** @internal */
_savedMessageHandler: SavedMessageHandler | undefined
/** @internal */
private readonly _main: Client
/** @internal */
private readonly _opt: ReturnType<typeof normalizeClientListenerOptions>
Expand Down Expand Up @@ -78,15 +85,21 @@ export class Connection extends EventEmitter implements IConnection {
* @param props The individual port connection options.
* Some values will be defaulted by the parent server connection.
* @param handler The function that will send the returned information back to the client after we got a response from the server.
* @param savedMessageHandler OPTIONAL - The function that will handle any messages to save if it has trouble sending it to a remote server.
* Having this here will prevent the message from being stored in memory,
* thus you have to listen for the `connection` emit to then check your code which stores the message,
* (i.e., in a flat file stored locally or RabbitMQ, etc.) and then retransmit the message.
* This should lesson the MEMORY usage your application might have to do if storing large messages, awaiting
* @example
* ```ts
* const OB = client.createConnection({ port: 3000 }, async (res) => {})
* ```
*/
constructor (client: Client, props: ClientListenerOptions, handler: OutboundHandler) {
constructor (client: Client, props: ClientListenerOptions, handler: OutboundHandler, savedMessageHandler?: SavedMessageHandler) {
super()

this._handler = handler
this._savedMessageHandler = savedMessageHandler
this._main = client
this._awaitingResponse = false

Expand Down Expand Up @@ -200,7 +213,7 @@ export class Connection extends EventEmitter implements IConnection {
* @example
* ```ts
*
* // the OB was set from the orginial 'createConnection' method.
* // the OB was set from the original 'createConnection' method.
*
* let message = new Message({
* messageHeader: {
Expand All @@ -227,14 +240,19 @@ export class Connection extends EventEmitter implements IConnection {
return this._awaitingResponse
}

const checkSend = async (_message: string): Promise<boolean> => {
const checkSend = async (message: string): Promise<boolean> => {
while (true) { // noinspection InfiniteLoopJS
try {
if ((this._readyState === ReadyState.CLOSED) || (this._readyState === ReadyState.CLOSING)) {
// noinspection ExceptionCaughtLocallyJS
throw new HL7FatalError('In an invalid state to be able to send message.')
}
if (this._readyState !== ReadyState.CONNECTED) {
// if we are not connected, and we are going and the client wants to use their own method to save messages,
if (typeof this._savedMessageHandler !== 'undefined') {
return await this._savedMessageHandler(message)
}

// if we are not connected,
// check to see if we are now connected.
if (this._pendingSetup === false) {
Expand Down
3 changes: 3 additions & 0 deletions src/client/module/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { InboundResponse } from './inboundResponse.js'

export { InboundResponse }
22 changes: 21 additions & 1 deletion src/utils/normalizedClient.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TcpSocketConnectOpts } from 'node:net'
import type { ConnectionOptions as TLSOptions } from 'node:tls'
import { InboundResponse } from '../client/module/inboundResponse.js'
import { InboundResponse } from '../client/module'
import { HL7FatalError } from './exception.js'
import { assertNumber, validIPv4, validIPv6 } from './utils.js'

Expand All @@ -12,6 +12,26 @@ import { assertNumber, validIPv4, validIPv6 } from './utils.js'
*/
export type OutboundHandler = (res: InboundResponse) => Promise<void> | void

/**
* Saved Message Handler
* @remarks Used
* to send back to the application, the Hl7 message that need to be saved.
* Currently, the message is not able to send normally,
* or we are awaiting a previous acknowledgement that has yet to come.
*
* Errors that the client could experience are its own networking issues, or the remote side is having an issue,
* but regardless, this handler will be hit if specified for each connection.
*
* Instead of the messages being stored in Node.js memory, the object is returned
*
* @since 2.4.0
* @param message
* @return Return true if we successfully saved the message locally.
* If false (default) is returned, the system will keep looping until it knows the message has been successfully saved
* per the application specifications.
*/
export type SavedMessageHandler = (message: string) => Promise<boolean> | boolean

const DEFAULT_CLIENT_OPTS = {
encoding: 'utf-8',
connectionTimeout: 10000,
Expand Down