@things-factory/integration-base 6.0.68 → 6.0.69

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": "6.0.68",
3
+ "version": "6.0.69",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "client/index.js",
6
6
  "things-factory": true,
@@ -45,5 +45,5 @@
45
45
  "devDependencies": {
46
46
  "@types/cron": "^2.0.1"
47
47
  },
48
- "gitHead": "832b257b49d4fcca76d5a428df8a2fcf44b4db1a"
48
+ "gitHead": "d0198bba193b275f29fffd8c3963f556db4dda92"
49
49
  }
@@ -57,7 +57,7 @@ export class HttpConnector implements Connector {
57
57
  }
58
58
 
59
59
  get taskPrefixes() {
60
- return ['http']
60
+ return ['http', 'headless']
61
61
  }
62
62
  }
63
63
 
@@ -0,0 +1,141 @@
1
+ import https from 'https'
2
+ import { URL } from 'url'
3
+
4
+ import { access } from '@things-factory/utils'
5
+ import { TaskRegistry } from '../task-registry'
6
+ import { ConnectionManager } from '../connection-manager'
7
+
8
+ import { getHeadlessPool } from './utils/headless-pool-for-scenario'
9
+
10
+ async function HeadlessPost(step, { logger, data, domain }) {
11
+ var { connection: connectionName, params: stepOptions } = step
12
+ var { headers: requestHeaders, contentType, path, accessor } = stepOptions || {}
13
+
14
+ var connection = ConnectionManager.getConnectionInstanceByName(domain, connectionName)
15
+
16
+ if (!connection) {
17
+ throw new Error(`connection '${connectionName}' is not established.`)
18
+ }
19
+
20
+ var { endpoint, params: connectionParams, authHeaders = {} } = connection
21
+
22
+ var headers = {
23
+ ...authHeaders,
24
+ ...requestHeaders
25
+ }
26
+
27
+ var body = access(accessor, data)
28
+ if (contentType && body) {
29
+ headers['content-type'] = contentType
30
+ switch (contentType) {
31
+ case 'text/plain':
32
+ body = JSON.stringify(body)
33
+ break
34
+ case 'application/json':
35
+ body = JSON.stringify(body)
36
+ break
37
+ case 'application/x-www-form-urlencoded':
38
+ const searchParams = new URLSearchParams()
39
+ for (const prop in body) {
40
+ searchParams.set(prop, body[prop])
41
+ }
42
+ body = searchParams
43
+ break
44
+ }
45
+ }
46
+
47
+ var options: any = {
48
+ method: 'POST',
49
+ headers,
50
+ body
51
+ }
52
+
53
+ var { rejectUnauthorized } = connectionParams
54
+
55
+ if (!rejectUnauthorized) {
56
+ const httpsAgent = new https.Agent({
57
+ rejectUnauthorized
58
+ })
59
+ options.agent = httpsAgent
60
+ }
61
+
62
+ const browser = (await getHeadlessPool().acquire()) as any
63
+ const page = await browser.newPage()
64
+
65
+ try {
66
+ page.on('console', msg => {
67
+ console.log(`[browser ${msg.type()}] ${msg.text()}`)
68
+ for (let i = 0; i < msg.args().length; ++i) console.log(`${i}: ${msg.args()[i]}`)
69
+ })
70
+
71
+ await page.goto(endpoint, { waitUntil: 'networkidle2' })
72
+
73
+ const response = await page.evaluate(
74
+ async (urlString, options) => {
75
+ const response = await fetch(urlString, options)
76
+
77
+ if (response.ok && response.headers.get('content-type').includes('application/json')) {
78
+ return await response.json()
79
+ } else {
80
+ return await response.text()
81
+ }
82
+ },
83
+ new URL(path, endpoint),
84
+ options
85
+ )
86
+
87
+ return {
88
+ data: response
89
+ }
90
+ } catch (e) {
91
+ console.error(e)
92
+ } finally {
93
+ page.close()
94
+ getHeadlessPool().release(browser)
95
+ }
96
+ }
97
+
98
+ HeadlessPost.parameterSpec = [
99
+ {
100
+ type: 'string',
101
+ name: 'path',
102
+ label: 'path'
103
+ },
104
+ {
105
+ type: 'http-headers',
106
+ name: 'headers',
107
+ label: 'headers'
108
+ },
109
+ {
110
+ type: 'select',
111
+ name: 'contentType',
112
+ label: 'content-type',
113
+ property: {
114
+ options: [
115
+ {
116
+ display: '',
117
+ value: ''
118
+ },
119
+ {
120
+ display: 'application/json',
121
+ value: 'application/json'
122
+ },
123
+ {
124
+ display: 'text/plain',
125
+ value: 'text/plain'
126
+ },
127
+ {
128
+ display: 'application/x-www-form-urlencoded',
129
+ value: 'application/x-www-form-urlencoded'
130
+ }
131
+ ]
132
+ }
133
+ },
134
+ {
135
+ type: 'scenario-step-input',
136
+ name: 'accessor',
137
+ label: 'accessor'
138
+ }
139
+ ]
140
+
141
+ TaskRegistry.registerTaskHandler('headless-post', HeadlessPost)
@@ -61,15 +61,11 @@ async function HttpPost(step, { logger, data, domain }) {
61
61
 
62
62
  var response = await fetch(url, fetchOptions)
63
63
 
64
- var responseData = await response.text()
65
-
66
- const responseContentType = response.headers.get('content-type')
67
- if (responseContentType && responseContentType.indexOf('application/json') !== -1) {
68
- responseData = JSON.stringify(responseData)
69
- }
70
-
71
64
  return {
72
- data: responseData
65
+ data:
66
+ response.ok && response.headers.get('content-type').includes('application/json')
67
+ ? await response.json()
68
+ : await response.text()
73
69
  }
74
70
  }
75
71
 
@@ -33,3 +33,4 @@ import './socket-listener'
33
33
  import './random'
34
34
  import './csv-readline'
35
35
  import './data-mapper'
36
+ import './headless-post'
@@ -0,0 +1,71 @@
1
+ import * as genericPool from 'generic-pool'
2
+
3
+ import { config, logger } from '@things-factory/env'
4
+
5
+ try {
6
+ var puppeteer = require('puppeteer')
7
+ } catch (err) {
8
+ logger.error(err)
9
+ }
10
+
11
+ var headlessPool
12
+
13
+ export function getHeadlessPool() {
14
+ if (!headlessPool) {
15
+ headlessPool = genericPool.createPool(
16
+ {
17
+ create() {
18
+ console.log('headless-pool-for-scensrio about to create')
19
+ return initializeChromium()
20
+ },
21
+ validate(browser) {
22
+ return Promise.race([
23
+ new Promise(res => setTimeout(() => res(false), 1500)),
24
+ browser
25
+ //@ts-ignore
26
+ .version()
27
+ .then(_ => true)
28
+ .catch(_ => false)
29
+ ])
30
+ },
31
+ destroy(browser) {
32
+ //@ts-ignore
33
+ return browser.close()
34
+ }
35
+ },
36
+ {
37
+ min: 2,
38
+ max: 10,
39
+ testOnBorrow: true,
40
+ acquireTimeoutMillis: 15000
41
+ }
42
+ )
43
+ }
44
+
45
+ return headlessPool
46
+ }
47
+
48
+ const CHROMIUM_PATH = config.get('CHROMIUM_PATH')
49
+
50
+ async function initializeChromium() {
51
+ try {
52
+ if (!puppeteer) {
53
+ return
54
+ }
55
+
56
+ var launchSetting = {
57
+ args: ['--mute-audio', '--no-sandbox'],
58
+ headless: 'new'
59
+ }
60
+
61
+ if (CHROMIUM_PATH) {
62
+ launchSetting['executablePath'] = CHROMIUM_PATH
63
+ }
64
+
65
+ const browser = await puppeteer.launch(launchSetting)
66
+
67
+ return browser
68
+ } catch (err) {
69
+ logger.error(err)
70
+ }
71
+ }