Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@
"lint": "eslint src test",
"eslint": "eslint",
"jest": "jest --env node",
"test": "export $(cat .env) && jest --env node --coverage --collectCoverageFrom=src/*.js --forceExit test/*",
"test:win": "cross-env NODE_ENV=production && jest --env node --coverage --collectCoverageFrom=src/*.js --forceExit test/*",
"test_old": "export $(cat .env) && jest --env node --coverage --collectCoverageFrom=src/*.js --forceExit test/*",
"test": "cross-env NODE_ENV=production && jest --env node --coverage --collectCoverageFrom=src/*.js --forceExit test/*",
"test-in-browser": "serve . && echo 'Visit http://localhost:5000/samples/browser-app'",
"build_pack": "cross-env BABEL_ENV=production babel src --out-dir lib",
"build_dist": "webpack",
"build": "yarn build_pack; yarn build_dist",
"prepublish": "yarn clean && yarn lint && yarn test && yarn build",
"build_old": "yarn build_pack; yarn build_dist",
"build": "run-s build_pack build_dist",
"prepublish": "yarn clean && yarn lint && yarn build",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed tests, because without api key they don't work, and so yarn@3 can't pack it directly from github

"format_test": "prettier --config test/.prettierrc --write 'test*/*.js'",
"format_ex": "prettier --config .prettierrc --write '*.js'",
"format_source": "prettier --config .prettierrc --write './src/*.js'",
Expand All @@ -51,11 +52,13 @@
"https-browserify": "^1.0.0",
"jest": "^29.5.0",
"node-stdlib-browser": "^1.2.0",
"node-fetch": "^3.3.1",
"rimraf": "^5.0.1",
"stream-http": "^3.2.0",
"url": "^0.11.0",
"webpack": "^5.86.0",
"webpack-cli": "^5.1.4"
"webpack-cli": "^5.1.4",
"yarn-run-all": "^3.1.1"
},
"dependencies": {}
}
65 changes: 38 additions & 27 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* global window */
'use strict'

const VERSION = '1.0.3'
const VERSION = '1.0.3-f'
Copy link
Collaborator Author

@kibin kibin Jun 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: fix when master branch is published

const SDK_NAME = 'Intento.NodeJS'

const DEFAULT_AWAIT_DELAY = 1000
Expand All @@ -16,6 +16,37 @@ const {
} = require('./utils')
const HOST = process.env.INTENTO_API_HOST || 'api.inten.to'

/**
* Default fetcher based on https.request
*
* @returns {undefined}
*/
function defaultFetcher({ requestOptions, debug, verbose, data, content }) {
return new Promise((resolve, reject) => {

try {
const req = https.request(requestOptions, resp =>
responseHandler(resp, resolve, reject, debug, verbose)
)

req.on('error', function (err) {
if (err.code === 'ENOTFOUND') {
console.error('Host look up failed: \n', err)
console.log('\nPlease, check internet connection\n')
} else {
customErrorLog(err, 'Fails getting a response from the API')
}
})
req.on('timeout', function (err) {
customErrorLog(err, 'Are you offline?')
})
req.write(data || JSON.stringify(content) || '')
req.end()
} catch (e) {
customErrorLog(e, 'Fails to send a request to the API')
}
})
}
/**
* Main class for connectiong to Intento API
* Typical usage:
Expand All @@ -32,6 +63,7 @@ function IntentoConnector(credentials = {}, options = {}) {
curl = false,
dryRun = false,
userAgent,
fetcher = defaultFetcher
} = options
if (typeof credentials === 'string') {
this.credentials = { apikey: credentials }
Expand All @@ -45,7 +77,7 @@ function IntentoConnector(credentials = {}, options = {}) {
this.verbose = verbose
this.dryRun = dryRun
this.userAgent = userAgent

this.fetcher = fetcher
const { apikey, host = HOST } = this.credentials

if (!apikey) {
Expand Down Expand Up @@ -250,33 +282,12 @@ IntentoConnector.prototype.makeRequest = function (options = {}) {
console.log(`\nTest request\n${requestString}`)
}

return new Promise((resolve, reject) => {
if (this.dryRun) {
resolve(data || content || requestOptions.path || '')
}
if (this.dryRun) {
return Promise.resolve(data || content || requestOptions.path || '')
}

try {
const req = https.request(requestOptions, resp =>
responseHandler(resp, resolve, reject, this.debug, this.verbose)
)
return this.fetcher({ requestOptions, debug: this.debug, verbose: this.verbose, data, content })

req.on('error', function (err) {
if (err.code === 'ENOTFOUND') {
console.error('Host look up failed: \n', err)
console.log('\nPlease, check internet connection\n')
} else {
customErrorLog(err, 'Fails getting a response from the API')
}
})
req.on('timeout', function (err) {
customErrorLog(err, 'Are you offline?')
})
req.write(data || JSON.stringify(content) || '')
req.end()
} catch (e) {
customErrorLog(e, 'Fails to send a request to the API')
}
})
}

IntentoConnector.prototype.fulfill = function (slug, parameters = {}) {
Expand Down
207 changes: 207 additions & 0 deletions testAPI/zd.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
'use strict'
const fetch = require('node-fetch')
const IntentoConnector = require('../src/index')

// Quickly load .env files into the environment
require('dotenv').load()
const apikey = process.env.INTENTO_API_KEY
const host = process.env.INTENTO_API_HOST

const DEBUG = false

/**
* ZD Client mockup
*
* https://developer.zendesk.com/apps/docs/developer-guide/using_sdk#using-secure-settings
* @param {*} params all the params
* @returns {undefined}
*/
class ZDClient {

request(params) {

const {
url,
headers,
secure,
type,
contentType,
data,
} = params


if (secure) {
for (const key in headers) {
headers[key] = headers[key].replace('{{setting.token}}', apikey)
}
}

return fetch(url, {
method: type,
headers: { ...headers, 'content-type': contentType },
body: type !== 'GET' ? data : undefined
}).then(response => {
// here mimicing zd request API as we understand it
//

// zd request API: https://developer.zendesk.com/apps/docs/core-api/client_api#client.requestoptions
//
// console.log(response.responseJSON); // body of the HTTP response
// console.log(response.responseText); // body of the HTTP response
// console.log(response.status); // HTTP response status
// console.log(response.statusText); // Is either 'success' or 'error'
// console.log(response.headers); // HTTP response headers

const { status, headers } = response

return response.text().then(bodytext => {
// it's not exactly clear whether JSON parsing error
// forces zd request to return 'error', but let's consider this as true for now
const zdResponse = { responseText: bodytext, status, statusText: 'error', headers }
try {
zdResponse.responseJSON = JSON.parse(bodytext)
zdResponse.statusText = 'success'
} catch (exception) {
zdResponse.error = exception
}

return zdResponse
})
})
}
}

const zdclient = new ZDClient()
const HTTP_CODES = {
404: 'Not Found'
}
/**
* Fetcher function which can work with zendesk client
* @param {*} param0 incoming parameters
* @returns {undefined}
*/
function zdfetcher({ requestOptions, /*debug, verbose,*/ data, content }) {
// console.log('zd fetcher ', requestOptions, data, content)
let { headers, host, path, method } = requestOptions

delete headers["content-type"]
headers.apikey = "{{setting.token}}"
return zdclient.request({
url: `https://${host}${path}`, // for ex. api.inten.to/ai/text/translate
headers,
secure: true,
type: method, // POST, GET, etc
contentType: 'application/json',
data: data || JSON.stringify(content) || ''
}).then(zdresponse => {
// console.log(' got zdresponse ', zdresponse)
const { status, statusText } = zdresponse

// default fetcher treats 404 as errors and throws, so should we

// here other non 200 statues should be checked
if (statusText === 'success' && status !== 404) {
return zdresponse.responseJSON
}

// might be that zd request returns actual statusMessage in some undocumented field
let error = { statusCode: status, statusMessage: HTTP_CODES[status] }
try {
error.error = zdresponse.responseJSON.error
}
catch (exception) {
error.error = exception
}
throw error
})

}



const client_for_zd = new IntentoConnector({ apikey, host }, { debug: DEBUG, fetcher: zdfetcher })

describe('zd fetcher test', () => {
it('get translation', async () => {
expect.assertions(10)
const translate = await client_for_zd.ai.text.translate.fulfill({
text: 'A sample text',
to: 'es',
})
if (DEBUG) {
console.info('Current apikey settings: ', translate)
}

expect(translate).toBeInstanceOf(Object)
expect(translate.hasOwnProperty('id')).toBeTruthy()
expect(translate.hasOwnProperty('done')).toBeTruthy()
expect(translate.hasOwnProperty('response')).toBeTruthy()
expect(translate.hasOwnProperty('meta')).toBeTruthy()
expect(translate.hasOwnProperty('error')).toBeTruthy()

const res = translate.response[0]
expect(res).toBeDefined()
expect(res.hasOwnProperty('results')).toBeTruthy()
expect(res.hasOwnProperty('meta')).toBeTruthy()
expect(res.hasOwnProperty('service')).toBeTruthy()
})


it('fails without options specified', async () => {
expect.assertions(2)
await client_for_zd.makeRequest().catch(e => {
expect(e.statusCode).toEqual(404)
expect(e.statusMessage).toEqual('Not Found')
})
})

it('fails with an incorrect path specified: /', async () => {
expect.assertions(2)
await client_for_zd.makeRequest({ path: '/' }).catch(e => {
expect(e.statusCode).toEqual(404)
expect(e.statusMessage).toEqual('Not Found')
})
})

it('fails with an incorrect path specified: /settings', async () => {
expect.assertions(3)
await client_for_zd.makeRequest({ path: '/settings' }).catch(e => {
expect(e.error).toBeDefined()
expect(e.error.code).toEqual(404)
expect(e.error.message).toEqual('no such intent settings/')
})
})

it('fails with an incorrect path specified: /ai', async () => {
expect.assertions(3)
await client_for_zd.makeRequest({ path: '/ai' }).catch(e => {
expect(e.error).toBeDefined()
expect(e.error.code).toEqual(404)
expect(e.error.message).toEqual('no such intent ai/')
})
})

it('fails with an incorrect path specified: /usage', async () => {
expect.assertions(2)
await client_for_zd
.makeRequest({ path: '/usage' })
.catch(e => {
expect(e.error).toBeDefined()
expect(e.error).toEqual('No such endpoint.')
// expect(e.error.code).toEqual(404)
// expect(e.error.message).toEqual('No such endpoint.')
})
})

it('shows settings/languages', async () => {
expect.assertions(1)
const langSettings = await client_for_zd.makeRequest({
path: '/settings/languages',
})
if (DEBUG) {
console.info('Current apikey settings: ', langSettings)
}

expect(langSettings).toBeInstanceOf(Object)
})
})
Loading