diff --git a/README.md b/README.md
index 8f6a6bf..639b86e 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-
+
Ctrl-Q makes life easier for Qlik Sense admins and developers.
diff --git a/package-lock.json b/package-lock.json
index 2a3b81e..87d5f50 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
"version": "4.0.0",
"license": "MIT",
"dependencies": {
- "@qlik/api": "^1.24.0",
+ "@qlik/api": "^1.25.0",
"axios": "^1.7.7",
"commander": "^12.1.0",
"csv-parse": "^5.5.6",
@@ -37,7 +37,7 @@
"devDependencies": {
"@babel/eslint-parser": "^7.25.9",
"@babel/plugin-syntax-import-assertions": "^7.26.0",
- "@eslint/js": "^9.14.0",
+ "@eslint/js": "^9.15.0",
"@jest/globals": "^29.7.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
@@ -1281,9 +1281,9 @@
"peer": true
},
"node_modules/@eslint/js": {
- "version": "9.14.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.14.0.tgz",
- "integrity": "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==",
+ "version": "9.15.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz",
+ "integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1803,9 +1803,9 @@
}
},
"node_modules/@qlik/api": {
- "version": "1.24.0",
- "resolved": "https://registry.npmjs.org/@qlik/api/-/api-1.24.0.tgz",
- "integrity": "sha512-TqN+DDk3x3T9xZBF9ULfctGP1Ki+isEqpAGVnRm5DEGlVBDIxxqpiuJLX9aJzKSnCfA7pXPuOsbLVJItgeVlOQ==",
+ "version": "1.25.0",
+ "resolved": "https://registry.npmjs.org/@qlik/api/-/api-1.25.0.tgz",
+ "integrity": "sha512-BvQv/kou2RJCPXtlKRZzI8l1jHDGEhMaqIrVfnXNg2pwDkXajK4a9oEhFekp5raYs1kZ0u6FM+m83ujGkrm9cw==",
"license": "ISC",
"dependencies": {
"enigma.js": "^2.14.0",
diff --git a/package.json b/package.json
index 92db133..d8dff5e 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,7 @@
"license": "MIT",
"type": "module",
"dependencies": {
- "@qlik/api": "^1.24.0",
+ "@qlik/api": "^1.25.0",
"axios": "^1.7.7",
"commander": "^12.1.0",
"csv-parse": "^5.5.6",
@@ -58,26 +58,12 @@
"devDependencies": {
"@babel/eslint-parser": "^7.25.9",
"@babel/plugin-syntax-import-assertions": "^7.26.0",
- "@eslint/js": "^9.14.0",
+ "@eslint/js": "^9.15.0",
"@jest/globals": "^29.7.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"jest": "^29.7.0",
"prettier": "^3.3.3",
"snyk": "^1.1294.0"
- },
- "pkg": {
- "assets": [
- "node_modules/axios/**/*",
- "node_modules/fsevents/fsevents.node",
- "node_modules/csv-stringify/dist/cjs",
- "node_modules/enigma.js/**/*.json",
- "src/static",
- "package.json"
- ],
- "scripts": [
- "node_modules/enigma.js/**/*.json",
- "node_modules/js-yaml/**/*.js"
- ]
}
}
diff --git a/release-please-config.json b/release-please-config.json
index b6db725..bba0de1 100644
--- a/release-please-config.json
+++ b/release-please-config.json
@@ -1,6 +1,4 @@
{
- "last-release-sha": "7b1d7a8f1b05f8b3b9d56f79abae725016a0ee82",
- "release-as": "4.0.0",
"draft": true,
"release-search-depth": 10,
"commit-search-depth": 200,
diff --git a/sea-config.json b/sea-config.json
index 1ad9cef..4fcf7e3 100644
--- a/sea-config.json
+++ b/sea-config.json
@@ -4,6 +4,17 @@
"disableExperimentalSEAWarning": true,
"assets": {
"package.json": "./package.json",
+ "/404.html": "./src/static/404.html",
+ "/android-chrome-192x192.png": "./src/static/android-chrome-192x192.png",
+ "/android-chrome-512x512.png": "./src/static/android-chrome-512x512.png",
+ "/apple-touch-icon.png": "./src/static/apple-touch-icon.png",
+ "/ctrl-q.png": "./src/static/ctrl-q.png",
+ "/favicon-16x16.png": "./src/static/favicon-16x16.png",
+ "/favicon-32x32.png": "./src/static/favicon-32x32.png",
+ "/favicon.ico": "./src/static/favicon.ico",
+ "/index.html": "./src/static/index.html",
+ "/vis-network.min.js": "./src/static/vis-network.min.js",
+ "/vis-network.min.js.map": "./src/static/vis-network.min.js",
"enigma_schema_12.170.2.json": "./node_modules/enigma.js/schemas/12.170.2.json",
"enigma_schema_12.612.0.json": "./node_modules/enigma.js/schemas/12.612.0.json",
"enigma_schema_12.936.0.json": "./node_modules/enigma.js/schemas/12.936.0.json",
diff --git a/src/__tests__/app_export_cert.test.js b/src/__tests__/app_export_cert.test.js
index 8f011b4..19fc487 100644
--- a/src/__tests__/app_export_cert.test.js
+++ b/src/__tests__/app_export_cert.test.js
@@ -3,7 +3,7 @@ import { jest, test, expect, describe } from '@jest/globals';
import fs from 'node:fs';
import path from 'node:path';
-import exportAppToFile from '../lib/cmd/qseow/exportapp.js';
+import { exportAppToFile } from '../lib/cmd/qseow/exportapp.js';
const options = {
logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info',
diff --git a/src/__tests__/app_export_jwt.test.js b/src/__tests__/app_export_jwt.test.js
index 68f593a..0c7842c 100644
--- a/src/__tests__/app_export_jwt.test.js
+++ b/src/__tests__/app_export_jwt.test.js
@@ -3,7 +3,7 @@ import { jest, test, expect, describe } from '@jest/globals';
import fs from 'node:fs';
import path from 'node:path';
-import exportAppToFile from '../lib/cmd/qseow/exportapp.js';
+import { exportAppToFile } from '../lib/cmd/qseow/exportapp.js';
const options = {
logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info',
diff --git a/src/__tests__/app_import_cert.test.js b/src/__tests__/app_import_cert.test.js
index 0671688..d6e31ec 100644
--- a/src/__tests__/app_import_cert.test.js
+++ b/src/__tests__/app_import_cert.test.js
@@ -1,7 +1,7 @@
/* eslint-disable no-console */
import { jest, test, expect, describe } from '@jest/globals';
-import importAppFromFile from '../lib/cmd/qseow/importapp.js';
+import { importAppFromFile } from '../lib/cmd/qseow/importapp.js';
import { appExistById, deleteAppById } from '../lib/util/qseow/app.js';
const options = {
diff --git a/src/__tests__/app_import_jwt.test.js b/src/__tests__/app_import_jwt.test.js
index 1ae4730..7fabd6b 100644
--- a/src/__tests__/app_import_jwt.test.js
+++ b/src/__tests__/app_import_jwt.test.js
@@ -1,7 +1,7 @@
/* eslint-disable no-console */
import { jest, test, expect, describe } from '@jest/globals';
-import importAppFromFile from '../lib/cmd/qseow/importapp.js';
+import { importAppFromFile } from '../lib/cmd/qseow/importapp.js';
import { appExistById, deleteAppById } from '../lib/util/qseow/app.js';
const options = {
diff --git a/src/__tests__/app_jwt.test.js b/src/__tests__/app_jwt.test.js
index c39676c..c8959f8 100644
--- a/src/__tests__/app_jwt.test.js
+++ b/src/__tests__/app_jwt.test.js
@@ -2,7 +2,7 @@
import { jest, test, expect, describe } from '@jest/globals';
import { getApps, getAppById, appExistById, deleteAppById } from '../lib/util/qseow/app.js';
-import importAppFromFile from '../lib/cmd/qseow/importapp.js';
+import { importAppFromFile } from '../lib/cmd/qseow/importapp.js';
import { sleep } from '../globals.js';
const options = {
diff --git a/src/__tests__/bookmark_get_cert.test.js b/src/__tests__/bookmark_get_cert.test.js
index 33cbf38..808654b 100644
--- a/src/__tests__/bookmark_get_cert.test.js
+++ b/src/__tests__/bookmark_get_cert.test.js
@@ -1,7 +1,7 @@
/* eslint-disable no-console */
import { jest, test, expect, describe } from '@jest/globals';
-import getBookmark from '../lib/cmd/qseow/getbookmark.js';
+import { getBookmark } from '../lib/cmd/qseow/getbookmark.js';
const options = {
logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info',
diff --git a/src/__tests__/bookmark_get_jwt.test.js b/src/__tests__/bookmark_get_jwt.test.js
index 85b8a5d..facc874 100644
--- a/src/__tests__/bookmark_get_jwt.test.js
+++ b/src/__tests__/bookmark_get_jwt.test.js
@@ -1,6 +1,6 @@
import { jest, test, expect, describe } from '@jest/globals';
-import getBookmark from '../lib/cmd/qseow/getbookmark.js';
+import { getBookmark } from '../lib/cmd/qseow/getbookmark.js';
const options = {
logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info',
diff --git a/src/__tests__/connection_test_cert.test.js b/src/__tests__/connection_test_cert.test.js
index 7c0eb79..877e69c 100644
--- a/src/__tests__/connection_test_cert.test.js
+++ b/src/__tests__/connection_test_cert.test.js
@@ -1,6 +1,6 @@
import { jest, test, expect, describe } from '@jest/globals';
-import testConnection from '../lib/cmd/qseow/testconnection.js';
+import { testConnection } from '../lib/cmd/qseow/testconnection.js';
const options = {
logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info',
diff --git a/src/__tests__/connection_test_jwt.test.js b/src/__tests__/connection_test_jwt.test.js
index cc1a1d5..2386762 100644
--- a/src/__tests__/connection_test_jwt.test.js
+++ b/src/__tests__/connection_test_jwt.test.js
@@ -1,6 +1,6 @@
import { jest, test, expect, describe } from '@jest/globals';
-import testConnection from '../lib/cmd/qseow/testconnection.js';
+import { testConnection } from '../lib/cmd/qseow/testconnection.js';
const options = {
logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info',
diff --git a/src/__tests__/script_get_cert.test.js b/src/__tests__/script_get_cert.test.js
index 7162292..40689e4 100644
--- a/src/__tests__/script_get_cert.test.js
+++ b/src/__tests__/script_get_cert.test.js
@@ -1,6 +1,6 @@
import { jest, test, expect, describe } from '@jest/globals';
-import getScript from '../lib/cmd/qseow/getscript.js';
+import { getScript } from '../lib/cmd/qseow/getscript.js';
const options = {
logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info',
diff --git a/src/__tests__/script_get_jwt.test.js b/src/__tests__/script_get_jwt.test.js
index eda1ce4..dcd9b4c 100644
--- a/src/__tests__/script_get_jwt.test.js
+++ b/src/__tests__/script_get_jwt.test.js
@@ -1,6 +1,6 @@
import { jest, test, expect, describe } from '@jest/globals';
-import getScript from '../lib/cmd/qseow/getscript.js';
+import { getScript } from '../lib/cmd/qseow/getscript.js';
const options = {
logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info',
diff --git a/src/__tests__/task_custom_property_set_cert.test.js b/src/__tests__/task_custom_property_set_cert.test.js
index 5b77c4c..2a840d4 100644
--- a/src/__tests__/task_custom_property_set_cert.test.js
+++ b/src/__tests__/task_custom_property_set_cert.test.js
@@ -1,6 +1,6 @@
import { jest, test, expect, describe } from '@jest/globals';
-import setTaskCustomProperty from '../lib/cmd/qseow/settaskcp.js';
+import { setTaskCustomProperty } from '../lib/cmd/qseow/settaskcp.js';
import { getTaskById } from '../lib/util/qseow/task.js';
const options = {
diff --git a/src/__tests__/task_custom_property_set_jwt.test.js b/src/__tests__/task_custom_property_set_jwt.test.js
index f99ca3c..b6b6461 100644
--- a/src/__tests__/task_custom_property_set_jwt.test.js
+++ b/src/__tests__/task_custom_property_set_jwt.test.js
@@ -1,6 +1,6 @@
import { jest, test, expect, describe } from '@jest/globals';
-import setTaskCustomProperty from '../lib/cmd/qseow/settaskcp.js';
+import { setTaskCustomProperty } from '../lib/cmd/qseow/settaskcp.js';
import { getTaskById } from '../lib/util/qseow/task.js';
const options = {
diff --git a/src/__tests__/task_get_cert.test.js b/src/__tests__/task_get_cert.test.js
index 7d690b8..60228b3 100644
--- a/src/__tests__/task_get_cert.test.js
+++ b/src/__tests__/task_get_cert.test.js
@@ -2,7 +2,7 @@ import { jest, test, expect, describe } from '@jest/globals';
import fs from 'node:fs';
import path from 'node:path';
-import getTask from '../lib/cmd/qseow/gettask.js';
+import { getTask } from '../lib/cmd/qseow/gettask.js';
const options = {
logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info',
diff --git a/src/__tests__/task_get_jwt.test.js b/src/__tests__/task_get_jwt.test.js
index 81e4848..11d60ec 100644
--- a/src/__tests__/task_get_jwt.test.js
+++ b/src/__tests__/task_get_jwt.test.js
@@ -2,7 +2,7 @@ import { jest, test, expect, describe } from '@jest/globals';
import fs from 'node:fs';
import path from 'node:path';
-import getTask from '../lib/cmd/qseow/gettask.js';
+import { getTask } from '../lib/cmd/qseow/gettask.js';
const options = {
logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info',
diff --git a/src/__tests__/task_import_cert.test.js b/src/__tests__/task_import_cert.test.js
index 29b6d20..7c92b5a 100644
--- a/src/__tests__/task_import_cert.test.js
+++ b/src/__tests__/task_import_cert.test.js
@@ -1,6 +1,6 @@
import { jest, test, expect, describe } from '@jest/globals';
-import importTaskFromFile from '../lib/cmd/qseow/importtask.js';
+import { importTaskFromFile } from '../lib/cmd/qseow/importtask.js';
import { getTaskById, deleteExternalProgramTaskById, deleteReloadTaskById } from '../lib/util/qseow/task.js';
import { mapTaskType } from '../lib/util/qseow/lookups.js';
diff --git a/src/__tests__/task_import_jwt.test.js b/src/__tests__/task_import_jwt.test.js
index 7d9faad..533bb82 100644
--- a/src/__tests__/task_import_jwt.test.js
+++ b/src/__tests__/task_import_jwt.test.js
@@ -1,6 +1,6 @@
import { jest, test, expect, describe } from '@jest/globals';
-import importTaskFromFile from '../lib/cmd/qseow/importtask.js';
+import { importTaskFromFile } from '../lib/cmd/qseow/importtask.js';
import { getTaskById, deleteExternalProgramTaskById, deleteReloadTaskById } from '../lib/util/qseow/task.js';
import { mapTaskType } from '../lib/util/qseow/lookups.js';
diff --git a/src/globals.js b/src/globals.js
index 30499a8..25a1057 100644
--- a/src/globals.js
+++ b/src/globals.js
@@ -13,19 +13,7 @@ let c;
export let appVersion;
// Are we running as a packaged app?
-if (process.pkg) {
- // Get path to JS file
- a = process.pkg.defaultEntrypoint;
-
- // Strip off the filename
- b = upath.dirname(a);
-
- // Add path to package.json file
- c = upath.join(b, filenamePackage);
-
- const { version } = JSON.parse(readFileSync(c));
- appVersion = version;
-} else if (sea.isSea()) {
+if (sea.isSea()) {
// Get contents of package.json file
packageJson = sea.getAsset('package.json', 'utf8');
const version = JSON.parse(packageJson).version;
@@ -75,8 +63,8 @@ export const logger = winston.createLogger({
});
// Are we running as standalone app or not?
-export const isPkg = typeof process.pkg !== 'undefined';
-export const execPath = isPkg ? upath.dirname(process.execPath) : process.cwd();
+export const isSea = sea.isSea();
+export const execPath = isSea ? upath.dirname(process.execPath) : process.cwd();
// Functions to get/set current console logging level
export const getLoggingLevel = () => logTransports.find((transport) => transport.name === 'console').level;
@@ -85,25 +73,16 @@ export const setLoggingLevel = (newLevel) => {
logTransports.find((transport) => transport.name === 'console').level = newLevel;
};
-export const verifyFileExists = async (file, silent = false) => {
+// Separate handling is needed depending on if we are running as a packaged SEA app or not
+// SEA apps cannot list bundled assets as directories, so we need to use a different approach
+export async function verifyFileSystemExists(file, silent = false) {
let exists = false;
try {
await Fs.stat(file);
exists = true;
} catch (err) {
if (!silent) {
- if (isPkg) {
- if (err.message) {
- // Make message a bit nicer than what's returned from stat()
- if (err.message.includes('no such file or directory')) {
- logger.error(`File "${file}" does not exist.`);
- } else {
- logger.error(`Error while checking if file ${file} exists: ${err.message}`);
- }
- } else {
- logger.error(`Error while checking if file ${file} exists: ${err}`);
- }
- } else if (err.message) {
+ if (err.message) {
if (err.message.includes('no such file or directory')) {
logger.error(`File "${file}" does not exist.`);
} else {
@@ -116,13 +95,33 @@ export const verifyFileExists = async (file, silent = false) => {
}
}
}
+ return exists;
+}
+export function verifySeaAssetExists(file, silent = false) {
+ let exists = false;
+ try {
+ sea.getAsset(file, 'utf8');
+ exists = true;
+ } catch (err) {
+ if (!silent) {
+ if (err.message) {
+ if (err.message.includes('no such file or directory')) {
+ logger.error(`Asset "${file}" does not exist.`);
+ } else {
+ logger.error(`Error while checking if asset ${file} exists: ${err.message}`);
+ }
+ } else {
+ logger.error(`Error while checking if asset ${file} exists: ${err}`);
+ }
+ }
+ }
return exists;
-};
+}
export const mergeDirFilePath = (pathElements) => {
let fullPath = '';
- if (isPkg) {
+ if (isSea) {
fullPath = upath.resolve(upath.dirname(process.execPath), ...pathElements);
} else {
// fullPath = upath.resolve(__dirname, ...pathElements);
@@ -179,20 +178,3 @@ export const setCliOptions = (options) => {
// Function to get CLI options
export const getCliOptions = () => cliOptions;
-
-// export default {
-// logger,
-// appVersion,
-// getLoggingLevel,
-// setLoggingLevel,
-// execPath,
-// isPkg,
-// verifyFileExists,
-// generateXrfKey,
-// readCert,
-// isNumeric,
-// mergeDirFilePath,
-// sleep,
-// getCliOptions,
-// setCliOptions,
-// };
diff --git a/src/lib/app/class_allapps.js b/src/lib/app/class_allapps.js
index 4d0e837..1c99374 100644
--- a/src/lib/app/class_allapps.js
+++ b/src/lib/app/class_allapps.js
@@ -7,17 +7,17 @@ import fs2 from 'node:fs';
import { v4 as uuidv4, validate } from 'uuid';
import yesno from 'yesno';
-import { logger, execPath, mergeDirFilePath, verifyFileExists, sleep } from '../../globals.js';
+import { logger, execPath, mergeDirFilePath, verifyFileSystemExists, sleep } from '../../globals.js';
import { setupQrsConnection } from '../util/qseow/qrs.js';
import { getAppColumnPosFromHeaderRow } from '../util/qseow/lookups.js';
-import QlikSenseApp from './class_app.js';
+import { QlikSenseApp } from './class_app.js';
import { getTagIdByName } from '../util/qseow/tag.js';
import { getAppById, deleteAppById } from '../util/qseow/app.js';
import { getCustomPropertyDefinitionByName, doesCustomPropertyValueExist } from '../util/qseow/customproperties.js';
import { catchLog } from '../util/log.js';
import { getCertFilePaths } from '../util/qseow/cert.js';
-class QlikSenseApps {
+export class QlikSenseApps {
constructor() {
//
}
@@ -125,31 +125,25 @@ class QlikSenseApps {
let axiosConfig;
if (this.options.authType === 'cert') {
if (filter === '') {
- axiosConfig = await setupQrsConnection(this.options, {
+ axiosConfig = setupQrsConnection(this.options, {
method: 'get',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: '/qrs/app/full',
});
} else {
- axiosConfig = await setupQrsConnection(this.options, {
+ axiosConfig = setupQrsConnection(this.options, {
method: 'get',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: '/qrs/app/full',
queryParameters: [{ name: 'filter', value: filter }],
});
}
} else if (this.options.authType === 'jwt') {
if (filter === '') {
- axiosConfig = await setupQrsConnection(this.options, {
+ axiosConfig = setupQrsConnection(this.options, {
method: 'get',
path: '/qrs/app/full',
});
} else {
- axiosConfig = await setupQrsConnection(this.options, {
+ axiosConfig = setupQrsConnection(this.options, {
method: 'get',
path: '/qrs/app/full',
queryParameters: [{ name: 'filter', value: filter }],
@@ -264,7 +258,7 @@ class QlikSenseApps {
}"`
);
- const qvfFileExists = await verifyFileExists(currentApp.fullQvfPath);
+ const qvfFileExists = await verifyFileSystemExists(currentApp.fullQvfPath);
if (!qvfFileExists) {
logger.error(
`Import of app file ${appRow[0][appFileColumnHeaders.appCounter.pos]} failed. QVF file does not exist: "${
@@ -552,9 +546,6 @@ class QlikSenseApps {
// Get info about just uploaded app
axiosConfigUploadedApp = setupQrsConnection(this.options, {
method: 'get',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: `/qrs/app/${uploadedAppId}`,
});
} else if (this.options.authType === 'jwt') {
@@ -594,9 +585,6 @@ class QlikSenseApps {
// Get info about just uploaded app
axiosConfigUser = setupQrsConnection(this.options, {
method: 'get',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: '/qrs/user',
queryParameters: [{ name: 'filter', value: filter }],
});
@@ -643,9 +631,6 @@ class QlikSenseApps {
// Uppdate app with tags, custom properties and app owner
axiosConfig2 = setupQrsConnection(this.options, {
method: 'put',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: `/qrs/app/${app.id}`,
body: app,
});
@@ -865,9 +850,6 @@ class QlikSenseApps {
// Build QRS query
axiosConfig = setupQrsConnection(this.options, {
method: 'put',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: `/qrs/app/${appId}/publish`,
queryParameters,
});
@@ -911,9 +893,6 @@ class QlikSenseApps {
// Build QRS query
axiosConfig = setupQrsConnection(this.options, {
method: 'put',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: `/qrs/app/${sourceAppId}/replace`,
queryParameters,
});
@@ -972,9 +951,6 @@ class QlikSenseApps {
// Build QRS query
axiosConfig = setupQrsConnection(this.options, {
method: 'get',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: `/qrs/app`,
queryParameters: [{ name: 'filter', value: filter }],
});
@@ -1019,9 +995,6 @@ class QlikSenseApps {
// Build QRS query
axiosConfig = setupQrsConnection(this.options, {
method: 'get',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: `/qrs/app`,
queryParameters: [{ name: 'filter', value: filter }],
});
@@ -1082,9 +1055,6 @@ class QlikSenseApps {
// Build QRS query
axiosConfigPublish = setupQrsConnection(this.options, {
method: 'get',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: `/qrs/stream/${uploadedAppInfo.appPublishToStream}`,
});
} else if (this.options.authType === 'jwt') {
@@ -1110,9 +1080,6 @@ class QlikSenseApps {
// Build QRS query
axiosConfigPublish = setupQrsConnection(this.options, {
method: 'get',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: '/qrs/stream',
queryParameters: [{ name: 'filter', value: filter }],
});
@@ -1177,9 +1144,6 @@ class QlikSenseApps {
// Build Axios config
const axiosConfig = setupQrsConnection(this.options, {
method: 'post',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: '/qrs/app/upload',
body: form,
headers: {
@@ -1276,9 +1240,6 @@ class QlikSenseApps {
// Build QRS query
axiosConfig = setupQrsConnection(this.options, {
method: 'post',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: `/qrs/app/${app.id}/export/${exportToken}`,
queryParameters: [{ name: 'skipData', value: excludeData }],
});
@@ -1310,7 +1271,7 @@ class QlikSenseApps {
}
}
- async exportAppStep2(resultStep1) {
+ async exportAppStep2(resultStep1, appCounter, appCountTotal) {
// resultStep.downloadPath has format
// /tempcontent/d989fffd-5310-43b5-b028-f313b53bb8e2/User%20retention.qvf?serverNodeId=80db9b97-8ea2-4208-a79a-c46b7e16c38c
@@ -1358,7 +1319,7 @@ class QlikSenseApps {
// Check if destination QVF file already exists
// 2nd parameter controls whether to log info or not about file's existence
- const fileExists = await verifyFileExists(fileName, true);
+ const fileExists = await verifyFileSystemExists(fileName, true);
let fileSkipped = false;
let writer;
@@ -1389,9 +1350,6 @@ class QlikSenseApps {
// Build QRS query
axiosConfig = setupQrsConnection(this.options, {
method: 'get',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: urlPath,
queryParameters: [{ name: paramName, value: paramValue }],
});
@@ -1406,7 +1364,9 @@ class QlikSenseApps {
axiosConfig.responseType = 'stream';
logger.info('------------------------------------');
- logger.info(`App [${resultStep2.appId}] "${resultStep2.appName}.qvf", download starting`);
+ logger.info(
+ `${appCounter} of ${appCountTotal}: App [${resultStep2.appId}] "${resultStep2.appName}.qvf", download starting`
+ );
const result = await axios.request(axiosConfig);
result.data.pipe(writer);
@@ -1435,5 +1395,3 @@ class QlikSenseApps {
});
}
}
-
-export default QlikSenseApps;
diff --git a/src/lib/app/class_app.js b/src/lib/app/class_app.js
index c95a58f..d735408 100644
--- a/src/lib/app/class_app.js
+++ b/src/lib/app/class_app.js
@@ -1,5 +1,4 @@
-class QlikSenseApp {
- // eslint-disable-next-line no-useless-constructor
+export class QlikSenseApp {
constructor() {
//
}
@@ -34,5 +33,3 @@ class QlikSenseApp {
this.options = options;
}
}
-
-export default QlikSenseApp;
diff --git a/src/lib/cli/qseow-delete-master-dimension.js b/src/lib/cli/qseow-delete-master-dimension.js
index 66739f2..78c4fd8 100644
--- a/src/lib/cli/qseow-delete-master-dimension.js
+++ b/src/lib/cli/qseow-delete-master-dimension.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions, masterItemDimDeleteAssertOptions } from '../util/qseow/assert-options.js';
-import deleteMasterDimension from '../cmd/qseow/getdim.js';
+import { deleteMasterDimension } from '../cmd/qseow/deletedim.js';
export function setupQseowDeleteMasterDimensionCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-delete-master-measure.js b/src/lib/cli/qseow-delete-master-measure.js
index cb5c045..e51fc78 100644
--- a/src/lib/cli/qseow-delete-master-measure.js
+++ b/src/lib/cli/qseow-delete-master-measure.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions, masterItemMeasureDeleteAssertOptions } from '../util/qseow/assert-options.js';
-import deleteMasterMeasure from '../cmd/qseow/deletemeasure.js';
+import { deleteMasterMeasure } from '../cmd/qseow/deletemeasure.js';
export function setupQseowDeleteMasterMeasureCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-delete-proxy-session.js b/src/lib/cli/qseow-delete-proxy-session.js
index 4b23249..9718e3b 100644
--- a/src/lib/cli/qseow-delete-proxy-session.js
+++ b/src/lib/cli/qseow-delete-proxy-session.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions, deleteSessionsAssertOptions } from '../util/qseow/assert-options.js';
-import deleteSessions from '../cmd/qseow/deletesessions.js';
+import { deleteSessions } from '../cmd/qseow/deletesessions.js';
export function setupQseowDeleteProxySessionsCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-delete-variable.js b/src/lib/cli/qseow-delete-variable.js
index 6046f83..312acaa 100644
--- a/src/lib/cli/qseow-delete-variable.js
+++ b/src/lib/cli/qseow-delete-variable.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions, variableDeleteAssertOptions } from '../util/qseow/assert-options.js';
-import deleteVariable from '../cmd/qseow/deletevariable.js';
+import { deleteVariable } from '../cmd/qseow/deletevariable.js';
export function setupQseowDeleteVariableCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-export-app-to-file.js b/src/lib/cli/qseow-export-app-to-file.js
index 145646a..82d27f1 100644
--- a/src/lib/cli/qseow-export-app-to-file.js
+++ b/src/lib/cli/qseow-export-app-to-file.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions, appExportAssertOptions } from '../util/qseow/assert-options.js';
-import exportAppToFile from '../cmd/qseow/importtask.js';
+import { exportAppToFile } from '../cmd/qseow/exportapp.js';
export function setupQseowExportAppCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-get-bookmark.js b/src/lib/cli/qseow-get-bookmark.js
index 1335766..3e7fdc9 100644
--- a/src/lib/cli/qseow-get-bookmark.js
+++ b/src/lib/cli/qseow-get-bookmark.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions, getBookmarkAssertOptions } from '../util/qseow/assert-options.js';
-import getBookmark from '../cmd/qseow/getbookmark.js';
+import { getBookmark } from '../cmd/qseow/getbookmark.js';
export function setupQseowGetBookmarkCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-get-master-dimension.js b/src/lib/cli/qseow-get-master-dimension.js
index 424c1cf..c5ea752 100644
--- a/src/lib/cli/qseow-get-master-dimension.js
+++ b/src/lib/cli/qseow-get-master-dimension.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions } from '../util/qseow/assert-options.js';
-import getMasterDimension from '../cmd/qseow/getdim.js';
+import { getMasterDimension } from '../cmd/qseow/getdim.js';
export function setupQseowGetMasterDimensionCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-get-master-measure.js b/src/lib/cli/qseow-get-master-measure.js
index 0d229c8..24ce12e 100644
--- a/src/lib/cli/qseow-get-master-measure.js
+++ b/src/lib/cli/qseow-get-master-measure.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions, masterItemGetAssertOptions } from '../util/qseow/assert-options.js';
-import getMasterMeasure from '../cmd/qseow/getmeasure.js';
+import { getMasterMeasure } from '../cmd/qseow/getmeasure.js';
export function setupQseowGetMasterMeasureCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-get-proxy-session.js b/src/lib/cli/qseow-get-proxy-session.js
index 766a0dd..6481e8a 100644
--- a/src/lib/cli/qseow-get-proxy-session.js
+++ b/src/lib/cli/qseow-get-proxy-session.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions, getSessionsAssertOptions } from '../util/qseow/assert-options.js';
-import getSessions from '../cmd/qseow/getsessions.js';
+import { getSessions } from '../cmd/qseow/getsessions.js';
export function setupQseowGetProxySessionsCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-get-script.js b/src/lib/cli/qseow-get-script.js
index d0a76d6..a9ef39e 100644
--- a/src/lib/cli/qseow-get-script.js
+++ b/src/lib/cli/qseow-get-script.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions, getScriptAssertOptions } from '../util/qseow/assert-options.js';
-import getScript from '../cmd/qseow/getscript.js';
+import { getScript } from '../cmd/qseow/getscript.js';
export function setupGetScriptCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-get-task.js b/src/lib/cli/qseow-get-task.js
index 66d369b..834d855 100644
--- a/src/lib/cli/qseow-get-task.js
+++ b/src/lib/cli/qseow-get-task.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions, getTaskAssertOptions } from '../util/qseow/assert-options.js';
-import getTask from '../cmd/qseow/gettask.js';
+import { getTask } from '../cmd/qseow/gettask.js';
export function setupGetTaskCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-get-variable.js b/src/lib/cli/qseow-get-variable.js
index 8667ddc..ed42ce8 100644
--- a/src/lib/cli/qseow-get-variable.js
+++ b/src/lib/cli/qseow-get-variable.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions, variableGetAssertOptions } from '../util/qseow/assert-options.js';
-import getVariable from '../cmd/qseow/getvariable.js';
+import { getVariable } from '../cmd/qseow/getvariable.js';
export function setpQseowGetVariableCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-import-app-from-file.js b/src/lib/cli/qseow-import-app-from-file.js
index a6a1841..b72c5bc 100644
--- a/src/lib/cli/qseow-import-app-from-file.js
+++ b/src/lib/cli/qseow-import-app-from-file.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions, appImportAssertOptions } from '../util/qseow/assert-options.js';
-import importAppFromFile from '../cmd/qseow/importapp.js';
+import { importAppFromFile } from '../cmd/qseow/importapp.js';
export function setupQseowImportAppFromFileCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-import-task-from-file.js b/src/lib/cli/qseow-import-task-from-file.js
index f0e6214..08a1ab1 100644
--- a/src/lib/cli/qseow-import-task-from-file.js
+++ b/src/lib/cli/qseow-import-task-from-file.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions, taskImportAssertOptions } from '../util/qseow/assert-options.js';
-import importTaskFromFile from '../cmd/qseow/importtask.js';
+import { importTaskFromFile } from '../cmd/qseow/importtask.js';
export function setupQseowImportTaskFromFileCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-master-item-import.js b/src/lib/cli/qseow-master-item-import.js
index 44643b2..0848766 100644
--- a/src/lib/cli/qseow-master-item-import.js
+++ b/src/lib/cli/qseow-master-item-import.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions, masterItemImportAssertOptions } from '../util/qseow/assert-options.js';
-import importMasterItemFromFile from '../cmd/qseow/import-masteritem-excel.js';
+import { importMasterItemFromFile } from '../cmd/qseow/import-masteritem-excel.js';
export function setupQseowMasterItemImportCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-scramble-field.js b/src/lib/cli/qseow-scramble-field.js
index a29e286..b3da76f 100644
--- a/src/lib/cli/qseow-scramble-field.js
+++ b/src/lib/cli/qseow-scramble-field.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions } from '../util/qseow/assert-options.js';
-import scrambleField from '../cmd/qseow/scramblefield.js';
+import { scrambleField } from '../cmd/qseow/scramblefield.js';
export function setupQseowScrambleFieldCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-set-task-cp.js b/src/lib/cli/qseow-set-task-cp.js
index afa5f8d..f6f2a5a 100644
--- a/src/lib/cli/qseow-set-task-cp.js
+++ b/src/lib/cli/qseow-set-task-cp.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions, setTaskCustomPropertyAssertOptions } from '../util/qseow/assert-options.js';
-import setTaskCustomProperty from '../cmd/qseow/settaskcp.js';
+import { setTaskCustomProperty } from '../cmd/qseow/settaskcp.js';
export function setupQseowSetTaskCustomPropertyCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-show-version.js b/src/lib/cli/qseow-show-version.js
index d5a159f..eda1f26 100644
--- a/src/lib/cli/qseow-show-version.js
+++ b/src/lib/cli/qseow-show-version.js
@@ -1,5 +1,7 @@
import { Option } from 'commander';
+import { appVersion, logger } from '../../globals.js';
+
export function setupQseowShowVersionCommand(qseow) {
qseow
.command('version')
@@ -8,6 +10,6 @@ export function setupQseowShowVersionCommand(qseow) {
new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info')
)
.action(async (options) => {
- logger.verbose(`Version: ${appVersion}`);
+ logger.info(`Version: ${appVersion}`);
});
}
diff --git a/src/lib/cli/qseow-test-connection.js b/src/lib/cli/qseow-test-connection.js
index 41a8b86..cb98684 100644
--- a/src/lib/cli/qseow-test-connection.js
+++ b/src/lib/cli/qseow-test-connection.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions } from '../util/qseow/assert-options.js';
-import testConnection from '../cmd/qseow/testconnection.js';
+import { testConnection } from '../cmd/qseow/testconnection.js';
export function setupQseowTestConnectionCommand(qseow) {
qseow
diff --git a/src/lib/cli/qseow-visualise-task.js b/src/lib/cli/qseow-visualise-task.js
index 1a9d8ff..75e9a9c 100644
--- a/src/lib/cli/qseow-visualise-task.js
+++ b/src/lib/cli/qseow-visualise-task.js
@@ -2,7 +2,7 @@ import { Option } from 'commander';
import { catchLog } from '../util/log.js';
import { qseowSharedParamAssertOptions } from '../util/qseow/assert-options.js';
-import visTask from '../cmd/qseow/vistask.js';
+import { visTask } from '../cmd/qseow/vistask.js';
export function setupQseowVisualiseTaskCommand(qseow) {
qseow
diff --git a/src/lib/cmd/qscloud/testconnection.js b/src/lib/cmd/qscloud/testconnection.js
index c065058..dec292f 100644
--- a/src/lib/cmd/qscloud/testconnection.js
+++ b/src/lib/cmd/qscloud/testconnection.js
@@ -1,4 +1,4 @@
-import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js';
+import { logger, setLoggingLevel, isSea, execPath } from '../../../globals.js';
import { getQscloudCurrentUser } from '../../util/qscloud/user.js';
import { catchLog } from '../../util/log.js';
@@ -7,7 +7,7 @@ export async function qscloudTestConnection(options) {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.info(`Testing connection to Qlik Sense Cloud tenant "${options.tenantUrl}"`);
diff --git a/src/lib/cmd/qseow/createdim.js b/src/lib/cmd/qseow/createdim.js
index c6efd18..52dfd87 100644
--- a/src/lib/cmd/qseow/createdim.js
+++ b/src/lib/cmd/qseow/createdim.js
@@ -1,18 +1,18 @@
import enigma from 'enigma.js';
import setupEnigmaConnection from '../../util/qseow/enigma_util.js';
-import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js';
+import { logger, setLoggingLevel, isSea, execPath } from '../../../globals.js';
import { catchLog } from '../../util/log.js';
/**
*
* @param {*} options
*/
-const createMasterDimension = async (options) => {
+export async function createMasterDimension(options) {
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.info('Create master dimension');
@@ -254,6 +254,4 @@ const createMasterDimension = async (options) => {
} catch (err) {
catchLog('Error creating master dimension', err);
}
-};
-
-export default createMasterDimension;
+}
diff --git a/src/lib/cmd/qseow/createuseractivitycp.js b/src/lib/cmd/qseow/createuseractivitycp.js
index 079394d..b1ab8a9 100644
--- a/src/lib/cmd/qseow/createuseractivitycp.js
+++ b/src/lib/cmd/qseow/createuseractivitycp.js
@@ -1,6 +1,6 @@
import axios from 'axios';
-import { logger, setLoggingLevel, isPkg, execPath, sleep } from '../../../globals.js';
+import { logger, setLoggingLevel, isSea, execPath, sleep } from '../../../globals.js';
import { setupQrsConnection } from '../../util/qseow/qrs.js';
import { catchLog } from '../../util/log.js';
import {
@@ -68,7 +68,7 @@ export async function createUserActivityBucketsCustomProperty(options) {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.info('== Step 1: Create custom property for tracking user activity in QMC');
@@ -123,18 +123,30 @@ export async function createUserActivityBucketsCustomProperty(options) {
// Update existing custom property
// First copy existing custom property to a new object, then replace the choiceValues with the new ones
const customPropertyDefinition = JSON.parse(JSON.stringify(customPropertyExisting));
+
+ // Add activity buckets to custom property definition as choice values
customPropertyDefinition.choiceValues = activityBucketsSorted;
- const result = await updateCustomProperty(options, customPropertyDefinition);
- if (result) {
- logger.verbose(
- ` Updated existing custom property "${options.customPropertyName}" with new allowed values passed in via command line.`
+ // Add default bucket to choice values. This is the last one in the list + "+" sign
+ customPropertyDefinition.choiceValues.push(`${activityBucketsSorted[activityBucketsSorted.length - 1]}+`);
+
+ // Is it a dry run?
+ if (options.dryRun) {
+ logger.info(
+ `(${importCount}/${importLimit}) Dry run: Would have updated custom property "${options.customPropertyName}" with activity info`
);
} else {
- logger.error(
- `Failed to update existing custom property "${options.customPropertyName}" with new allowed values.`
- );
- return false;
+ const result = await updateCustomProperty(options, customPropertyDefinition);
+ if (result) {
+ logger.verbose(
+ ` Updated existing custom property "${options.customPropertyName}" with new allowed values passed in via command line.`
+ );
+ } else {
+ logger.error(
+ `Failed to update existing custom property "${options.customPropertyName}" with new allowed values.`
+ );
+ return false;
+ }
}
} else {
// Don't force overwrite the existing custom property.
@@ -163,14 +175,23 @@ export async function createUserActivityBucketsCustomProperty(options) {
const customPropertyDefinition = JSON.parse(JSON.stringify(customPropertyExisting));
customPropertyDefinition.choiceValues = activityBucketsSorted;
- const result = await updateCustomProperty(options, customPropertyDefinition);
- if (result) {
- logger.verbose(
- ` Updated existing custom property "${options.customPropertyName}" with new allowed values passed in via command line.`
+ // Is it a dry run?
+ if (options.dryRun) {
+ logger.info(
+ `(${importCount}/${importLimit}) Dry run: Would have updated custom property "${options.customPropertyName}" to have new activity bucket values.`
);
} else {
- logger.error(`Failed to update existing custom property "${options.customPropertyName}" with new allowed values.`);
- return false;
+ const result = await updateCustomProperty(options, customPropertyDefinition);
+ if (result) {
+ logger.verbose(
+ ` Updated existing custom property "${options.customPropertyName}" with new allowed values passed in via command line.`
+ );
+ } else {
+ logger.error(
+ `Failed to update existing custom property "${options.customPropertyName}" with new allowed values.`
+ );
+ return false;
+ }
}
}
}
@@ -187,12 +208,17 @@ export async function createUserActivityBucketsCustomProperty(options) {
choiceValues: activityBucketsSorted,
};
- const result = await createCustomProperty(options, customPropertyDefinition);
- if (result) {
- logger.verbose(` Created custom property "${options.customPropertyName}"`);
+ // Is it a dry run?
+ if (options.dryRun) {
+ logger.info(`(${importCount}/${importLimit}) Dry run: Would have created custom property "${options.customPropertyName}"`);
} else {
- logger.error(`Failed to create custom property "${options.customPropertyName}"`);
- return false;
+ const result = await createCustomProperty(options, customPropertyDefinition);
+ if (result) {
+ logger.verbose(` Created custom property "${options.customPropertyName}"`);
+ } else {
+ logger.error(`Failed to create custom property "${options.customPropertyName}"`);
+ return false;
+ }
}
}
@@ -436,6 +462,11 @@ export async function createUserActivityBucketsCustomProperty(options) {
break;
}
}
+
+ // If user is not assigned to a bucket, assign to the default bucket (last one in the list + "+" sign)
+ if (!user.activityBucket) {
+ user.activityBucket = `${activityBucketsSorted[activityBucketsSorted.length - 1]}+`;
+ }
}
logger.verbose(` Assigned activity buckets to users via custom property ${options.customPropertyName}`);
@@ -554,23 +585,32 @@ export async function createUserActivityBucketsCustomProperty(options) {
// Loop over the users in the batch, writing the user activity custom property to QRS
for (const user of usersBatch) {
- // Payload: array of user objects
- const axiosConfig = setupQrsConnection(options, {
- method: 'put',
- path: `qrs/user/${user.id}`,
- body: user,
- });
-
- const result = await axios.request(axiosConfig);
- if (result.status === 200) {
+ // Is it a dry run?
+ if (options.dryRun) {
logger.info(
- ` Updated user ${userCounter} of ${outputUserArray.length}, "${user.userDirectory}\\${user.userId}" in batch ${
+ `(${importCount}/${importLimit}) Dry run: Would have updated ${userCounter} of ${outputUserArray.length}, "${user.userDirectory}\\${user.userId}" in batch ${
i + 1
} of ${totalBatches}`
);
} else {
- logger.error(`Error ${result.status} updating user activity custom property for batch ${i + 1} of ${totalBatches}`);
- return false;
+ // Payload: array of user objects
+ const axiosConfig = setupQrsConnection(options, {
+ method: 'put',
+ path: `qrs/user/${user.id}`,
+ body: user,
+ });
+
+ const result = await axios.request(axiosConfig);
+ if (result.status === 200) {
+ logger.info(
+ ` Updated user ${userCounter} of ${outputUserArray.length}, "${user.userDirectory}\\${user.userId}" in batch ${
+ i + 1
+ } of ${totalBatches}`
+ );
+ } else {
+ logger.error(`Error ${result.status} updating user activity custom property for batch ${i + 1} of ${totalBatches}`);
+ return false;
+ }
}
userCounter++;
diff --git a/src/lib/cmd/qseow/deletedim.js b/src/lib/cmd/qseow/deletedim.js
index f2d6f98..3ee687d 100644
--- a/src/lib/cmd/qseow/deletedim.js
+++ b/src/lib/cmd/qseow/deletedim.js
@@ -1,6 +1,6 @@
import enigma from 'enigma.js';
import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js';
-import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js';
+import { logger, setLoggingLevel, isSea, execPath } from '../../../globals.js';
import { catchLog } from '../../util/log.js';
// Variable to keep track of how many dimensions have been deleted
@@ -10,12 +10,12 @@ let deleteCount = 0;
*
* @param {*} options
*/
-const deleteMasterDimension = async (options) => {
+export async function deleteMasterDimension(options) {
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.info('Delete master dimensions');
@@ -154,6 +154,4 @@ const deleteMasterDimension = async (options) => {
} catch (err) {
catchLog('Error in deleteMasterDimension', err);
}
-};
-
-export default deleteMasterDimension;
+}
diff --git a/src/lib/cmd/qseow/deletemeasure.js b/src/lib/cmd/qseow/deletemeasure.js
index d7e6991..f7a7770 100644
--- a/src/lib/cmd/qseow/deletemeasure.js
+++ b/src/lib/cmd/qseow/deletemeasure.js
@@ -1,6 +1,6 @@
import enigma from 'enigma.js';
import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js';
-import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js';
+import { logger, setLoggingLevel, isSea, execPath } from '../../../globals.js';
import { catchLog } from '../../util/log.js';
// Variable to keep track of how many measures have been deleted
@@ -10,12 +10,12 @@ let deleteCount = 0;
*
* @param {*} options
*/
-const deleteMasterMeasure = async (options) => {
+export async function deleteMasterMeasure(options) {
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.info('Delete master measures');
@@ -85,7 +85,6 @@ const deleteMasterMeasure = async (options) => {
deleteMasterItems = deleteMasterItems.concat(measureObj.qMeasureList.qItems);
} else {
// Loop over all master items (identified by name or ID) we should get data for
- // eslint-disable-next-line no-restricted-syntax
for (const masterItem of options.masterItem) {
// Can we find this master item in the list retrieved from the app?
if (options.idType === 'name') {
@@ -115,10 +114,8 @@ const deleteMasterMeasure = async (options) => {
if (deleteMasterItems.length === 0) {
logger.warn(`No matching master item measures found`);
} else {
- // eslint-disable-next-line no-restricted-syntax
for (const item of deleteMasterItems) {
if (options.dryRun === undefined || options.dryRun === false) {
- // eslint-disable-next-line no-await-in-loop
const res = await app.destroyMeasure(item.qInfo.qId);
if (res !== true) {
logger.error(`Failed deleting measure "${item.qMeta.title}", id=${item.qInfo.qId} in app "${item.qInfo.qId}"`);
@@ -148,6 +145,4 @@ const deleteMasterMeasure = async (options) => {
} catch (err) {
catchLog('Error in deleteMasterMeasure', err);
}
-};
-
-export default deleteMasterMeasure;
+}
diff --git a/src/lib/cmd/qseow/deletesessions.js b/src/lib/cmd/qseow/deletesessions.js
index 417d76b..83c0c8d 100644
--- a/src/lib/cmd/qseow/deletesessions.js
+++ b/src/lib/cmd/qseow/deletesessions.js
@@ -1,17 +1,17 @@
import { deleteSessionsFromQSEoWIds } from '../../util/qseow/session.js';
-import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js';
+import { logger, setLoggingLevel, isSea, execPath } from '../../../globals.js';
import { catchLog } from '../../util/log.js';
/**
* Delete Qlik Sense proxy sessions
* @param {object} options - Options object
*/
-const deleteSessions = async (options) => {
+export async function deleteSessions(options) {
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.info('Delete Qlik Sense proxy sessions');
@@ -31,6 +31,4 @@ const deleteSessions = async (options) => {
return false;
}
-};
-
-export default deleteSessions;
+}
diff --git a/src/lib/cmd/qseow/deletevariable.js b/src/lib/cmd/qseow/deletevariable.js
index d5f1f78..6611539 100644
--- a/src/lib/cmd/qseow/deletevariable.js
+++ b/src/lib/cmd/qseow/deletevariable.js
@@ -2,19 +2,19 @@ import enigma from 'enigma.js';
import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js';
import { getApps } from '../../util/qseow/app.js';
-import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js';
+import { logger, setLoggingLevel, isSea, execPath } from '../../../globals.js';
import { catchLog } from '../../util/log.js';
/**
*
* @param {*} options
*/
-const deleteVariable = async (options) => {
+export async function deleteVariable(options) {
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.debug(`Options: ${JSON.stringify(options, null, 2)}`);
@@ -183,5 +183,3 @@ const deleteVariable = async (options) => {
catchLog('Error in deleteVariable', err);
}
};
-
-export default deleteVariable;
diff --git a/src/lib/cmd/qseow/exportapp.js b/src/lib/cmd/qseow/exportapp.js
index 8a13fc9..bf41ab6 100644
--- a/src/lib/cmd/qseow/exportapp.js
+++ b/src/lib/cmd/qseow/exportapp.js
@@ -3,16 +3,16 @@ import fs from 'node:fs';
import path from 'node:path';
import yesno from 'yesno';
-import { logger, setLoggingLevel, isPkg, execPath, mergeDirFilePath, verifyFileExists, isNumeric, sleep } from '../../../globals.js';
-import QlikSenseApps from '../../app/class_allapps.js';
+import { logger, setLoggingLevel, isSea, execPath, mergeDirFilePath, verifyFileSystemExists, sleep } from '../../../globals.js';
+import { QlikSenseApps } from '../../app/class_allapps.js';
import { catchLog } from '../../util/log.js';
-const exportAppToFile = async (options) => {
+export async function exportAppToFile(options) {
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
// Verify output directory exists. Create if not.
@@ -23,23 +23,9 @@ const exportAppToFile = async (options) => {
await fs.promises.mkdir(outputDir);
}
- // const appFileExists = await verifyFileExists(options.fileName);
- // if (appFileExists === false) {
- // logger.error(`Missing apps definition file "${options.fileName}". Aborting`);
- // process.exit(1);
- // } else {
- // logger.verbose(`Apps definition file "${options.fileName}" found`);
- // }
-
logger.info(`Export apps to directory "${outputDir}"`);
logger.debug(`Options: ${JSON.stringify(options, null, 2)}`);
- // Get all tags
- // const tagsExisting = await getTagsFromQseow(options);
-
- // Get all custom properties
- // const cpExisting = await getCustomPropertiesFromQseow(options);
-
// Set up app structure
const qlikSenseApps = new QlikSenseApps();
await qlikSenseApps.init(options);
@@ -73,39 +59,39 @@ const exportAppToFile = async (options) => {
logger.info(`Number of apps to export: ${appsToExport.length}`);
let exportCount = 0;
- // eslint-disable-next-line no-restricted-syntax
for (const app of appsToExport) {
- // eslint-disable-next-line no-await-in-loop
- const exportAppData = await qlikSenseApps.exportAppStep1(app);
-
- // eslint-disable-next-line no-await-in-loop
- const resultDownloadApp = await qlikSenseApps.exportAppStep2(exportAppData);
-
- // eslint-disable-next-line no-await-in-loop
- await sleep(options.sleepAppExport);
-
- // keep track of app metadata
- appCounter += 1;
- appMetadata.push([
- appCounter,
- app.name,
- app.id,
- options.outputDir,
- resultDownloadApp.qvfFileName,
- options.excludeAppData,
- app.tags.map((item) => item.name).join(' / '),
- app.customProperties.map((item) => `${item.definition.name}=${item.value}`).join(' / '),
- app.owner.userDirectory,
- app.owner.userId,
- app.stream ? app.stream.name : '',
- ]);
-
- exportCount += 1;
- if (exportCount === parseInt(options.limitExportCount, 10)) {
- logger.warn(
- `Exported ${options.limitExportCount} app(s), which is the limit set by the --limit-export-count parameter.`
- );
- break;
+ try {
+ appCounter += 1;
+ const exportAppData = await qlikSenseApps.exportAppStep1(app);
+
+ const resultDownloadApp = await qlikSenseApps.exportAppStep2(exportAppData, appCounter, appsToExport.length);
+
+ await sleep(options.sleepAppExport);
+
+ // keep track of app metadata
+ appMetadata.push([
+ appCounter,
+ app.name,
+ app.id,
+ options.outputDir,
+ resultDownloadApp.qvfFileName,
+ options.excludeAppData,
+ app.tags.map((item) => item.name).join(' / '),
+ app.customProperties.map((item) => `${item.definition.name}=${item.value}`).join(' / '),
+ app.owner.userDirectory,
+ app.owner.userId,
+ app.stream ? app.stream.name : '',
+ ]);
+
+ exportCount += 1;
+ if (exportCount === parseInt(options.limitExportCount, 10)) {
+ logger.warn(
+ `Exported ${options.limitExportCount} app(s), which is the limit set by the --limit-export-count parameter.`
+ );
+ break;
+ }
+ } catch (error) {
+ logger.error(`Failed to export app ${app.name} (${app.id}): ${error.message}`);
}
}
@@ -121,7 +107,7 @@ const exportAppToFile = async (options) => {
// Check if app metadata file already exists
// 2nd parameter = true => don't output anything to log
- const fileExists = await verifyFileExists(fileName, true);
+ const fileExists = await verifyFileSystemExists(fileName, true);
logger.info('------------------------------------');
@@ -155,6 +141,4 @@ const exportAppToFile = async (options) => {
} catch (err) {
catchLog('Export app', err);
}
-};
-
-export default exportAppToFile;
+}
diff --git a/src/lib/cmd/qseow/getbookmark.js b/src/lib/cmd/qseow/getbookmark.js
index 3a43199..36836f3 100644
--- a/src/lib/cmd/qseow/getbookmark.js
+++ b/src/lib/cmd/qseow/getbookmark.js
@@ -1,7 +1,7 @@
import enigma from 'enigma.js';
import { table } from 'table';
import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js';
-import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js';
+import { logger, setLoggingLevel, isSea, execPath } from '../../../globals.js';
import { catchLog } from '../../util/log.js';
const consoleTableConfig = {
@@ -37,14 +37,14 @@ const consoleTableConfig = {
*
* @param {*} options
*/
-const getBookmark = async (options) => {
+export async function getBookmark(options) {
let session;
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.info('Get bookmarks');
@@ -217,6 +217,4 @@ const getBookmark = async (options) => {
}
return false;
}
-};
-
-export default getBookmark;
+}
diff --git a/src/lib/cmd/qseow/getdim.js b/src/lib/cmd/qseow/getdim.js
index 8f8e7f4..f7b2bed 100644
--- a/src/lib/cmd/qseow/getdim.js
+++ b/src/lib/cmd/qseow/getdim.js
@@ -2,7 +2,7 @@ import enigma from 'enigma.js';
import { table } from 'table';
import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js';
-import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js';
+import { logger, setLoggingLevel, isSea, execPath } from '../../../globals.js';
import { catchLog } from '../../util/log.js';
const consoleTableConfig = {
@@ -39,12 +39,12 @@ const consoleTableConfig = {
*
* @param {*} options
*/
-const getMasterDimension = async (options) => {
+export async function getMasterDimension(options) {
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.info('Get master dimensions');
@@ -267,6 +267,4 @@ const getMasterDimension = async (options) => {
} catch (err) {
catchLog(`Error getting master dimensions`, err);
}
-};
-
-export default getMasterDimension;
+}
diff --git a/src/lib/cmd/qseow/getmeasure.js b/src/lib/cmd/qseow/getmeasure.js
index 589f733..8724f6d 100644
--- a/src/lib/cmd/qseow/getmeasure.js
+++ b/src/lib/cmd/qseow/getmeasure.js
@@ -2,7 +2,7 @@ import enigma from 'enigma.js';
import { table } from 'table';
import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js';
-import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js';
+import { logger, setLoggingLevel, isSea, execPath } from '../../../globals.js';
import { catchLog } from '../../util/log.js';
const consoleTableConfig = {
@@ -38,12 +38,12 @@ const consoleTableConfig = {
*
* @param {*} options
*/
-const getMasterMeasure = async (options) => {
+export async function getMasterMeasure(options) {
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.info('Get master measures');
@@ -242,6 +242,4 @@ const getMasterMeasure = async (options) => {
} catch (err) {
catchLog('Error in getMasterMeasure', err);
}
-};
-
-export default getMasterMeasure;
+}
diff --git a/src/lib/cmd/qseow/getscript.js b/src/lib/cmd/qseow/getscript.js
index 34f4822..111712b 100644
--- a/src/lib/cmd/qseow/getscript.js
+++ b/src/lib/cmd/qseow/getscript.js
@@ -1,6 +1,6 @@
import enigma from 'enigma.js';
-import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js';
+import { logger, setLoggingLevel, isSea, execPath } from '../../../globals.js';
import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js';
import { catchLog } from '../../util/log.js';
@@ -8,12 +8,12 @@ import { catchLog } from '../../util/log.js';
*
* @param {*} options
*/
-const getScript = async (options) => {
+export async function getScript(options) {
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.verbose('Get app script');
@@ -92,6 +92,4 @@ const getScript = async (options) => {
} catch (err) {
catchLog('Error in getScript', err);
}
-};
-
-export default getScript;
+}
diff --git a/src/lib/cmd/qseow/getsessions.js b/src/lib/cmd/qseow/getsessions.js
index f0b4faf..d41e5e5 100644
--- a/src/lib/cmd/qseow/getsessions.js
+++ b/src/lib/cmd/qseow/getsessions.js
@@ -1,6 +1,6 @@
import { table } from 'table';
import { getSessionsFromQseow } from '../../util/qseow/session.js';
-import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js';
+import { logger, setLoggingLevel, isSea, execPath } from '../../../globals.js';
import { catchLog } from '../../util/log.js';
const consoleTableConfig = {
@@ -36,12 +36,12 @@ const consoleTableConfig = {
*
* @param {*} options
*/
-const getSessions = async (options) => {
+export async function getSessions(options) {
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.info('Get Qlik Sense proxy sessions');
@@ -160,7 +160,6 @@ const getSessions = async (options) => {
}
// Add to table that will be printed to console
- // eslint-disable-next-line no-restricted-syntax
for (const s of sessionsTabular) {
sessionsTable.push([
s.vpDescription,
@@ -188,6 +187,4 @@ const getSessions = async (options) => {
return false;
}
-};
-
-export default getSessions;
+}
diff --git a/src/lib/cmd/qseow/gettask.js b/src/lib/cmd/qseow/gettask.js
index 4869910..0617001 100644
--- a/src/lib/cmd/qseow/gettask.js
+++ b/src/lib/cmd/qseow/gettask.js
@@ -4,8 +4,8 @@ import { promises as Fs } from 'node:fs';
import xlsx from 'node-xlsx';
import { stringify } from 'csv-stringify';
import yesno from 'yesno';
-import { logger, setLoggingLevel, isPkg, execPath, verifyFileExists } from '../../../globals.js';
-import QlikSenseTasks from '../../task/class_alltasks.js';
+import { logger, setLoggingLevel, isSea, execPath, verifyFileSystemExists } from '../../../globals.js';
+import { QlikSenseTasks } from '../../task/class_alltasks.js';
import { mapEventType, mapIncrementOption, mapDaylightSavingTime, mapRuleState } from '../../util/qseow/lookups.js';
import { getTagsFromQseow } from '../../util/qseow/tag.js';
import { catchLog } from '../../util/log.js';
@@ -85,12 +85,12 @@ function compareTable(a, b) {
// get-task command
// Options are assumed to be verified before calling this function
-const getTask = async (options) => {
+export async function getTask(options) {
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.verbose('Get tasks');
@@ -257,7 +257,7 @@ const getTask = async (options) => {
}
// Check if file exists
- if ((await verifyFileExists(options.outputFileName, true)) === false) {
+ if ((await verifyFileSystemExists(options.outputFileName, true)) === false) {
// File doesn't exist
} else if (!options.outputFileOverwrite) {
// Target file exist. Ask if user wants to overwrite
@@ -746,7 +746,7 @@ const getTask = async (options) => {
}
// Check if file exists
- if ((await verifyFileExists(options.outputFileName, true)) === false) {
+ if ((await verifyFileSystemExists(options.outputFileName, true)) === false) {
// File doesn't exist
} else if (!options.outputFileOverwrite) {
// Target file exist. Ask if user wants to overwrite
@@ -773,6 +773,4 @@ const getTask = async (options) => {
catchLog('Get task', err);
return false;
}
-};
-
-export default getTask;
+}
diff --git a/src/lib/cmd/qseow/getvariable.js b/src/lib/cmd/qseow/getvariable.js
index 3ca271b..4ae9c61 100644
--- a/src/lib/cmd/qseow/getvariable.js
+++ b/src/lib/cmd/qseow/getvariable.js
@@ -3,7 +3,7 @@ import enigma from 'enigma.js';
import { table } from 'table';
import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js';
import { getApps } from '../../util/qseow/app.js';
-import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js';
+import { logger, setLoggingLevel, isSea, execPath } from '../../../globals.js';
import { catchLog } from '../../util/log.js';
const consoleTableConfig = {
@@ -39,12 +39,12 @@ const consoleTableConfig = {
*
* @param {*} options
*/
-const getVariable = async (options) => {
+export async function getVariable(options) {
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.debug(`Options: ${JSON.stringify(options, null, 2)}`);
@@ -279,6 +279,4 @@ const getVariable = async (options) => {
} catch (err) {
catchLog(`Error in getVariable`, err);
}
-};
-
-export default getVariable;
+}
diff --git a/src/lib/cmd/qseow/import-masteritem-excel.js b/src/lib/cmd/qseow/import-masteritem-excel.js
index 95c1f75..cf65f98 100644
--- a/src/lib/cmd/qseow/import-masteritem-excel.js
+++ b/src/lib/cmd/qseow/import-masteritem-excel.js
@@ -2,7 +2,7 @@ import enigma from 'enigma.js';
import xlsx from 'node-xlsx';
import { v4 as uuidCreate } from 'uuid';
-import { logger, setLoggingLevel, isPkg, execPath, verifyFileExists, sleep } from '../../../globals.js';
+import { logger, setLoggingLevel, isSea, execPath, verifyFileSystemExists, sleep } from '../../../globals.js';
import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js';
import { catchLog } from '../../util/log.js';
@@ -205,7 +205,7 @@ const updateDimension = async (
[existingColorMapModel] = await Promise.all([existingColorMapPromise]);
existingColorMapLayout = await existingColorMapModel.getLayout();
} catch (err) {
- catchLog(`No per-value color map exists for existing dimension "${existingDimensionLayout.qMeta.title}"`, err);
+ logger.verbose(`No per-value color map exists for existing dimension "${existingDimensionLayout.qMeta.title}"`);
}
// Do we have new per-value color data?
@@ -960,14 +960,14 @@ const importMasterItemFromExcel = async (options) => {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.info(`Import master items from definitions in Excel file "${options.file}"`);
logger.debug(`Options: ${JSON.stringify(options, null, 2)}`);
// Verify Master items Excel file exists
- const excelFileExists = await verifyFileExists(options.file);
+ const excelFileExists = await verifyFileSystemExists(options.file);
if (excelFileExists === false) {
logger.error(`Missing master item Excel file ${options.file}. Aborting`);
process.exit(1);
@@ -1141,11 +1141,9 @@ const importMasterItemFromExcel = async (options) => {
}
};
-const importMasterItemFromFile = async (options) => {
+export async function importMasterItemFromFile(options) {
if (options.fileType === 'excel') {
// Source file type is Excel
await importMasterItemFromExcel(options);
}
-};
-
-export default importMasterItemFromFile;
+}
diff --git a/src/lib/cmd/qseow/importapp.js b/src/lib/cmd/qseow/importapp.js
index 9ff5830..bea1143 100644
--- a/src/lib/cmd/qseow/importapp.js
+++ b/src/lib/cmd/qseow/importapp.js
@@ -1,18 +1,18 @@
import xlsx from 'node-xlsx';
-import { logger, setLoggingLevel, isPkg, execPath, verifyFileExists, isNumeric } from '../../../globals.js';
-import QlikSenseApps from '../../app/class_allapps.js';
+import { logger, setLoggingLevel, isSea, execPath, verifyFileSystemExists } from '../../../globals.js';
+import { QlikSenseApps } from '../../app/class_allapps.js';
import { getAppColumnPosFromHeaderRow } from '../../util/qseow/lookups.js';
import { getTagsFromQseow } from '../../util/qseow/tag.js';
import { getCustomPropertiesFromQseow } from '../../util/qseow/customproperties.js';
import { catchLog } from '../../util/log.js';
-const importAppFromFile = async (options) => {
+export async function importAppFromFile(options) {
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.info(`Import apps from definitions in file "${options.fileName}"`);
@@ -26,7 +26,7 @@ const importAppFromFile = async (options) => {
logger.info(`Successfully retrieved ${cpExisting.length} custom properties from QSEoW`);
// Verify file exists
- const appFileExists = await verifyFileExists(options.fileName);
+ const appFileExists = await verifyFileSystemExists(options.fileName);
if (appFileExists === false) {
logger.error(`Missing apps definition file "${options.fileName}". Aborting`);
process.exit(1);
@@ -85,6 +85,4 @@ const importAppFromFile = async (options) => {
catchLog('IMPORT APP', err);
return false;
}
-};
-
-export default importAppFromFile;
+}
diff --git a/src/lib/cmd/qseow/importtask.js b/src/lib/cmd/qseow/importtask.js
index ff047cd..3fc288e 100644
--- a/src/lib/cmd/qseow/importtask.js
+++ b/src/lib/cmd/qseow/importtask.js
@@ -2,9 +2,9 @@ import xlsx from 'node-xlsx';
import { parse } from 'csv-parse';
import fs from 'node:fs';
-import { logger, setLoggingLevel, isPkg, execPath, verifyFileExists, isNumeric } from '../../../globals.js';
-import QlikSenseTasks from '../../task/class_alltasks.js';
-import QlikSenseApps from '../../app/class_allapps.js';
+import { logger, setLoggingLevel, isSea, execPath, verifyFileSystemExists, isNumeric } from '../../../globals.js';
+import { QlikSenseTasks } from '../../task/class_alltasks.js';
+import { QlikSenseApps } from '../../app/class_allapps.js';
import { getTaskColumnPosFromHeaderRow } from '../../util/qseow/lookups.js';
import { getTagsFromQseow } from '../../util/qseow/tag.js';
import { getCustomPropertiesFromQseow } from '../../util/qseow/customproperties.js';
@@ -299,12 +299,12 @@ const processCsvFile = async (options) => {
return records;
};
-const importTaskFromFile = async (options) => {
+export async function importTaskFromFile(options) {
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.info(`Import tasks from definitions in file "${options.fileName}"`);
@@ -318,7 +318,7 @@ const importTaskFromFile = async (options) => {
logger.info(`Successfully retrieved ${cpExisting.length} custom properties from QSEoW`);
// Verify task definitions file exists
- const taskFileExists = await verifyFileExists(options.fileName);
+ const taskFileExists = await verifyFileSystemExists(options.fileName);
if (taskFileExists === false) {
logger.error(`Missing task file "${options.fileName}". Aborting`);
process.exit(1);
@@ -415,6 +415,4 @@ const importTaskFromFile = async (options) => {
} catch (err) {
catchLog('IMPORT TASK 2', err);
}
-};
-
-export default importTaskFromFile;
+}
diff --git a/src/lib/cmd/qseow/scramblefield.js b/src/lib/cmd/qseow/scramblefield.js
index babeff2..d1bb6e0 100644
--- a/src/lib/cmd/qseow/scramblefield.js
+++ b/src/lib/cmd/qseow/scramblefield.js
@@ -1,18 +1,18 @@
import enigma from 'enigma.js';
import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js';
-import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js';
+import { logger, setLoggingLevel, isSea, execPath } from '../../../globals.js';
import { catchLog } from '../../util/log.js';
/**
*
* @param {*} options
*/
-const scrambleField = async (options) => {
+export async function scrambleField(options) {
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.info('Scramble field');
@@ -89,6 +89,4 @@ const scrambleField = async (options) => {
} catch (err) {
catchLog('Error in scrambleField', err);
}
-};
-
-export default scrambleField;
+}
diff --git a/src/lib/cmd/qseow/settaskcp.js b/src/lib/cmd/qseow/settaskcp.js
index ea3a3fe..ad50e35 100644
--- a/src/lib/cmd/qseow/settaskcp.js
+++ b/src/lib/cmd/qseow/settaskcp.js
@@ -138,7 +138,7 @@ const updateTask = async (options, customPropertyDef, task) =>
}
});
-const setTaskCustomProperty = async (options) => {
+export async function setTaskCustomProperty(options) {
try {
// == Meta code ==
// - Get definition for the custom property that should be updated
@@ -201,6 +201,4 @@ const setTaskCustomProperty = async (options) => {
catchLog(`SET RELOAD TASK CP`, err);
return false;
}
-};
-
-export default setTaskCustomProperty;
+}
diff --git a/src/lib/cmd/qseow/testconnection.js b/src/lib/cmd/qseow/testconnection.js
index 21b0ddb..403c91f 100644
--- a/src/lib/cmd/qseow/testconnection.js
+++ b/src/lib/cmd/qseow/testconnection.js
@@ -1,13 +1,13 @@
-import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js';
-import getAboutFromQseow from '../../util/qseow/about.js';
+import { logger, setLoggingLevel, isSea, execPath } from '../../../globals.js';
+import { getAboutFromQseow } from '../../util/qseow/about.js';
import { catchLog } from '../../util/log.js';
-const testConnection = async (options) => {
+export async function testConnection(options) {
try {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.info(`Testing connection to Qlik Sense server ${options.host} on port ${options.port}`);
@@ -30,6 +30,4 @@ const testConnection = async (options) => {
logger.error(`EXPORT APP: ${err.stack}`);
return false;
}
-};
-
-export default testConnection;
+}
diff --git a/src/lib/cmd/qseow/vistask.js b/src/lib/cmd/qseow/vistask.js
index d11b58c..a13f0bb 100644
--- a/src/lib/cmd/qseow/vistask.js
+++ b/src/lib/cmd/qseow/vistask.js
@@ -3,8 +3,10 @@ import path from 'node:path';
import fs from 'node:fs';
import handlebars from 'handlebars';
import { Readable } from 'node:stream';
-import { appVersion, logger, setLoggingLevel, isPkg, execPath, verifyFileExists } from '../../../globals.js';
-import QlikSenseTasks from '../../task/class_alltasks.js';
+import sea from 'node:sea';
+
+import { appVersion, logger, setLoggingLevel, isSea, execPath, verifyFileSystemExists, verifySeaAssetExists } from '../../../globals.js';
+import { QlikSenseTasks } from '../../task/class_alltasks.js';
// js: 'application/javascript',
const MIME_TYPES = {
@@ -20,12 +22,11 @@ const MIME_TYPES = {
// Get path to static html files
let STATIC_PATH = '';
-if (isPkg) {
- // Running as standalone app
- STATIC_PATH = path.join(__dirname, 'src/static');
- // STATIC_PATH = path.resolve(`${__dirname}/../../static`);
+if (isSea) {
+ // Running as standalone SEA app
+ STATIC_PATH = '';
} else {
- // Running packaged app
+ // Running Node.js script
STATIC_PATH = path.join(execPath, './src/static');
}
@@ -152,39 +153,87 @@ function getSchemaText(incrementOption, incrementDescription) {
}
const prepareFile = async (url) => {
+ logger.verbose('-----------------------------------');
+ logger.verbose(`==> Preparing file for url: ${url}`);
+
const paths = [STATIC_PATH, url];
if (url.endsWith('/')) paths.push('index.html');
- const filePath = path.join(...paths);
+ logger.verbose(`Paths: ${paths}`);
+
+ let filePath = path.join(...paths);
logger.verbose(`Serving file ${filePath}`);
const pathTraversal = !filePath.startsWith(STATIC_PATH);
logger.verbose(`Path traversal: ${pathTraversal}`);
- // Verify file filePath exists. Do not use fs.access() as it does not work with pkg.
- // let exists = false;
- // try {
- // await fs.promises.stat(filePath);
- // exists = true;
- // } catch (error) {
- // logger.verbose(`File does not exist: ${filePath}`);
- // }
- // const exists = await fs.promises.access(filePath).then(...toBool);
- const exists = await verifyFileExists(filePath);
- logger.verbose(`File exists: ${exists}`);
-
- const found = !pathTraversal && exists;
- logger.verbose(`File found: ${found}`);
-
- const streamPath = found ? filePath : `${STATIC_PATH}/404.html`;
- logger.verbose(`Stream path: ${streamPath}`);
+ let exists, streamPath, ext, stream;
+
+ if (isSea) {
+ // Prepend with STATIC_PATH
+ logger.verbose(`url: ${url}`);
+ logger.verbose(`STATIC_PATH: ${STATIC_PATH}`);
+ logger.verbose(`filePath: ${filePath}`);
+ try {
+ exists = verifySeaAssetExists(filePath);
+ logger.verbose(`SEA file exists: ${exists}`);
+ if (exists) {
+ ext = path.extname(filePath).substring(1).toLowerCase();
+ logger.verbose(`SEA file extension: ${ext}`);
+
+ if (ext === 'html' || ext === 'js' || ext === 'css') {
+ const asset = sea.getAsset(filePath, 'utf8');
+ logger.verbose(`Asset type: ${typeof asset}`);
+
+ stream = Readable.from([asset]);
+ } else if (ext === 'png' || ext === 'jpg' || ext === 'gif' || ext === 'ico') {
+ let asset = sea.getAsset(filePath);
+
+ if (asset instanceof ArrayBuffer) {
+ asset = Buffer.from(asset);
+ }
+ stream = Readable.from([asset]);
+ } else {
+ logger.warn(`File extension not supported: ${ext}`);
+ exists = false;
+ }
+
+ streamPath = filePath;
+ } else {
+ logger.error(`Asset not found: ${filePath}`);
+ throw new Error('Asset not found');
+ }
+ } catch (error) {
+ logger.error(`Error while getting SEA asset: ${error}`);
+ exists = false;
+ streamPath = `${STATIC_PATH}/404.html`;
+ ext = 'html';
+ stream = Readable.from(['404 Not Found in SEA app
']);
+ }
+ } else {
+ exists = await verifyFileSystemExists(filePath);
+ logger.verbose(`File system file exists: ${exists}`);
+
+ streamPath = exists ? filePath : `${STATIC_PATH}/404.html`;
+ ext = path.extname(streamPath).substring(1).toLowerCase();
+ stream = fs.createReadStream(streamPath);
+ }
- const ext = path.extname(streamPath).substring(1).toLowerCase();
+ logger.verbose(`File exists (2): ${exists}`);
+ logger.verbose(`File found: ${exists && !pathTraversal}`);
+ logger.verbose(`Stream path: ${streamPath}`);
logger.verbose(`File extension: ${ext}`);
- let stream;
if (ext === 'html') {
- const file = await fs.promises.readFile(streamPath, 'utf8');
+ logger.verbose(`Serving html file ${streamPath}`);
+ let file;
+
+ if (!isSea) {
+ file = await fs.promises.readFile(streamPath, 'utf8');
+ } else if (isSea) {
+ file = sea.getAsset(streamPath, 'utf8');
+ }
+
const template = handlebars.compile(file, { noEscape: true });
// Get task network model
@@ -289,18 +338,61 @@ const prepareFile = async (url) => {
const result = template(templateData);
stream = Readable.from([result]);
- } else {
- stream = fs.createReadStream(streamPath);
+ } else if (ext === 'js' || ext === 'css' || ext === 'svg') {
+ logger.verbose(`Serving js, css or svg file ${streamPath}`);
+
+ let asset;
+ if (!isSea) {
+ asset = await fs.promises.readFile(streamPath, 'utf8');
+ } else if (isSea) {
+ asset = sea.getAsset(streamPath, 'utf8');
+ }
+
+ stream = Readable.from([asset]);
+ } else if (ext === 'png' || ext === 'jpg' || ext === 'gif' || ext === 'ico') {
+ logger.verbose(`Serving image file ${streamPath}`);
+
+ let asset;
+ if (!isSea) {
+ asset = await fs.promises.readFile(streamPath);
+ } else if (isSea) {
+ asset = sea.getAsset(streamPath);
+ }
+
+ if (asset instanceof ArrayBuffer) {
+ asset = Buffer.from(asset);
+
+ // Show new type of asset
+ if (asset instanceof Uint8Array) {
+ logger.verbose('asset is Uint8Array');
+ } else if (asset instanceof ArrayBuffer) {
+ logger.verbose('asset is ArrayBuffer');
+ } else if (asset instanceof Buffer) {
+ logger.verbose('asset is Buffer');
+ }
+ }
+
+ stream = Readable.from([asset]);
}
- return { found, ext, stream };
+ return { found: exists && !pathTraversal, ext, stream };
};
// Request handler for http server
const requestHandler = async (req, res) => {
const file = await prepareFile(req.url);
+
+ // console.log('File:');
+ // console.log(file);
+
+ logger.verbose(`File found: ${file.found}`);
+ logger.verbose(`File extension: ${file.ext}`);
+
const statusCode = file.found ? 200 : 404;
const mimeType = MIME_TYPES[file.ext] || MIME_TYPES.default;
+
+ logger.verbose(`Serving file ${req.url} with status code ${statusCode} and mime type ${mimeType}`);
+
res.writeHead(statusCode, { 'Content-Type': mimeType });
file.stream.pipe(res);
@@ -323,42 +415,63 @@ const startHttpServer = async (options) => {
});
};
-const visTask = async (options) => {
+export async function visTask(options) {
// Set log level
setLoggingLevel(options.logLevel);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.verbose('Visulise tasks');
logger.debug(`Options: ${JSON.stringify(options, null, 2)}`);
- // List all files in __dirname directory if log level is debug
logger.verbose(`Path to html files: ${STATIC_PATH}`);
- fs.readdir(STATIC_PATH, (err, files) => {
- if (err) {
- return logger.error(`Unable to scan html directory: ${err}`);
- }
- files.forEach((file) => {
- logger.debug(file);
- const stats = fs.statSync(`${STATIC_PATH}/${file}`);
- const fileSizeInBytes = stats.size;
- logger.debug(`File size: ${fileSizeInBytes}`);
- logger.debug('-------------------');
+
+ // List all files in __dirname directory if log level is debug and not SEA app
+ if (!isSea) {
+ fs.readdir(STATIC_PATH, (err, files) => {
+ if (err) {
+ return logger.error(`Unable to scan html directory: ${err}`);
+ }
+ files.forEach((file) => {
+ logger.debug(file);
+ const stats = fs.statSync(`${STATIC_PATH}/${file}`);
+ const fileSizeInBytes = stats.size;
+ logger.debug(`File size: ${fileSizeInBytes}`);
+ logger.debug('-------------------');
+ });
+
+ return true;
});
+ }
- return true;
- });
+ // NOTE: If running as SEA app, it is not possible to list files in the static directory
// Verify files used by http server exist
- let fileExists = await verifyFileExists(`${STATIC_PATH}/index.html`);
- if (!fileExists) {
+ logger.verbose(`Verifying that files used by http server exist`);
+ let fileExists;
+ if (isSea) {
+ fileExists = verifySeaAssetExists(`/index.html`);
+ } else {
+ fileExists = await verifyFileSystemExists(`${STATIC_PATH}/index.html`);
+ }
+ if (!fileExists && isSea) {
+ logger.error(`File /index.html does not exist`);
+ return false;
+ } else if (!fileExists && !isSea) {
logger.error(`File ${STATIC_PATH}/index.html does not exist`);
return false;
}
- fileExists = await verifyFileExists(`${STATIC_PATH}/404.html`);
- if (!fileExists) {
+ if (isSea) {
+ fileExists = verifySeaAssetExists(`/404.html`);
+ } else {
+ fileExists = await verifyFileSystemExists(`${STATIC_PATH}/404.html`);
+ }
+ if (!fileExists && isSea) {
+ logger.error(`File /404.html does not exist`);
+ return false;
+ } else if (!fileExists && !isSea) {
logger.error(`File ${STATIC_PATH}/404.html does not exist`);
return false;
}
@@ -395,6 +508,4 @@ const visTask = async (options) => {
startHttpServer(optionsNew);
return true;
-};
-
-export default visTask;
+}
diff --git a/src/lib/task/class_allcompositeevents.js b/src/lib/task/class_allcompositeevents.js
index 7e7e1a1..39cb7c1 100644
--- a/src/lib/task/class_allcompositeevents.js
+++ b/src/lib/task/class_allcompositeevents.js
@@ -1,13 +1,13 @@
import axios from 'axios';
import path from 'node:path';
-import { logger, execPath, verifyFileExists } from '../../globals.js';
+import { logger } from '../../globals.js';
import { setupQrsConnection } from '../util/qseow/qrs.js';
-import QlikSenseCompositeEvent from './class_compositeevent.js';
+import { QlikSenseCompositeEvent } from './class_compositeevent.js';
import { catchLog } from '../util/log.js';
import { getCertFilePaths } from '../util/qseow/cert.js';
-class QlikSenseCompositeEvents {
+export class QlikSenseCompositeEvents {
constructor() {
//
}
@@ -46,11 +46,8 @@ class QlikSenseCompositeEvents {
try {
logger.debug('GET SCHEMAEVENT: Starting get composite events from QSEoW');
- const axiosConfig = await setupQrsConnection(this.options, {
+ const axiosConfig = setupQrsConnection(this.options, {
method: 'get',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: '/qrs/compositeevent/full',
});
@@ -79,5 +76,3 @@ class QlikSenseCompositeEvents {
});
}
}
-
-export default QlikSenseCompositeEvents;
diff --git a/src/lib/task/class_allschemaevents.js b/src/lib/task/class_allschemaevents.js
index 80007d9..fab1c2f 100644
--- a/src/lib/task/class_allschemaevents.js
+++ b/src/lib/task/class_allschemaevents.js
@@ -1,11 +1,11 @@
import axios from 'axios';
import { logger } from '../../globals.js';
import { setupQrsConnection } from '../util/qseow/qrs.js';
-import QlikSenseSchemaEvent from './class_schemaevent.js';
+import { QlikSenseSchemaEvent } from './class_schemaevent.js';
import { catchLog } from '../util/log.js';
import { getCertFilePaths } from '../util/qseow/cert.js';
-class QlikSenseSchemaEvents {
+export class QlikSenseSchemaEvents {
constructor() {
//
}
@@ -58,11 +58,8 @@ class QlikSenseSchemaEvents {
try {
logger.debug('GET SCHEMA EVENT: Starting get schema events from QSEoW');
- const axiosConfig = await setupQrsConnection(this.options, {
+ const axiosConfig = setupQrsConnection(this.options, {
method: 'get',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: '/qrs/schemaevent/full',
});
@@ -90,5 +87,3 @@ class QlikSenseSchemaEvents {
});
}
}
-
-export default QlikSenseSchemaEvents;
diff --git a/src/lib/task/class_alltasks.js b/src/lib/task/class_alltasks.js
index 2dccbde..4e35f26 100644
--- a/src/lib/task/class_alltasks.js
+++ b/src/lib/task/class_alltasks.js
@@ -12,9 +12,9 @@ import {
getTaskColumnPosFromHeaderRow,
} from '../util/qseow/lookups.js';
-import QlikSenseTask from './class_task.js';
-import QlikSenseSchemaEvents from './class_allschemaevents.js';
-import QlikSenseCompositeEvents from './class_allcompositeevents.js';
+import { QlikSenseTask } from './class_task.js';
+import { QlikSenseSchemaEvents } from './class_allschemaevents.js';
+import { QlikSenseCompositeEvents } from './class_allcompositeevents.js';
import { getTagIdByName } from '../util/qseow/tag.js';
import { getCustomPropertyIdByName } from '../util/qseow/customproperties.js';
import { getAppById } from '../util/qseow/app.js';
@@ -22,7 +22,7 @@ import { taskExistById, getTaskById } from '../util/qseow/task.js';
import { catchLog } from '../util/log.js';
import { getCertFilePaths } from '../util/qseow/cert.js';
-class QlikSenseTasks {
+export class QlikSenseTasks {
constructor() {
//
}
@@ -1271,9 +1271,6 @@ class QlikSenseTasks {
// Save task to QSEoW
const axiosConfig = setupQrsConnection(this.options, {
method: 'post',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: '/qrs/compositeevent',
body,
});
@@ -1340,9 +1337,6 @@ class QlikSenseTasks {
// Save task to QSEoW
const axiosConfig = setupQrsConnection(this.options, {
method: 'post',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: '/qrs/reloadtask/create',
body,
});
@@ -1402,9 +1396,6 @@ class QlikSenseTasks {
// Save task to QSEoW
const axiosConfig = setupQrsConnection(this.options, {
method: 'post',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: '/qrs/externalprogramtask/create',
body,
});
@@ -1466,9 +1457,6 @@ class QlikSenseTasks {
// Save task to QSEoW
const axiosConfig = setupQrsConnection(this.options, {
method: 'post',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: '/qrs/reloadtask/create',
body,
});
@@ -1572,17 +1560,11 @@ class QlikSenseTasks {
if (filter === '') {
axiosConfig = setupQrsConnection(this.options, {
method: 'get',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: '/qrs/reloadtask/full',
});
} else {
axiosConfig = setupQrsConnection(this.options, {
method: 'get',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: '/qrs/reloadtask/full',
queryParameters: [{ name: 'filter', value: filter }],
});
@@ -1602,17 +1584,11 @@ class QlikSenseTasks {
if (filter === '') {
axiosConfig = setupQrsConnection(this.options, {
method: 'get',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: '/qrs/externalprogramtask/full',
});
} else {
axiosConfig = setupQrsConnection(this.options, {
method: 'get',
- fileCert: this.fileCert,
- fileCertKey: this.fileCertKey,
- fileCertCA: this.fileCertCA,
path: '/qrs/externalprogramtask/full',
queryParameters: [{ name: 'filter', value: filter }],
});
@@ -2400,5 +2376,3 @@ class QlikSenseTasks {
return this.taskNetwork;
}
}
-
-export default QlikSenseTasks;
diff --git a/src/lib/task/class_compositeevent.js b/src/lib/task/class_compositeevent.js
index 414f402..fcde6af 100644
--- a/src/lib/task/class_compositeevent.js
+++ b/src/lib/task/class_compositeevent.js
@@ -1,6 +1,6 @@
import { logger } from '../../globals.js';
-class QlikSenseCompositeEvent {
+export class QlikSenseCompositeEvent {
constructor(compositeEvent) {
this.compositeEvent = compositeEvent;
}
@@ -9,5 +9,3 @@ class QlikSenseCompositeEvent {
// Other methods
}
-
-export default QlikSenseCompositeEvent;
diff --git a/src/lib/task/class_schemaevent.js b/src/lib/task/class_schemaevent.js
index 2893ead..5ccbe42 100644
--- a/src/lib/task/class_schemaevent.js
+++ b/src/lib/task/class_schemaevent.js
@@ -1,6 +1,6 @@
import { logger } from '../../globals.js';
-class QlikSenseSchemaEvent {
+export class QlikSenseSchemaEvent {
constructor(schemaEvent) {
this.schemaEvent = schemaEvent;
}
@@ -9,5 +9,3 @@ class QlikSenseSchemaEvent {
// Other methods
}
-
-export default QlikSenseSchemaEvent;
diff --git a/src/lib/task/class_task.js b/src/lib/task/class_task.js
index 5e35cdf..acea975 100644
--- a/src/lib/task/class_task.js
+++ b/src/lib/task/class_task.js
@@ -4,8 +4,7 @@ import { logger } from '../../globals.js';
import { mapTaskExecutionStatus } from '../util/qseow/lookups.js';
// const randomWords2 = (...args) => import('random-words').then(({ default: randomWords }) => randomWords(...args));
-class QlikSenseTask {
- // eslint-disable-next-line no-useless-constructor
+export class QlikSenseTask {
constructor() {
//
}
@@ -184,5 +183,3 @@ class QlikSenseTask {
}
}
}
-
-export default QlikSenseTask;
diff --git a/src/lib/task/task_qrs.js b/src/lib/task/task_qrs.js
index d8bfe85..b867d20 100644
--- a/src/lib/task/task_qrs.js
+++ b/src/lib/task/task_qrs.js
@@ -30,11 +30,8 @@ export const getCustomProperty = async (options) => {
// Build QRS query string using custom property name
const filter = encodeURIComponent(`name eq '${options.customPropertyName}'`);
- const axiosConfig = await setupQrsConnection(options, {
+ const axiosConfig = setupQrsConnection(options, {
method: 'get',
- fileCert: certFilesFullPath.fileCert,
- fileCertKey: certFilesFullPath.fileCertKey,
- fileCertCA: certFilesFullPath.fileCertCA,
path: '/qrs/CustomPropertyDefinition/full',
queryParameters: [{ name: 'filter', value: filter }],
});
@@ -104,7 +101,7 @@ export const getTasksFromQseow = async (options) => {
}
logger.debug(`GET TASK: Final QRS query filter: ${filter}`);
- const axiosConfig = await setupQrsConnection(options, {
+ const axiosConfig = setupQrsConnection(options, {
method: 'get',
path: '/qrs/reloadtask/full',
queryParameters: [{ name: 'filter', value: filter }],
@@ -131,7 +128,7 @@ export const getTasksFromQseow = async (options) => {
export const updateReloadTask = async (options, payload) => {
try {
// TODO Should be using PUT instead of POST if updating an existing task?
- const axiosConfig = await setupQrsConnection(options, {
+ const axiosConfig = setupQrsConnection(options, {
method: 'post',
path: '/qrs/reloadtask/update',
body: payload,
diff --git a/src/lib/util/log.js b/src/lib/util/log.js
index 28da856..b047c64 100644
--- a/src/lib/util/log.js
+++ b/src/lib/util/log.js
@@ -1,4 +1,4 @@
-import { logger, appVersion, isPkg, execPath } from '../../globals.js';
+import { logger, appVersion, isSea, execPath } from '../../globals.js';
export const logStartupInfo = (options, cmd, cmdDesc) => {
logger.info('-----------------------------------------------------------');
@@ -15,7 +15,7 @@ export const logStartupInfo = (options, cmd, cmdDesc) => {
logger.info(`| https://github.com/ptarmiganlabs/ctrl-q`);
logger.info('----------------------------------------------------------');
logger.info(``);
- logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`);
+ logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isSea}`);
logger.verbose(`Ctrl-Q was started from ${execPath}`);
logger.verbose(`Options: ${JSON.stringify(options, null, 2)}`);
logger.verbose(``);
@@ -23,7 +23,7 @@ export const logStartupInfo = (options, cmd, cmdDesc) => {
// Function used to provide consistent logging to all try-catch blocks
export const catchLog = (msgContext, err) => {
- if (isPkg) {
+ if (isSea) {
if (err.message) {
logger.error(`${msgContext}: ${err.message}`);
} else {
diff --git a/src/lib/util/qscloud/assert-options.js b/src/lib/util/qscloud/assert-options.js
index 51e807d..d10e9b6 100644
--- a/src/lib/util/qscloud/assert-options.js
+++ b/src/lib/util/qscloud/assert-options.js
@@ -1,6 +1,5 @@
-import path from 'node:path';
import { version as uuidVersion, validate as uuidValidate } from 'uuid';
-import { logger, execPath, verifyFileExists } from '../../../globals.js';
+import { logger, execPath } from '../../../globals.js';
export const qscloudSharedParamAssertOptions = async (options) => {
// Ensure that parameters common to all QS Cloud commands are valid
diff --git a/src/lib/util/qseow/about.js b/src/lib/util/qseow/about.js
index 844e1d9..43769f2 100644
--- a/src/lib/util/qseow/about.js
+++ b/src/lib/util/qseow/about.js
@@ -4,7 +4,7 @@ import { logger, execPath } from '../../../globals.js';
import { setupQrsConnection } from './qrs.js';
import { catchLog } from '../log.js';
-async function getAboutFromQseow(options) {
+export async function getAboutFromQseow(options) {
logger.verbose(`Getting about info from QSEoW...`);
try {
@@ -29,5 +29,3 @@ async function getAboutFromQseow(options) {
return false;
}
}
-
-export default getAboutFromQseow;
diff --git a/src/lib/util/qseow/assert-options.js b/src/lib/util/qseow/assert-options.js
index 0e10c4c..089dae0 100644
--- a/src/lib/util/qseow/assert-options.js
+++ b/src/lib/util/qseow/assert-options.js
@@ -1,6 +1,6 @@
import { version as uuidVersion, validate as uuidValidate } from 'uuid';
-import { logger, execPath, verifyFileExists } from '../../../globals.js';
+import { logger, execPath, verifyFileSystemExists } from '../../../globals.js';
import { getCertFilePaths } from '../qseow/cert.js';
export const qseowSharedParamAssertOptions = async (options) => {
@@ -23,7 +23,7 @@ export const qseowSharedParamAssertOptions = async (options) => {
// Get certificate paths
const { fileCert, fileCertKey, fileCertCA } = getCertFilePaths(options);
- const fileCertExists = await verifyFileExists(fileCert);
+ const fileCertExists = await verifyFileSystemExists(fileCert);
if (fileCertExists === false) {
logger.error(`Missing certificate file ${fileCert}. Aborting`);
process.exit(1);
@@ -31,7 +31,7 @@ export const qseowSharedParamAssertOptions = async (options) => {
logger.verbose(`Certificate file ${fileCert} found`);
}
- const fileCertKeyExists = await verifyFileExists(fileCertKey);
+ const fileCertKeyExists = await verifyFileSystemExists(fileCertKey);
if (fileCertKeyExists === false) {
logger.error(`Missing certificate key file ${fileCertKey}. Aborting`);
process.exit(1);
@@ -39,7 +39,7 @@ export const qseowSharedParamAssertOptions = async (options) => {
logger.verbose(`Certificate key file ${fileCertKey} found`);
}
- const fileCertCAExists = await verifyFileExists(fileCertCA);
+ const fileCertCAExists = await verifyFileSystemExists(fileCertCA);
if (fileCertCAExists === false) {
logger.error(`Missing certificate CA file ${fileCertCA}. Aborting`);
process.exit(1);
diff --git a/src/lib/util/qseow/cert.js b/src/lib/util/qseow/cert.js
index e025361..699ebdb 100644
--- a/src/lib/util/qseow/cert.js
+++ b/src/lib/util/qseow/cert.js
@@ -1,5 +1,5 @@
import path from 'node:path';
-import { logger, execPath, verifyFileExists } from '../../../globals.js';
+import { execPath } from '../../../globals.js';
import { catchLog } from '../log.js';
export function getCertFilePaths(options) {
diff --git a/src/lib/util/qseow/enigma_util.js b/src/lib/util/qseow/enigma_util.js
index 1889906..9d59d8a 100644
--- a/src/lib/util/qseow/enigma_util.js
+++ b/src/lib/util/qseow/enigma_util.js
@@ -5,11 +5,11 @@ import { fileURLToPath } from 'node:url';
import upath from 'upath';
import sea from 'node:sea';
-import { logger, readCert } from '../../../globals.js';
+import { logger, readCert, isSea } from '../../../globals.js';
import { getCertFilePaths } from '../../util/qseow/cert.js';
// Function to get Enigma.js schema file
-const getEnigmaSchema = (processPkgFlag, seaFlag, options) => {
+const getEnigmaSchema = (options) => {
// Array of supported schema versions
const supportedSchemaVersions = ['12.170.2', '12.612.0', '12.936.0', '12.1306.0', '12.1477.0', '12.1657.0', '12.1823.0', '12.2015.0'];
@@ -27,25 +27,7 @@ const getEnigmaSchema = (processPkgFlag, seaFlag, options) => {
}
// Are we running as a packaged app?
- if (processPkgFlag) {
- const schemaFile = `./node_modules/enigma.js/schemas/${options.schemaVersion}.json`;
- logger.debug(`Enigma.js schema file: ${schemaFile}`);
-
- // Yes, we are running as a packaged app
- // Get path to JS file const
- const a = process.pkg.defaultEntrypoint;
- logger.debug(`APPDUMP schema path a: ${a}`);
-
- // Strip off the filename
- const b = upath.dirname(a);
- logger.debug(`APPDUMP schema path b: ${b}`);
-
- // Add path to schema file
- const c = upath.join(b, schemaFile);
- logger.debug(`APPDUMP schema path c: ${c}`);
-
- qixSchemaJson = readFileSync(c);
- } else if (seaFlag) {
+ if (isSea) {
// Load schema file
qixSchemaJson = sea.getAsset(`enigma_schema_${options.schemaVersion}.json`, 'utf8');
} else {
@@ -83,7 +65,7 @@ export const setupEnigmaConnection = (options, sessionId) => {
// Set up enigma.js configuration
logger.debug(`Enigma.js schema version: ${options.schemaVersion}`);
- const qixSchema = getEnigmaSchema(process.pkg, sea.isSea(), options);
+ const qixSchema = getEnigmaSchema(options);
let enigmaConfig;
// Should certificates be used for authentication?
diff --git a/src/lib/util/qseow/proxy.js b/src/lib/util/qseow/proxy.js
index 2eaa563..2ddb486 100644
--- a/src/lib/util/qseow/proxy.js
+++ b/src/lib/util/qseow/proxy.js
@@ -4,7 +4,7 @@ import { logger, execPath } from '../../../globals.js';
import { setupQrsConnection } from './qrs.js';
import { catchLog } from '../log.js';
-const getProxiesFromQseow = async (options, _sessionCookie) => {
+export async function getProxiesFromQseow(options, _sessionCookie) {
logger.verbose(`Getting all proxies from QSEoW...`);
// TODO Should support JWTs here too?
@@ -30,6 +30,4 @@ const getProxiesFromQseow = async (options, _sessionCookie) => {
}
return proxies;
-};
-
-export default getProxiesFromQseow;
+}
diff --git a/src/lib/util/qseow/qps.js b/src/lib/util/qseow/qps.js
index efc7126..6deec02 100644
--- a/src/lib/util/qseow/qps.js
+++ b/src/lib/util/qseow/qps.js
@@ -1,7 +1,18 @@
import https from 'node:https';
+
import { logger, generateXrfKey, readCert } from '../../../globals.js';
+import { getCertFilePaths } from './cert.js';
+
+export function setupQpsConnection(options, param) {
+ // Ensure correct auth info is present
+ if (options.authType === 'cert') {
+ // options.authUserDir and options.authUserId should be set
+ if (!options.authUserDir || !options.authUserId) {
+ logger.error(`Setting up connection to QRS. Missing user directory and/or user ID. Exiting.`);
+ process.exit(1);
+ }
+ }
-const setupQPSConnection = (options, param) => {
// Ensure valid http method
if (!param.method || (param.method.toLowerCase() !== 'get' && param.method.toLowerCase() !== 'delete')) {
logger.error(`Setting up connection to QPS. Invalid http method '${param.method}'. Exiting.`);
@@ -22,11 +33,29 @@ const setupQPSConnection = (options, param) => {
logger.debug(`QPS host: ${options.hostProxy}`);
logger.debug(`Reject unauthorized certificate: ${!!options.secure}`);
+ // Get certificate paths
+ // If specified in the param object, use those paths
+ // Otherwise, use the paths from the command line options
+ let { fileCert, fileCertKey, fileCertCA } = getCertFilePaths(options);
+
+ // If the paths are specified in the param object, use those paths
+ if (param.fileCert) {
+ fileCert = param.fileCert;
+ }
+
+ if (param.fileCertKey) {
+ fileCertKey = param.fileCertKey;
+ }
+
+ if (param.fileCertCA) {
+ fileCertCA = param.fileCertCA;
+ }
+
const httpsAgent = new https.Agent({
rejectUnauthorized: options.secure !== 'false',
- cert: readCert(param.fileCert),
- key: readCert(param.fileCertKey),
- ca: readCert(param.fileCertCA),
+ cert: readCert(fileCert),
+ key: readCert(fileCertKey),
+ ca: readCert(fileCertCA),
});
axiosConfig = {
@@ -73,6 +102,4 @@ const setupQPSConnection = (options, param) => {
}
return axiosConfig;
-};
-
-export default setupQPSConnection;
+}
diff --git a/src/lib/util/qseow/qrs.js b/src/lib/util/qseow/qrs.js
index 7b46606..4bfefed 100644
--- a/src/lib/util/qseow/qrs.js
+++ b/src/lib/util/qseow/qrs.js
@@ -1,7 +1,7 @@
import https from 'node:https';
import { logger, generateXrfKey, readCert } from '../../../globals.js';
-import { getCertFilePaths } from '../qseow/cert.js';
+import { getCertFilePaths } from './cert.js';
// Function to sanitize virtual proxy
export function sanitizeVirtualProxy(virtualProxy) {
@@ -87,6 +87,7 @@ export function setupQrsConnection(options, param) {
// Otherwise, use the paths from the command line options
let { fileCert, fileCertKey, fileCertCA } = getCertFilePaths(options);
+ // If the paths are specified in the param object, use those paths
if (param.fileCert) {
fileCert = param.fileCert;
}
diff --git a/src/lib/util/qseow/session.js b/src/lib/util/qseow/session.js
index 5bd5532..db6b03f 100644
--- a/src/lib/util/qseow/session.js
+++ b/src/lib/util/qseow/session.js
@@ -3,10 +3,10 @@ import path from 'node:path';
import { table } from 'table';
import yesno from 'yesno';
import { logger, execPath } from '../../../globals.js';
-import setupQPSConnection from './qps.js';
+import { setupQpsConnection } from './qps.js';
import { setupQrsConnection } from './qrs.js';
import { catchLog } from '../log.js';
-import getProxiesFromQseow from './proxy.js';
+import { getProxiesFromQseow } from './proxy.js';
const consoleProxiesTableConfig = {
border: {
@@ -207,12 +207,9 @@ export const getSessionsFromQseow = async (options, sessionCookie) => {
}
// Get sessions for this virtual proxy
- axiosConfig = setupQPSConnection(options, {
+ axiosConfig = setupQpsConnection(options, {
hostProxy: proxy.serverNodeConfiguration.hostName,
method: 'get',
- fileCert,
- fileCertKey,
- fileCertCA,
path: `/qps/${vp.prefix}/session`,
sessionCookie: null,
});
@@ -336,7 +333,7 @@ export const deleteSessionsFromQSEoWIds = async (options) => {
logger.debug(`Session metadata: ${JSON.stringify(s, null, 2)}`);
try {
- const axiosConfig = setupQPSConnection(options, {
+ const axiosConfig = setupQpsConnection(options, {
hostProxy: options.hostProxy,
method: 'delete',
path: `/qps/${options.sessionVirtualProxy}/session/${s.sessionId}`,
diff --git a/src/static/404.html b/src/static/404.html
index 8f58182..5632554 100644
--- a/src/static/404.html
+++ b/src/static/404.html
@@ -34,7 +34,7 @@
-
+
Err. That's an error.
The page you were looking for could not be found.
diff --git a/src/static/ctrl-q_2.png b/src/static/ctrl-q.png
similarity index 100%
rename from src/static/ctrl-q_2.png
rename to src/static/ctrl-q.png
diff --git a/src/static/index.html b/src/static/index.html
index b4b2867..c07435d 100644
--- a/src/static/index.html
+++ b/src/static/index.html
@@ -216,7 +216,7 @@