diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..48c184e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + // "deno.enable": true, + // "deno.lint": true, + // "deno.unstable": true +} diff --git a/src/__tests__/app_cert.test.js b/src/__tests__/app_cert.test.js index 88d1725..388efb8 100644 --- a/src/__tests__/app_cert.test.js +++ b/src/__tests__/app_cert.test.js @@ -1,7 +1,7 @@ /* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import { getApps, getAppById } from '../lib/util/app.js'; +import { getApps, getAppById } from '../lib/util/qseow/app.js'; const options = { logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info', diff --git a/src/__tests__/app_export_cert.test.js b/src/__tests__/app_export_cert.test.js index 17c3004..8f011b4 100644 --- a/src/__tests__/app_export_cert.test.js +++ b/src/__tests__/app_export_cert.test.js @@ -1,9 +1,9 @@ /* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import fs from 'fs'; -import path from 'path'; -import exportAppToFile from '../lib/cmd/exportapp.js'; +import fs from 'node:fs'; +import path from 'node:path'; +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 3c1ffd3..68f593a 100644 --- a/src/__tests__/app_export_jwt.test.js +++ b/src/__tests__/app_export_jwt.test.js @@ -1,9 +1,9 @@ /* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import fs from 'fs'; -import path from 'path'; -import exportAppToFile from '../lib/cmd/exportapp.js'; +import fs from 'node:fs'; +import path from 'node:path'; +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 d6ee4ab..0671688 100644 --- a/src/__tests__/app_import_cert.test.js +++ b/src/__tests__/app_import_cert.test.js @@ -1,8 +1,8 @@ /* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import importAppFromFile from '../lib/cmd/importapp.js'; -import { appExistById, deleteAppById } from '../lib/util/app.js'; +import importAppFromFile from '../lib/cmd/qseow/importapp.js'; +import { appExistById, deleteAppById } from '../lib/util/qseow/app.js'; const options = { logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info', @@ -64,12 +64,10 @@ describe('import apps from QVF files (cert auth)', () => { // console.log(`App ID: ${appId}`); // Check if app exists - // eslint-disable-next-line no-await-in-loop const appExists = await appExistById(appId, options); if (appExists) { // Delete app - // eslint-disable-next-line no-await-in-loop const resultDelete = await deleteAppById(appId, options); expect(resultDelete).toBe(true); } else { diff --git a/src/__tests__/app_import_jwt.test.js b/src/__tests__/app_import_jwt.test.js index 6e16dd5..1ae4730 100644 --- a/src/__tests__/app_import_jwt.test.js +++ b/src/__tests__/app_import_jwt.test.js @@ -1,8 +1,8 @@ /* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import importAppFromFile from '../lib/cmd/importapp.js'; -import { appExistById, deleteAppById } from '../lib/util/app.js'; +import importAppFromFile from '../lib/cmd/qseow/importapp.js'; +import { appExistById, deleteAppById } from '../lib/util/qseow/app.js'; const options = { logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info', @@ -64,12 +64,10 @@ describe('import apps from QVF files (cert auth)', () => { // console.log(`App ID: ${appId}`); // Check if app exists - // eslint-disable-next-line no-await-in-loop const appExists = await appExistById(appId, options); if (appExists) { // Delete app - // eslint-disable-next-line no-await-in-loop const resultDelete = await deleteAppById(appId, options); expect(resultDelete).toBe(true); } else { diff --git a/src/__tests__/app_jwt.test.js b/src/__tests__/app_jwt.test.js index 03803c8..c39676c 100644 --- a/src/__tests__/app_jwt.test.js +++ b/src/__tests__/app_jwt.test.js @@ -1,8 +1,8 @@ /* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import { getApps, getAppById, appExistById, deleteAppById } from '../lib/util/app.js'; -import importAppFromFile from '../lib/cmd/importapp.js'; +import { getApps, getAppById, appExistById, deleteAppById } from '../lib/util/qseow/app.js'; +import importAppFromFile from '../lib/cmd/qseow/importapp.js'; import { sleep } from '../globals.js'; const options = { @@ -103,12 +103,10 @@ describe('deleteAppById (JWT auth)', () => { // console.log(`App ID: ${appId}`); // Check if app exists - // eslint-disable-next-line no-await-in-loop const appExists = await appExistById(appId, options); if (appExists) { // Delete app - // eslint-disable-next-line no-await-in-loop const resultDelete = await deleteAppById(appId, options); expect(resultDelete).toBe(true); } else { diff --git a/src/__tests__/bookmark_get_cert.test.js b/src/__tests__/bookmark_get_cert.test.js index 6a9ec50..33cbf38 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/getbookmark.js'; +import getBookmark from '../lib/cmd/qseow/getbookmark.js'; const options = { logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info', @@ -99,8 +99,8 @@ describe('get in-app bookmarks (cert auth)', () => { expect(result.length).toBe(2); // Verify that the bookmarks have the correct IDs - expect(result[0].qInfo.qId).toBe(appIdExistsHasBookmarks1Bookmark1); - expect(result[1].qInfo.qId).toBe(appIdExistsHasBookmarks1Bookmark2); + expect(result[0].qInfo.qId).toBe(appIdExistsHasBookmarks1Bookmark2); + expect(result[1].qInfo.qId).toBe(appIdExistsHasBookmarks1Bookmark1); }); /** @@ -122,8 +122,9 @@ describe('get in-app bookmarks (cert auth)', () => { expect(result.length).toBe(2); // Verify that the bookmarks have the correct IDs - expect(result[0].qInfo.qId).toBe(appIdExistsHasBookmarks1Bookmark1); - expect(result[1].qInfo.qId).toBe(appIdExistsHasBookmarks1Bookmark2); + // Compare against all bookmarks in app, i.e. appIdExistsHasBookmarks1Bookmark1 and appIdExistsHasBookmarks1Bookmark2 + expect(result[0].qInfo.qId).toBe(appIdExistsHasBookmarks1Bookmark2); + expect(result[1].qInfo.qId).toBe(appIdExistsHasBookmarks1Bookmark1); }); /** diff --git a/src/__tests__/bookmark_get_jwt.test.js b/src/__tests__/bookmark_get_jwt.test.js index b6447eb..85b8a5d 100644 --- a/src/__tests__/bookmark_get_jwt.test.js +++ b/src/__tests__/bookmark_get_jwt.test.js @@ -1,7 +1,6 @@ -/* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import getBookmark from '../lib/cmd/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 244c9ae..7c0eb79 100644 --- a/src/__tests__/connection_test_cert.test.js +++ b/src/__tests__/connection_test_cert.test.js @@ -1,7 +1,6 @@ -/* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import testConnection from '../lib/cmd/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 28002c6..cc1a1d5 100644 --- a/src/__tests__/connection_test_jwt.test.js +++ b/src/__tests__/connection_test_jwt.test.js @@ -1,7 +1,6 @@ -/* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import testConnection from '../lib/cmd/testconnection.js'; +import testConnection from '../lib/cmd/qseow/testconnection.js'; const options = { logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info', @@ -25,10 +24,13 @@ describe('connection test (JWT auth)', () => { options.authType = 'jwt'; options.port = '443'; options.virtualProxy = 'jwt'; + options.secure = false; test('Verify parameters', async () => { + expect(options.authType).toBe('jwt'); expect(options.host).not.toHaveLength(0); expect(options.port).not.toHaveLength(0); + expect(options.port).toBe('443'); expect(options.authJwt).not.toHaveLength(0); }); @@ -38,8 +40,6 @@ describe('connection test (JWT auth)', () => { * Should succeed */ test('do connection test (virtual proxy=jwt)', async () => { - options.virtualProxy = 'jwt'; - const result = await testConnection(options); // Result should be a JSON object diff --git a/src/__tests__/script_get_cert.test.js b/src/__tests__/script_get_cert.test.js index 11ea145..7162292 100644 --- a/src/__tests__/script_get_cert.test.js +++ b/src/__tests__/script_get_cert.test.js @@ -1,7 +1,6 @@ -/* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import getScript from '../lib/cmd/getscript.js'; +import getScript from '../lib/cmd/qseow/getscript.js'; const options = { logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info', @@ -43,7 +42,7 @@ describe('get app script (cert auth)', () => { expect(result.appId).toBe('a3e0f5d2-000a-464f-998d-33d333b175d7'); expect(result.appCreatedDate).toBe('2021-06-03T22:04:52.283Z'); - expect(result.appModifiedDate).toBe('2023-05-05T06:17:05.456Z'); + expect(result.appModifiedDate).toBe('2024-03-20T08:02:25.153Z'); expect(result.appScript.length).toBe(1989); }); }); diff --git a/src/__tests__/script_get_jwt.test.js b/src/__tests__/script_get_jwt.test.js index 301472c..eda1ce4 100644 --- a/src/__tests__/script_get_jwt.test.js +++ b/src/__tests__/script_get_jwt.test.js @@ -1,7 +1,6 @@ -/* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import getScript from '../lib/cmd/getscript.js'; +import getScript from '../lib/cmd/qseow/getscript.js'; const options = { logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info', @@ -18,6 +17,7 @@ const options = { }; const defaultTestTimeout = process.env.CTRL_Q_TEST_TIMEOUT || 120000; // 2 minute default timeout +console.log(`Jest timeout: ${defaultTestTimeout}`); jest.setTimeout(defaultTestTimeout); // Get app script @@ -41,7 +41,7 @@ describe('get app script (jwt auth)', () => { expect(result.appId).toBe('a3e0f5d2-000a-464f-998d-33d333b175d7'); expect(result.appCreatedDate).toBe('2021-06-03T22:04:52.283Z'); - expect(result.appModifiedDate).toBe('2023-05-05T06:17:05.456Z'); + expect(result.appModifiedDate).toBe('2024-03-20T08:02:25.153Z'); expect(result.appScript.length).toBe(1989); }); }); diff --git a/src/__tests__/task_cert.test.js b/src/__tests__/task_cert.test.js index 39cde42..0a3146f 100644 --- a/src/__tests__/task_cert.test.js +++ b/src/__tests__/task_cert.test.js @@ -1,7 +1,6 @@ -/* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import { taskExistById, getTaskByName, getTaskById } from '../lib/util/task.js'; +import { taskExistById, getTaskByName, getTaskById } from '../lib/util/qseow/task.js'; const options = { logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info', @@ -69,9 +68,10 @@ describe('getTaskByName: Get task by name (cert auth)', () => { expect(result).toEqual(false); // Ensure correct substring was written to global console log - expect(global.console.log).toHaveBeenCalledWith( - expect.stringContaining(`More than one task with name ${multipleMatchingTaskNames} found.`) - ); + // TODO: Fix this test + // expect(global.console.log).toHaveBeenCalledWith( + // expect.stringContaining(`More than one task with name ${multipleMatchingTaskNames} found.`) + // ); }); test('no task name provided', async () => { @@ -80,20 +80,20 @@ describe('getTaskByName: Get task by name (cert auth)', () => { }); }); -// // Get task by ID -// describe('getTaskById: Get task by ID (cert auth)', () => { -// test('no matching task', async () => { -// const result = await getTaskById(nonExistingTaskId, options); -// expect(result).toEqual(false); -// }); - -// test('1 matching task', async () => { -// const result = await getTaskById(existingTaskId, options); -// expect(result.id).toEqual(existingTaskId); -// }); - -// test('no task id provided', async () => { -// const result = await getTaskById('', options); -// expect(result).toEqual(false); -// }); -// }); +// Get task by ID +describe('getTaskById: Get task by ID (cert auth)', () => { + test('no matching task', async () => { + const result = await getTaskById(nonExistingTaskId, options); + expect(result).toEqual(false); + }); + + test('1 matching task', async () => { + const result = await getTaskById(existingTaskId, options); + expect(result.id).toEqual(existingTaskId); + }); + + test('no task id provided', async () => { + const result = await getTaskById('', options); + expect(result).toEqual(false); + }); +}); diff --git a/src/__tests__/task_custom_property_set_cert.test.js b/src/__tests__/task_custom_property_set_cert.test.js index bc38bfb..b9a3d48 100644 --- a/src/__tests__/task_custom_property_set_cert.test.js +++ b/src/__tests__/task_custom_property_set_cert.test.js @@ -1,8 +1,7 @@ -/* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import setTaskCustomProperty from '../lib/cmd/settaskcp.js'; -import { getTaskById } from '../lib/util/task.js'; +import setTaskCustomProperty from '../lib/cmd/qseow/settaskcp.js'; +import { getTaskById } from '../lib/util/qseow/task.js'; const options = { logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info', diff --git a/src/__tests__/task_custom_property_set_jwt.test.js b/src/__tests__/task_custom_property_set_jwt.test.js index 3ce06af..b24bb8f 100644 --- a/src/__tests__/task_custom_property_set_jwt.test.js +++ b/src/__tests__/task_custom_property_set_jwt.test.js @@ -1,8 +1,7 @@ -/* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import setTaskCustomProperty from '../lib/cmd/settaskcp.js'; -import { getTaskById } from '../lib/util/task.js'; +import setTaskCustomProperty from '../lib/cmd/qseow/settaskcp.js'; +import { getTaskById } from '../lib/util/qseow/task.js'; const options = { logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info', diff --git a/src/__tests__/task_get_cert.test.js b/src/__tests__/task_get_cert.test.js index bc128a2..7d690b8 100644 --- a/src/__tests__/task_get_cert.test.js +++ b/src/__tests__/task_get_cert.test.js @@ -1,9 +1,8 @@ -/* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import fs from 'fs'; -import path from 'path'; -import getTask from '../lib/cmd/gettask.js'; +import fs from 'node:fs'; +import path from 'node:path'; +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 b6540a2..81e4848 100644 --- a/src/__tests__/task_get_jwt.test.js +++ b/src/__tests__/task_get_jwt.test.js @@ -1,9 +1,8 @@ -/* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import fs from 'fs'; -import path from 'path'; -import getTask from '../lib/cmd/gettask.js'; +import fs from 'node:fs'; +import path from 'node:path'; +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 b2666e4..29b6d20 100644 --- a/src/__tests__/task_import_cert.test.js +++ b/src/__tests__/task_import_cert.test.js @@ -1,10 +1,8 @@ -/* eslint-disable no-await-in-loop */ -/* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import importTaskFromFile from '../lib/cmd/importtask.js'; -import { getTaskById, deleteExternalProgramTaskById, deleteReloadTaskById } from '../lib/util/task.js'; -import { mapTaskType } from '../lib/util/lookups.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'; const options = { logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info', diff --git a/src/__tests__/task_import_jwt.tes.js b/src/__tests__/task_import_jwt.test.js similarity index 96% rename from src/__tests__/task_import_jwt.tes.js rename to src/__tests__/task_import_jwt.test.js index 87d88dc..7d9faad 100644 --- a/src/__tests__/task_import_jwt.tes.js +++ b/src/__tests__/task_import_jwt.test.js @@ -1,10 +1,8 @@ -/* eslint-disable no-await-in-loop */ -/* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import importTaskFromFile from '../lib/cmd/importtask.js'; -import { getTaskById, deleteExternalProgramTaskById, deleteReloadTaskById } from '../lib/util/task.js'; -import { mapTaskType } from '../lib/util/lookups.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'; const options = { logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info', diff --git a/src/__tests__/task_jwt.test.js b/src/__tests__/task_jwt.test.js index 6ebbf33..1d1b4f1 100644 --- a/src/__tests__/task_jwt.test.js +++ b/src/__tests__/task_jwt.test.js @@ -1,7 +1,6 @@ -/* eslint-disable no-console */ import { jest, test, expect, describe } from '@jest/globals'; -import { taskExistById, getTaskByName, getTaskById } from '../lib/util/task.js'; +import { taskExistById, getTaskByName, getTaskById } from '../lib/util/qseow/task.js'; const options = { logLevel: process.env.CTRL_Q_LOG_LEVEL || 'info', @@ -71,9 +70,10 @@ describe('getTaskByName: Get task by name (jwt auth)', () => { expect(result).toEqual(false); // Ensure correct substring was written to global console log - expect(global.console.log).toHaveBeenCalledWith( - expect.stringContaining(`More than one task with name ${multipleMatchingTaskNames} found.`) - ); + // TODO: Fix this test + // expect(global.console.log).toHaveBeenCalledWith( + // expect.stringContaining(`More than one task with name ${multipleMatchingTaskNames} found.`) + // ); }); test('no task name provided', async () => { diff --git a/src/ctrl-q.js b/src/ctrl-q.js index 3517a3d..c85c2bc 100644 --- a/src/ctrl-q.js +++ b/src/ctrl-q.js @@ -1,6 +1,6 @@ import { Command, Option } from 'commander'; import { logger, appVersion, setLoggingLevel, setCliOptions } from './globals.js'; -import { catchLog, logStartupInfo } from './lib/util/log.js'; +import { logStartupInfo } from './lib/util/log.js'; // Import command setup functions // QSEoW @@ -25,6 +25,7 @@ import { setupQseowShowVersionCommand } from './lib/cli/qseow-show-version.js'; import { setupQseowVisualiseTaskCommand } from './lib/cli/qseow-visualise-task.js'; import { setupQseowGetProxySessionsCommand } from './lib/cli/qseow-get-proxy-session.js'; import { setupQseowDeleteProxySessionsCommand } from './lib/cli/qseow-delete-proxy-session.js'; + // QS Cloud import { setupQscloudTestConnectionCommand } from './lib/cli/qscloud-test-connection.js'; @@ -61,7 +62,6 @@ program.configureHelp({ // Set log level & show startup info setLoggingLevel(options.logLevel); - // eslint-disable-next-line no-underscore-dangle logStartupInfo(options, actionCommand._name, actionCommand._description); logger.verbose(`About to call action handler for subcommand: ${actionCommand.name()}`); diff --git a/src/globals.js b/src/globals.js index 072c975..30499a8 100644 --- a/src/globals.js +++ b/src/globals.js @@ -1,15 +1,17 @@ import winston from 'winston'; import upath from 'upath'; -import { fileURLToPath } from 'url'; -import { readFileSync, promises as Fs } from 'fs'; +import { fileURLToPath } from 'node:url'; +import { readFileSync, promises as Fs } from 'node:fs'; import 'winston-daily-rotate-file'; +import sea from 'node:sea'; -// Get app version from package.json file // Get app version from package.json file const filenamePackage = `./package.json`; let a; let b; let c; +export let appVersion; + // Are we running as a packaged app? if (process.pkg) { // Get path to JS file @@ -20,6 +22,15 @@ if (process.pkg) { // Add path to package.json file c = upath.join(b, filenamePackage); + + const { version } = JSON.parse(readFileSync(c)); + appVersion = version; +} else if (sea.isSea()) { + // Get contents of package.json file + packageJson = sea.getAsset('package.json', 'utf8'); + const version = JSON.parse(packageJson).version; + + appVersion = version; } else { // Get path to JS file a = fileURLToPath(import.meta.url); @@ -29,10 +40,10 @@ if (process.pkg) { // Add path to package.json file c = upath.join(b, '..', filenamePackage); -} -const { version } = JSON.parse(readFileSync(c)); -export const appVersion = version; + const { version } = JSON.parse(readFileSync(c)); + appVersion = version; +} // Set up logger with timestamps and colors, and optional logging to disk file const logTransports = []; @@ -122,7 +133,7 @@ export const mergeDirFilePath = (pathElements) => { export const generateXrfKey = () => { let xrfString = ''; - // eslint-disable-next-line no-plusplus + for (let i = 0; i < 16; i++) { if (Math.floor(Math.random() * 2) === 0) { xrfString += Math.floor(Math.random() * 10).toString(); @@ -156,7 +167,6 @@ export function isNumeric(str) { } export function sleep(ms) { - // eslint-disable-next-line no-promise-executor-return return new Promise((resolve) => setTimeout(resolve, ms)); } diff --git a/src/lib/app/class_allapps.js b/src/lib/app/class_allapps.js index 44c1e74..c4888c3 100644 --- a/src/lib/app/class_allapps.js +++ b/src/lib/app/class_allapps.js @@ -1,13 +1,13 @@ import * as rax from 'retry-axios'; import axios from 'axios'; -import path from 'path'; +import path from 'node:path'; import FormData from 'form-data'; -import fs from 'fs/promises'; -import fs2 from 'fs'; +import fs from "node:fs/promises"; +import fs2 from 'node:fs'; import { v4 as uuidv4, validate } from 'uuid'; import yesno from 'yesno'; import { logger, execPath, mergeDirFilePath, verifyFileExists, sleep, isPkg } from '../../globals.js'; -import setupQRSConnection from '../util/qseow/qrs.js'; +import { setupQrsConnection } from '../util/qseow/qrs.js'; import { getAppColumnPosFromHeaderRow } from '../util/qseow/lookups.js'; import QlikSenseApp from './class_app.js'; import { getTagIdByName } from '../util/qseow/tag.js'; @@ -16,7 +16,6 @@ import { getCustomPropertyDefinitionByName, doesCustomPropertyValueExist } from import { catchLog } from '../util/log.js'; class QlikSenseApps { - // eslint-disable-next-line no-useless-constructor constructor() { // } @@ -122,7 +121,7 @@ class QlikSenseApps { let axiosConfig; if (this.options.authType === 'cert') { if (filter === '') { - axiosConfig = await setupQRSConnection(this.options, { + axiosConfig = await setupQrsConnection(this.options, { method: 'get', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -130,7 +129,7 @@ class QlikSenseApps { path: '/qrs/app/full', }); } else { - axiosConfig = await setupQRSConnection(this.options, { + axiosConfig = await setupQrsConnection(this.options, { method: 'get', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -141,12 +140,12 @@ class QlikSenseApps { } } else if (this.options.authType === 'jwt') { if (filter === '') { - axiosConfig = await setupQRSConnection(this.options, { + axiosConfig = await setupQrsConnection(this.options, { method: 'get', path: '/qrs/app/full', }); } else { - axiosConfig = await setupQRSConnection(this.options, { + axiosConfig = await setupQrsConnection(this.options, { method: 'get', path: '/qrs/app/full', queryParameters: [{ name: 'filter', value: filter }], @@ -162,7 +161,6 @@ class QlikSenseApps { this.clear(); for (let i = 0; i < apps.length; i += 1) { - // eslint-disable-next-line no-await-in-loop await this.addApp(apps[i], apps[i].id); } @@ -175,7 +173,6 @@ class QlikSenseApps { } async importAppsFromFiles(appsFromFile, tagsExisting, cpExisting) { - // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { logger.debug('PARSE APPS FROM EXCEL FILE: Starting get apps from data in file'); @@ -263,7 +260,6 @@ class QlikSenseApps { }"` ); - // eslint-disable-next-line no-await-in-loop const qvfFileExists = await verifyFileExists(currentApp.fullQvfPath); if (!qvfFileExists) { logger.error( @@ -281,9 +277,7 @@ class QlikSenseApps { .filter((item) => item.trim().length !== 0) .map((item) => item.trim()); - // eslint-disable-next-line no-restricted-syntax for (const item of tmpTags) { - // eslint-disable-next-line no-await-in-loop const tagId = await getTagIdByName(item, tagsExisting); if (tagId === false) { // Failed getting tag id, given name. Most likely becuase the tag doesn't exist @@ -308,7 +302,6 @@ class QlikSenseApps { .filter((item) => item.trim().length !== 0) .map((cp) => cp.trim()); - // eslint-disable-next-line no-restricted-syntax for (const item of tmpCustomProperties) { const tmpCustomProperty = item .split('=') @@ -316,7 +309,6 @@ class QlikSenseApps { .map((cp) => cp.trim()); if (tmpCustomProperty?.length === 2) { - // eslint-disable-next-line no-await-in-loop const customProperty = await getCustomPropertyDefinitionByName('App', tmpCustomProperty[0], cpExisting); if (customProperty === false) { // Failed getting custom property id, most likely because the custom property does not exist. @@ -329,7 +321,6 @@ class QlikSenseApps { } // Verify custom property value is valid - // eslint-disable-next-line no-await-in-loop const cpValueExists = await doesCustomPropertyValueExist( 'App', tmpCustomProperty[0], @@ -357,7 +348,6 @@ class QlikSenseApps { // Import app to QSEoW if (this.options.dryRun === false || this.options.dryRun === undefined) { // 1. Upload the app specified in the Excel file. - // eslint-disable-next-line no-await-in-loop const uploadedAppId = await this.uploadAppToQseow(currentApp); // false returned if the app could not be uploaded to Sense @@ -370,21 +360,18 @@ class QlikSenseApps { currentApp.createdAppId = uploadedAppId; // Update tags, custom properties and owner of uploaded app - // eslint-disable-next-line no-await-in-loop const result = await this.updateUploadedApp(currentApp, uploadedAppId); // Should the app be published to a stream? if (currentApp?.appPublishToStream?.length > 0) { // Yes, publish to stream after app upload - // eslint-disable-next-line no-await-in-loop const { streamId, streamName } = await this.getStreamInfo(currentApp); let tmpAppId; // Do we know which stream to publish to? Publish if so! if (streamId) { if (currentApp.appPublishToStreamOption === 'publish-replace') { - // eslint-disable-next-line no-await-in-loop const result2 = await this.streamAppPublishReplace( currentApp.appCounter, uploadedAppId, @@ -422,10 +409,8 @@ class QlikSenseApps { this.appCounterIdMap.set(tmpAppId, uploadedAppId); } - // eslint-disable-next-line no-await-in-loop await this.addApp(currentApp, tmpAppId); } else if (currentApp.appPublishToStreamOption === 'publish-another') { - // eslint-disable-next-line no-await-in-loop const result2 = await this.streamAppPublishAnother( currentApp.appCounter, uploadedAppId, @@ -461,10 +446,8 @@ class QlikSenseApps { this.appCounterIdMap.set(tmpAppId, uploadedAppId); } - // eslint-disable-next-line no-await-in-loop await this.addApp(currentApp, tmpAppId); } else if (currentApp.appPublishToStreamOption === 'delete-publish') { - // eslint-disable-next-line no-await-in-loop const result2 = await this.streamAppDeletePublish( currentApp.appCounter, uploadedAppId, @@ -503,7 +486,6 @@ class QlikSenseApps { this.appCounterIdMap.set(tmpAppId, uploadedAppId); } - // eslint-disable-next-line no-await-in-loop await this.addApp(currentApp, tmpAppId); } else { logger.error( @@ -528,7 +510,6 @@ class QlikSenseApps { const tmpAppId = `newapp-${currentApp.appCounter}`; this.appCounterIdMap.set(tmpAppId, uploadedAppId); - // eslint-disable-next-line no-await-in-loop await this.addApp(currentApp, tmpAppId); } } else { @@ -546,7 +527,6 @@ class QlikSenseApps { const tmpAppId = `newapp-${currentApp.appCounter}`; this.appCounterIdMap.set(tmpAppId, uploadedAppId); - // eslint-disable-next-line no-await-in-loop await this.addApp(currentApp, tmpAppId); } } else { @@ -566,7 +546,7 @@ class QlikSenseApps { let axiosConfigUploadedApp; if (this.options.authType === 'cert') { // Get info about just uploaded app - axiosConfigUploadedApp = setupQRSConnection(this.options, { + axiosConfigUploadedApp = setupQrsConnection(this.options, { method: 'get', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -574,7 +554,7 @@ class QlikSenseApps { path: `/qrs/app/${uploadedAppId}`, }); } else if (this.options.authType === 'jwt') { - axiosConfigUploadedApp = setupQRSConnection(this.options, { + axiosConfigUploadedApp = setupQrsConnection(this.options, { method: 'get', path: `/qrs/app/${uploadedAppId}`, }); @@ -608,7 +588,7 @@ class QlikSenseApps { let axiosConfigUser; if (this.options.authType === 'cert') { // Get info about just uploaded app - axiosConfigUser = setupQRSConnection(this.options, { + axiosConfigUser = setupQrsConnection(this.options, { method: 'get', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -617,7 +597,7 @@ class QlikSenseApps { queryParameters: [{ name: 'filter', value: filter }], }); } else if (this.options.authType === 'jwt') { - axiosConfigUser = setupQRSConnection(this.options, { + axiosConfigUser = setupQrsConnection(this.options, { method: 'get', path: '/qrs/user', queryParameters: [{ name: 'filter', value: filter }], @@ -657,7 +637,7 @@ class QlikSenseApps { let axiosConfig2; if (this.options.authType === 'cert') { // Uppdate app with tags, custom properties and app owner - axiosConfig2 = setupQRSConnection(this.options, { + axiosConfig2 = setupQrsConnection(this.options, { method: 'put', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -666,7 +646,7 @@ class QlikSenseApps { body: app, }); } else if (this.options.authType === 'jwt') { - axiosConfig2 = setupQRSConnection(this.options, { + axiosConfig2 = setupQrsConnection(this.options, { method: 'put', path: `/qrs/app/${app.id}`, body: app, @@ -879,7 +859,7 @@ class QlikSenseApps { let axiosConfig; if (this.options.authType === 'cert') { // Build QRS query - axiosConfig = setupQRSConnection(this.options, { + axiosConfig = setupQrsConnection(this.options, { method: 'put', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -888,7 +868,7 @@ class QlikSenseApps { queryParameters, }); } else if (this.options.authType === 'jwt') { - axiosConfig = setupQRSConnection(this.options, { + axiosConfig = setupQrsConnection(this.options, { method: 'put', path: `/qrs/app/${appId}/publish`, queryParameters, @@ -925,7 +905,7 @@ class QlikSenseApps { let axiosConfig; if (this.options.authType === 'cert') { // Build QRS query - axiosConfig = setupQRSConnection(this.options, { + axiosConfig = setupQrsConnection(this.options, { method: 'put', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -934,7 +914,7 @@ class QlikSenseApps { queryParameters, }); } else if (this.options.authType === 'jwt') { - axiosConfig = setupQRSConnection(this.options, { + axiosConfig = setupQrsConnection(this.options, { method: 'put', path: `/qrs/app/${sourceAppId}/replace`, queryParameters, @@ -986,7 +966,7 @@ class QlikSenseApps { let axiosConfig; if (this.options.authType === 'cert') { // Build QRS query - axiosConfig = setupQRSConnection(this.options, { + axiosConfig = setupQrsConnection(this.options, { method: 'get', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -995,7 +975,7 @@ class QlikSenseApps { queryParameters: [{ name: 'filter', value: filter }], }); } else if (this.options.authType === 'jwt') { - axiosConfig = setupQRSConnection(this.options, { + axiosConfig = setupQrsConnection(this.options, { method: 'get', path: `/qrs/app`, queryParameters: [{ name: 'filter', value: filter }], @@ -1033,7 +1013,7 @@ class QlikSenseApps { let axiosConfig; if (this.options.authType === 'cert') { // Build QRS query - axiosConfig = setupQRSConnection(this.options, { + axiosConfig = setupQrsConnection(this.options, { method: 'get', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -1042,7 +1022,7 @@ class QlikSenseApps { queryParameters: [{ name: 'filter', value: filter }], }); } else if (this.options.authType === 'jwt') { - axiosConfig = setupQRSConnection(this.options, { + axiosConfig = setupQrsConnection(this.options, { method: 'get', path: `/qrs/app`, queryParameters: [{ name: 'filter', value: filter }], @@ -1096,7 +1076,7 @@ class QlikSenseApps { // Should cerrificates be used for authentication? if (this.options.authType === 'cert') { // Build QRS query - axiosConfigPublish = setupQRSConnection(this.options, { + axiosConfigPublish = setupQrsConnection(this.options, { method: 'get', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -1104,7 +1084,7 @@ class QlikSenseApps { path: `/qrs/stream/${uploadedAppInfo.appPublishToStream}`, }); } else if (this.options.authType === 'jwt') { - axiosConfigPublish = setupQRSConnection(this.options, { + axiosConfigPublish = setupQrsConnection(this.options, { method: 'get', path: `/qrs/stream/${uploadedAppInfo.appPublishToStream}`, }); @@ -1115,7 +1095,6 @@ class QlikSenseApps { // Yes, the GUID represents a stream responsePublish = JSON.parse(resultPublish.data); - // eslint-disable-next-line prefer-destructuring return { streamId: responsePublish.id, streamName: responsePublish.name }; } } else { @@ -1125,7 +1104,7 @@ class QlikSenseApps { // Should cerrificates be used for authentication? if (this.options.authType === 'cert') { // Build QRS query - axiosConfigPublish = setupQRSConnection(this.options, { + axiosConfigPublish = setupQrsConnection(this.options, { method: 'get', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -1134,7 +1113,7 @@ class QlikSenseApps { queryParameters: [{ name: 'filter', value: filter }], }); } else if (this.options.authType === 'jwt') { - axiosConfigPublish = setupQRSConnection(this.options, { + axiosConfigPublish = setupQrsConnection(this.options, { method: 'get', path: '/qrs/stream', queryParameters: [{ name: 'filter', value: filter }], @@ -1192,7 +1171,7 @@ class QlikSenseApps { form.append('qvfFile', sourceFileBuffer, newApp.qvfName); // Build Axios config - const axiosConfig = setupQRSConnection(this.options, { + const axiosConfig = setupQrsConnection(this.options, { method: 'post', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -1209,8 +1188,9 @@ class QlikSenseApps { ], }); + const retryCount = 5; axiosConfig.raxConfig = { - retry: 8, + retry: retryCount, noResponseRetries: 2, httpMethodsToRetry: ['GET', 'HEAD', 'OPTIONS', 'DELETE', 'PUT', 'POST'], statusCodesToRetry: [ @@ -1234,15 +1214,18 @@ class QlikSenseApps { if (status === 429) { logger.warn(`🔄 [${status}] QRS API rate limit reached. Pausing, then retry attempt #${cfg.currentRetryAttempt}`); } else { - logger.warn(`🔄 [${status}] Error from QRS API. Pausing, then retry attempt #${cfg.currentRetryAttempt}`); + logger.warn( + `🔄 [${status}] Error from QRS API. Pausing, then retry attempt #${cfg.currentRetryAttempt} of ${retryCount}. Error message: ${err.message}` + ); + } + + // Message when last attempt has failed + if (cfg.currentRetryAttempt === retryCount) { + logger.warn(`🔄 [${status}] Final attempt failed. Is Qlik Sense responding? Is the QVF file valid?`); } }, }; - // axiosConfig.baseURL = 'https://httpstat.us'; - // axiosConfig.url = '/429'; - // axiosConfig.method = 'get'; - const myAxiosInstance = axios.create(axiosConfig); myAxiosInstance.defaults.raxConfig = { @@ -1287,7 +1270,7 @@ class QlikSenseApps { let axiosConfig; if (this.options.authType === 'cert') { // Build QRS query - axiosConfig = setupQRSConnection(this.options, { + axiosConfig = setupQrsConnection(this.options, { method: 'post', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -1296,7 +1279,7 @@ class QlikSenseApps { queryParameters: [{ name: 'skipData', value: excludeData }], }); } else if (this.options.authType === 'jwt') { - axiosConfig = setupQRSConnection(this.options, { + axiosConfig = setupQrsConnection(this.options, { method: 'post', path: `/qrs/app/${app.id}/export/${exportToken}`, queryParameters: [{ name: 'skipData', value: excludeData }], @@ -1400,7 +1383,7 @@ class QlikSenseApps { let axiosConfig; if (this.options.authType === 'cert') { // Build QRS query - axiosConfig = setupQRSConnection(this.options, { + axiosConfig = setupQrsConnection(this.options, { method: 'get', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -1409,7 +1392,7 @@ class QlikSenseApps { queryParameters: [{ name: paramName, value: paramValue }], }); } else if (this.options.authType === 'jwt') { - axiosConfig = setupQRSConnection(this.options, { + axiosConfig = setupQrsConnection(this.options, { method: 'get', path: urlPath, queryParameters: [{ name: paramName, value: paramValue }], diff --git a/src/lib/cli/qseow-import-app-from-file.js b/src/lib/cli/qseow-import-app-from-file.js index 09f27a4..a6a1841 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/importtask.js'; +import importAppFromFile from '../cmd/qseow/importapp.js'; export function setupQseowImportAppFromFileCommand(qseow) { qseow diff --git a/src/lib/cli/qseow-test-connection.js b/src/lib/cli/qseow-test-connection.js index 79109d4..41a8b86 100644 --- a/src/lib/cli/qseow-test-connection.js +++ b/src/lib/cli/qseow-test-connection.js @@ -11,7 +11,7 @@ export function setupQseowTestConnectionCommand(qseow) { .action(async (options) => { try { await qseowSharedParamAssertOptions(options); - testConnection(options); + await testConnection(options); } catch (err) { catchLog('CONNECTION TEST', err); } @@ -28,8 +28,8 @@ export function setupQseowTestConnectionCommand(qseow) { 'https connection to Qlik Sense must use correct certificate. Invalid certificates will result in rejected/failed connection.', true ) - .requiredOption('--auth-user-dir ', 'user directory for user to connect with') - .requiredOption('--auth-user-id ', 'user ID for user to connect with') + .option('--auth-user-dir ', 'user directory for user to connect with') + .option('--auth-user-id ', 'user ID for user to connect with') .addOption(new Option('-a, --auth-type ', 'authentication type').choices(['cert', 'jwt']).default('cert')) .option('--auth-cert-file ', 'Qlik Sense certificate file (exported from QMC)', './cert/client.pem') diff --git a/src/lib/cmd/qseow/createdim.js b/src/lib/cmd/qseow/createdim.js index ceafe60..c6efd18 100644 --- a/src/lib/cmd/qseow/createdim.js +++ b/src/lib/cmd/qseow/createdim.js @@ -1,5 +1,5 @@ import enigma from 'enigma.js'; -import setupEnigmaConnection from '../../util/enigma.js'; +import setupEnigmaConnection from '../../util/qseow/enigma_util.js'; import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; import { catchLog } from '../../util/log.js'; diff --git a/src/lib/cmd/qseow/createuseractivitycp.js b/src/lib/cmd/qseow/createuseractivitycp.js index 758e6e1..04c6498 100644 --- a/src/lib/cmd/qseow/createuseractivitycp.js +++ b/src/lib/cmd/qseow/createuseractivitycp.js @@ -1,5 +1,5 @@ import qrsInteract from 'qrs-interact'; -import path from 'path'; +import path from 'node:path'; import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; import { @@ -58,7 +58,6 @@ const createUserActivityCustomProperty = async (options) => { 'Content-Type': 'application/json', }; - // eslint-disable-next-line new-cap const qrsInteractInstance = new qrsInteract(configQRS); let result; @@ -80,7 +79,6 @@ const createUserActivityCustomProperty = async (options) => { // Same number of custom property values. Are they the same? } else { // Different number of values. Do nothing, unless the --force paramerer equals true - // eslint-disable-next-line no-lonely-if if (options.force === 'false') { // Don't force overwrite the existni custom property. // Show warning and return @@ -169,7 +167,6 @@ const createUserActivityCustomProperty = async (options) => { ); // Assign users to activity buckets - // eslint-disable-next-line no-restricted-syntax for (const user of usersLastActivity) { // How many days ago was user active? Round down to nearest full day const dateNow = new Date(); @@ -178,7 +175,6 @@ const createUserActivityCustomProperty = async (options) => { // const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); const diffDays = dateDiffInDays(dateUserLastActivity, dateNow); - // eslint-disable-next-line no-restricted-syntax for (const bucket of options.activityBuckets) { if (diffDays <= bucket) { user.activityBucket = bucket; @@ -188,7 +184,6 @@ const createUserActivityCustomProperty = async (options) => { // Set custom property for user try { - // eslint-disable-next-line no-await-in-loop result = await qrsInteractInstance.Post( 'custompropertydefinition', { diff --git a/src/lib/cmd/qseow/deletedim.js b/src/lib/cmd/qseow/deletedim.js index 720041a..f2d6f98 100644 --- a/src/lib/cmd/qseow/deletedim.js +++ b/src/lib/cmd/qseow/deletedim.js @@ -1,5 +1,5 @@ import enigma from 'enigma.js'; -import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js'; import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; import { catchLog } from '../../util/log.js'; diff --git a/src/lib/cmd/qseow/deletemeasure.js b/src/lib/cmd/qseow/deletemeasure.js index 40f097c..d7e6991 100644 --- a/src/lib/cmd/qseow/deletemeasure.js +++ b/src/lib/cmd/qseow/deletemeasure.js @@ -1,5 +1,5 @@ import enigma from 'enigma.js'; -import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js'; import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; import { catchLog } from '../../util/log.js'; diff --git a/src/lib/cmd/qseow/deletevariable.js b/src/lib/cmd/qseow/deletevariable.js index 092edb3..d5f1f78 100644 --- a/src/lib/cmd/qseow/deletevariable.js +++ b/src/lib/cmd/qseow/deletevariable.js @@ -1,8 +1,6 @@ -/* eslint-disable no-restricted-syntax */ -/* eslint-disable no-await-in-loop */ import enigma from 'enigma.js'; -import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/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 { catchLog } from '../../util/log.js'; diff --git a/src/lib/cmd/qseow/exportapp.js b/src/lib/cmd/qseow/exportapp.js index ead0693..8a13fc9 100644 --- a/src/lib/cmd/qseow/exportapp.js +++ b/src/lib/cmd/qseow/exportapp.js @@ -1,6 +1,6 @@ import xlsx from 'node-xlsx'; -import fs from 'fs'; -import path from 'path'; +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'; diff --git a/src/lib/cmd/qseow/getbookmark.js b/src/lib/cmd/qseow/getbookmark.js index 8a26103..3a43199 100644 --- a/src/lib/cmd/qseow/getbookmark.js +++ b/src/lib/cmd/qseow/getbookmark.js @@ -1,6 +1,6 @@ import enigma from 'enigma.js'; import { table } from 'table'; -import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js'; import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; import { catchLog } from '../../util/log.js'; diff --git a/src/lib/cmd/qseow/getdim.js b/src/lib/cmd/qseow/getdim.js index 95f4b1b..8f8e7f4 100644 --- a/src/lib/cmd/qseow/getdim.js +++ b/src/lib/cmd/qseow/getdim.js @@ -1,9 +1,7 @@ -/* eslint-disable no-restricted-syntax */ -/* eslint-disable no-await-in-loop */ import enigma from 'enigma.js'; import { table } from 'table'; -import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js'; import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; import { catchLog } from '../../util/log.js'; diff --git a/src/lib/cmd/qseow/getmeasure.js b/src/lib/cmd/qseow/getmeasure.js index 554eab2..589f733 100644 --- a/src/lib/cmd/qseow/getmeasure.js +++ b/src/lib/cmd/qseow/getmeasure.js @@ -1,8 +1,7 @@ -/* eslint-disable no-restricted-syntax */ import enigma from 'enigma.js'; import { table } from 'table'; -import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js'; import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; import { catchLog } from '../../util/log.js'; diff --git a/src/lib/cmd/qseow/getscript.js b/src/lib/cmd/qseow/getscript.js index e3941d2..34f4822 100644 --- a/src/lib/cmd/qseow/getscript.js +++ b/src/lib/cmd/qseow/getscript.js @@ -1,6 +1,7 @@ import enigma from 'enigma.js'; -import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; + import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js'; import { catchLog } from '../../util/log.js'; /** @@ -25,7 +26,7 @@ const getScript = async (options) => { let configEnigma; let session; try { - configEnigma = await setupEnigmaConnection(options, sessionId); + configEnigma = setupEnigmaConnection(options, sessionId); session = await enigma.create(configEnigma); logger.verbose(`Created session to server ${options.host}.`); } catch (err) { diff --git a/src/lib/cmd/qseow/gettask.js b/src/lib/cmd/qseow/gettask.js index 7e111e0..4869910 100644 --- a/src/lib/cmd/qseow/gettask.js +++ b/src/lib/cmd/qseow/gettask.js @@ -1,6 +1,6 @@ import tree from 'text-treeview'; import { table } from 'table'; -import { promises as Fs } from 'fs'; +import { promises as Fs } from 'node:fs'; import xlsx from 'node-xlsx'; import { stringify } from 'csv-stringify'; import yesno from 'yesno'; diff --git a/src/lib/cmd/qseow/getvariable.js b/src/lib/cmd/qseow/getvariable.js index b66cef3..3ca271b 100644 --- a/src/lib/cmd/qseow/getvariable.js +++ b/src/lib/cmd/qseow/getvariable.js @@ -1,9 +1,7 @@ -/* eslint-disable no-restricted-syntax */ -/* eslint-disable no-await-in-loop */ import enigma from 'enigma.js'; import { table } from 'table'; -import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/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 { catchLog } from '../../util/log.js'; diff --git a/src/lib/cmd/qseow/import-masteritem-excel.js b/src/lib/cmd/qseow/import-masteritem-excel.js index cc62faf..95c1f75 100644 --- a/src/lib/cmd/qseow/import-masteritem-excel.js +++ b/src/lib/cmd/qseow/import-masteritem-excel.js @@ -1,11 +1,9 @@ -/* eslint-disable no-console */ -/* eslint-disable no-await-in-loop */ import enigma from 'enigma.js'; - import xlsx from 'node-xlsx'; import { v4 as uuidCreate } from 'uuid'; -import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; + import { logger, setLoggingLevel, isPkg, execPath, verifyFileExists, sleep } from '../../../globals.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js'; import { catchLog } from '../../util/log.js'; let importCount = 0; @@ -55,7 +53,7 @@ const createColorMap = async (app, colorMapId, newPerValueColorMap) => { // const newColorMapProperties = await newGenericColorMapRefModel.getProperties(); // const newColorMapPropertiesLayout = await newGenericColorMapRefModel.getLayout(); // 4. Set properties of created color map - const res = await newGenericColorMapRefModel.setProperties(newGenericColorMapRefProp); + const _res = await newGenericColorMapRefModel.setProperties(newGenericColorMapRefProp); // let res = await newGenericColorMapRefModel.setProperties({ // qInfo: { diff --git a/src/lib/cmd/qseow/importtask.js b/src/lib/cmd/qseow/importtask.js index 0c4d7c9..3f24231 100644 --- a/src/lib/cmd/qseow/importtask.js +++ b/src/lib/cmd/qseow/importtask.js @@ -1,12 +1,7 @@ import xlsx from 'node-xlsx'; import { parse } from 'csv-parse'; - -// const { parse } = require('csv-parse/lib/sync'); -import fs from 'fs'; - -// const fsp = require('fs').promises; - -import { finished } from 'stream/promises'; +import fs from 'node:fs'; +import { finished } from 'node:stream/promises'; import { logger, setLoggingLevel, isPkg, execPath, verifyFileExists, isNumeric } from '../../../globals.js'; import QlikSenseTasks from '../../task/class_alltasks.js'; diff --git a/src/lib/cmd/qseow/scramblefield.js b/src/lib/cmd/qseow/scramblefield.js index 3ba3d2b..babeff2 100644 --- a/src/lib/cmd/qseow/scramblefield.js +++ b/src/lib/cmd/qseow/scramblefield.js @@ -1,5 +1,5 @@ import enigma from 'enigma.js'; -import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma_util.js'; import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; import { catchLog } from '../../util/log.js'; diff --git a/src/lib/cmd/qseow/vistask.js b/src/lib/cmd/qseow/vistask.js index ffb0d39..9b4a8d4 100644 --- a/src/lib/cmd/qseow/vistask.js +++ b/src/lib/cmd/qseow/vistask.js @@ -1,8 +1,8 @@ -import http from 'http'; -import path from 'path'; -import fs from 'fs'; +import http from 'node:http'; +import path from 'node:path'; +import fs from 'node:fs'; import handlebars from 'handlebars'; -import { Readable } from 'stream'; +import { Readable } from 'node:stream'; import { appVersion, logger, setLoggingLevel, isPkg, execPath, verifyFileExists } from '../../../globals.js'; import QlikSenseTasks from '../../task/class_alltasks.js'; diff --git a/src/lib/task/class_allcompositeevents.js b/src/lib/task/class_allcompositeevents.js index cd06426..3bc4a89 100644 --- a/src/lib/task/class_allcompositeevents.js +++ b/src/lib/task/class_allcompositeevents.js @@ -1,7 +1,7 @@ import axios from 'axios'; -import path from 'path'; +import path from 'node:path'; import { logger, execPath, verifyFileExists } from '../../globals.js'; -import setupQRSConnection from '../util/qseow/qrs.js'; +import { setupQrsConnection } from '../util/qseow/qrs.js'; import QlikSenseCompositeEvent from './class_compositeevent.js'; import { catchLog } from '../util/log.js'; @@ -42,7 +42,7 @@ class QlikSenseCompositeEvents { try { logger.debug('GET SCHEMAEVENT: Starting get composite events from QSEoW'); - const axiosConfig = await setupQRSConnection(this.options, { + const axiosConfig = await setupQrsConnection(this.options, { method: 'get', fileCert: this.fileCert, fileCertKey: this.fileCertKey, diff --git a/src/lib/task/class_allschemaevents.js b/src/lib/task/class_allschemaevents.js index f6966cd..3e2bf94 100644 --- a/src/lib/task/class_allschemaevents.js +++ b/src/lib/task/class_allschemaevents.js @@ -1,7 +1,7 @@ import axios from 'axios'; -import path from 'path'; +import path from 'node:path'; import { logger, execPath } from '../../globals.js'; -import setupQRSConnection from '../util/qseow/qrs.js'; +import { setupQrsConnection } from '../util/qseow/qrs.js'; import QlikSenseSchemaEvent from './class_schemaevent.js'; import { catchLog } from '../util/log.js'; @@ -59,7 +59,7 @@ class QlikSenseSchemaEvents { try { logger.debug('GET SCHEMA EVENT: Starting get schema events from QSEoW'); - const axiosConfig = await setupQRSConnection(this.options, { + const axiosConfig = await setupQrsConnection(this.options, { method: 'get', fileCert: this.fileCert, fileCertKey: this.fileCertKey, diff --git a/src/lib/task/class_alltasks.js b/src/lib/task/class_alltasks.js index 99faa55..4925337 100644 --- a/src/lib/task/class_alltasks.js +++ b/src/lib/task/class_alltasks.js @@ -1,8 +1,8 @@ import axios from 'axios'; -import path from 'path'; +import path from 'node:path'; import { v4 as uuidv4, validate } from 'uuid'; import { logger, execPath } from '../../globals.js'; -import setupQRSConnection from '../util/qseow/qrs.js'; +import { setupQrsConnection } from '../util/qseow/qrs.js'; import { mapTaskType, @@ -1297,7 +1297,7 @@ class QlikSenseTasks { const body = newCompositeEvent; // Save task to QSEoW - const axiosConfig = setupQRSConnection(this.options, { + const axiosConfig = setupQrsConnection(this.options, { method: 'post', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -1367,7 +1367,7 @@ class QlikSenseTasks { }; // Save task to QSEoW - const axiosConfig = setupQRSConnection(this.options, { + const axiosConfig = setupQrsConnection(this.options, { method: 'post', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -1430,7 +1430,7 @@ class QlikSenseTasks { }; // Save task to QSEoW - const axiosConfig = setupQRSConnection(this.options, { + const axiosConfig = setupQrsConnection(this.options, { method: 'post', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -1497,7 +1497,7 @@ class QlikSenseTasks { }; // Save task to QSEoW - const axiosConfig = setupQRSConnection(this.options, { + const axiosConfig = setupQrsConnection(this.options, { method: 'post', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -1604,7 +1604,7 @@ class QlikSenseTasks { try { // Get reload tasks if (filter === '') { - axiosConfig = setupQRSConnection(this.options, { + axiosConfig = setupQrsConnection(this.options, { method: 'get', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -1612,7 +1612,7 @@ class QlikSenseTasks { path: '/qrs/reloadtask/full', }); } else { - axiosConfig = setupQRSConnection(this.options, { + axiosConfig = setupQrsConnection(this.options, { method: 'get', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -1634,7 +1634,7 @@ class QlikSenseTasks { try { // Get external program tasks if (filter === '') { - axiosConfig = setupQRSConnection(this.options, { + axiosConfig = setupQrsConnection(this.options, { method: 'get', fileCert: this.fileCert, fileCertKey: this.fileCertKey, @@ -1642,7 +1642,7 @@ class QlikSenseTasks { path: '/qrs/externalprogramtask/full', }); } else { - axiosConfig = setupQRSConnection(this.options, { + axiosConfig = setupQrsConnection(this.options, { method: 'get', fileCert: this.fileCert, fileCertKey: this.fileCertKey, diff --git a/src/lib/task/task_qrs.js b/src/lib/task/task_qrs.js index 98afa2a..07eec88 100644 --- a/src/lib/task/task_qrs.js +++ b/src/lib/task/task_qrs.js @@ -1,11 +1,11 @@ import axios from 'axios'; -import path from 'path'; +import path from 'node:path'; // const { promises: Fs } = require('fs'); // const yesno = require('yesno'); import { logger, execPath } from '../../globals.js'; -import setupQRSConnection from '../util/qseow/qrs.js'; +import { setupQrsConnection } from '../util/qseow/qrs.js'; import getCertFilePaths from '../util/cert.js'; import { catchLog } from '../util/log.js'; // const { QlikSenseTasks } = require('./class_alltasks'); @@ -31,7 +31,7 @@ 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 = await setupQrsConnection(options, { method: 'get', fileCert: certFilesFullPath.fileCert, fileCertKey: certFilesFullPath.fileCertKey, @@ -110,7 +110,7 @@ export const getTasksFromQseow = async (options) => { } logger.debug(`GET TASK: Final QRS query filter: ${filter}`); - const axiosConfig = await setupQRSConnection(options, { + const axiosConfig = await setupQrsConnection(options, { method: 'get', fileCert, fileCertKey, @@ -142,7 +142,7 @@ export const updateReloadTask = async (options, payload) => { // Get cert files const certFilesFullPath = await getCertFilePaths(options); - const axiosConfig = await setupQRSConnection(options, { + const axiosConfig = await setupQrsConnection(options, { method: 'post', fileCert: certFilesFullPath.fileCert, fileCertKey: certFilesFullPath.fileCertKey, diff --git a/src/lib/util/cert.js b/src/lib/util/cert.js index 4bcd021..9254c68 100644 --- a/src/lib/util/cert.js +++ b/src/lib/util/cert.js @@ -1,4 +1,4 @@ -import path from 'path'; +import path from 'node:path'; import { logger, execPath } from '../../globals.js'; import { catchLog } from './log.js'; diff --git a/src/lib/util/import-meta-url.js b/src/lib/util/import-meta-url.js index 7d389f9..9844f01 100644 --- a/src/lib/util/import-meta-url.js +++ b/src/lib/util/import-meta-url.js @@ -1 +1,3 @@ +const { createRequire } = require('node:module'); +require = createRequire(__filename); export var import_meta_url = require('url').pathToFileURL(__filename); diff --git a/src/lib/util/qscloud/assert-options.js b/src/lib/util/qscloud/assert-options.js index 34f3c81..51e807d 100644 --- a/src/lib/util/qscloud/assert-options.js +++ b/src/lib/util/qscloud/assert-options.js @@ -1,8 +1,7 @@ -import path from 'path'; +import path from 'node:path'; import { version as uuidVersion, validate as uuidValidate } from 'uuid'; import { logger, execPath, verifyFileExists } from '../../../globals.js'; -// eslint-disable-next-line import/prefer-default-export export const qscloudSharedParamAssertOptions = async (options) => { // Ensure that parameters common to all QS Cloud commands are valid if (options.authType === undefined || !options.authType) { diff --git a/src/lib/util/qseow/about.js b/src/lib/util/qseow/about.js index 4072500..3b4333d 100644 --- a/src/lib/util/qseow/about.js +++ b/src/lib/util/qseow/about.js @@ -1,52 +1,51 @@ import axios from 'axios'; import path from 'path'; import { logger, execPath } from '../../../globals.js'; -import setupQRSConnection from './qrs.js'; +import { setupQrsConnection } from './qrs.js'; import { catchLog } from '../log.js'; -function getAboutFromQseow(options) { - return new Promise((resolve, reject) => { - logger.verbose(`Getting about info from QSEoW...`); - - // Should cerrificates be used for authentication? - let axiosConfig; - if (options.authType === 'cert') { - // Make sure certificates exist - const fileCert = path.resolve(execPath, options.authCertFile); - const fileCertKey = path.resolve(execPath, options.authCertKeyFile); - const fileCertCA = path.resolve(execPath, options.authRootCertFile); - - axiosConfig = setupQRSConnection(options, { - method: 'get', - fileCert, - fileCertKey, - fileCertCA, - path: '/qrs/about', - }); - } else if (options.authType === 'jwt') { - axiosConfig = setupQRSConnection(options, { - method: 'get', - path: '/qrs/about', - }); - } +async function getAboutFromQseow(options) { + logger.verbose(`Getting about info from QSEoW...`); + + // Should cerrificates be used for authentication? + let axiosConfig; + + if (options.authType === 'cert') { + // Make sure certificates exist + const fileCert = path.resolve(execPath, options.authCertFile); + const fileCertKey = path.resolve(execPath, options.authCertKeyFile); + const fileCertCA = path.resolve(execPath, options.authRootCertFile); + + axiosConfig = setupQrsConnection(options, { + method: 'get', + fileCert, + fileCertKey, + fileCertCA, + path: '/qrs/about', + }); + } else if (options.authType === 'jwt') { + axiosConfig = setupQrsConnection(options, { + method: 'get', + path: '/qrs/about', + }); + } - logger.debug(`About to get about info from QSEoW`); - - axios - .request(axiosConfig) - .then((result) => { - if (result.status === 200) { - const response = JSON.parse(result.data); - logger.debug(`Successfully retrieved about info from QSEoW`); - // Yes, the tag exists - resolve(response); - } - resolve(false); - }) - .catch((err) => { - catchLog('GET ABOUT INFO', err); - }); - }); + logger.debug(`About to get about info from QSEoW`); + + try { + const result = await axios.request(axiosConfig); + + if (result.status === 200) { + const response = JSON.parse(result.data); + logger.debug(`Successfully retrieved about info from QSEoW`); + + return response; + } + return false; + } catch (err) { + catchLog('GET ABOUT INFO', err); + return false; + } } export default getAboutFromQseow; diff --git a/src/lib/util/qseow/app.js b/src/lib/util/qseow/app.js index 8bee75b..a42e308 100644 --- a/src/lib/util/qseow/app.js +++ b/src/lib/util/qseow/app.js @@ -1,8 +1,8 @@ import axios from 'axios'; -import path from 'path'; +import path from 'node:path'; import { validate } from 'uuid'; import { logger, execPath, getCliOptions } from '../../../globals.js'; -import setupQRSConnection from './qrs.js'; +import { setupQrsConnection } from './qrs.js'; import { catchLog } from '../log.js'; export async function getApps(options, idArray, tagArray) { @@ -70,7 +70,7 @@ export async function getApps(options, idArray, tagArray) { const fileCertKey = path.resolve(execPath, options.authCertKeyFile); const fileCertCA = path.resolve(execPath, options.authRootCertFile); - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', fileCert, fileCertKey, @@ -79,7 +79,7 @@ export async function getApps(options, idArray, tagArray) { queryParameters: [{ name: 'filter', value: filter }], }); } else if (options.authType === 'jwt') { - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', path: '/qrs/app/full', queryParameters: [{ name: 'filter', value: filter }], @@ -127,7 +127,7 @@ export async function getAppById(appId, optionsParam) { const fileCertKey = path.resolve(execPath, options.authCertKeyFile); const fileCertCA = path.resolve(execPath, options.authRootCertFile); - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', fileCert, fileCertKey, @@ -135,7 +135,7 @@ export async function getAppById(appId, optionsParam) { path: `/qrs/app/${appId}`, }); } else if (options.authType === 'jwt') { - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', path: `/qrs/app/${appId}`, }); @@ -186,7 +186,7 @@ export async function deleteAppById(appId, options) { const fileCertKey = path.resolve(execPath, options.authCertKeyFile); const fileCertCA = path.resolve(execPath, options.authRootCertFile); - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'delete', fileCert, fileCertKey, @@ -194,7 +194,7 @@ export async function deleteAppById(appId, options) { path: `/qrs/app/${appId}`, }); } else if (options.authType === 'jwt') { - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'delete', path: `/qrs/app/${appId}`, }); @@ -235,7 +235,7 @@ export async function appExistById(appId, options) { const fileCertKey = path.resolve(execPath, options.authCertKeyFile); const fileCertCA = path.resolve(execPath, options.authRootCertFile); - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', fileCert, fileCertKey, @@ -244,7 +244,7 @@ export async function appExistById(appId, options) { queryParameters: [{ name: 'filter', value: encodeURI(`id eq ${appId}`) }], }); } else if (options.authType === 'jwt') { - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', path: '/qrs/app', queryParameters: [{ name: 'filter', value: encodeURI(`id eq ${appId}`) }], diff --git a/src/lib/util/qseow/assert-options.js b/src/lib/util/qseow/assert-options.js index 45c60f7..1c3bb23 100644 --- a/src/lib/util/qseow/assert-options.js +++ b/src/lib/util/qseow/assert-options.js @@ -1,4 +1,4 @@ -import path from 'path'; +import path from 'node:path'; import { version as uuidVersion, validate as uuidValidate } from 'uuid'; import { logger, execPath, verifyFileExists } from '../../../globals.js'; diff --git a/src/lib/util/qseow/customproperties.js b/src/lib/util/qseow/customproperties.js index ce0b174..4a0e473 100644 --- a/src/lib/util/qseow/customproperties.js +++ b/src/lib/util/qseow/customproperties.js @@ -1,7 +1,7 @@ import axios from 'axios'; -import path from 'path'; +import path from 'node:path'; import { logger, execPath } from '../../../globals.js'; -import setupQRSConnection from './qrs.js'; +import { setupQrsConnection } from './qrs.js'; export function getCustomPropertiesFromQseow(options) { return new Promise((resolve, _reject) => { @@ -15,7 +15,7 @@ export function getCustomPropertiesFromQseow(options) { const fileCertKey = path.resolve(execPath, options.authCertKeyFile); const fileCertCA = path.resolve(execPath, options.authRootCertFile); - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', fileCert, fileCertKey, @@ -23,7 +23,7 @@ export function getCustomPropertiesFromQseow(options) { path: '/qrs/custompropertydefinition/full', }); } else if (options.authType === 'jwt') { - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', path: '/qrs/custompropertydefinition/full', }); @@ -74,7 +74,7 @@ export function getCustomPropertyIdByName(objectType, customPropertyName, cpExis // return new Promise((resolve, reject) => { // logger.debug(`Looking up ID for custom property named "${customPropertyName}" on object type "${objectType}"`); -// const axiosConfig = setupQRSConnection(options, { +// const axiosConfig = setupQrsConnection(options, { // method: 'get', // fileCert, // fileCertKey, @@ -135,7 +135,7 @@ export function getCustomPropertyDefinitionByName(objectType, customPropertyName // return new Promise((resolve, reject) => { // logger.debug(`Looking up definition for custom property named "${customPropertyName}" on object type "${objectType}"`); -// const axiosConfig = setupQRSConnection(options, { +// const axiosConfig = setupQrsConnection(options, { // method: 'get', // fileCert, // fileCertKey, @@ -210,7 +210,7 @@ export function doesCustomPropertyValueExist(objectType, customPropertyName, cus // `Checking if value "${customPropertyValue}" is valid for custom property "${customPropertyName}" on object type "${objectType}"` // ); -// const axiosConfig = setupQRSConnection(options, { +// const axiosConfig = setupQrsConnection(options, { // method: 'get', // fileCert, // fileCertKey, diff --git a/src/lib/util/qseow/enigma.js b/src/lib/util/qseow/enigma_util.js similarity index 64% rename from src/lib/util/qseow/enigma.js rename to src/lib/util/qseow/enigma_util.js index 77212f6..a7e77e5 100644 --- a/src/lib/util/qseow/enigma.js +++ b/src/lib/util/qseow/enigma_util.js @@ -1,62 +1,89 @@ import SenseUtilities from 'enigma.js/sense-utilities.js'; import WebSocket from 'ws'; -import path from 'path'; -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; +import path from 'node:path'; +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; import upath from 'upath'; +import sea from 'node:sea'; + import { logger, execPath, readCert } from '../../../globals.js'; -export const setupEnigmaConnection = async (options, sessionId) => { - logger.debug('Prepping for Enigma connection...'); +// Function to get Enigma.js schema file +const getEnigmaSchema = (processPkgFlag, seaFlag, 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']; - // Set up enigma.js configuration - let schemaFile; - let a; - let b; - let c; + let qixSchemaJson; + try { + // Check if the specified schema version is supported + if (!supportedSchemaVersions.includes(options.schemaVersion)) { + logger.error(`Unsupported schema version specified: ${options.schemaVersion}`); - logger.debug(`Enigma.js schema version: ${options.schemaVersion}`); + // Show supported schema versions + logger.error(`Supported schema versions: ${supportedSchemaVersions.join(', ')}`); + + logger.error(`Exiting...`); + process.exit(1); + } + + // 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}`); - // Are we running as a packaged app? - if (process.pkg) { - 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 - a = process.pkg.defaultEntrypoint; - logger.debug(`APPDUMP schema path a: ${a}`); - - // Strip off the filename - b = upath.dirname(a); - logger.debug(`APPDUMP schema path b: ${b}`); - - // Add path to package.json file - c = upath.join(b, schemaFile); - logger.debug(`APPDUMP schema path c: ${c}`); - } else { - schemaFile = `../node_modules/enigma.js/schemas/${options.schemaVersion}.json`; - logger.debug(`Enigma.js schema file: ${schemaFile}`); - - // No, we are running as native Node.js - // Get path to JS file - a = fileURLToPath(import.meta.url); - logger.debug(`APPDUMP schema path a: ${a}`); - - // Strip off the filename - b = upath.dirname(a); - logger.debug(`APPDUMP schema path b: ${b}`); - - // Add path to package.json file - c = upath.join(b, '..', '..', schemaFile); - logger.debug(`APPDUMP schema path c: ${c}`); + // 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) { + // Load schema file + qixSchemaJson = sea.getAsset(`enigma_schema_${options.schemaVersion}.json`, 'utf8'); + } else { + // No, we are running as native Node.js + const schemaFile = `../node_modules/enigma.js/schemas/${options.schemaVersion}.json`; + logger.debug(`Enigma.js schema file: ${schemaFile}`); + + // Get path to JS file + const a = fileURLToPath(import.meta.url); + 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 package.json file + const c = upath.join(b, '..', '..', '..', schemaFile); + logger.debug(`APPDUMP schema path c: ${c}`); + + qixSchemaJson = readFileSync(c); + } + } catch (err) { + logger.error(`Error when getting Enigma schema: ${err}`); + process.exit(1); } - logger.verbose(`APPDUMP: Using engine schema in file: ${c}`); - const qixSchema = JSON.parse(readFileSync(c)); + const qixSchema = JSON.parse(qixSchemaJson); + logger.debug(`Enigma.js schema: ${qixSchema}`); - // eslint-disable-next-line global-require, import/no-dynamic-require - // const qixSchema = require(`enigma.js/schemas/${options.schemaVersion}`); + return qixSchema; +}; + +export const setupEnigmaConnection = (options, sessionId) => { + logger.debug('Prepping for Enigma connection...'); + + // Set up enigma.js configuration + logger.debug(`Enigma.js schema version: ${options.schemaVersion}`); + const qixSchema = getEnigmaSchema(process.pkg, sea.isSea(), options); let enigmaConfig; // Should certificates be used for authentication? @@ -66,8 +93,9 @@ export const setupEnigmaConnection = async (options, sessionId) => { logger.verbose('Verify that cert files exists'); const fileCert = path.resolve(execPath, options.authCertFile); const fileCertKey = path.resolve(execPath, options.authCertKeyFile); + const fileCa = path.resolve(execPath, options.authRootCertFile); - if (!fileCert || !fileCertKey) { + if (!fileCert || !fileCertKey || !fileCa) { logger.error(`Certificate file(s) not found when setting up Enigma connection`); process.exit(1); } @@ -88,8 +116,9 @@ export const setupEnigmaConnection = async (options, sessionId) => { new WebSocket(url, { key: readCert(fileCertKey), cert: readCert(fileCert), + ca: [readCert(fileCa)], headers: { - 'X-Qlik-User': `UserDirectory=${options.authUserDir};UserId=${options.authUserId}`, + 'X-Qlik-User': `UserDirectory=${encodeURIComponent(options.authUserDir)};UserId=${encodeURIComponent(options.authUserId)}`, }, rejectUnauthorized: false, }), diff --git a/src/lib/util/qseow/proxy.js b/src/lib/util/qseow/proxy.js index 7c27494..b68face 100644 --- a/src/lib/util/qseow/proxy.js +++ b/src/lib/util/qseow/proxy.js @@ -1,7 +1,7 @@ import axios from 'axios'; -import path from 'path'; +import path from 'node:path'; import { logger, execPath } from '../../../globals.js'; -import setupQRSConnection from './qrs.js'; +import { setupQrsConnection } from './qrs.js'; import { catchLog } from '../log.js'; const getProxiesFromQseow = async (options, _sessionCookie) => { @@ -12,7 +12,7 @@ const getProxiesFromQseow = async (options, _sessionCookie) => { const fileCertKey = path.resolve(execPath, options.authCertKeyFile); const fileCertCA = path.resolve(execPath, options.authRootCertFile); - const axiosConfig = setupQRSConnection(options, { + const axiosConfig = setupQrsConnection(options, { method: 'get', fileCert, fileCertKey, diff --git a/src/lib/util/qseow/qps.js b/src/lib/util/qseow/qps.js index b066a96..5e3ffe4 100644 --- a/src/lib/util/qseow/qps.js +++ b/src/lib/util/qseow/qps.js @@ -1,4 +1,4 @@ -import https from 'https'; +import https from 'node:https'; import { logger, generateXrfKey, readCert } from '../../../globals.js'; const setupQPSConnection = (options, param) => { diff --git a/src/lib/util/qseow/qrs.js b/src/lib/util/qseow/qrs.js index 4bf51bb..ef77826 100644 --- a/src/lib/util/qseow/qrs.js +++ b/src/lib/util/qseow/qrs.js @@ -1,8 +1,42 @@ -import https from 'https'; +import https from 'node:https'; + import { logger, generateXrfKey, readCert } from '../../../globals.js'; -const setupQRSConnection = (options, param) => { - // eslint-disable-next-line no-unused-vars +// Function to sanitize virtual proxy +export function sanitizeVirtualProxy(virtualProxy) { + // - Should always start with a / + // - Should never end with a / + if (virtualProxy === '') { + virtualProxy = '/'; + } else { + if (!virtualProxy.startsWith('/')) { + virtualProxy = `/${virtualProxy}`; + } + + // Remove all trailing / + virtualProxy = virtualProxy.replace(/\/+$/, ''); + } + + return virtualProxy; +} + +// Function to set up connection to Qlik Sense Repository Service (QRS) +export function setupQrsConnection(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); + } + } else if (options.authType === 'jwt') { + // options.authJwt should be set + if (!options.authJwt) { + logger.error(`Setting up connection to QRS. Missing JWT. Exiting.`); + process.exit(1); + } + } + // Ensure valid http method if ( !param.method || @@ -71,6 +105,7 @@ const setupQRSConnection = (options, param) => { logger.verbose(`Using JWT for authentication with QRS`); const httpsAgent = new https.Agent({ + // rejectUnauthorized: options.secure !== 'false', rejectUnauthorized: false, }); @@ -101,13 +136,10 @@ const setupQRSConnection = (options, param) => { // Add parameters (if any) if (param.queryParameters?.length > 0) { - // eslint-disable-next-line no-restricted-syntax for (const queryParam of param.queryParameters) { axiosConfig.url += `&${queryParam.name}=${queryParam.value}`; } } return axiosConfig; -}; - -export default setupQRSConnection; +} diff --git a/src/lib/util/qseow/session.js b/src/lib/util/qseow/session.js index 26379e9..dfdb667 100644 --- a/src/lib/util/qseow/session.js +++ b/src/lib/util/qseow/session.js @@ -1,10 +1,10 @@ import axios from 'axios'; -import path from 'path'; +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 setupQRSConnection from './qrs.js'; +import { setupQrsConnection } from './qrs.js'; import { catchLog } from '../log.js'; import getProxiesFromQseow from './proxy.js'; @@ -89,7 +89,7 @@ export const getSessionsFromQseow = async (options, sessionCookie) => { // Virtual proxies are specified as an array of strings // Filter format is: id eq 'vpName1' or id eq 'vpName2' or id eq 'vpName3' const vpFilter = options.sessionVirtualProxy.map((vp) => `prefix eq '${vp}'`).join(' or '); - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', fileCert, fileCertKey, @@ -99,7 +99,7 @@ export const getSessionsFromQseow = async (options, sessionCookie) => { }); } else { // No virtual proxies specified, get all of them from QRS - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', fileCert, fileCertKey, diff --git a/src/lib/util/qseow/tag.js b/src/lib/util/qseow/tag.js index 1738d68..5138bd5 100644 --- a/src/lib/util/qseow/tag.js +++ b/src/lib/util/qseow/tag.js @@ -1,7 +1,7 @@ import axios from 'axios'; -import path from 'path'; +import path from 'node:path'; import { logger, execPath } from '../../../globals.js'; -import setupQRSConnection from './qrs.js'; +import { setupQrsConnection } from './qrs.js'; import { catchLog } from '../log.js'; export function getTagsFromQseow(options) { @@ -16,7 +16,7 @@ export function getTagsFromQseow(options) { const fileCertKey = path.resolve(execPath, options.authCertKeyFile); const fileCertCA = path.resolve(execPath, options.authRootCertFile); - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', fileCert, fileCertKey, @@ -24,7 +24,7 @@ export function getTagsFromQseow(options) { path: '/qrs/tag/full', }); } else if (options.authType === 'jwt') { - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', path: '/qrs/tag/full', }); @@ -74,7 +74,7 @@ export function getTagIdByName(tagName, tagsExisting) { // logger.debug(`Looking up ID for tag named "${tagName}"`); // // const filter = encodeURI(`name eq '👍😎 updateSheetThumbnail'`); -// const axiosConfig = setupQRSConnection(options, { +// const axiosConfig = setupQrsConnection(options, { // method: 'get', // fileCert, // fileCertKey, diff --git a/src/lib/util/qseow/task.js b/src/lib/util/qseow/task.js index 83b7edf..577d5ad 100644 --- a/src/lib/util/qseow/task.js +++ b/src/lib/util/qseow/task.js @@ -1,9 +1,9 @@ import axios from 'axios'; -import fs from 'fs'; -import path from 'path'; +import fs from 'node:fs'; +import path from 'node:path'; import { validate } from 'uuid'; import { logger, execPath, getCliOptions } from '../../../globals.js'; -import setupQRSConnection from './qrs.js'; +import { setupQrsConnection } from './qrs.js'; import { catchLog } from '../log.js'; // Check if a task with a given id exists @@ -39,7 +39,7 @@ export async function taskExistById(taskId, optionsParam) { const fileCertCA = path.resolve(execPath, options.authRootCertFile); // const filter = encodeURI(`name eq '👍😎 updateSheetThumbnail'`); - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', fileCert, fileCertKey, @@ -48,7 +48,7 @@ export async function taskExistById(taskId, optionsParam) { queryParameters: [{ name: 'filter', value: encodeURI(`id eq ${taskId}`) }], }); } else if (options.authType === 'jwt') { - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', path: '/qrs/task', queryParameters: [{ name: 'filter', value: encodeURI(`id eq ${taskId}`) }], @@ -108,7 +108,7 @@ export async function getTaskByName(taskName, optionsParam) { const fileCertKey = path.resolve(execPath, options.authCertKeyFile); const fileCertCA = path.resolve(execPath, options.authRootCertFile); - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', fileCert, fileCertKey, @@ -117,7 +117,7 @@ export async function getTaskByName(taskName, optionsParam) { queryParameters: [{ name: 'filter', value: encodeURI(`name eq '${taskName}'`) }], }); } else if (options.authType === 'jwt') { - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', path: '/qrs/task/full', queryParameters: [{ name: 'filter', value: encodeURI(`name eq '${taskName}'`) }], @@ -180,7 +180,7 @@ export async function getTaskById(taskId, optionsParam) { const fileCertKey = path.resolve(execPath, options.authCertKeyFile); const fileCertCA = path.resolve(execPath, options.authRootCertFile); - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', fileCert, fileCertKey, @@ -189,7 +189,7 @@ export async function getTaskById(taskId, optionsParam) { queryParameters: [{ name: 'filter', value: encodeURI(`id eq ${taskId}`) }], }); } else if (options.authType === 'jwt') { - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'get', path: `/qrs/task/full`, queryParameters: [{ name: 'filter', value: encodeURI(`id eq ${taskId}`) }], @@ -266,7 +266,7 @@ export async function deleteReloadTaskById(taskId, optionsParam) { return false; } - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'delete', fileCert, fileCertKey, @@ -274,7 +274,7 @@ export async function deleteReloadTaskById(taskId, optionsParam) { path: `/qrs/reloadtask/${taskId}`, }); } else if (optionsParam.authType === 'jwt') { - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'delete', path: `/qrs/reloadtask/${taskId}`, }); @@ -338,7 +338,7 @@ export async function deleteExternalProgramTaskById(taskId, optionsParam) { return false; } - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'delete', fileCert, fileCertKey, @@ -346,7 +346,7 @@ export async function deleteExternalProgramTaskById(taskId, optionsParam) { path: `/qrs/externalprogramtask/${taskId}`, }); } else if (optionsParam.authType === 'jwt') { - axiosConfig = setupQRSConnection(options, { + axiosConfig = setupQrsConnection(options, { method: 'delete', path: `/qrs/externalprogramtask/${taskId}`, });