@things-factory/integration-base 8.0.89 → 8.0.92

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/integration-base",
3
- "version": "8.0.89",
3
+ "version": "8.0.92",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "client/index.js",
6
6
  "things-factory": true,
@@ -44,5 +44,5 @@
44
44
  "readline": "^1.3.0",
45
45
  "ses": "^1.5.0"
46
46
  },
47
- "gitHead": "984bd30a606d1e24a66a0685c41bff369c54b779"
47
+ "gitHead": "7f6ea0914d9117e9fe4ca3fff64a8a0faedb616f"
48
48
  }
@@ -130,9 +130,10 @@ export class OperatoConnector implements Connector {
130
130
  })
131
131
 
132
132
  const subscriptions: SubscriberData[] = []
133
- Object.keys(subscriptionHandlers).forEach(async tag => {
134
- if (!tag || !subscriptionHandlers[tag]) return
133
+ const tags = Object.keys(subscriptionHandlers).filter(tag => tag && subscriptionHandlers[tag])
135
134
 
135
+ /* 시나리오 조회 및 subscription 등록을 순차적으로 처리 */
136
+ for (const tag of tags) {
136
137
  const scenarioName = subscriptionHandlers[tag]
137
138
 
138
139
  // fetch a scenario
@@ -143,6 +144,14 @@ export class OperatoConnector implements Connector {
143
144
  relations: ['steps', 'domain']
144
145
  })
145
146
 
147
+ /* 시나리오가 DB에 없으면 경고 로그를 남기고 건너뜀 */
148
+ if (!selectedScenario) {
149
+ ConnectionManager.logger.warn(
150
+ `(${connection.name}) scenario not found for tag "${tag}" (scenarioName: "${scenarioName}") - skipping subscription`
151
+ )
152
+ continue
153
+ }
154
+
146
155
  const subscription = client.subscribe({
147
156
  query: gql`
148
157
  subscription {
@@ -156,8 +165,13 @@ export class OperatoConnector implements Connector {
156
165
 
157
166
  const subscriptionObserver = subscription.subscribe({
158
167
  next: async data => {
159
- debug('received pubsub msg.:', data?.data)
160
- await this.runScenario(subscriptions, data?.data?.data)
168
+ /* subscription 수신 시 에러가 발생해도 프로세스가 죽지 않도록 catch 처리 */
169
+ try {
170
+ debug('received pubsub msg.:', data?.data)
171
+ await this.runScenario(subscriptions, data?.data?.data)
172
+ } catch (e) {
173
+ ConnectionManager.logger.error(`(${connection.name}:${tag}) runScenario failed`, e)
174
+ }
161
175
  },
162
176
  error: error => {
163
177
  ConnectionManager.logger.error(`(${connection.name}:${connection.endpoint}) subscription error`, error)
@@ -177,7 +191,7 @@ export class OperatoConnector implements Connector {
177
191
  subscriptionObserver
178
192
  })
179
193
  ConnectionManager.logger.info(`(${tag}:${scenarioName}) subscription closed flag: ${subscriptionObserver.closed}`)
180
- })
194
+ }
181
195
 
182
196
  client['subscriptions'] = subscriptions
183
197
  ConnectionManager.addConnectionInstance(connection, client)
@@ -16,23 +16,37 @@ export class SocketServer implements Connector {
16
16
  async connect(config: InputConnection): Promise<void> {
17
17
  var em = new EventEmitter()
18
18
  var [host = '0.0.0.0', port = 8124] = config.endpoint.split(':')
19
+ var clientIPs = new Set<string>()
19
20
  return new Promise((resolve, reject) => {
20
21
  var server = net.createServer(tcp => {
22
+ const remoteIP = `${tcp.remoteAddress}:${tcp.remotePort}`
23
+ clientIPs.add(remoteIP)
21
24
  ConnectionManager.logger.info('Client connection: ')
22
25
  ConnectionManager.logger.info(' local = %s:%s', tcp.localAddress, tcp.localPort)
23
26
  ConnectionManager.logger.info(' remote = %s:%s', tcp.remoteAddress, tcp.remotePort)
27
+ console.log(
28
+ `[socket-server] ${config.name} clients: ${clientIPs.size}\n${[...clientIPs].sort().map(ip => ` - ${ip}`).join('\n')}`
29
+ )
24
30
  tcp.on('data', (data: any) => {
25
31
  em['__tcp__'] = tcp
26
32
  var message = data.toString()
27
33
  message = message.replace('SB#', '')
28
34
  message = message.replace(/(\r\n|\n|\r)/gm, '')
29
- em.emit('socket-message-arrive', message)
35
+ // IP 정보를 함께 전달한다
36
+ em.emit('socket-message-arrive', message, tcp.remoteAddress)
30
37
  ConnectionManager.logger.info(`socket ${message}`)
31
38
  tcp.write('ok')
32
39
  })
33
40
 
34
- tcp.on('end', () => {
41
+ tcp.on('close', hadError => {
35
42
  ConnectionManager.logger.info('Client disconnected')
43
+ if (hadError) {
44
+ ConnectionManager.logger.info('Client disconnected with error: ' + hadError)
45
+ }
46
+ clientIPs.delete(remoteIP)
47
+ console.log(
48
+ `[socket-server] ${config.name} clients: ${clientIPs.size}\n${[...clientIPs].sort().map(ip => ` - ${ip}`).join('\n')}`
49
+ )
36
50
  })
37
51
 
38
52
  tcp.on('error', ex => {
@@ -4,18 +4,29 @@ import { ConnectionManager } from '../connection-manager'
4
4
  import { InputStep } from '../../service/step/step-type'
5
5
  import { Context } from '../types'
6
6
 
7
- function convertDataFormat(data, format) {
7
+ const WITH_IP_FORMATS = ['string-with-ip', 'json-with-ip']
8
+
9
+ function convertDataFormat(data: string, format: string, ip?: string) {
10
+ let converted: any
8
11
  try {
9
- if (format == 'json') {
10
- return JSON.parse(data)
11
- } else if (format == 'csv') {
12
- return data.split(',')
12
+ const baseFormat = format.replace('-with-ip', '')
13
+ if (baseFormat == 'json') {
14
+ converted = JSON.parse(data)
15
+ } else if (baseFormat == 'csv') {
16
+ converted = data.split(',')
17
+ } else {
18
+ converted = data
13
19
  }
14
20
  } catch (e) {
15
- return data.toString()
21
+ converted = data.toString()
22
+ }
23
+
24
+ // IP 포함 포맷이면 { data, ip } 형태로 반환한다
25
+ if (WITH_IP_FORMATS.includes(format) && ip) {
26
+ return { data: converted, ip }
16
27
  }
17
28
 
18
- return data
29
+ return converted
19
30
  }
20
31
 
21
32
  async function SocketListener(step: InputStep, context: Context) {
@@ -54,9 +65,9 @@ async function SocketListener(step: InputStep, context: Context) {
54
65
  }
55
66
  }
56
67
 
57
- await connection.addListener('socket-message-arrive', async message => {
68
+ await connection.addListener('socket-message-arrive', async (message: string, ip: string) => {
58
69
  logger.info(message.toString())
59
- MESSAGES.push(convertDataFormat(message.toString(), dataFormat))
70
+ MESSAGES.push(convertDataFormat(message.toString(), dataFormat, ip))
60
71
  })
61
72
  } catch (e) {
62
73
  logger.error(e)
@@ -88,6 +99,14 @@ SocketListener.parameterSpec = [
88
99
  {
89
100
  display: 'String',
90
101
  value: 'string'
102
+ },
103
+ {
104
+ display: 'String with IP',
105
+ value: 'string-with-ip'
106
+ },
107
+ {
108
+ display: 'JSON with IP',
109
+ value: 'json-with-ip'
91
110
  }
92
111
  ]
93
112
  }