@things-factory/integration-base 8.0.39 → 9.0.0-9.0.0-beta.59.0

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.
Files changed (174) hide show
  1. package/dist-server/engine/connector/headless-connector.d.ts +23 -0
  2. package/dist-server/engine/connector/headless-connector.js +357 -0
  3. package/dist-server/engine/connector/headless-connector.js.map +1 -0
  4. package/dist-server/engine/connector/http-connector.js +1 -1
  5. package/dist-server/engine/connector/http-connector.js.map +1 -1
  6. package/dist-server/engine/connector/index.d.ts +1 -0
  7. package/dist-server/engine/connector/index.js +1 -0
  8. package/dist-server/engine/connector/index.js.map +1 -1
  9. package/dist-server/engine/index.d.ts +3 -2
  10. package/dist-server/engine/index.js +3 -2
  11. package/dist-server/engine/index.js.map +1 -1
  12. package/dist-server/engine/resource-pool/headless-pool.d.ts +1 -0
  13. package/dist-server/engine/resource-pool/headless-pool.js +62 -0
  14. package/dist-server/engine/resource-pool/headless-pool.js.map +1 -0
  15. package/dist-server/engine/resource-pool/index.d.ts +1 -0
  16. package/dist-server/engine/resource-pool/index.js +5 -0
  17. package/dist-server/engine/resource-pool/index.js.map +1 -0
  18. package/dist-server/engine/task/headless-post.js +19 -33
  19. package/dist-server/engine/task/headless-post.js.map +1 -1
  20. package/dist-server/engine/task/headless-scrap.js +20 -13
  21. package/dist-server/engine/task/headless-scrap.js.map +1 -1
  22. package/dist-server/engine/task/mqtt-publish.js +29 -6
  23. package/dist-server/engine/task/mqtt-publish.js.map +1 -1
  24. package/dist-server/engine/task/mqtt-subscribe.js +178 -64
  25. package/dist-server/engine/task/mqtt-subscribe.js.map +1 -1
  26. package/dist-server/index.d.ts +6 -6
  27. package/dist-server/index.js +10 -10
  28. package/dist-server/index.js.map +1 -1
  29. package/dist-server/routes.js +6 -6
  30. package/dist-server/routes.js.map +1 -1
  31. package/dist-server/service/connection/connection-mutation.js +17 -5
  32. package/dist-server/service/connection/connection-mutation.js.map +1 -1
  33. package/dist-server/service/connection/connection-query.js +3 -0
  34. package/dist-server/service/connection/connection-query.js.map +1 -1
  35. package/dist-server/service/index.d.ts +1 -1
  36. package/dist-server/tsconfig.tsbuildinfo +1 -1
  37. package/package.json +12 -11
  38. package/translations/en.json +12 -4
  39. package/translations/ja.json +12 -4
  40. package/translations/ko.json +12 -4
  41. package/translations/ms.json +12 -4
  42. package/translations/zh.json +12 -4
  43. package/server/controllers/index.ts +0 -2
  44. package/server/controllers/publish-data.ts +0 -29
  45. package/server/controllers/scenario-controller.ts +0 -154
  46. package/server/engine/analyzer/analyze-integration.ts +0 -115
  47. package/server/engine/connection-manager.ts +0 -232
  48. package/server/engine/connector/echo-back-connector.ts +0 -51
  49. package/server/engine/connector/echo-back-server.ts +0 -72
  50. package/server/engine/connector/graphql-connector.ts +0 -126
  51. package/server/engine/connector/http-connector.ts +0 -65
  52. package/server/engine/connector/index.ts +0 -12
  53. package/server/engine/connector/mqtt-connector.ts +0 -78
  54. package/server/engine/connector/mssql-connector.ts +0 -152
  55. package/server/engine/connector/mysql-connector.ts +0 -94
  56. package/server/engine/connector/operato-connector.ts +0 -264
  57. package/server/engine/connector/oracle-connector.ts +0 -218
  58. package/server/engine/connector/postgresql-connector.ts +0 -152
  59. package/server/engine/connector/proxy-connector.ts +0 -53
  60. package/server/engine/connector/socket-server.ts +0 -86
  61. package/server/engine/connector/sqlite-connector.ts +0 -69
  62. package/server/engine/edge-client.ts +0 -45
  63. package/server/engine/index.ts +0 -10
  64. package/server/engine/pending-queue.ts +0 -97
  65. package/server/engine/scenario-engine.ts +0 -106
  66. package/server/engine/task/book-up-scenario.ts +0 -73
  67. package/server/engine/task/csv-readline.ts +0 -127
  68. package/server/engine/task/data-accessor.ts +0 -36
  69. package/server/engine/task/data-mapper.ts +0 -47
  70. package/server/engine/task/database-query.ts +0 -56
  71. package/server/engine/task/echo-receive.ts +0 -21
  72. package/server/engine/task/echo-send.ts +0 -32
  73. package/server/engine/task/empty-check.ts +0 -38
  74. package/server/engine/task/end.ts +0 -18
  75. package/server/engine/task/floating-point.ts +0 -71
  76. package/server/engine/task/goto.ts +0 -27
  77. package/server/engine/task/graphql-mutate.ts +0 -79
  78. package/server/engine/task/graphql-query.ts +0 -78
  79. package/server/engine/task/headless-post.ts +0 -147
  80. package/server/engine/task/headless-scrap.ts +0 -80
  81. package/server/engine/task/http-get.ts +0 -117
  82. package/server/engine/task/http-post.ts +0 -148
  83. package/server/engine/task/index.ts +0 -45
  84. package/server/engine/task/jsonata.ts +0 -45
  85. package/server/engine/task/local-graphql-mutate.ts +0 -100
  86. package/server/engine/task/local-graphql-query.ts +0 -100
  87. package/server/engine/task/log.ts +0 -78
  88. package/server/engine/task/mqtt-publish.ts +0 -45
  89. package/server/engine/task/mqtt-subscribe.ts +0 -139
  90. package/server/engine/task/mssql-procedure.ts +0 -128
  91. package/server/engine/task/oracle-procedure.ts +0 -124
  92. package/server/engine/task/pick-pending-scenario.ts +0 -80
  93. package/server/engine/task/publish.ts +0 -40
  94. package/server/engine/task/random.ts +0 -53
  95. package/server/engine/task/reset-pending-queue.ts +0 -17
  96. package/server/engine/task/script.ts +0 -63
  97. package/server/engine/task/set-domain.ts +0 -37
  98. package/server/engine/task/sleep.ts +0 -34
  99. package/server/engine/task/socket-listener.ts +0 -96
  100. package/server/engine/task/state-group-read.ts +0 -69
  101. package/server/engine/task/state-read.ts +0 -56
  102. package/server/engine/task/state-write.ts +0 -65
  103. package/server/engine/task/stop-scenario.ts +0 -44
  104. package/server/engine/task/sub-scenario.ts +0 -57
  105. package/server/engine/task/switch-goto.ts +0 -43
  106. package/server/engine/task/switch-range-goto.ts +0 -53
  107. package/server/engine/task/switch-range-scenario.ts +0 -79
  108. package/server/engine/task/switch-range-set.ts +0 -48
  109. package/server/engine/task/switch-scenario.ts +0 -67
  110. package/server/engine/task/switch-set.ts +0 -37
  111. package/server/engine/task/throw.ts +0 -27
  112. package/server/engine/task/utils/headless-pool-for-scenario.ts +0 -71
  113. package/server/engine/task/utils/substitute.ts +0 -44
  114. package/server/engine/task/variables.ts +0 -17
  115. package/server/engine/task-registry.ts +0 -23
  116. package/server/engine/types.ts +0 -114
  117. package/server/index.ts +0 -20
  118. package/server/migrations/index.ts +0 -9
  119. package/server/restful/index.ts +0 -1
  120. package/server/restful/unstable/index.ts +0 -7
  121. package/server/restful/unstable/run-scenario.ts +0 -51
  122. package/server/restful/unstable/scenario-instance.ts +0 -52
  123. package/server/restful/unstable/scenario-instances.ts +0 -80
  124. package/server/restful/unstable/scenario.ts +0 -41
  125. package/server/restful/unstable/scenarios.ts +0 -69
  126. package/server/restful/unstable/start-scenario.ts +0 -33
  127. package/server/restful/unstable/stop-scenario.ts +0 -30
  128. package/server/routers/scenario-schedule-callback-router.ts +0 -69
  129. package/server/routers/scenario-view-router.ts +0 -46
  130. package/server/routes.ts +0 -35
  131. package/server/service/analysis/analysis-query.ts +0 -13
  132. package/server/service/analysis/index.ts +0 -3
  133. package/server/service/connection/connection-mutation.ts +0 -190
  134. package/server/service/connection/connection-query.ts +0 -87
  135. package/server/service/connection/connection-subscription.ts +0 -104
  136. package/server/service/connection/connection-type.ts +0 -305
  137. package/server/service/connection/index.ts +0 -7
  138. package/server/service/connector/connector-query.ts +0 -62
  139. package/server/service/connector/connector-type.ts +0 -29
  140. package/server/service/connector/index.ts +0 -4
  141. package/server/service/index.ts +0 -52
  142. package/server/service/payload-log/index.ts +0 -7
  143. package/server/service/payload-log/payload-log-mutation.ts +0 -151
  144. package/server/service/payload-log/payload-log-query.ts +0 -49
  145. package/server/service/payload-log/payload-log-type.ts +0 -36
  146. package/server/service/payload-log/payload-log.ts +0 -100
  147. package/server/service/property-spec.ts +0 -24
  148. package/server/service/scenario/index.ts +0 -6
  149. package/server/service/scenario/scenario-mutation.ts +0 -396
  150. package/server/service/scenario/scenario-query.ts +0 -109
  151. package/server/service/scenario/scenario-type.ts +0 -78
  152. package/server/service/scenario/scenario.ts +0 -124
  153. package/server/service/scenario-flow/scenario-flow.ts +0 -17
  154. package/server/service/scenario-instance/index.ts +0 -6
  155. package/server/service/scenario-instance/scenario-instance-mutation.ts +0 -44
  156. package/server/service/scenario-instance/scenario-instance-query.ts +0 -42
  157. package/server/service/scenario-instance/scenario-instance-subscription.ts +0 -118
  158. package/server/service/scenario-instance/scenario-instance-type.ts +0 -557
  159. package/server/service/scenario-queue/index.ts +0 -4
  160. package/server/service/scenario-queue/scenario-queue-subscription.ts +0 -55
  161. package/server/service/scenario-queue/scenario-queue-type.ts +0 -27
  162. package/server/service/state-register/data-resolver.ts +0 -56
  163. package/server/service/state-register/index.ts +0 -8
  164. package/server/service/state-register/state-register-mutation.ts +0 -166
  165. package/server/service/state-register/state-register-query.ts +0 -80
  166. package/server/service/state-register/state-register-type.ts +0 -80
  167. package/server/service/state-register/state-register.ts +0 -113
  168. package/server/service/step/index.ts +0 -6
  169. package/server/service/step/step-mutation.ts +0 -52
  170. package/server/service/step/step-query.ts +0 -55
  171. package/server/service/step/step-type.ts +0 -238
  172. package/server/service/task-type/index.ts +0 -4
  173. package/server/service/task-type/task-type-query.ts +0 -95
  174. package/server/service/task-type/task-type-type.ts +0 -29
@@ -6,17 +6,16 @@ const url_1 = require("url");
6
6
  const utils_1 = require("@things-factory/utils");
7
7
  const task_registry_1 = require("../task-registry");
8
8
  const connection_manager_1 = require("../connection-manager");
9
- const headless_pool_for_scenario_1 = require("./utils/headless-pool-for-scenario");
10
9
  async function HeadlessPost(step, { logger, data, domain }) {
11
- var { connection: connectionName, params: stepOptions } = step;
12
- var { headers: requestHeaders, contentType, path, accessor } = stepOptions || {};
13
- var connection = connection_manager_1.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
10
+ const { connection: connectionName, params: stepOptions } = step;
11
+ const { headers: requestHeaders, contentType, path, accessor } = stepOptions || {};
12
+ const connection = connection_manager_1.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
14
13
  if (!connection) {
15
- throw new Error(`connection '${connectionName}' is not established.`);
14
+ throw new Error(`Connection '${connectionName}' is not established.`);
16
15
  }
17
- var { endpoint, params: connectionParams, authHeaders = {} } = connection;
18
- var headers = Object.assign(Object.assign({}, authHeaders), requestHeaders);
19
- var body = (0, utils_1.access)(accessor, data);
16
+ const { endpoint, params: connectionParams, acquireSessionPage, releasePage } = connection;
17
+ const headers = Object.assign({}, requestHeaders);
18
+ let body = (0, utils_1.access)(accessor, data);
20
19
  if (contentType && body) {
21
20
  headers['content-type'] = contentType;
22
21
  switch (contentType) {
@@ -31,24 +30,23 @@ async function HeadlessPost(step, { logger, data, domain }) {
31
30
  for (const prop in body) {
32
31
  searchParams.set(prop, body[prop]);
33
32
  }
34
- body = searchParams;
33
+ body = searchParams.toString();
35
34
  break;
36
35
  }
37
36
  }
38
- var options = {
37
+ const options = {
39
38
  method: 'POST',
40
39
  headers,
41
40
  body
42
41
  };
43
- var { rejectUnauthorized } = connectionParams;
42
+ const { rejectUnauthorized } = connectionParams;
44
43
  if (!rejectUnauthorized) {
45
44
  const httpsAgent = new https_1.default.Agent({
46
45
  rejectUnauthorized
47
46
  });
48
47
  options.agent = httpsAgent;
49
48
  }
50
- const browser = (await (0, headless_pool_for_scenario_1.getHeadlessPool)().acquire());
51
- const page = await browser.newPage();
49
+ const page = await acquireSessionPage();
52
50
  try {
53
51
  page.on('console', async (msg) => {
54
52
  console.log(`[browser ${msg.type()}] ${msg.text()}`);
@@ -70,12 +68,12 @@ async function HeadlessPost(step, { logger, data, domain }) {
70
68
  data: response
71
69
  };
72
70
  }
73
- catch (e) {
74
- console.error(e);
71
+ catch (error) {
72
+ logger.error('Error in HeadlessPost:', error);
73
+ throw error;
75
74
  }
76
75
  finally {
77
- page.close();
78
- (0, headless_pool_for_scenario_1.getHeadlessPool)().release(browser);
76
+ await releasePage(page);
79
77
  }
80
78
  }
81
79
  HeadlessPost.parameterSpec = [
@@ -95,22 +93,10 @@ HeadlessPost.parameterSpec = [
95
93
  label: 'content-type',
96
94
  property: {
97
95
  options: [
98
- {
99
- display: '',
100
- value: ''
101
- },
102
- {
103
- display: 'application/json',
104
- value: 'application/json'
105
- },
106
- {
107
- display: 'text/plain',
108
- value: 'text/plain'
109
- },
110
- {
111
- display: 'application/x-www-form-urlencoded',
112
- value: 'application/x-www-form-urlencoded'
113
- }
96
+ { display: '', value: '' },
97
+ { display: 'application/json', value: 'application/json' },
98
+ { display: 'text/plain', value: 'text/plain' },
99
+ { display: 'application/x-www-form-urlencoded', value: 'application/x-www-form-urlencoded' }
114
100
  ]
115
101
  }
116
102
  },
@@ -1 +1 @@
1
- {"version":3,"file":"headless-post.js","sourceRoot":"","sources":["../../../server/engine/task/headless-post.ts"],"names":[],"mappings":"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","sourcesContent":["import https from 'https'\nimport { URL } from 'url'\n\nimport { access } from '@things-factory/utils'\nimport { TaskRegistry } from '../task-registry'\nimport { ConnectionManager } from '../connection-manager'\n\nimport { getHeadlessPool } from './utils/headless-pool-for-scenario'\n\nimport { InputStep } from '../../service/step/step-type'\nimport { Context } from '../types'\n\nasync function HeadlessPost(step: InputStep, { logger, data, domain }: Context) {\n var { connection: connectionName, params: stepOptions } = step\n var { headers: requestHeaders, contentType, path, accessor } = stepOptions || {}\n\n var connection = ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n\n if (!connection) {\n throw new Error(`connection '${connectionName}' is not established.`)\n }\n\n var { endpoint, params: connectionParams, authHeaders = {} } = connection\n\n var headers = {\n ...authHeaders,\n ...requestHeaders\n }\n\n var body = access(accessor, data)\n if (contentType && body) {\n headers['content-type'] = contentType\n switch (contentType) {\n case 'text/plain':\n body = JSON.stringify(body)\n break\n case 'application/json':\n body = JSON.stringify(body)\n break\n case 'application/x-www-form-urlencoded':\n const searchParams = new URLSearchParams()\n for (const prop in body) {\n searchParams.set(prop, body[prop])\n }\n body = searchParams\n break\n }\n }\n\n var options: any = {\n method: 'POST',\n headers,\n body\n }\n\n var { rejectUnauthorized } = connectionParams\n\n if (!rejectUnauthorized) {\n const httpsAgent = new https.Agent({\n rejectUnauthorized\n })\n options.agent = httpsAgent\n }\n\n const browser = (await getHeadlessPool().acquire()) as any\n const page = await browser.newPage()\n\n try {\n page.on('console', async msg => {\n console.log(`[browser ${msg.type()}] ${msg.text()}`)\n })\n\n page.on('requestfailed', request => {\n console.log('Request failed:', request.url())\n })\n\n await page.goto(endpoint, { waitUntil: 'networkidle2' })\n\n const response = await page.evaluate(\n async (urlString, options) => {\n const response = await fetch(urlString, options)\n\n if (response.ok && response.headers.get('content-type').includes('application/json')) {\n return await response.json()\n } else {\n return await response.text()\n }\n },\n new URL(path, endpoint),\n options\n )\n\n return {\n data: response\n }\n } catch (e) {\n console.error(e)\n } finally {\n page.close()\n getHeadlessPool().release(browser)\n }\n}\n\nHeadlessPost.parameterSpec = [\n {\n type: 'string',\n name: 'path',\n label: 'path'\n },\n {\n type: 'http-headers',\n name: 'headers',\n label: 'headers'\n },\n {\n type: 'select',\n name: 'contentType',\n label: 'content-type',\n property: {\n options: [\n {\n display: '',\n value: ''\n },\n {\n display: 'application/json',\n value: 'application/json'\n },\n {\n display: 'text/plain',\n value: 'text/plain'\n },\n {\n display: 'application/x-www-form-urlencoded',\n value: 'application/x-www-form-urlencoded'\n }\n ]\n }\n },\n {\n type: 'scenario-step-input',\n name: 'accessor',\n label: 'accessor'\n }\n]\n\nTaskRegistry.registerTaskHandler('headless-post', HeadlessPost)\n"]}
1
+ {"version":3,"file":"headless-post.js","sourceRoot":"","sources":["../../../server/engine/task/headless-post.ts"],"names":[],"mappings":"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","sourcesContent":["import https from 'https'\nimport { URL } from 'url'\n\nimport { access } from '@things-factory/utils'\nimport { TaskRegistry } from '../task-registry'\nimport { ConnectionManager } from '../connection-manager'\n\nasync function HeadlessPost(step, { logger, data, domain }) {\n const { connection: connectionName, params: stepOptions } = step\n const { headers: requestHeaders, contentType, path, accessor } = stepOptions || {}\n\n const connection = ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n\n if (!connection) {\n throw new Error(`Connection '${connectionName}' is not established.`)\n }\n\n const { endpoint, params: connectionParams, acquireSessionPage, releasePage } = connection\n\n const headers = {\n ...requestHeaders\n }\n\n let body = access(accessor, data)\n if (contentType && body) {\n headers['content-type'] = contentType\n switch (contentType) {\n case 'text/plain':\n body = JSON.stringify(body)\n break\n case 'application/json':\n body = JSON.stringify(body)\n break\n case 'application/x-www-form-urlencoded':\n const searchParams = new URLSearchParams()\n for (const prop in body) {\n searchParams.set(prop, body[prop])\n }\n body = searchParams.toString()\n break\n }\n }\n\n const options = {\n method: 'POST',\n headers,\n body\n } as any\n\n const { rejectUnauthorized } = connectionParams\n\n if (!rejectUnauthorized) {\n const httpsAgent = new https.Agent({\n rejectUnauthorized\n })\n options.agent = httpsAgent\n }\n\n const page = await acquireSessionPage()\n\n try {\n page.on('console', async msg => {\n console.log(`[browser ${msg.type()}] ${msg.text()}`)\n })\n\n page.on('requestfailed', request => {\n console.log('Request failed:', request.url())\n })\n\n await page.goto(endpoint, { waitUntil: 'networkidle2' })\n\n const response = await page.evaluate(\n async (urlString, options) => {\n const response = await fetch(urlString, options)\n\n if (response.ok && response.headers.get('content-type').includes('application/json')) {\n return await response.json()\n } else {\n return await response.text()\n }\n },\n new URL(path, endpoint),\n options\n )\n\n return {\n data: response\n }\n } catch (error) {\n logger.error('Error in HeadlessPost:', error)\n throw error\n } finally {\n await releasePage(page)\n }\n}\n\nHeadlessPost.parameterSpec = [\n {\n type: 'string',\n name: 'path',\n label: 'path'\n },\n {\n type: 'http-headers',\n name: 'headers',\n label: 'headers'\n },\n {\n type: 'select',\n name: 'contentType',\n label: 'content-type',\n property: {\n options: [\n { display: '', value: '' },\n { display: 'application/json', value: 'application/json' },\n { display: 'text/plain', value: 'text/plain' },\n { display: 'application/x-www-form-urlencoded', value: 'application/x-www-form-urlencoded' }\n ]\n }\n },\n {\n type: 'scenario-step-input',\n name: 'accessor',\n label: 'accessor'\n }\n]\n\nTaskRegistry.registerTaskHandler('headless-post', HeadlessPost)\n"]}
@@ -3,24 +3,31 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const url_1 = require("url");
4
4
  const task_registry_1 = require("../task-registry");
5
5
  const connection_manager_1 = require("../connection-manager");
6
- const headless_pool_for_scenario_1 = require("./utils/headless-pool-for-scenario");
7
6
  async function HeadlessScrap(step, { logger, data, domain }) {
8
- var { connection: connectionName, params: stepOptions } = step;
9
- var { headers: requestHeaders, path, selectors = [] } = stepOptions || {};
10
- var connection = connection_manager_1.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
7
+ const { connection: connectionName, params: stepOptions } = step;
8
+ const { headers: requestHeaders, path, selectors = [] } = stepOptions || {};
9
+ const connection = connection_manager_1.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
11
10
  if (!connection) {
12
- throw new Error(`connection '${connectionName}' is not established.`);
11
+ throw new Error(`Connection '${connectionName}' is not established.`);
13
12
  }
14
- var { endpoint, params: connectionParams, authHeaders = {} } = connection;
15
- var headers = Object.assign(Object.assign({}, authHeaders), requestHeaders);
16
- const browser = (await (0, headless_pool_for_scenario_1.getHeadlessPool)().acquire());
17
- const page = await browser.newPage();
13
+ const { endpoint, params: connectionParams, acquireSessionPage, releasePage } = connection;
14
+ const headers = Object.assign({}, requestHeaders);
15
+ const page = await acquireSessionPage();
18
16
  try {
19
17
  page.on('console', async (msg) => {
20
18
  console.log(`[browser ${msg.type()}] ${msg.text()}`);
21
19
  });
22
20
  page.on('requestfailed', request => {
23
- console.log('Request failed:', request.url());
21
+ var _a;
22
+ console.log('Request failed:');
23
+ console.log(`- URL: ${request.url()}`);
24
+ console.log(`- Method: ${request.method()}`);
25
+ console.log(`- Failure Text: ${(_a = request.failure()) === null || _a === void 0 ? void 0 : _a.errorText}`);
26
+ console.log(`- Headers:`, request.headers());
27
+ // POST 데이터 (필요한 경우)
28
+ if (request.postData()) {
29
+ console.log(`- Post Data: ${request.postData()}`);
30
+ }
24
31
  });
25
32
  await page.setExtraHTTPHeaders(headers);
26
33
  await page.goto(new url_1.URL(path, endpoint), { waitUntil: 'networkidle2' });
@@ -36,11 +43,11 @@ async function HeadlessScrap(step, { logger, data, domain }) {
36
43
  };
37
44
  }
38
45
  catch (e) {
39
- console.error(e);
46
+ logger.error('Error in HeadlessScrap:', e);
47
+ throw e;
40
48
  }
41
49
  finally {
42
- page.close();
43
- (0, headless_pool_for_scenario_1.getHeadlessPool)().release(browser);
50
+ await releasePage(page);
44
51
  }
45
52
  }
46
53
  HeadlessScrap.parameterSpec = [
@@ -1 +1 @@
1
- {"version":3,"file":"headless-scrap.js","sourceRoot":"","sources":["../../../server/engine/task/headless-scrap.ts"],"names":[],"mappings":"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","sourcesContent":["import { URL } from 'url'\n\nimport { TaskRegistry } from '../task-registry'\nimport { ConnectionManager } from '../connection-manager'\n\nimport { getHeadlessPool } from './utils/headless-pool-for-scenario'\nimport { InputStep } from '../../service/step/step-type'\nimport { Context } from '../types'\n\nasync function HeadlessScrap(step: InputStep, { logger, data, domain }: Context) {\n var { connection: connectionName, params: stepOptions } = step\n var { headers: requestHeaders, path, selectors = [] } = stepOptions || {}\n\n var connection = ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n\n if (!connection) {\n throw new Error(`connection '${connectionName}' is not established.`)\n }\n\n var { endpoint, params: connectionParams, authHeaders = {} } = connection\n\n var headers = {\n ...authHeaders,\n ...requestHeaders\n }\n\n const browser = (await getHeadlessPool().acquire()) as any\n const page = await browser.newPage()\n\n try {\n page.on('console', async msg => {\n console.log(`[browser ${msg.type()}] ${msg.text()}`)\n })\n\n page.on('requestfailed', request => {\n console.log('Request failed:', request.url())\n })\n\n await page.setExtraHTTPHeaders(headers)\n await page.goto(new URL(path, endpoint), { waitUntil: 'networkidle2' })\n\n const result = {}\n\n for (const selector of selectors) {\n const { text, value } = selector\n result[text] = await page.$$eval(value, elements => {\n return elements.map(element => element.textContent.trim())\n })\n }\n\n return {\n data: result\n }\n } catch (e) {\n console.error(e)\n } finally {\n page.close()\n getHeadlessPool().release(browser)\n }\n}\n\nHeadlessScrap.parameterSpec = [\n {\n type: 'string',\n name: 'path',\n label: 'path'\n },\n {\n type: 'http-headers',\n name: 'headers',\n label: 'headers'\n },\n {\n type: 'options',\n name: 'selectors',\n label: 'selectors'\n }\n]\n\nTaskRegistry.registerTaskHandler('headless-scrap', HeadlessScrap)\n"]}
1
+ {"version":3,"file":"headless-scrap.js","sourceRoot":"","sources":["../../../server/engine/task/headless-scrap.ts"],"names":[],"mappings":"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","sourcesContent":["import { URL } from 'url'\n\nimport { TaskRegistry } from '../task-registry'\nimport { ConnectionManager } from '../connection-manager'\n\nasync function HeadlessScrap(step, { logger, data, domain }) {\n const { connection: connectionName, params: stepOptions } = step\n const { headers: requestHeaders, path, selectors = [] } = stepOptions || {}\n\n const connection = ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n\n if (!connection) {\n throw new Error(`Connection '${connectionName}' is not established.`)\n }\n\n const { endpoint, params: connectionParams, acquireSessionPage, releasePage } = connection\n\n const headers = {\n ...requestHeaders\n }\n\n const page = await acquireSessionPage()\n\n try {\n page.on('console', async msg => {\n console.log(`[browser ${msg.type()}] ${msg.text()}`)\n })\n\n page.on('requestfailed', request => {\n console.log('Request failed:')\n console.log(`- URL: ${request.url()}`)\n console.log(`- Method: ${request.method()}`)\n console.log(`- Failure Text: ${request.failure()?.errorText}`)\n console.log(`- Headers:`, request.headers())\n\n // POST 데이터 (필요한 경우)\n if (request.postData()) {\n console.log(`- Post Data: ${request.postData()}`)\n }\n })\n\n await page.setExtraHTTPHeaders(headers)\n await page.goto(new URL(path, endpoint), { waitUntil: 'networkidle2' })\n\n const result = {}\n\n for (const selector of selectors) {\n const { text, value } = selector\n result[text] = await page.$$eval(value, elements => {\n return elements.map(element => element.textContent.trim())\n })\n }\n\n return {\n data: result\n }\n } catch (e) {\n logger.error('Error in HeadlessScrap:', e)\n throw e\n } finally {\n await releasePage(page)\n }\n}\n\nHeadlessScrap.parameterSpec = [\n {\n type: 'string',\n name: 'path',\n label: 'path'\n },\n {\n type: 'http-headers',\n name: 'headers',\n label: 'headers'\n },\n {\n type: 'options',\n name: 'selectors',\n label: 'selectors'\n }\n]\n\nTaskRegistry.registerTaskHandler('headless-scrap', HeadlessScrap)\n"]}
@@ -1,18 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const utils_1 = require("@things-factory/utils");
4
- const task_registry_1 = require("../task-registry");
5
- const connection_manager_1 = require("../connection-manager");
4
+ const task_registry_js_1 = require("../task-registry.js");
5
+ const connection_manager_js_1 = require("../connection-manager.js");
6
6
  async function MqttPublish(step, { logger, data, domain }) {
7
- var { connection: connectionName, params: { topic, accessor } } = step;
8
- const { client } = connection_manager_1.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
7
+ var { connection: connectionName, params: { topic, accessor, dataFormat = 'json' } } = step;
8
+ const { client } = connection_manager_js_1.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
9
9
  if (!client) {
10
10
  throw Error(`connection is not found : ${connectionName}`);
11
11
  }
12
12
  if (!topic || !accessor) {
13
13
  throw Error(`topic and accessor should be defined: : topic - '${topic}', accessor - '${accessor}'`);
14
14
  }
15
- var message = JSON.stringify((0, utils_1.access)(accessor, data));
15
+ var message = (0, utils_1.access)(accessor, data);
16
+ if (dataFormat === 'text') {
17
+ message = String(message);
18
+ }
19
+ else {
20
+ message = JSON.stringify(message);
21
+ }
16
22
  await client.publish(topic, message);
17
23
  return {
18
24
  data: message
@@ -28,8 +34,25 @@ MqttPublish.parameterSpec = [
28
34
  type: 'scenario-step-input',
29
35
  name: 'accessor',
30
36
  label: 'accessor'
37
+ },
38
+ {
39
+ type: 'select',
40
+ label: 'data-format',
41
+ name: 'dataFormat',
42
+ property: {
43
+ options: [
44
+ {
45
+ display: 'Plain Text',
46
+ value: 'text'
47
+ },
48
+ {
49
+ display: 'JSON',
50
+ value: 'json'
51
+ }
52
+ ]
53
+ }
31
54
  }
32
55
  ];
33
56
  MqttPublish.help = 'integration/task/mqtt-publish';
34
- task_registry_1.TaskRegistry.registerTaskHandler('mqtt-publish', MqttPublish);
57
+ task_registry_js_1.TaskRegistry.registerTaskHandler('mqtt-publish', MqttPublish);
35
58
  //# sourceMappingURL=mqtt-publish.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mqtt-publish.js","sourceRoot":"","sources":["../../../server/engine/task/mqtt-publish.ts"],"names":[],"mappings":";;AAAA,iDAA8C;AAC9C,oDAA+C;AAC/C,8DAAyD;AAIzD,KAAK,UAAU,WAAW,CAAC,IAAe,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAW;IAC3E,IAAI,EACF,UAAU,EAAE,cAAc,EAC1B,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,EAC5B,GAAG,IAAI,CAAA;IAER,MAAM,EAAE,MAAM,EAAE,GAAG,sCAAiB,CAAC,2BAA2B,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;IACxF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,KAAK,CAAC,6BAA6B,cAAc,EAAE,CAAC,CAAA;IAC5D,CAAC;IAED,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxB,MAAM,KAAK,CAAC,oDAAoD,KAAK,kBAAkB,QAAQ,GAAG,CAAC,CAAA;IACrG,CAAC;IAED,IAAI,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAA,cAAM,EAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAA;IACpD,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAEpC,OAAO;QACL,IAAI,EAAE,OAAO;KACd,CAAA;AACH,CAAC;AAED,WAAW,CAAC,aAAa,GAAG;IAC1B;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,OAAO;KACf;IACD;QACE,IAAI,EAAE,qBAAqB;QAC3B,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,UAAU;KAClB;CACF,CAAA;AAED,WAAW,CAAC,IAAI,GAAG,+BAA+B,CAAA;AAElD,4BAAY,CAAC,mBAAmB,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA","sourcesContent":["import { access } from '@things-factory/utils'\nimport { TaskRegistry } from '../task-registry'\nimport { ConnectionManager } from '../connection-manager'\nimport { InputStep } from '../../service/step/step-type'\nimport { Context } from '../types'\n\nasync function MqttPublish(step: InputStep, { logger, data, domain }: Context) {\n var {\n connection: connectionName,\n params: { topic, accessor }\n } = step\n\n const { client } = ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n if (!client) {\n throw Error(`connection is not found : ${connectionName}`)\n }\n\n if (!topic || !accessor) {\n throw Error(`topic and accessor should be defined: : topic - '${topic}', accessor - '${accessor}'`)\n }\n\n var message = JSON.stringify(access(accessor, data))\n await client.publish(topic, message)\n\n return {\n data: message\n }\n}\n\nMqttPublish.parameterSpec = [\n {\n type: 'string',\n name: 'topic',\n label: 'topic'\n },\n {\n type: 'scenario-step-input',\n name: 'accessor',\n label: 'accessor'\n }\n]\n\nMqttPublish.help = 'integration/task/mqtt-publish'\n\nTaskRegistry.registerTaskHandler('mqtt-publish', MqttPublish)\n"]}
1
+ {"version":3,"file":"mqtt-publish.js","sourceRoot":"","sources":["../../../server/engine/task/mqtt-publish.ts"],"names":[],"mappings":";;AAAA,iDAA8C;AAC9C,0DAAkD;AAClD,oEAA4D;AAI5D,KAAK,UAAU,WAAW,CAAC,IAAe,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAW;IAC3E,IAAI,EACF,UAAU,EAAE,cAAc,EAC1B,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,GAAG,MAAM,EAAE,EACjD,GAAG,IAAI,CAAA;IAER,MAAM,EAAE,MAAM,EAAE,GAAG,yCAAiB,CAAC,2BAA2B,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;IACxF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,KAAK,CAAC,6BAA6B,cAAc,EAAE,CAAC,CAAA;IAC5D,CAAC;IAED,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxB,MAAM,KAAK,CAAC,oDAAoD,KAAK,kBAAkB,QAAQ,GAAG,CAAC,CAAA;IACrG,CAAC;IAED,IAAI,OAAO,GAAG,IAAA,cAAM,EAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;IAEpC,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;QAC1B,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAA;IAC3B,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IACnC,CAAC;IAED,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAEpC,OAAO;QACL,IAAI,EAAE,OAAO;KACd,CAAA;AACH,CAAC;AAED,WAAW,CAAC,aAAa,GAAG;IAC1B;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,OAAO;KACf;IACD;QACE,IAAI,EAAE,qBAAqB;QAC3B,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,UAAU;KAClB;IACD;QACE,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,aAAa;QACpB,IAAI,EAAE,YAAY;QAClB,QAAQ,EAAE;YACR,OAAO,EAAE;gBACP;oBACE,OAAO,EAAE,YAAY;oBACrB,KAAK,EAAE,MAAM;iBACd;gBACD;oBACE,OAAO,EAAE,MAAM;oBACf,KAAK,EAAE,MAAM;iBACd;aACF;SACF;KACF;CACF,CAAA;AAED,WAAW,CAAC,IAAI,GAAG,+BAA+B,CAAA;AAElD,+BAAY,CAAC,mBAAmB,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA","sourcesContent":["import { access } from '@things-factory/utils'\nimport { TaskRegistry } from '../task-registry.js'\nimport { ConnectionManager } from '../connection-manager.js'\nimport { InputStep } from '../../service/step/step-type.js'\nimport { Context } from '../types.js'\n\nasync function MqttPublish(step: InputStep, { logger, data, domain }: Context) {\n var {\n connection: connectionName,\n params: { topic, accessor, dataFormat = 'json' }\n } = step\n\n const { client } = ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n if (!client) {\n throw Error(`connection is not found : ${connectionName}`)\n }\n\n if (!topic || !accessor) {\n throw Error(`topic and accessor should be defined: : topic - '${topic}', accessor - '${accessor}'`)\n }\n\n var message = access(accessor, data)\n\n if (dataFormat === 'text') {\n message = String(message)\n } else {\n message = JSON.stringify(message)\n }\n\n await client.publish(topic, message)\n\n return {\n data: message\n }\n}\n\nMqttPublish.parameterSpec = [\n {\n type: 'string',\n name: 'topic',\n label: 'topic'\n },\n {\n type: 'scenario-step-input',\n name: 'accessor',\n label: 'accessor'\n },\n {\n type: 'select',\n label: 'data-format',\n name: 'dataFormat',\n property: {\n options: [\n {\n display: 'Plain Text',\n value: 'text'\n },\n {\n display: 'JSON',\n value: 'json'\n }\n ]\n }\n }\n]\n\nMqttPublish.help = 'integration/task/mqtt-publish'\n\nTaskRegistry.registerTaskHandler('mqtt-publish', MqttPublish)\n"]}
@@ -2,87 +2,201 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const async_mqtt_1 = tslib_1.__importDefault(require("async-mqtt"));
5
- const task_registry_1 = require("../task-registry");
6
- const connection_manager_1 = require("../connection-manager");
7
- const utils_1 = require("@things-factory/utils");
5
+ const task_registry_js_1 = require("../task-registry.js");
6
+ const connection_manager_js_1 = require("../connection-manager.js");
8
7
  function convertDataFormat(data, format) {
9
8
  if (format == 'json') {
10
- return JSON.parse(data);
9
+ try {
10
+ return JSON.parse(data);
11
+ }
12
+ catch (e) {
13
+ console.error('JSON 파싱 오류:', e.message);
14
+ return data.toString();
15
+ }
11
16
  }
12
17
  else {
13
18
  return data.toString();
14
19
  }
15
20
  }
16
- async function MqttSubscribe(step, context) {
17
- const { connection: connectionName, params: { topic, dataFormat }, name } = step;
18
- const { domain, logger, closures, __mqtt_subscriber } = context;
19
- if (!__mqtt_subscriber) {
20
- context.__mqtt_subscriber = {};
21
+ // MQTT 연결을 위한 브로커 관리 클래스
22
+ class MqttBrokerManager {
23
+ // 브로커 연결 (또는 기존 연결 반환)
24
+ static async getBroker(uri, options) {
25
+ const brokerKey = `${uri}_${JSON.stringify(options || {})}`;
26
+ if (!this.brokers[brokerKey]) {
27
+ const client = await async_mqtt_1.default.connectAsync(uri, options);
28
+ this.brokers[brokerKey] = {
29
+ client,
30
+ topics: new Set(),
31
+ messageHandlers: new Map()
32
+ };
33
+ // 메시지 수신 핸들러
34
+ client.on('message', (topic, message) => {
35
+ // 해당 토픽에 등록된 핸들러가 있으면 호출
36
+ this.brokers[brokerKey].messageHandlers.forEach((handler, handlerId) => {
37
+ if (handlerId.startsWith(`${topic}:`)) {
38
+ handler(topic, message);
39
+ }
40
+ });
41
+ });
42
+ }
43
+ return this.brokers[brokerKey];
44
+ }
45
+ // 토픽 구독 등록
46
+ static async subscribe(brokerKey, topic) {
47
+ const broker = this.brokers[brokerKey];
48
+ if (!broker) {
49
+ throw new Error(`브로커가 연결되지 않음: ${brokerKey}`);
50
+ }
51
+ // 새 토픽인 경우 구독
52
+ if (!broker.topics.has(topic)) {
53
+ await broker.client.subscribe(topic);
54
+ broker.topics.add(topic);
55
+ }
56
+ }
57
+ // 메시지 핸들러 등록
58
+ static registerMessageHandler(brokerKey, topic, handlerId, handler) {
59
+ const broker = this.brokers[brokerKey];
60
+ if (!broker) {
61
+ throw new Error(`브로커가 연결되지 않음: ${brokerKey}`);
62
+ }
63
+ // 핸들러 ID는 topic:handlerId 형식으로 저장
64
+ const fullHandlerId = `${topic}:${handlerId}`;
65
+ broker.messageHandlers.set(fullHandlerId, handler);
66
+ return () => {
67
+ // 핸들러 제거 함수 반환
68
+ broker.messageHandlers.delete(fullHandlerId);
69
+ };
70
+ }
71
+ // 연결 종료
72
+ static async disconnect(brokerKey) {
73
+ const broker = this.brokers[brokerKey];
74
+ if (broker) {
75
+ await broker.client.end();
76
+ delete this.brokers[brokerKey];
77
+ }
21
78
  }
22
- const { connection: { endpoint: uri, params: { user, password } } } = connection_manager_1.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
79
+ // 브로커 생성 유틸리티
80
+ static getBrokerKey(uri, options) {
81
+ return `${uri}_${JSON.stringify(options || {})}`;
82
+ }
83
+ }
84
+ MqttBrokerManager.brokers = {};
85
+ async function MqttSubscribe(step, context) {
86
+ const { connection: connectionName, params: { topic, dataFormat }, name: stepName } = step;
87
+ const { domain, logger, closures } = context;
88
+ // MQTT 브로커 접속 정보 가져오기
89
+ const { connection: { endpoint: uri, params: { user, password } } } = connection_manager_js_1.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
23
90
  if (!topic) {
24
- throw Error(`topic is not found for ${connectionName}`);
91
+ throw Error(`토픽이 지정되지 않음: ${connectionName}`);
25
92
  }
26
- /*
27
- * 1. subscriber list에서 subscriber를 찾는다. 없으면, 생성한다.
28
- * 2. client.once(...)로 메시지를 취한다.
29
- *
30
- * TODO 동일 브로커의 다중 subscribe 태스크에 대해서 완벽한 지원을 해야한다.
31
- * - 현재는 여러 태스크가 동일 topic을 subscribe 하는 경우에 정상동작하지 않을 것이다.
32
- */
33
- if (!context.__mqtt_subscriber[name]) {
34
- try {
35
- var broker = null;
36
- if (user && password) {
37
- broker = await async_mqtt_1.default.connectAsync(uri, { username: user, password: password });
38
- }
39
- else {
40
- broker = await async_mqtt_1.default.connectAsync(uri);
41
- }
42
- logger.info(`mqtt-connector connection(${connectionName}:${uri}) is connected`);
43
- await broker.subscribe(topic);
44
- logger.info(`success subscribing topic '${topic}'`);
45
- var TOPIC;
46
- var MESSAGE;
47
- context.__mqtt_subscriber[name] = async () => {
48
- while (!MESSAGE) {
49
- await (0, utils_1.sleep)(100);
50
- }
51
- var topic = TOPIC;
52
- var message = MESSAGE;
53
- TOPIC = null;
54
- MESSAGE = null;
55
- return {
56
- topic,
57
- message
58
- };
59
- };
60
- broker.on('message', async (messageTopic, message) => {
61
- if (topic !== messageTopic) {
62
- return;
63
- }
64
- TOPIC = topic;
65
- MESSAGE = convertDataFormat(message, dataFormat);
66
- // logger.info(`mqtt-subscribe :\n'${message.toString()}'`)
67
- });
93
+ // 브로커 연결 키 생성
94
+ const connectionOptions = user && password ? { username: user, password } : undefined;
95
+ const brokerKey = MqttBrokerManager.getBrokerKey(uri, connectionOptions);
96
+ // 구독자 ID 생성 (도메인, 연결명, 토픽, 스텝명 조합)
97
+ const subscriberId = `${domain}_${connectionName}_${topic}_${stepName}`;
98
+ try {
99
+ // 브로커 연결 (또는 기존 연결 가져오기)
100
+ await MqttBrokerManager.getBroker(uri, connectionOptions);
101
+ logger.info(`MQTT 연결 완료: ${connectionName}:${uri}`);
102
+ // 토픽 구독 등록
103
+ await MqttBrokerManager.subscribe(brokerKey, topic);
104
+ logger.info(`토픽 구독 완료: ${topic}`);
105
+ // 리졸버 저장소 초기화
106
+ if (!context.__mqtt_resolvers) {
107
+ context.__mqtt_resolvers = new Map();
108
+ }
109
+ // 클로저에 연결 종료 함수 등록
110
+ if (!context.__mqtt_connections) {
111
+ context.__mqtt_connections = new Set();
112
+ }
113
+ if (!context.__mqtt_handlers) {
114
+ context.__mqtt_handlers = new Map();
115
+ }
116
+ // 연결 추적 (중복 종료 방지)
117
+ if (!context.__mqtt_connections.has(brokerKey)) {
118
+ context.__mqtt_connections.add(brokerKey);
119
+ // 연결 종료 함수 등록
68
120
  closures.push(async () => {
69
121
  try {
70
- broker && (await broker.end());
71
- logger.info(`mqtt-connector connection(${connectionName}:${uri}) is disconnected`);
122
+ // 핸들러 모두 제거
123
+ if (context.__mqtt_handlers) {
124
+ context.__mqtt_handlers.forEach(removeHandler => {
125
+ removeHandler();
126
+ });
127
+ context.__mqtt_handlers.clear();
128
+ }
129
+ // 대기 중인 모든 Promise 해결
130
+ if (context.__mqtt_resolvers) {
131
+ context.__mqtt_resolvers.forEach(resolver => {
132
+ resolver({ data: null, terminated: true });
133
+ });
134
+ context.__mqtt_resolvers.clear();
135
+ }
136
+ // 연결 종료
137
+ await MqttBrokerManager.disconnect(brokerKey);
138
+ logger.info(`MQTT 연결 종료: ${connectionName}:${uri}`);
72
139
  }
73
140
  catch (e) {
74
- logger.error(e);
141
+ logger.error(`MQTT 연결 종료 오류: ${e.message}`);
75
142
  }
76
143
  });
77
144
  }
78
- catch (e) {
79
- logger.error(e);
80
- }
145
+ // Promise로 메시지 수신 대기
146
+ return new Promise(resolve => {
147
+ var _a;
148
+ // 이 태스크의 resolver 저장
149
+ if (context.__mqtt_resolvers) {
150
+ context.__mqtt_resolvers.set(subscriberId, resolve);
151
+ }
152
+ // 이미 등록된 핸들러가 있으면 제거
153
+ if ((_a = context.__mqtt_handlers) === null || _a === void 0 ? void 0 : _a.has(subscriberId)) {
154
+ const removeHandler = context.__mqtt_handlers.get(subscriberId);
155
+ if (removeHandler) {
156
+ removeHandler();
157
+ }
158
+ }
159
+ // 새로운 메시지 핸들러 등록
160
+ const removeHandler = MqttBrokerManager.registerMessageHandler(brokerKey, topic, subscriberId, (messageTopic, message) => {
161
+ var _a, _b;
162
+ try {
163
+ // 메시지 변환
164
+ const convertedMessage = convertDataFormat(message, dataFormat);
165
+ // resolver 가져오기 및 삭제
166
+ if ((_a = context.__mqtt_resolvers) === null || _a === void 0 ? void 0 : _a.has(subscriberId)) {
167
+ const resolver = context.__mqtt_resolvers.get(subscriberId);
168
+ context.__mqtt_resolvers.delete(subscriberId);
169
+ // 이 태스크에 대한 핸들러 제거 (한 번만 실행되도록)
170
+ if ((_b = context.__mqtt_handlers) === null || _b === void 0 ? void 0 : _b.has(subscriberId)) {
171
+ const removeHandler = context.__mqtt_handlers.get(subscriberId);
172
+ if (removeHandler) {
173
+ removeHandler();
174
+ }
175
+ context.__mqtt_handlers.delete(subscriberId);
176
+ }
177
+ // Promise 해결
178
+ if (resolver) {
179
+ resolver({
180
+ data: convertedMessage
181
+ });
182
+ }
183
+ }
184
+ }
185
+ catch (error) {
186
+ logger.error(`메시지 처리 오류: ${error.message}`);
187
+ }
188
+ });
189
+ // 핸들러 제거 함수 저장
190
+ if (context.__mqtt_handlers) {
191
+ context.__mqtt_handlers.set(subscriberId, removeHandler);
192
+ }
193
+ logger.info(`MQTT 메시지 대기 중: ${topic}`);
194
+ });
195
+ }
196
+ catch (e) {
197
+ logger.error(`MQTT 구독 오류: ${e.message}`);
198
+ throw e;
81
199
  }
82
- var { message } = await context.__mqtt_subscriber[name]();
83
- return {
84
- data: message
85
- };
86
200
  }
87
201
  MqttSubscribe.parameterSpec = [
88
202
  {
@@ -109,5 +223,5 @@ MqttSubscribe.parameterSpec = [
109
223
  }
110
224
  ];
111
225
  MqttSubscribe.help = 'integration/task/mqtt-subscribe';
112
- task_registry_1.TaskRegistry.registerTaskHandler('mqtt-subscribe', MqttSubscribe);
226
+ task_registry_js_1.TaskRegistry.registerTaskHandler('mqtt-subscribe', MqttSubscribe);
113
227
  //# sourceMappingURL=mqtt-subscribe.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mqtt-subscribe.js","sourceRoot":"","sources":["../../../server/engine/task/mqtt-subscribe.ts"],"names":[],"mappings":"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","sourcesContent":["import mqtt from 'async-mqtt'\n\nimport { TaskRegistry } from '../task-registry'\nimport { ConnectionManager } from '../connection-manager'\nimport { sleep } from '@things-factory/utils'\nimport { InputStep } from '../../service/step/step-type'\nimport { Context } from '../types'\n\nfunction convertDataFormat(data, format) {\n if (format == 'json') {\n return JSON.parse(data)\n } else {\n return data.toString()\n }\n}\n\nasync function MqttSubscribe(step: InputStep, context: Context) {\n const {\n connection: connectionName,\n params: { topic, dataFormat },\n name\n } = step\n\n const { domain, logger, closures, __mqtt_subscriber } = context\n if (!__mqtt_subscriber) {\n context.__mqtt_subscriber = {}\n }\n\n const {\n connection: {\n endpoint: uri,\n params: { user, password }\n }\n } = ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n\n if (!topic) {\n throw Error(`topic is not found for ${connectionName}`)\n }\n\n /*\n * 1. subscriber list에서 subscriber를 찾는다. 없으면, 생성한다.\n * 2. client.once(...)로 메시지를 취한다.\n *\n * TODO 동일 브로커의 다중 subscribe 태스크에 대해서 완벽한 지원을 해야한다.\n * - 현재는 여러 태스크가 동일 topic을 subscribe 하는 경우에 정상동작하지 않을 것이다.\n */\n if (!context.__mqtt_subscriber[name]) {\n try {\n var broker = null\n if (user && password) {\n broker = await mqtt.connectAsync(uri, { username: user, password: password })\n } else {\n broker = await mqtt.connectAsync(uri)\n }\n\n logger.info(`mqtt-connector connection(${connectionName}:${uri}) is connected`)\n\n await broker.subscribe(topic)\n logger.info(`success subscribing topic '${topic}'`)\n\n var TOPIC\n var MESSAGE\n\n context.__mqtt_subscriber[name] = async () => {\n while (!MESSAGE) {\n await sleep(100)\n }\n\n var topic = TOPIC\n var message = MESSAGE\n\n TOPIC = null\n MESSAGE = null\n\n return {\n topic,\n message\n }\n }\n\n broker.on('message', async (messageTopic, message) => {\n if (topic !== messageTopic) {\n return\n }\n\n TOPIC = topic\n MESSAGE = convertDataFormat(message, dataFormat)\n\n // logger.info(`mqtt-subscribe :\\n'${message.toString()}'`)\n })\n\n closures.push(async () => {\n try {\n broker && (await broker.end())\n logger.info(`mqtt-connector connection(${connectionName}:${uri}) is disconnected`)\n } catch (e) {\n logger.error(e)\n }\n })\n } catch (e) {\n logger.error(e)\n }\n }\n\n var { message } = await context.__mqtt_subscriber[name]()\n\n return {\n data: message\n }\n}\n\nMqttSubscribe.parameterSpec = [\n {\n type: 'string',\n name: 'topic',\n label: 'topic'\n },\n {\n type: 'select',\n label: 'data-format',\n name: 'dataFormat',\n property: {\n options: [\n {\n display: 'Plain Text',\n value: 'text'\n },\n {\n display: 'JSON',\n value: 'json'\n }\n ]\n }\n }\n]\n\nMqttSubscribe.help = 'integration/task/mqtt-subscribe'\n\nTaskRegistry.registerTaskHandler('mqtt-subscribe', MqttSubscribe)\n"]}
1
+ {"version":3,"file":"mqtt-subscribe.js","sourceRoot":"","sources":["../../../server/engine/task/mqtt-subscribe.ts"],"names":[],"mappings":"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","sourcesContent":["import mqtt from 'async-mqtt'\n\nimport { TaskRegistry } from '../task-registry.js'\nimport { ConnectionManager } from '../connection-manager.js'\nimport { InputStep } from '../../service/step/step-type.js'\nimport { Context } from '../types.js'\n\nfunction convertDataFormat(data, format) {\n if (format == 'json') {\n try {\n return JSON.parse(data)\n } catch (e) {\n console.error('JSON 파싱 오류:', e.message)\n return data.toString()\n }\n } else {\n return data.toString()\n }\n}\n\n// MQTT 연결을 위한 브로커 관리 클래스\nclass MqttBrokerManager {\n private static brokers: Record<\n string,\n {\n client: mqtt.AsyncMqttClient\n topics: Set<string>\n messageHandlers: Map<string, (topic: string, message: Buffer) => void>\n }\n > = {}\n\n // 브로커 연결 (또는 기존 연결 반환)\n static async getBroker(uri: string, options?: mqtt.IClientOptions) {\n const brokerKey = `${uri}_${JSON.stringify(options || {})}`\n\n if (!this.brokers[brokerKey]) {\n const client = await mqtt.connectAsync(uri, options)\n\n this.brokers[brokerKey] = {\n client,\n topics: new Set<string>(),\n messageHandlers: new Map()\n }\n\n // 메시지 수신 핸들러\n client.on('message', (topic, message) => {\n // 해당 토픽에 등록된 핸들러가 있으면 호출\n this.brokers[brokerKey].messageHandlers.forEach((handler, handlerId) => {\n if (handlerId.startsWith(`${topic}:`)) {\n handler(topic, message)\n }\n })\n })\n }\n\n return this.brokers[brokerKey]\n }\n\n // 토픽 구독 등록\n static async subscribe(brokerKey: string, topic: string) {\n const broker = this.brokers[brokerKey]\n if (!broker) {\n throw new Error(`브로커가 연결되지 않음: ${brokerKey}`)\n }\n\n // 새 토픽인 경우 구독\n if (!broker.topics.has(topic)) {\n await broker.client.subscribe(topic)\n broker.topics.add(topic)\n }\n }\n\n // 메시지 핸들러 등록\n static registerMessageHandler(\n brokerKey: string,\n topic: string,\n handlerId: string,\n handler: (topic: string, message: Buffer) => void\n ) {\n const broker = this.brokers[brokerKey]\n if (!broker) {\n throw new Error(`브로커가 연결되지 않음: ${brokerKey}`)\n }\n\n // 핸들러 ID는 topic:handlerId 형식으로 저장\n const fullHandlerId = `${topic}:${handlerId}`\n broker.messageHandlers.set(fullHandlerId, handler)\n\n return () => {\n // 핸들러 제거 함수 반환\n broker.messageHandlers.delete(fullHandlerId)\n }\n }\n\n // 연결 종료\n static async disconnect(brokerKey: string) {\n const broker = this.brokers[brokerKey]\n if (broker) {\n await broker.client.end()\n delete this.brokers[brokerKey]\n }\n }\n\n // 브로커 키 생성 유틸리티\n static getBrokerKey(uri: string, options?: any) {\n return `${uri}_${JSON.stringify(options || {})}`\n }\n}\n\ninterface MqttContext extends Context {\n __mqtt_connections?: Set<string>\n __mqtt_handlers?: Map<string, () => void>\n __mqtt_resolvers?: Map<string, (result: any) => void>\n}\n\nasync function MqttSubscribe(step: InputStep, context: MqttContext) {\n const {\n connection: connectionName,\n params: { topic, dataFormat },\n name: stepName\n } = step\n\n const { domain, logger, closures } = context\n\n // MQTT 브로커 접속 정보 가져오기\n const {\n connection: {\n endpoint: uri,\n params: { user, password }\n }\n } = ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n\n if (!topic) {\n throw Error(`토픽이 지정되지 않음: ${connectionName}`)\n }\n\n // 브로커 연결 키 생성\n const connectionOptions = user && password ? { username: user, password } : undefined\n const brokerKey = MqttBrokerManager.getBrokerKey(uri, connectionOptions)\n\n // 구독자 ID 생성 (도메인, 연결명, 토픽, 스텝명 조합)\n const subscriberId = `${domain}_${connectionName}_${topic}_${stepName}`\n\n try {\n // 브로커 연결 (또는 기존 연결 가져오기)\n await MqttBrokerManager.getBroker(uri, connectionOptions)\n logger.info(`MQTT 연결 완료: ${connectionName}:${uri}`)\n\n // 토픽 구독 등록\n await MqttBrokerManager.subscribe(brokerKey, topic)\n logger.info(`토픽 구독 완료: ${topic}`)\n\n // 리졸버 저장소 초기화\n if (!context.__mqtt_resolvers) {\n context.__mqtt_resolvers = new Map()\n }\n\n // 클로저에 연결 종료 함수 등록\n if (!context.__mqtt_connections) {\n context.__mqtt_connections = new Set()\n }\n\n if (!context.__mqtt_handlers) {\n context.__mqtt_handlers = new Map()\n }\n\n // 연결 추적 (중복 종료 방지)\n if (!context.__mqtt_connections.has(brokerKey)) {\n context.__mqtt_connections.add(brokerKey)\n\n // 연결 종료 함수 등록\n closures.push(async () => {\n try {\n // 핸들러 모두 제거\n if (context.__mqtt_handlers) {\n context.__mqtt_handlers.forEach(removeHandler => {\n removeHandler()\n })\n context.__mqtt_handlers.clear()\n }\n\n // 대기 중인 모든 Promise 해결\n if (context.__mqtt_resolvers) {\n context.__mqtt_resolvers.forEach(resolver => {\n resolver({ data: null, terminated: true })\n })\n context.__mqtt_resolvers.clear()\n }\n\n // 연결 종료\n await MqttBrokerManager.disconnect(brokerKey)\n logger.info(`MQTT 연결 종료: ${connectionName}:${uri}`)\n } catch (e) {\n logger.error(`MQTT 연결 종료 오류: ${e.message}`)\n }\n })\n }\n\n // Promise로 메시지 수신 대기\n return new Promise(resolve => {\n // 이 태스크의 resolver 저장\n if (context.__mqtt_resolvers) {\n context.__mqtt_resolvers.set(subscriberId, resolve)\n }\n\n // 이미 등록된 핸들러가 있으면 제거\n if (context.__mqtt_handlers?.has(subscriberId)) {\n const removeHandler = context.__mqtt_handlers.get(subscriberId)\n if (removeHandler) {\n removeHandler()\n }\n }\n\n // 새로운 메시지 핸들러 등록\n const removeHandler = MqttBrokerManager.registerMessageHandler(\n brokerKey,\n topic,\n subscriberId,\n (messageTopic, message) => {\n try {\n // 메시지 변환\n const convertedMessage = convertDataFormat(message, dataFormat)\n\n // resolver 가져오기 및 삭제\n if (context.__mqtt_resolvers?.has(subscriberId)) {\n const resolver = context.__mqtt_resolvers.get(subscriberId)\n context.__mqtt_resolvers.delete(subscriberId)\n\n // 이 태스크에 대한 핸들러 제거 (한 번만 실행되도록)\n if (context.__mqtt_handlers?.has(subscriberId)) {\n const removeHandler = context.__mqtt_handlers.get(subscriberId)\n if (removeHandler) {\n removeHandler()\n }\n context.__mqtt_handlers.delete(subscriberId)\n }\n\n // Promise 해결\n if (resolver) {\n resolver({\n data: convertedMessage\n })\n }\n }\n } catch (error) {\n logger.error(`메시지 처리 오류: ${error.message}`)\n }\n }\n )\n\n // 핸들러 제거 함수 저장\n if (context.__mqtt_handlers) {\n context.__mqtt_handlers.set(subscriberId, removeHandler)\n }\n\n logger.info(`MQTT 메시지 대기 중: ${topic}`)\n })\n } catch (e) {\n logger.error(`MQTT 구독 오류: ${e.message}`)\n throw e\n }\n}\n\nMqttSubscribe.parameterSpec = [\n {\n type: 'string',\n name: 'topic',\n label: 'topic'\n },\n {\n type: 'select',\n label: 'data-format',\n name: 'dataFormat',\n property: {\n options: [\n {\n display: 'Plain Text',\n value: 'text'\n },\n {\n display: 'JSON',\n value: 'json'\n }\n ]\n }\n }\n]\n\nMqttSubscribe.help = 'integration/task/mqtt-subscribe'\n\nTaskRegistry.registerTaskHandler('mqtt-subscribe', MqttSubscribe)\n"]}