Skip to content

Commit 6795ef6

Browse files
committed
Merge pull request #99 from cbcrc:feat/support_no_messageId
feat: support for no messageId in non-strict mode
2 parents 8d6e3ee + 8e2743f commit 6795ef6

File tree

6 files changed

+128
-9
lines changed

6 files changed

+128
-9
lines changed

packages/connector/src/MosConnection.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ export class MosConnection extends EventEmitter implements IMosConnection {
8282
this._conf.mosID,
8383
connectionOptions.primary.timeout,
8484
connectionOptions.primary.heartbeatInterval,
85-
this._debug
85+
this._debug,
86+
this.mosTypes.strict
8687
)
8788
let secondary = null
8889
this._ncsConnections[connectionOptions.primary.host] = primary
@@ -128,7 +129,8 @@ export class MosConnection extends EventEmitter implements IMosConnection {
128129
this._conf.mosID,
129130
connectionOptions.secondary.timeout,
130131
connectionOptions.secondary.heartbeatInterval,
131-
this._debug
132+
this._debug,
133+
this.mosTypes.strict
132134
)
133135
this._ncsConnections[connectionOptions.secondary.host] = secondary
134136
secondary.on('rawMessage', (type: string, message: string) => {
@@ -459,7 +461,8 @@ export class MosConnection extends EventEmitter implements IMosConnection {
459461
this._conf.mosID,
460462
undefined,
461463
undefined,
462-
this._debug
464+
this._debug,
465+
this.mosTypes.strict
463466
)
464467
this._ncsConnections[remoteAddress] = primary
465468

packages/connector/src/__tests__/Profile0-non-strict.spec.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import {
22
checkMessageSnapshot,
33
clearMocks,
44
decode,
5+
DEFAULT_TIMEOUT,
6+
delay,
57
doBeforeAll,
68
encode,
79
getMessageId,
@@ -104,6 +106,41 @@ describe('Profile 0 - non strict', () => {
104106
expect(socketMockUpper).toBeTruthy()
105107
expect(serverSocketMockLower).toBeTruthy()
106108
})
109+
test('send heartbeats - no messageId', async () => {
110+
socketMockLower.setAutoReplyToHeartBeat(false) // Handle heartbeat manually
111+
socketMockUpper.setAutoReplyToHeartBeat(false) // Handle heartbeat manually
112+
113+
const heartbeatCount = {
114+
upper: 0,
115+
lower: 0,
116+
}
117+
118+
const mockReply = (portType: 'upper' | 'lower') => {
119+
return (data: any) => {
120+
const str = decode(data)
121+
122+
if (str.match(/<heartbeat/)) {
123+
const repl = getXMLReply('', xmlData.heartbeat)
124+
heartbeatCount[portType]++
125+
return encode(repl)
126+
} else throw new Error('Mock: Unhandled message: ' + str)
127+
}
128+
}
129+
130+
for (let i = 0; i < 100; i++) {
131+
socketMockUpper.mockAddReply(mockReply('upper'))
132+
socketMockLower.mockAddReply(mockReply('lower'))
133+
}
134+
135+
// During this time, there should have been sent a few heartbeats to the server:
136+
await delay(DEFAULT_TIMEOUT * 4.5)
137+
expect(heartbeatCount.upper).toBeGreaterThanOrEqual(4)
138+
expect(heartbeatCount.lower).toBeGreaterThanOrEqual(4)
139+
expect(mosDevice.getConnectionStatus()).toMatchObject({ PrimaryConnected: true })
140+
141+
socketMockLower.setAutoReplyToHeartBeat(true) // reset
142+
socketMockUpper.setAutoReplyToHeartBeat(true) // reset
143+
})
107144
test('requestMachineInfo - missing <time>', async () => {
108145
// Prepare mock server response:
109146
const mockReply = jest.fn((data) => {
@@ -152,6 +189,28 @@ describe('Profile 0 - non strict', () => {
152189
expect(returnedMachineInfo).toMatchObject(replyMessage)
153190
// expect(returnedMachineInfo.opTime).toBeUndefined()
154191
})
192+
test('requestMachineInfo - empty <time>, no messageId', async () => {
193+
// Prepare mock server response:
194+
const mockReply = jest.fn((_data) => {
195+
const replyMessage = xmlData.machineInfo.replace(/<time>.*<\/time>/, '<time></time>')
196+
const repl = getXMLReply('', replyMessage)
197+
return encode(repl)
198+
})
199+
socketMockLower.mockAddReply(mockReply)
200+
if (!xmlApiData.mosObj.ID) throw new Error('xmlApiData.mosObj.ID not set')
201+
202+
const returnedMachineInfo: IMOSListMachInfo = await mosDevice.requestMachineInfo()
203+
expect(mockReply).toHaveBeenCalledTimes(1)
204+
const msg = decode(mockReply.mock.calls[0][0])
205+
expect(msg).toMatch(/<reqMachInfo\/>/)
206+
checkMessageSnapshot(msg)
207+
208+
const replyMessage = { ...xmlApiData.machineInfoReply }
209+
replyMessage.time = mosTypes.mosTime.fallback()
210+
211+
expect(returnedMachineInfo).toMatchObject(replyMessage)
212+
// expect(returnedMachineInfo.opTime).toBeUndefined()
213+
})
155214
test('requestMachineInfo - bad formatted <time>', async () => {
156215
// Prepare mock server response:
157216
const mockReply = jest.fn((data) => {

packages/connector/src/__tests__/__snapshots__/Profile0-non-strict.spec.ts.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ exports[`Profile 0 - non strict requestMachineInfo - empty <time> 1`] = `
1818
</mos>"
1919
`;
2020
21+
exports[`Profile 0 - non strict requestMachineInfo - empty <time>, no messageId 1`] = `
22+
"<mos>
23+
<ncsID>their.mos.id</ncsID>
24+
<mosID>our.mos.id</mosID>
25+
<messageID>xx</messageID>
26+
<reqMachInfo/>
27+
</mos>"
28+
`;
29+
2130
exports[`Profile 0 - non strict requestMachineInfo - missing <opTime> 1`] = `
2231
"<mos>
2332
<ncsID>their.mos.id</ncsID>

packages/connector/src/__tests__/lib.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ export function getXMLReply(
120120
ourMosId?: string,
121121
theirMosId?: string
122122
): string {
123+
//add field only if messageId exist
124+
if(messageId) messageId = '<messageID>' + messageId + '</messageID>'
123125
return (
124126
'<mos>' +
125127
'<mosID>' +
@@ -128,9 +130,7 @@ export function getXMLReply(
128130
'<ncsID>' +
129131
(theirMosId || 'their.mos.id') +
130132
'</ncsID>' +
131-
'<messageID>' +
132133
messageId +
133-
'</messageID>' +
134134
content +
135135
'</mos>\r\n'
136136
)

packages/connector/src/connection/NCSServerConnection.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export class NCSServerConnection extends EventEmitter implements INCSServerConne
3030
private _timeout: number
3131
private _mosID: string
3232
private _debug: boolean
33+
private _strict = false
3334
private _disposed = false
3435

3536
private _clients: { [clientID: string]: ClientDescription } = {}
@@ -45,7 +46,8 @@ export class NCSServerConnection extends EventEmitter implements INCSServerConne
4546
mosID: string,
4647
timeout: number | undefined,
4748
heartbeatsInterval: number | undefined,
48-
debug: boolean
49+
debug: boolean,
50+
strict: boolean
4951
) {
5052
super()
5153
this._id = id
@@ -55,13 +57,21 @@ export class NCSServerConnection extends EventEmitter implements INCSServerConne
5557
this._mosID = mosID
5658
this._connected = false
5759
this._debug = debug ?? false
60+
this._strict = strict ?? false
5861
}
5962
get timeout(): number {
6063
return this._timeout
6164
}
6265
/** Create a MOS client, which talks to */
6366
createClient(clientID: string, port: number, clientDescription: ConnectionType, useHeartbeats: boolean): void {
64-
const client = new MosSocketClient(this._host, port, clientDescription, this._timeout, this._debug)
67+
const client = new MosSocketClient(
68+
this._host,
69+
port,
70+
clientDescription,
71+
this._timeout,
72+
this._debug,
73+
this._strict
74+
)
6575
this.debugTrace('registerOutgoingConnection', clientID)
6676

6777
this._clients[clientID] = {

packages/connector/src/connection/mosSocketClient.ts

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export class MosSocketClient extends EventEmitter {
2020
private _reconnectDelay = 3000
2121
private _reconnectAttempts = 0
2222
private _debug: boolean
23+
private _strict: boolean
2324

2425
private _description: string
2526
private _client: Socket | undefined
@@ -48,13 +49,14 @@ export class MosSocketClient extends EventEmitter {
4849
private messageParser: MosMessageParser
4950

5051
/** */
51-
constructor(host: string, port: number, description: string, timeout: number, debug: boolean) {
52+
constructor(host: string, port: number, description: string, timeout: number, debug: boolean, strict: boolean) {
5253
super()
5354
this._host = host
5455
this._port = port
5556
this._description = description
5657
this._commandTimeout = timeout || DEFAULT_COMMAND_TIMEOUT
5758
this._debug = debug ?? false
59+
this._strict = strict ?? false
5860

5961
this.messageParser = new MosMessageParser(description)
6062
this.messageParser.debug = this._debug
@@ -390,7 +392,7 @@ export class MosSocketClient extends EventEmitter {
390392
}
391393

392394
private _handleMessage(parsedData: any, messageString: string) {
393-
const messageId = parsedData.mos.messageID
395+
const messageId = this._getMessageId(parsedData, messageString)
394396
if (messageId) {
395397
const sentMessage = this._sentMessage || this._lingeringMessage
396398
if (sentMessage) {
@@ -441,6 +443,42 @@ export class MosSocketClient extends EventEmitter {
441443
this.processQueue()
442444
}
443445

446+
private _getMessageId(parsedData: any, messageString: string): string | undefined {
447+
// If there is a messageID, just return it:
448+
if (typeof parsedData.mos.messageID === 'string' && parsedData.mos.messageID !== '')
449+
return parsedData.mos.messageID
450+
451+
if (this._strict) {
452+
this.debugTrace(`Reply with no messageId: ${messageString}. Try non-strict mode.`)
453+
return undefined
454+
} else {
455+
// In non-strict mode: handle special cases:
456+
457+
// <heartbeat> response doesn't contain messageId (compliant with MOS version 2.8)
458+
// we can assume it's the same as our sent message:
459+
if (
460+
this._sentMessage &&
461+
this._sentMessage.msg.toString().search('<heartbeat>') >= 0 &&
462+
parsedData.mos.heartbeat
463+
) {
464+
return `${this._sentMessage.msg.messageID}`
465+
}
466+
467+
// <reqMachInfo> response doesn't contain messageId (compliant with MOS version 2.8)
468+
// we can assume it's the same as our sent message:
469+
if (
470+
this._sentMessage &&
471+
this._sentMessage.msg.toString().search('<reqMachInfo/>') >= 0 &&
472+
parsedData.mos.listMachInfo
473+
) {
474+
return `${this._sentMessage.msg.messageID}`
475+
} else {
476+
this.debugTrace(`Invalid reply with no messageId in non-strict mode: ${messageString}`)
477+
return undefined
478+
}
479+
}
480+
}
481+
444482
/** */
445483
private _onError(error: Error) {
446484
// dispatch error!!!!!

0 commit comments

Comments
 (0)