diff --git a/.github/pr-title-checker-config.json b/.github/pr-title-checker-config.json new file mode 100644 index 0000000..3894be0 --- /dev/null +++ b/.github/pr-title-checker-config.json @@ -0,0 +1,15 @@ +{ + "LABEL": { + "name": "title needs formatting", + "color": "EEEEEE" + }, + "CHECKS": { + "prefixes": ["[Bot] docs: "], + "regexp": "^(feat|fix|docs|test|ci|chore)!?(\\(.*\\))?!?:.*" + }, + "MESSAGES": { + "success": "PR title is valid", + "failure": "PR title is invalid", + "notice": "PR Title needs to pass regex '^(feat|fix|docs|test|ci|chore)!?(\\(.*\\))?!?:.*" + } +} \ No newline at end of file diff --git a/.github/workflows/pr-title-check.yml b/.github/workflows/pr-title-check.yml new file mode 100644 index 0000000..8b9628a --- /dev/null +++ b/.github/workflows/pr-title-check.yml @@ -0,0 +1,29 @@ +name: "Lint PR" + +on: + pull_request_target: + types: [opened, edited, reopened, synchronize] + +# IMPORTANT: No checkout actions, scripts, or builds should be added to this workflow. Permissions should always be used +# with extreme caution. https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target +permissions: {} + +# PR updates can happen in quick succession, leading to this +# workflow being trigger a number of times. This limits it +# to one run per PR. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + + +jobs: + validate: + permissions: + contents: read + pull-requests: read + name: Validate PR Title + runs-on: ubuntu-latest + steps: + - uses: thehanimo/pr-title-checker@0cf5902181e78341bb97bb06646396e5bd354b3f # v1.4.0 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + configuration_path: ".github/pr-title-checker-config.json" \ No newline at end of file diff --git a/__tests__/__data__/constants.ts b/__tests__/__data__/constants.ts new file mode 100644 index 0000000..4ea47c4 --- /dev/null +++ b/__tests__/__data__/constants.ts @@ -0,0 +1,7 @@ +import { HL7_2_7_MSH } from '../../src/specification/2.7' + +export const MSH_HEADER: HL7_2_7_MSH = { + msh_9_1: 'ADT', + msh_9_2: 'A01', + msh_11_1: 'D' +} diff --git a/__tests__/hl7.build.test.ts b/__tests__/hl7.build.test.ts index d761b9d..a5c7640 100644 --- a/__tests__/hl7.build.test.ts +++ b/__tests__/hl7.build.test.ts @@ -1,133 +1,12 @@ import { randomUUID } from 'crypto' import fs from 'fs' import path from 'path' -import { - FileBatch, Batch, Message, createHL7Date, isBatch, isFile - , HL7Node, EmptyNode -} from '../src' -import { HL7_2_7_MSH, HL7_2_1, HL7_2_7, HL7_2_2, HL7_2_3, HL7_2_3_1, HL7_2_4, HL7_2_5, HL7_2_5_1, HL7_2_6, HL7_2_7_1, HL7_2_8 } from '../src/hl7' +import { FileBatch, Batch, Message, createHL7Date, HL7Node, EmptyNode } from '../src' +import { HL7_2_1, HL7_2_7, HL7_2_2, HL7_2_3, HL7_2_3_1, HL7_2_4, HL7_2_5, HL7_2_5_1, HL7_2_6, HL7_2_7_1, HL7_2_8 } from '../src/hl7' +import {MSH_HEADER} from "./__data__/constants"; import { sleep } from './__utils__' -const MSH_HEADER: HL7_2_7_MSH = { - msh_9_1: 'ADT', - msh_9_2: 'A01', - msh_11_1: 'D' -} - describe('node hl7 client - builder tests', () => { - describe('sanity checks', () => { - test('error - Message Object - nothing passed', async () => { - try { - const message = new Message() - message.set('MSH.5', '1') - } catch (err) { - expect(err).toEqual(new Error('mshHeader must be set if no HL7 message is being passed.')) - } - }) - - test('error - Message Object - text empty ', async () => { - try { - new Message({ text: '' }) - } catch (err) { - expect(err).toEqual(new Error('mshHeader must be set if no HL7 message is being passed.')) - } - }) - - test('error - Message Object - text must start with MSH ', async () => { - try { - new Message({ text: 'PV1|||||||^Jones\r' }) - } catch (err) { - expect(err).toEqual(new Error('text must begin with the MSH segment.')) - } - }) - - test('error - Message Object - msh 9.1 is empty ', async () => { - try { - new Message({ - // @ts-expect-error 9.1 should be not empty - messageHeader: { - msh_9_1: '' - } - }) - } catch (err) { - expect(err).toEqual(new Error('MSH.9.1 & MSH 9.2 must be defined.')) - } - }) - - test('error - Message Object - msh 9.2 is empty ', async () => { - try { - new Message({ - // @ts-expect-error 9.2 should be not empty - messageHeader: { - msh_9_1: 'ADT', - msh_9_2: '' - } - }) - } catch (err) { - expect(err).toEqual(new Error('MSH.9.2 must be 3 characters in length.')) - } - }) - - test('error - Message Object - msh 9.1 is not 3 character long ', async () => { - try { - new Message({ - // @ts-expect-error not filling this out for unit testing - messageHeader: { - msh_9_1: 'ADTY', - msh_9_2: 'A01', - msh_10: '123456' - } - }) - } catch (err) { - expect(err).toEqual(new Error('MSH.9.1 must be 3 characters in length.')) - } - }) - - test('error - Message Object - msh 9.2 is not 3 character long ', async () => { - try { - new Message({ - // @ts-expect-error not filling this out for unit testing - messageHeader: { - msh_9_1: 'ADT', - msh_9_2: 'A01Y', - msh_10: '123456' - } - }) - } catch (err) { - expect(err).toEqual(new Error('MSH.9.2 must be 3 characters in length.')) - } - }) - - test('error - Message Object - msh 10 is more than 199 characters ', async () => { - try { - new Message({ - // @ts-expect-error not filling this out for unit testing - messageHeader: { - msh_9_1: 'ADT', - msh_9_2: 'A01', - msh_10: 'AaasdfjlsdjflskdfsdflkjjsdlkflkasdflsjdflkjasdlfjsldkjlskjdfksajflksjdfAaasdfjlsdjflskdfsdflkjjsdlkflkasdflsjdflkjasdlfjsldkjlskjdfksajflksjdfAaasdfjlsdjflskdfsdflkjjsdlkflkasdflsjdflkjasdlfjsldkjlskjdfksajflksjdf' - } - }) - } catch (err) { - expect(err).toEqual(new Error('MSH.10 must be greater than 0 and less than 199 characters.')) - } - }) - - test('error - Message Object - msh 10 can not be blank', async () => { - try { - new Message({ - // @ts-expect-error not filling this out for unit testing - messageHeader: { - msh_9_1: 'ADT', - msh_9_2: 'A01', - msh_10: '' - } - }) - } catch (err) { - expect(err).toEqual(new Error('MSH.10 must be greater than 0 and less than 199 characters.')) - } - }) - }) describe('build message basics', () => { let message: Message @@ -904,126 +783,6 @@ describe('node hl7 client - builder tests', () => { expect(count).toBe(2) }) - test('...isFile - on Message - false', async () => { - const message = new Message({ - messageHeader: { - ...MSH_HEADER, - msh_10: 'CONTROL_ID' - } - }) - expect(isFile(message.toString())).toBe(false) - }) - - test('...isBatch - on Message - false', async () => { - const message = new Message({ - messageHeader: { - ...MSH_HEADER, - msh_10: 'CONTROL_ID' - } - }) - expect(isBatch(message.toString())).toBe(false) - }) - - test('...isBatch -should be false', async () => { - const batch = new Batch() - batch.start() - batch.end() - expect(isFile(batch.toString())).toBe(false) - }) - - test('...isBatch - should now be true', async () => { - const batch = new Batch() - batch.start() - batch.end() - expect(isBatch(batch.toString())).toBe(true) - }) - - test('...isFile - should now be true', async () => { - const file = new FileBatch() - file.start() - file.end() - expect(isFile(file.toString())).toBe(true) - }) - }) - - describe('parse message, batch, and file', () => { - const hl7_string: string = 'MSH|^~\\&|||||20081231||ADT^A01^ADT_A01|12345||2.7\rEVN||20081231' - const hl7_batch_msh_string: string = 'MSH|^~\\&|||||20081231||ADT^A01^ADT_A01|12345||2.7\rEVN||20081231\rMSH|^~\\&|||||20081231||ADT^A01^ADT_A01|12345||2.7\rEVN||20081231' - const hl7_non_standard: string = 'MSH:-+?*:field2:field3component1-field3component2:field4repeat1+field4repeat2:field5subcomponent1*field5subcomponent2:field6?R?' - const hl7_batch: string = 'BHS|^~\\&|||||20231208\rMSH|^~\\&|||||20231208||ADT^A01^ADT_A01|CONTROL_ID||2.7\rEVN||20081231\rEVN||20081231\rBTS|1' - const hl7_batch_non_standard: string = 'BHS:-+?*:::::20231208\rMSH|^~\\&|||||20231208||ADT^A01^ADT_A01|CONTROL_ID||2.7\rEVN||20081231\rEVN||20081231\rBTS|1' - - - test('...verify MSH input', async () => { - const message = new Message({ text: hl7_string }) - - expect(message.toString().substring(0, 8)).toBe('MSH|^~\\&') - expect(message.get('MSH.3').exists('')).toBe(false) - expect(message.get('MSH.9.1').toString()).toBe('ADT') - expect(message.get('MSH.9.2').toString()).toBe('A01') - expect(message.get('MSH.9.3').toString()).toBe('ADT_A01') - expect(message.get('MSH.10').toString()).toBe('12345') - expect(message.get('MSH.11.1').toString()).toBe('') - expect(message.get('MSH.12').toString()).toBe('2.7') - expect(message.get('EVN.2').toString()).toBe('20081231') - - let count: number = 0 - message.get('EVN').forEach((segment: HL7Node): void => { - expect(segment.name).toBe('EVN') - count++ - }) - - expect(count).toBe(1) - }) - - test('...parse non standard delimiters defined in MSH header', async () => { - const message = new Message({ text: hl7_non_standard }) - - expect(message.get('MSH.3').toString()).toBe('field2') - expect(message.get('MSH.4.2').toString()).toBe('field3component2') - expect(message.get('MSH.5').get(0).toString()).toBe('field4repeat1') - expect(message.get('MSH.5').get(1).toString()).toBe('field4repeat2') - expect(message.get('MSH.6.1.1').toString()).toBe('field5subcomponent1') - expect(message.get('MSH.6.1.2').toString()).toBe('field5subcomponent2') - expect(message.get('MSH.7').toString()).toBe('field6+') - }) - - test('...verify BHS input', async () => { - const batch = new Batch({ text: hl7_batch }) - - expect(batch.toString().substring(0, 8)).toBe('BHS|^~\\&') - }) - - test('...parse non standard delimiters defined in BHS header', async () => { - const batch = new Batch({ text: hl7_batch_non_standard }) - - expect(batch.toString().substring(0, 8)).toBe('BHS:-+?*') - }) - - test('...verify BHS input ... 1 message should exist ... 2 EVN segments inside', async () => { - const batch = new Batch({ text: hl7_batch }) - const messages = batch.messages() - expect(messages.length).toBe(1) - - messages.forEach((message: Message): void => { - let count: number = 0 - message.get('EVN').forEach((segment: HL7Node): void => { - expect(segment.name).toBe('EVN') - count++ - }) - expect(count).toBe(2) - }) - }) - - test('...should be used as a batch', async () => { - try { - // @ts-expect-error - const message = new Message({ text: hl7_batch_msh_string }) - }catch (err) { - expect(err).toEqual(new Error('Multiple MSH segments found. Use Batch.')) - } - }) - }) describe('file tests', () => { diff --git a/__tests__/hl7.sanity.test.ts b/__tests__/hl7.sanity.test.ts new file mode 100644 index 0000000..2498d7b --- /dev/null +++ b/__tests__/hl7.sanity.test.ts @@ -0,0 +1,275 @@ +import {Batch, FileBatch, isBatch, isFile, Message} from "../src"; +import {MSH_HEADER} from "./__data__/constants"; + +describe('node hl7 client - sanity tests', () => { + + describe('...Message', () => { + + test('error - Message Object - nothing passed', async () => { + try { + // @ts-expect-error + const message = new Message() + } catch (err) { + expect(err).toEqual(new Error('mshHeader must be set if no HL7 message is being passed.')) + } + }) + + test('error - Message Object - text empty ', async () => { + try { + new Message({ text: '' }) + } catch (err) { + expect(err).toEqual(new Error('mshHeader must be set if no HL7 message is being passed.')) + } + }) + + test('error - Message Object - text must start with MSH ', async () => { + try { + new Message({ text: 'PV1|||||||^Jones\r' }) + } catch (err) { + expect(err).toEqual(new Error('text must begin with the MSH segment.')) + } + }) + + test('error - Message Object - msh 9.1 is empty ', async () => { + try { + new Message({ + // @ts-expect-error 9.1 should be not empty + messageHeader: { + msh_9_1: '' + } + }) + } catch (err) { + expect(err).toEqual(new Error('MSH.9.1 & MSH 9.2 must be defined.')) + } + }) + + test('error - Message Object - msh 9.2 is empty ', async () => { + try { + new Message({ + // @ts-expect-error 9.2 should be not empty + messageHeader: { + msh_9_1: 'ADT', + msh_9_2: '' + } + }) + } catch (err) { + expect(err).toEqual(new Error('MSH.9.2 must be 3 characters in length.')) + } + }) + + test('error - Message Object - msh 9.1 is not 3 character long ', async () => { + try { + new Message({ + // @ts-expect-error not filling this out for unit testing + messageHeader: { + msh_9_1: 'ADTY', + msh_9_2: 'A01', + msh_10: '123456' + } + }) + } catch (err) { + expect(err).toEqual(new Error('MSH.9.1 must be 3 characters in length.')) + } + }) + + test('error - Message Object - msh 9.2 is not 3 character long ', async () => { + try { + new Message({ + // @ts-expect-error not filling this out for unit testing + messageHeader: { + msh_9_1: 'ADT', + msh_9_2: 'A01Y', + msh_10: '123456' + } + }) + } catch (err) { + expect(err).toEqual(new Error('MSH.9.2 must be 3 characters in length.')) + } + }) + + test('error - Message Object - msh 10 is more than 199 characters ', async () => { + try { + new Message({ + // @ts-expect-error not filling this out for unit testing + messageHeader: { + msh_9_1: 'ADT', + msh_9_2: 'A01', + msh_10: 'AaasdfjlsdjflskdfsdflkjjsdlkflkasdflsjdflkjasdlfjsldkjlskjdfksajflksjdfAaasdfjlsdjflskdfsdflkjjsdlkflkasdflsjdflkjasdlfjsldkjlskjdfksajflksjdfAaasdfjlsdjflskdfsdflkjjsdlkflkasdflsjdflkjasdlfjsldkjlskjdfksajflksjdf' + } + }) + } catch (err) { + expect(err).toEqual(new Error('MSH.10 must be greater than 0 and less than 199 characters.')) + } + }) + + test('error - Message Object - msh 10 can not be blank', async () => { + try { + new Message({ + // @ts-expect-error not filling this out for unit testing + messageHeader: { + msh_9_1: 'ADT', + msh_9_2: 'A01', + msh_10: '' + } + }) + } catch (err) { + expect(err).toEqual(new Error('MSH.10 must be greater than 0 and less than 199 characters.')) + } + }) + + }) + + describe('...Batch', () => { + + test('error - Batch Object - single MSH passed', async () => { + try { + // @ts-expect-error + const batch = new Batch({ text: 'MSH|^~\\&|||||20081231||ADT^A01^ADT_A01|12345||2.7\rEVN||20081231' }) + } catch (err) { + expect(err).toEqual(new Error('Unable to process a single MSH as a batch. Use Message.')) + } + }) + + test('utils - isBatch - on Message - false', async () => { + const message = new Message({ + messageHeader: { + ...MSH_HEADER, + msh_10: 'CONTROL_ID' + } + }) + expect(isBatch(message.toString())).toBe(false) + }) + + test('utils - isBatch -should be false', async () => { + const batch = new Batch() + batch.start() + batch.end() + expect(isFile(batch.toString())).toBe(false) + }) + + test('utils - isBatch - should now be true', async () => { + const batch = new Batch() + batch.start() + batch.end() + expect(isBatch(batch.toString())).toBe(true) + }) + + }) + + describe('...FileBatch', () => { + + test('utils - isFile - on Message - false', async () => { + const message = new Message({ + messageHeader: { + ...MSH_HEADER, + msh_10: 'CONTROL_ID' + } + }) + expect(isFile(message.toString())).toBe(false) + }) + + test('utils - isFile - should now be true', async () => { + const file = new FileBatch() + file.start() + file.end() + expect(isFile(file.toString())).toBe(true) + }) + + }) + + describe('parse message, batch, and file', () => { + const hl7_string: string = 'MSH|^~\\&|||||20081231||ADT^A01^ADT_A01|12345||2.7\rEVN||20081231' + const hl7_batch_msh_string: string = 'MSH|^~\\&|||||20081231||ADT^A01^ADT_A01|12345||2.7\rEVN||20081231\rMSH|^~\\&|||||20081231||ADT^A01^ADT_A01|12345||2.7\rEVN||20081231' + const hl7_non_standard: string = 'MSH:-+?*:field2:field3component1-field3component2:field4repeat1+field4repeat2:field5subcomponent1*field5subcomponent2:field6?R?' + const hl7_batch: string = 'BHS|^~\\&|||||20231208\rMSH|^~\\&|||||20231208||ADT^A01^ADT_A01|CONTROL_ID||2.7\rEVN||20081231\rEVN||20081231\rBTS|1' + const hl7_batch_non_standard: string = 'BHS:-+?*:::::20231208\rMSH|^~\\&|||||20231208||ADT^A01^ADT_A01|CONTROL_ID||2.7\rEVN||20081231\rEVN||20081231\rBTS|1' + + + test('...verify MSH input', async () => { + const message = new Message({ text: hl7_string }) + + expect(message.toString().substring(0, 8)).toBe('MSH|^~\\&') + expect(message.get('MSH.3').exists('')).toBe(false) + expect(message.get('MSH.9.1').toString()).toBe('ADT') + expect(message.get('MSH.9.2').toString()).toBe('A01') + expect(message.get('MSH.9.3').toString()).toBe('ADT_A01') + expect(message.get('MSH.10').toString()).toBe('12345') + expect(message.get('MSH.11.1').toString()).toBe('') + expect(message.get('MSH.12').toString()).toBe('2.7') + expect(message.get('EVN.2').toString()).toBe('20081231') + + let count: number = 0 + message.get('EVN').forEach((segment): void => { + expect(segment.name).toBe('EVN') + count++ + }) + + expect(count).toBe(1) + }) + + test('...parse non standard delimiters defined in MSH header', async () => { + const message = new Message({ text: hl7_non_standard }) + + expect(message.get('MSH.3').toString()).toBe('field2') + expect(message.get('MSH.4.2').toString()).toBe('field3component2') + expect(message.get('MSH.5').get(0).toString()).toBe('field4repeat1') + expect(message.get('MSH.5').get(1).toString()).toBe('field4repeat2') + expect(message.get('MSH.6.1.1').toString()).toBe('field5subcomponent1') + expect(message.get('MSH.6.1.2').toString()).toBe('field5subcomponent2') + expect(message.get('MSH.7').toString()).toBe('field6+') + }) + + test('...verify BHS input', async () => { + const batch = new Batch({ text: hl7_batch }) + + expect(batch.toString().substring(0, 8)).toBe('BHS|^~\\&') + }) + + test('...parse non standard delimiters defined in BHS header', async () => { + const batch = new Batch({ text: hl7_batch_non_standard }) + + expect(batch.toString().substring(0, 8)).toBe('BHS:-+?*') + }) + + test('...verify BHS input ... 1 message should exist ... 2 EVN segments inside', async () => { + const batch = new Batch({ text: hl7_batch }) + const messages = batch.messages() + expect(messages.length).toBe(1) + + messages.forEach((message: Message): void => { + let count: number = 0 + message.get('EVN').forEach((segment): void => { + expect(segment.name).toBe('EVN') + count++ + }) + expect(count).toBe(2) + }) + }) + + test('...should be used as a Batch', async () => { + try { + // @ts-expect-error + const message = new Message({ text: hl7_batch_msh_string }) + }catch (err) { + expect(err).toEqual(new Error('Multiple MSH segments found. Use Batch.')) + } + }) + + test('...many MSH not wrapped in a BHS is still a Batch', async () => { + const batch = new Batch({ text: hl7_batch_msh_string }) + + const messages = batch.messages() + expect(messages.length).toBe(2) + + messages.forEach((message: Message): void => { + let count: number = 0 + message.get('EVN').forEach((segment): void => { + expect(segment.name).toBe('EVN') + count++ + }) + expect(count).toBe(1) + }) + }) + }) + +}) \ No newline at end of file diff --git a/package.json b/package.json index 6092ac6..b7ceb29 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "pack": "npm pack", "prepublishOnly": "npm run clean && npm run build && npm run pack", "test": "jest", + "test:verbose": "jest --verbose", "test:open": "jest --detectOpenHandles", "test:watch": "jest --watch", "test:ci": "jest --ci", diff --git a/src/utils/normalizedBuilder.ts b/src/utils/normalizedBuilder.ts index 1333d58..16e0836 100644 --- a/src/utils/normalizedBuilder.ts +++ b/src/utils/normalizedBuilder.ts @@ -1,7 +1,9 @@ import fs from 'fs' import { HL7_2_7 } from '../specification/2.7.js' import { MSH } from '../specification/specification' +import { HL7FatalError } from './exception.js' import { ParserPlan } from './parserPlan.js' +import { isBatch } from './utils.js' const DEFAULT_CLIENT_BUILDER_OPTS = { date: '14', @@ -111,13 +113,13 @@ export function normalizedClientMessageBuilderOptions (raw?: ClientBuilderMessag const props: ClientBuilderMessageOptions = { ...DEFAULT_CLIENT_BUILDER_OPTS, ...raw } if (typeof props.messageHeader === 'undefined' && props.text === '') { - throw new Error('mshHeader must be set if no HL7 message is being passed.') + throw new HL7FatalError(500, 'mshHeader must be set if no HL7 message is being passed.') } else if (typeof props.messageHeader === 'undefined' && typeof props.text !== 'undefined' && props.text.slice(0, 3) !== 'MSH') { throw new Error('text must begin with the MSH segment.') } if ((typeof props.newLine !== 'undefined' && props.newLine === '\\r') || props.newLine === '\\n') { - throw new Error('newLine must be \r or \n') + throw new HL7FatalError(500, ('newLine must be \r or \n')) } if (props.date !== '8' && props.date !== '12' && props.date !== '14') { @@ -147,11 +149,15 @@ export function normalizedClientBatchBuilderOptions (raw?: ClientBuilderOptions) const props: ClientBuilderOptions = { ...DEFAULT_CLIENT_BUILDER_OPTS, ...raw } if (typeof props.text !== 'undefined' && props.text !== '' && props.text.slice(0, 3) !== 'BHS' && props.text.slice(0, 3) !== 'MSH') { - throw new Error('text must begin with the BHS or MSH segment.') + throw new HL7FatalError(500, ('text must begin with the BHS or MSH segment.')) + } + + if (typeof props.text !== 'undefined' && props.text !== '' && props.text.slice(0, 3) === 'MSH' && !isBatch(props.text)) { + throw new HL7FatalError(500, ('Unable to process a single MSH as a batch. Use Message.')) } if ((typeof props.newLine !== 'undefined' && props.newLine === '\\r') || props.newLine === '\\n') { - throw new Error('newLine must be \r or \n') + throw new HL7FatalError(500, ('newLine must be \r or \n')) } if (props.date !== '8' && props.date !== '12' && props.date !== '14') { @@ -179,19 +185,19 @@ export function normalizedClientFileBuilderOptions (raw?: ClientBuilderFileOptio const props: ClientBuilderFileOptions = { ...DEFAULT_CLIENT_FILE_OPTS, ...DEFAULT_CLIENT_BUILDER_OPTS, ...raw } if (typeof props.text !== 'undefined' && props.text !== '' && props.text.slice(0, 3) !== 'FHS') { - throw new Error('text must begin with the FHS segment.') + throw new HL7FatalError(500, ('text must begin with the FHS segment.')) } if ((typeof props.newLine !== 'undefined' && props.newLine === '\\r') || props.newLine === '\\n') { - throw new Error('newLine must be \r or \n') + throw new HL7FatalError(500, ('newLine must be \r or \n')) } if (typeof props.extension !== 'undefined' && props.extension.length !== 3) { - throw new Error('The extension for file save must be 3 characters long.') + throw new HL7FatalError(500, ('The extension for file save must be 3 characters long.')) } if (typeof props.fullFilePath !== 'undefined' && typeof props.fileBuffer !== 'undefined') { - throw new Error('You can not have specified a file path and a buffer. Please choose one or the other.') + throw new HL7FatalError(500, ('You can not have specified a file path and a buffer. Please choose one or the other.')) } if (props.date !== '8' && props.date !== '12' && props.date !== '14') { diff --git a/src/utils/normalizedClient.ts b/src/utils/normalizedClient.ts index 2b32f96..5b2f216 100644 --- a/src/utils/normalizedClient.ts +++ b/src/utils/normalizedClient.ts @@ -1,6 +1,7 @@ import { TcpSocketConnectOpts } from 'node:net' import type { ConnectionOptions as TLSOptions } from 'node:tls' import { InboundResponse } from '../client/module/inboundResponse.js' +import { HL7FatalError } from './exception.js' import { assertNumber, validIPv4, validIPv6 } from './utils.js' /** @@ -124,22 +125,22 @@ export function normalizeClientOptions (raw?: ClientOptions): ValidatedClientOpt const props: any = { ...DEFAULT_CLIENT_OPTS, ...raw } if (typeof props.host === 'undefined' || props.host.length <= 0) { - throw new Error('host is not defined or the length is less than 0.') + throw new HL7FatalError(500, 'host is not defined or the length is less than 0.') } if (props.ipv4 === true && props.ipv6 === true) { - throw new Error('ipv4 and ipv6 both can\'t be set to be both used exclusively.') + throw new HL7FatalError(500, 'ipv4 and ipv6 both can\'t be set to be both used exclusively.') } if (typeof props.host !== 'string' && props.ipv4 === false && props.ipv6 === false) { - throw new Error('host is not valid string.') + throw new HL7FatalError(500, 'host is not valid string.') } else if (typeof props.host === 'string' && props.ipv4 === true && props.ipv6 === false) { if (!validIPv4(props.host)) { - throw new Error('host is not a valid IPv4 address.') + throw new HL7FatalError(500, 'host is not a valid IPv4 address.') } } else if (typeof props.host === 'string' && props.ipv4 === false && props.ipv6 === true) { if (!validIPv6(props.host)) { - throw new Error('host is not a valid IPv6 address.') + throw new HL7FatalError(500, 'host is not a valid IPv6 address.') } } @@ -158,11 +159,11 @@ export function normalizeClientListenerOptions (raw?: ClientListenerOptions): Va const props: any = { ...DEFAULT_LISTEN_CLIENT_OPTS, ...raw } if (typeof props.port === 'undefined') { - throw new Error('port is not defined.') + throw new HL7FatalError(500, 'port is not defined.') } if (typeof props.port !== 'number') { - throw new Error('port is not valid number.') + throw new HL7FatalError(500, 'port is not valid number.') } assertNumber(props, 'connectionTimeout', 0) diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 42f5821..b0c5f8a 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -89,11 +89,16 @@ export const expBackoff = (step: number, high: number, attempts: number, exp = 2 */ export const isBatch = (message: string): boolean => { const lines = split(message).filter(line => line.startsWith('MSH')).length > 1 || false - return message.startsWith('BHS') || (lines && message.startsWith('BHS')) + if (lines) { + return true + } else if (message.startsWith('MSH') && !lines) { + return false + } + return message.startsWith('BHS') } /** - * Check to see if the message starts with a a File Batch (FHS) header segment. + * Check to see if the message starts with a File Batch (FHS) header segment. * @param message */ export const isFile = (message: string): boolean => {