qlik-oss/enigma.js

Reload App Script

davidgutierrez opened this issue · 6 comments

Description

I been working in a Script that allows the reload of an app under petition, but im getting a "socket hang up". error:

Steps to Reproduce

Script Used:

/* eslint import/no-unresolved:0, import/extensions:0, no-console:0 */
const enigma    = require('enigma.js');
const WebSocket = require('ws');
const schema    = require('enigma.js/schemas/12.20.0.json');
const path      = require('path');
const fs        = require('fs');


process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

var args        = process.argv.slice(2)
var urlws       = args[0]; // path
var nombreDoc   = args[1];
var userDir     = args[2];  // The Sense Enterprise-configured user directory
							// for the user you want to identify as
var userId      = args[3];  // The user to use when creating the session
var certPath    = args[4];  // Path to a local folder containing the Sense
							// Enterprise exported certificates

var readCert    = filename => fs.readFileSync(path.resolve(__dirname, certPath, filename)); 
// Helper function to read the contents of the certificate files:

var certificates = {
	cert: fs.readFileSync(path.resolve(certPath, 'client.pem')),
	key: fs.readFileSync(path.resolve(certPath, 'client_key.pem')),
	root: fs.readFileSync(path.resolve(certPath, 'root.pem'))
	};

var session     = enigma.create({
 schema,
 url: `${urlws}`,
 // Notice the non-standard second parameter here, this is how you pass in
 // additional configuration to the 'ws' npm library, if you use a different
 // library you may configure this differently:
 createSocket: url => new WebSocket(url, {
ca: [certificates.root],
		cert: certificates.cert,
		key: certificates.key,
headers: {
 'X-Qlik-User': `UserDirectory=${encodeURIComponent(userDir)}; UserId=${encodeURIComponent(userId)}`,
},
 }),
});

start();

function start(){
	var docId = '';
	// bind traffic events to log what is sent and received on the socket:
	session.on('traffic:sent', data => console.log('sent:', data));
	session.on('traffic:received', data => console.log('received:', data));

	session.open()
    .then( (global) => {
		var docList =global.getDocList();
		docList.then(function(docList) {
			console.log('Reloaded',nombreDoc);
			docId = docList.filter(obj => obj.qTitle ==nombreDoc )[0].qDocId;			
			console.log(docId);
			global.openDoc(docId).then((app) => {reload(app)})
				.catch((err) => {
					console.log(err);
					process.exit(1);
			    });
		}).catch((error) => {
				console.log('Error using docList:', error);
				process.exit(1);
		});
				
	})
	.catch((error) => {
		console.log('Error using global:', error);
		process.exit(1);
	});
}

async function reload(app){
	await app.doReload();
	console.log('Completed');
    save(app);	
}

 function save(app){
	 app.doSave().then(function (value) {
        console.log('save promise returned',value);
        session.close();
   }, function(reason) {
		setTimeout(() =>{
			console.log('razon',reason);
			guardar(app);
			},50000);
   })
}

Is called from a console using this command:

node reloadApp.js wss://<<IP QLIK ENTERPRISE>>:4747/app/engineData "<<APP NAME>>" "<<user directory>>" "<<USER>>" "./"
Expected behavior

This must uptate the apication.

Actual behavior
Error using global: ErrorEvent {
  target: WebSocket {
    _events: [Object: null prototype] {
      open: [Function],
      close: [Function],
      error: [Function],
      message: [Function]
    },
    _eventsCount: 4,
    _maxListeners: undefined,
    readyState: 3,
    protocol: '',
    _binaryType: 'nodebuffer',
    _closeFrameReceived: false,
    _closeFrameSent: false,
    _closeMessage: '',
    _closeTimer: null,
    _closeCode: 1006,
    _extensions: {},
    _receiver: null,
    _sender: null,
    _socket: null,
    _bufferedAmount: 0,
    _isServer: false,
    _redirects: 0,
    url: 'wss://<<IP QLIK ENTERPRISE>>:4747/app/engineData',
    _req: null
  },
  type: 'error',
  message: 'socket hang up',
  error: Error: socket hang up
      at connResetException (internal/errors.js:570:14)
      at TLSSocket.socketOnEnd (_http_client.js:440:23)
      at TLSSocket.emit (events.js:215:7)
      at endReadableNT (_stream_readable.js:1184:12)
      at processTicksAndRejections (internal/process/task_queues.js:80:21) {
    code: 'ECONNRESET'
  }
}

Environment

Library
[X ] Node.js
[ ] Browser
Operating system
[X ] Windows
[ ] OSX
[ X] Linux
Qlik Sense
[ ] Desktop
[ X] Enterprise

Versions

  • Node.js: v12.14.0
  • Browser:
  • Qlik Sense: April 2020
  • Operating system: Windows 10 and Ubuntu Linux.

I'm having a similar issue but surely not the same. I don't want to hijack your issue, just sharing.

I'm getting an "Error: Socket closed. (onMaxParallelSessionsExceeded)" but I'm consistently closing it for every single session.

session.on('opened', () => { global.semaphore++; console.log('opened: ${this.semaphore}'); });
session.on('closed', () => { global.semaphore--; console.log('closed: ${this.semaphore}'); });

this never exceeds 1.

Node.js + Qlik Sense April 2020 + Windows .

@davidgutierrez When do you get "socket hang up". I tried against a Qlik Sense Enterprise and can't reproduce this behaviour

Did a small change to make the whole script async

const enigma = require('enigma.js');
const WebSocket = require('ws');
const schema = require('enigma.js/schemas/12.20.0.json');
const path = require('path');
const fs = require('fs');

// process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

var args = process.argv.slice(2)
var urlws = args[0];
var nombreDoc = args[1];
var userDir = args[2];
var userId = args[3];
var certPath = args[4];

var session = enigma.create({
  schema,
  url: `${urlws}`,
  createSocket: url => new WebSocket(url, {
    ca: fs.readFileSync(path.resolve(certPath, 'root.pem')),
    cert: fs.readFileSync(path.resolve(certPath, 'client.pem')),
    key: fs.readFileSync(path.resolve(certPath, 'client_key.pem')),
    headers: {
      'X-Qlik-User': `UserDirectory=${encodeURIComponent(userDir)}; UserId=${encodeURIComponent(userId)}`,
    },
  }),
});

(async () => {
  const global = await session.open();
  const docList = await global.getDocList();
  docId = docList.filter(obj => obj.qTitle == nombreDoc)[0].qDocId;
  console.log(`\nStarting to RELOAD the app '${nombreDoc}' with 'docId'=${docId}`);
  
  const app = await global.openDoc(docId);
  console.log(`\t- Opening the app`);
  
  await app.doReload();
  console.log(`\t- Reloading the app`);
  
  await app.doSave();
  console.log(`\t- Saving the app`);
  
  const appInfo = await app.getAppProperties();
  console.log(`The app '${nombreDoc}' was last reloaded at:${appInfo.qLastReloadTime}`);
  
  session.close();
  console.log(`Closing the session`);
})().catch(err => {
  console.error(err);
});

@willenjs when connecting through the Qlik Sense Proxy the closed sockets are quarantined for some time before they can be "re-used". This is for preventing misusage of the license model.
I'm not sure what the "cool-down" time is for closed connections.

If you need more connection simultaneous you could use certificates and connect directly to the engine

Thank you very much @axelssonHakan . The default 'cool-down' seems to be something around ~5 mins.
I will definitely change it to certificates or jwt?.

Marking this as resolved and closing issue

it worked very well

/* eslint import/no-unresolved:0, import/extensions:0, no-console:0 */
const enigma = require('enigma.js');
const WebSocket = require('ws');
const schema = require('enigma.js/schemas/12.20.0.json');
const path = require('path');
const fs = require('fs');

//process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

var args = process.argv.slice(2)
var urlws = args[0];
var nombreDoc = args[1];
var userDir = args[2];
var userId = args[3];
var certPath = args[4];

var session = enigma.create({
  schema,
  url: urlws,
  createSocket: url => new WebSocket(url, {
    ca: fs.readFileSync(path.resolve(certPath, 'root.pem')),
    cert: fs.readFileSync(path.resolve(certPath, 'client.pem')),
    key: fs.readFileSync(path.resolve(certPath, 'client_key.pem')),
    headers: {
      'X-Qlik-User': `UserDirectory=${encodeURIComponent(userDir)}; UserId=${encodeURIComponent(userId)}`,
    },
  }),
});

(async () => {
  const global = await session.open();
  const docList = await global.getDocList();
console.log(docList);
  docId = docList.filter(obj => obj.qTitle == nombreDoc)[0].qDocId;
  console.log(`\nStarting to RELOAD the app ${nombreDoc} with docId=${docId}`);
  
  const app = await global.openDoc(docId);
  console.log('\t- Opening the app');
  
  await app.doReload();
  console.log('\t- Reloading the app');
  
  await app.doSave();
  console.log('\t- Saving the app');
  
  const appInfo = await app.getAppProperties();
  console.log(`The app ${nombreDoc} was last reloaded at:${appInfo.qLastReloadTime}`);
  
  session.close();
  console.log('Closing the session');
})().catch(err => {
  console.error(err);
});

i just made a small changes to your code.

And found that the problem the "socket hang up" is more that the certificates are not longer working outside of the server... is more a configuration of the server problem.