API call in custom Action - loop & Sleep

Hi ,
I’m trying to perform polling through a custom action. I already solved several issues ( system-sleep package not working - infinite loop feature bloking …)
For some reasons, my loop and my API call are not performed in the code below and I don’t get why
Any insights ? ( I assume I’m missing something on promises concept )
Thanks in advance
copy/paste from code editor :

async function action(bp: typeof sdk, event: sdk.IO.IncomingEvent, args: any, { user, temp, session } = event.state) {
/** Your code starts below */

console.log(’================Init ======================’)
console.log(’======================================’)

//console.log('Debug - state: ’ + temp.state)
console.log('assertion: ’ + temp.assertion_id)
console.log('ssid: ’ + temp.ssid)
let x = 0
// let body = 0

console.log(’======================================’)
console.log(’====================end init==================’)

var http = require(‘http’)
var fs = require(‘fs’)

var options = {
method: ‘POST’,
hostname: ‘myhost’,
port: 80,
path: ‘/api/assert?uid=john&sid=’ + temp.ssid,
headers:
Authorization: ‘Tok test; tid=test’,
‘Content-Type’: ‘application/json’
},
maxRedirects: 20
}

var req = http.request(options, function(res) {
var chunks = []

res.on('data', function(chunk) {
  chunks.push(chunk)
})

res.on('end', function(chunk) {
  let body = Buffer.concat(chunks)
  console.log('body :' + body.toString())

  var result = JSON.parse(body.toString())
  console.log('Assert Result :' + result.data.state)
  temp.state = result.data.state
})

res.on('error', function(error) {
  console.error(error)
})

})

var postData = JSON.stringify({
assertion_id: temp.assertion_id,
action: ‘authentication’,
data: { approval_id: temp.approval_id }
})

do {
console.log(’===============Begin =======================’)
console.log(‘wait 8 sec’)
console.log(’===============before wait =======================’)
const myAction = async (name, value) => {
await new Promise(resolve => setTimeout(resolve, 8000))
}
return myAction(args.name, args.value)

console.log('PostData: ' + postData)
console.log('===============API Call =======================')
req.write(postData)
req.end()

console.log('===============before break =======================')
console.log('State before break :' + temp.state)
if ((temp.state = 'completed')) break
x = x++
console.log('X value : ' + x)

console.log('===============END=======================')

} while (x < 5)
/** Your code ends here */
}

and so , here is the logs :

08:39:37.061  bp:dialog (test) [R-0dsgjgjalgd54kybNov] execute action "builtin/Poll_status"{ botId: 'test' }, +2ms
08:39:37.061  bp:actions (test) run action{ actionName: 'builtin/Poll_status',
  incomingEvent:
   IOEvent {
     type: 'text',
     channel: 'web',
     direction: 'incoming',
     payload: { type: 'text', text: 'd' },
     target: 'R-0dsgjgjalgd54kybNov',
     botId: 'test',
     createdOn: 2020-01-14T08:39:36.070Z,
     threadId: '102',
     id: '157899117607037340',
     preview: 'd',
     flags: {},
     state:
      { __stacktrace: [Array],
        user: [Object],
        context: [Object],
        session: [Object],
        temp: [Object],
        bot: undefined },
     suggestions: [],
     credentials: undefined,
     nlu:
      { entities: [],
        language: 'n/a',
        detectedLanguage: 'n/a',
        ambiguous: false,
        slots: {},
        intent: [Object],
        intents: [],
        errored: true,
        includedContexts: [Array],
        ms: 0 },
     decision:
      { decision: [Object],
        confidence: 1,
        payloads: [],
        source: 'decisionEngine',
        sourceDetails: 'execute default flow' } },
  actionArgs: {} }, { botId: 'test' }, +9ms
================Init ======================
======================================
assertion: chlUP3RXiCawiBRVqtKq8G0+

ssid: c69a67a5-4f34-461f-a0e3-e83fc8cf47db
======================================
====================end init==================
===============Begin =======================
wait 8 sec
===============before wait =======================
08:39:45.079  bp:actions (test) done running{ result: undefined,
  actionName: 'builtin/Poll_status',
  actionArgs: {} }, { botId: 'test' }, +8s
08:39:45.080  bp:dialog (test) [R-0dsgjgjalgd54kybNov] eval transition "always" to [node-8922]{ botId: 'test' }, +8s
08:39:45.081  bp:dialog (test) [R-0dsgjgjalgd54kybNov] transit (main.flow.json) [poll_status] -> [node-8922]{ botId: 'test' }, +8s
08:39:45.082  bp:dialog (test) [R-0dsgjgjalgd54kybNov] render element "#!builtin_text-W_y65s"{ botId: 'test' }, +2ms
08:39:45.087  bp:dialog (test) [R-0dsgjgjalgd54kybNov] ending flow{ botId: 'test' }, +5ms
08:40:37.088 Launcher Unhandled Rejection [Error, read ECONNRESET]
STACK TRACE
Error: read ECONNRESET
    at TCP.onread (net.js:656:25)
08:40:38.342 Launcher ========================================
                                  Botpress Server
                                   Version 12.3.1
                                  OS linux ubuntu
                      ========================================
08:40:38.346 Launcher App Data Dir: "/root/botpress"
08:40:39.555 Launcher Using 9 modules
                      ⦿ MODULES_ROOT/analytics
                      ⦿ MODULES_ROOT/basic-skills
                      ⦿ MODULES_ROOT/builtin
                      ⊝ MODULES_ROOT/channel-messenger (disabled)
                      ⊝ MODULES_ROOT/channel-telegram (disabled)
                      ⦿ MODULES_ROOT/channel-web
                      ⦿ MODULES_ROOT/code-editor
                      ⦿ MODULES_ROOT/extensions
                      ⊝ MODULES_ROOT/history (disabled)
                      ⊝ MODULES_ROOT/hitl (disabled)
                      ⦿ MODULES_ROOT/nlu
                      ⦿ MODULES_ROOT/qna

Hi!
There are some hints I may give about it:

  1. Try to use request or request-promise module instead of http. It could help with better readability.

const myAction = async (name, value) => {
await new Promise(resolve => setTimeout(resolve, 8000))
}
return myAction(args.name, args.value)

As far I can see, you just call myAction here, but don’t wait for it’s results. Also formatting is slightly broken, so hard to understand. May I ask you to provide you code with better readability, for ex., using https://jsfiddle.net/ or better formatting there?

1 Like

hi @Anton_Trofimov, thanks a lot for your answer.
I’m not a coder at all.
1 - I tried to replace http with request-promise as you mentionned - There are some points I’m missing ( probably the sames as the one for the promise for the delay )

2 - here is below now the code indented thanks to JSfiddle ( with request -promise ) :


Thanks for your help

What Anton meant, that you copy your code in JSfiddle, save it there, and share its link, so instead of copy/paste code, or copy image, others can easily edit/see your code.

Anyhow, please use:

async function action(bp: typeof sdk, event: sdk.IO.IncomingEvent, args: any, { user, temp, session } = event.state) {
  /** Your code starts below */

  const myAction = async () => {
    console.log('================Init ======================')
    console.log('======================================')

    //console.log('Debug - state: ' + temp.state)
    console.log('assertion: ' + temp.assertion_id)
    console.log('ssid: ' + temp.ssid)
    let x = 0
    // let body = 0

    console.log('======================================')
    console.log('====================end init==================')

    var http = require('http')
    var fs = require('fs')

    var options = {
      method: 'POST',
      hostname: 'myhost',
      port: 80,
      path: '/api/assert?uid=john&sid=' + temp.ssid,
      headers: {
        Authorization: 'Tok test; tid=test',
        'Content-Type': 'application/json'
      },
      maxRedirects: 20
    }

    var req = http.request(options, function(res) {
      var chunks = []

      res.on('data', function(chunk) {
        chunks.push(chunk)
      })

      res.on('end', function(chunk) {
        let body = Buffer.concat(chunks)
        console.log('body :' + body.toString())

        var result = JSON.parse(body.toString())
        console.log('Assert Result :' + result.data.state)
        temp.state = result.data.state
      })

      res.on('error', function(error) {
        console.error(error)
      })
    })

    var postData = JSON.stringify({
      assertion_id: temp.assertion_id,
      action: 'authentication',
      data: { approval_id: temp.approval_id }
    })

    do {
      console.log('===============Begin =======================')
      console.log('wait 8 sec')
      console.log('===============before wait =======================')

      await new Promise(resolve => setTimeout(resolve, 8000))

      console.log('PostData: ' + postData)
      console.log('===============API Call =======================')
      req.write(postData)
      req.end()

      console.log('===============before break =======================')
      console.log('State before break :' + temp.state)
      if ((temp.state = 'completed')) break
      x = x++
      console.log('X value : ' + x)

      console.log('===============END=======================')
    } while (x < 5)
  }
  return myAction()
  /** Your code ends here */
}
1 Like

I’ve try to cleanup code a bit, try it please.

const rp = require('request-promise');

(async ()=>{
	const options = {
      method: 'POST',
      hostname: 'myhost',
      port: 80,
      path: '/api/assert?uid=john&sid=' + temp.ssid,
      headers: {
        Authorization: 'Tok test; tid=test',
        'Content-Type': 'application/json'
      },
	  body: {
		assertion_id: temp.assertion_id,
		action: 'authentication',
		data: { approval_id: temp.approval_id }
	  },
	  json: true,
      maxRedirects: 20
    }
	
	
	for(let i=0; i<5; i++){
		try{
			const parsedBody = await rp(options);
			temp.state = parsedBody.data.state;
			await new Promise(resolve=>setTimeout(resolve, 8000));
			if(temp.state==='completed'){
				break;
			}
		}
		catch(ex){
			console.error(ex);
		}
	}
	
})

hi @asashour, @Anton_Trofimov
Sorry for my mis-understanding. I’m a very bad in coding.
@asashour : I’m now using code editor but it changes all " by ’ which create issues with JSON

I created an account on JSfiddle now.
@Anton_Trofimov, just tried your code,thanks a lot , no syntax errors , but it is not looping :

16:32:49.578 bp:dialog (test) [R-0dsgjgjalgd54kybNov] execute action “builtin/Poll_test”{ botId: ‘test’ }, +2ms
16:32:49.579 bp:actions (test) run action{ actionName: ‘builtin/Poll_test’,
incomingEvent:
IOEvent {
type: ‘text’,
channel: ‘web’,
direction: ‘incoming’,
payload: { type: ‘text’, text: ‘sss’ },
target: ‘R-0dsgjgjalgd54kybNov’,
botId: ‘test’,
createdOn: 2020-01-14T16:32:48.461Z,
threadId: ‘102’,
id: ‘157901956846177250’,
preview: ‘sss’,
flags: {},
state:
{ __stacktrace: [Array],
user: [Object],
context: [Object],
session: [Object],
temp: [Object],
bot: undefined },
suggestions: [],
credentials: undefined,
nlu:
{ entities: [],
language: ‘n/a’,
detectedLanguage: ‘n/a’,
ambiguous: false,
slots: {},
intent: [Object],
intents: [],
errored: true,
includedContexts: [Array],
ms: 0 },
decision:
{ decision: [Object],
confidence: 1,
payloads: [],
source: ‘decisionEngine’,
sourceDetails: ‘execute default flow’ } },
actionArgs: {} }, { botId: ‘test’ }, +11ms
16:32:49.725 bp:actions (test) done running{ result: undefined,
actionName: ‘builtin/Poll_Test’,
actionArgs: {} }, { botId: ‘test’ }, +147ms
16:32:49.728 bp:dialog (test) [R-0dsgjgjalgd54kybNov] eval transition “always” to [node-8922]{ botId: ‘test’ }, +149ms
16:32:49.728 bp:dialog (test) [R-0dsgjgjalgd54kybNov] transit (main.flow.json) [poll_test] -> [node-8922]{ botId: ‘test’ }, +151ms
16:32:49.729 bp:dialog (test) [R-0dsgjgjalgd54kybNov] render element “#!builtin_text-W_y65s”{ botId: ‘test’ }, +2ms
16:32:49.736 bp:dialog (test) [R-0dsgjgjalgd54kybNov] ending flow{ botId: ‘test’ }, +8ms

Jsfiddle link : https://jsfiddle.net/smalldragoon/1jzdp4yw/5/

Add console.log(parsedBody) after that and send results please.
This code suppose to be run 5 times till parsedBody.data.state couldn’t be equal to ‘completed’ or code crashed.

1 Like

Line added : link to JSfiddle : https://jsfiddle.net/smalldragoon/1jzdp4yw/8/
did a lot of tests, do not see why it is not looping indeed…
no changes in the logs :

19:04:04.495 bp:dialog (test) [zIQ-R45kqJQyvpb_AJeK] eval transition “always” to [poll_Test]{ botId: ‘test’ }, +7ms
19:04:04.496 bp:dialog (test) [zIQ-R45kqJQyvpb_AJeK] transit (main.flow.json) [store_Var_in_memory] -> [poll_Test]{ botId: ‘test’ }, +17ms
19:04:04.497 bp:dialog (test) [zIQ-R45kqJQyvpb_AJeK] execute action “builtin/Poll_Test”{ botId: ‘test’ }, +2ms
19:04:04.498 bp:actions (test) run action{ actionName: ‘builtin/Poll_Test’,
incomingEvent:
IOEvent {
type: ‘text’,
channel: ‘web’,
direction: ‘incoming’,
payload: { type: ‘text’, text: ‘go’ },
target: ‘zIQ-R45kqJQyvpb_AJeK’,
botId: ‘test’,
createdOn: 2020-01-14T19:04:03.518Z,
threadId: ‘118’,
id: ‘157902864351897300’,
preview: ‘go’,
flags: {},
state:
{ __stacktrace: [Array],
user: [Object],
context: [Object],
session: [Object],
temp: [Object],
bot: undefined },
suggestions: [],
credentials: undefined,
nlu:
{ entities: [],
language: ‘n/a’,
detectedLanguage: ‘n/a’,
ambiguous: false,
slots: {},
intent: [Object],
intents: [],
errored: true,
includedContexts: [Array],
ms: 0 },
decision:
{ decision: [Object],
confidence: 1,
payloads: [],
source: ‘decisionEngine’,
sourceDetails: ‘execute default flow’ } },
actionArgs: {} }, { botId: ‘test’ }, +10ms
19:04:04.501 bp:actions (test) done running{ result: undefined,
actionName: ‘builtin/Poll_Test’,
actionArgs: {} }, { botId: ‘test’ }, +4ms
19:04:04.502 bp:dialog (test) [zIQ-R45kqJQyvpb_AJeK] eval transition “always” to [node-8922]{ botId: ‘test’ }, +5ms
19:04:04.503 bp:dialog (test) [zIQ-R45kqJQyvpb_AJeK] transit (main.flow.json) [poll_Test] -> [node-8922]{ botId: ‘test’ }, +7ms
19:04:04.504 bp:dialog (test) [zIQ-R45kqJQyvpb_AJeK] render element “#!builtin_text-W_y65s”{ botId: ‘test’ }, +1ms
19:04:04.512 bp:dialog (test) [zIQ-R45kqJQyvpb_AJeK] ending flow{ botId: ‘test’ }, +9ms

in case of, BP restarted , Tested via “normal window” and "debug in admin console ", same result

additional info : checked on server side, no api call is received
Please note that compared to your code, the online editor removes some parenthesis and reformat the text :
image

here some code working that @Anton_Trofimov shared with me
a big thanks for this working code !

const rp = require('request-promise');

(async ()=>{
	const options = {
      method: 'POST',
      hostname: 'myhost',
      port: 80,
      path: '/api/assert?uid=john&sid=' + temp.ssid,
      headers: {
        Authorization: 'Tok test; tid=test',
        'Content-Type': 'application/json'
      },
	  body: {
		assertion_id: temp.assertion_id,
		action: 'authentication',
		data: { approval_id: temp.approval_id }
	  },
	  json: true,
      maxRedirects: 20
    }
	
	
	for(let i=0; i<5; i++){
		try{
			const parsedBody = await rp(options);
console.log(parsedBody);
			temp.state = parsedBody.data.state;
			await new Promise(resolve=>setTimeout(resolve, 8000));
			if(temp.state==='completed'){
				break;
			}
		}
		catch(ex){
			console.error(ex);
		}
	}
	
})()