From f21f4f9ea8ff9105b8c61b901bd497500d67081f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6ran=20Sander?= Date: Thu, 24 Oct 2024 20:31:41 +0000 Subject: [PATCH] refactor to better support coming QS Cloud commands --- .eslintignore | 2 - .eslintrc.yml | 32 - .jshintrc | 3 - eslint.config.js | 37 + src/ctrl-q.js | 1045 ++--------------- src/lib/app/class_allapps.js | 10 +- src/lib/cli/qscloud-test-connection.js | 37 + src/lib/cli/qseow-cp-user-activity-bucket.js | 59 + src/lib/cli/qseow-delete-master-dimension.js | 43 + src/lib/cli/qseow-delete-master-measure.js | 43 + src/lib/cli/qseow-delete-proxy-session.js | 45 + src/lib/cli/qseow-delete-variable.js | 47 + src/lib/cli/qseow-export-app-to-file.js | 73 ++ src/lib/cli/qseow-get-bookmark.js | 46 + src/lib/cli/qseow-get-master-dimension.js | 43 + src/lib/cli/qseow-get-master-measure.js | 44 + src/lib/cli/qseow-get-proxy-session.js | 52 + src/lib/cli/qseow-get-script.js | 39 + src/lib/cli/qseow-get-task.js | 78 ++ src/lib/cli/qseow-get-variable.js | 46 + src/lib/cli/qseow-import-app-from-file.js | 53 + src/lib/cli/qseow-import-task-from-file.js | 62 + src/lib/cli/qseow-master-item-import.js | 101 ++ src/lib/cli/qseow-scramble-field.js | 40 + src/lib/cli/qseow-set-task-cp.js | 55 + src/lib/cli/qseow-show-version.js | 16 + src/lib/cli/qseow-test-connection.js | 39 + src/lib/cli/qseow-visualise-task.js | 41 + src/lib/cmd/qscloud/testconnection.js | 35 + src/lib/cmd/{ => qseow}/createdim.js | 6 +- .../cmd/{ => qseow}/createuseractivitycp.js | 4 +- src/lib/cmd/{ => qseow}/deletedim.js | 6 +- src/lib/cmd/{ => qseow}/deletemeasure.js | 6 +- src/lib/cmd/{ => qseow}/deletesessions.js | 6 +- src/lib/cmd/{ => qseow}/deletevariable.js | 8 +- src/lib/cmd/{ => qseow}/exportapp.js | 6 +- src/lib/cmd/{ => qseow}/getbookmark.js | 6 +- src/lib/cmd/{ => qseow}/getdim.js | 6 +- src/lib/cmd/{ => qseow}/getmeasure.js | 6 +- src/lib/cmd/{ => qseow}/getscript.js | 6 +- src/lib/cmd/{ => qseow}/getsessions.js | 6 +- src/lib/cmd/{ => qseow}/gettask.js | 10 +- src/lib/cmd/{ => qseow}/getvariable.js | 8 +- .../{ => qseow}/import-masteritem-excel.js | 6 +- src/lib/cmd/{ => qseow}/importapp.js | 12 +- src/lib/cmd/{ => qseow}/importtask.js | 14 +- src/lib/cmd/{ => qseow}/scramblefield.js | 6 +- src/lib/cmd/{ => qseow}/settaskcp.js | 6 +- src/lib/cmd/{ => qseow}/testconnection.js | 6 +- src/lib/cmd/{ => qseow}/useractivity.js | 4 +- src/lib/cmd/{ => qseow}/vistask.js | 4 +- src/lib/task/class_allcompositeevents.js | 2 +- src/lib/task/class_allschemaevents.js | 2 +- src/lib/task/class_alltasks.js | 12 +- src/lib/task/class_task.js | 2 +- src/lib/task/task_qrs.js | 2 +- src/lib/util/qscloud/assert-options.js | 23 + src/lib/util/{ => qseow}/about.js | 4 +- src/lib/util/{ => qseow}/app.js | 4 +- src/lib/util/{ => qseow}/assert-options.js | 4 +- src/lib/util/{ => qseow}/customproperties.js | 2 +- src/lib/util/{ => qseow}/enigma.js | 2 +- src/lib/util/{ => qseow}/lookups.js | 0 src/lib/util/{ => qseow}/proxy.js | 4 +- src/lib/util/{ => qseow}/qps.js | 2 +- src/lib/util/{ => qseow}/qrs.js | 2 +- src/lib/util/{ => qseow}/session.js | 4 +- src/lib/util/{ => qseow}/tag.js | 4 +- src/lib/util/{ => qseow}/task.js | 4 +- 69 files changed, 1393 insertions(+), 1100 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc.yml delete mode 100644 .jshintrc create mode 100644 eslint.config.js create mode 100644 src/lib/cli/qscloud-test-connection.js create mode 100644 src/lib/cli/qseow-cp-user-activity-bucket.js create mode 100644 src/lib/cli/qseow-delete-master-dimension.js create mode 100644 src/lib/cli/qseow-delete-master-measure.js create mode 100644 src/lib/cli/qseow-delete-proxy-session.js create mode 100644 src/lib/cli/qseow-delete-variable.js create mode 100644 src/lib/cli/qseow-export-app-to-file.js create mode 100644 src/lib/cli/qseow-get-bookmark.js create mode 100644 src/lib/cli/qseow-get-master-dimension.js create mode 100644 src/lib/cli/qseow-get-master-measure.js create mode 100644 src/lib/cli/qseow-get-proxy-session.js create mode 100644 src/lib/cli/qseow-get-script.js create mode 100644 src/lib/cli/qseow-get-task.js create mode 100644 src/lib/cli/qseow-get-variable.js create mode 100644 src/lib/cli/qseow-import-app-from-file.js create mode 100644 src/lib/cli/qseow-import-task-from-file.js create mode 100644 src/lib/cli/qseow-master-item-import.js create mode 100644 src/lib/cli/qseow-scramble-field.js create mode 100644 src/lib/cli/qseow-set-task-cp.js create mode 100644 src/lib/cli/qseow-show-version.js create mode 100644 src/lib/cli/qseow-test-connection.js create mode 100644 src/lib/cli/qseow-visualise-task.js create mode 100644 src/lib/cmd/qscloud/testconnection.js rename src/lib/cmd/{ => qseow}/createdim.js (98%) rename src/lib/cmd/{ => qseow}/createuseractivitycp.js (99%) rename src/lib/cmd/{ => qseow}/deletedim.js (97%) rename src/lib/cmd/{ => qseow}/deletemeasure.js (97%) rename src/lib/cmd/{ => qseow}/deletesessions.js (89%) rename src/lib/cmd/{ => qseow}/deletevariable.js (97%) rename src/lib/cmd/{ => qseow}/exportapp.js (97%) rename src/lib/cmd/{ => qseow}/getbookmark.js (98%) rename src/lib/cmd/{ => qseow}/getdim.js (98%) rename src/lib/cmd/{ => qseow}/getmeasure.js (98%) rename src/lib/cmd/{ => qseow}/getscript.js (95%) rename src/lib/cmd/{ => qseow}/getsessions.js (98%) rename src/lib/cmd/{ => qseow}/gettask.js (99%) rename src/lib/cmd/{ => qseow}/getvariable.js (98%) rename src/lib/cmd/{ => qseow}/import-masteritem-excel.js (99%) rename src/lib/cmd/{ => qseow}/importapp.js (90%) rename src/lib/cmd/{ => qseow}/importtask.js (97%) rename src/lib/cmd/{ => qseow}/scramblefield.js (95%) rename src/lib/cmd/{ => qseow}/settaskcp.js (98%) rename src/lib/cmd/{ => qseow}/testconnection.js (91%) rename src/lib/cmd/{ => qseow}/useractivity.js (98%) rename src/lib/cmd/{ => qseow}/vistask.js (99%) create mode 100644 src/lib/util/qscloud/assert-options.js rename src/lib/util/{ => qseow}/about.js (94%) rename src/lib/util/{ => qseow}/app.js (98%) rename src/lib/util/{ => qseow}/assert-options.js (98%) rename src/lib/util/{ => qseow}/customproperties.js (99%) rename src/lib/util/{ => qseow}/enigma.js (99%) rename src/lib/util/{ => qseow}/lookups.js (100%) rename src/lib/util/{ => qseow}/proxy.js (92%) rename src/lib/util/{ => qseow}/qps.js (97%) rename src/lib/util/{ => qseow}/qrs.js (98%) rename src/lib/util/{ => qseow}/session.js (99%) rename src/lib/util/{ => qseow}/tag.js (97%) rename src/lib/util/{ => qseow}/task.js (99%) diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 8894739..0000000 --- a/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -./node_modules/**/*.js -./src/node_modules/**/*.js \ No newline at end of file diff --git a/.eslintrc.yml b/.eslintrc.yml deleted file mode 100644 index 61fcb09..0000000 --- a/.eslintrc.yml +++ /dev/null @@ -1,32 +0,0 @@ -env: - # es6: true - es2023: true - node: true - # commonjs: false -extends: - - airbnb-base - # - eslint:recommended - - prettier -parserOptions: - ecmaVersion: 2023 - sourceType: module - requireConfigFile: false - babelOptions: - plugins: - - '@babel/plugin-syntax-import-assertions' -parser: '@babel/eslint-parser' -rules: - prettier/prettier: error - import/no-unresolved: 2 - import/no-commonjs: 2 - import/extensions: [2, 'ignorePackages'] -plugins: - - prettier - - import -globals: - beforeAll: readonly - afterAll: readonly - describe: readonly - expect: readonly - test: readonly - it: readonly diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 31a0964..0000000 --- a/.jshintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "esversion": 9 -} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..44fc837 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,37 @@ +import prettier from 'eslint-plugin-prettier'; +import globals from 'globals'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import js from '@eslint/js'; +import { FlatCompat } from '@eslint/eslintrc'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const compat = new FlatCompat({ + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all, +}); + +// export default [...compat.extends("airbnb-base", "prettier"), { +export default [ + ...compat.extends('prettier'), + { + plugins: { + prettier, + }, + + languageOptions: { + globals: { + ...globals.node, + }, + + ecmaVersion: 12, + sourceType: 'module', + }, + + rules: { + 'prettier/prettier': 'error', + }, + }, +]; diff --git a/src/ctrl-q.js b/src/ctrl-q.js index 2d43db2..3517a3d 100644 --- a/src/ctrl-q.js +++ b/src/ctrl-q.js @@ -2,47 +2,31 @@ import { Command, Option } from 'commander'; import { logger, appVersion, setLoggingLevel, setCliOptions } from './globals.js'; import { catchLog, logStartupInfo } from './lib/util/log.js'; -// const { createUserActivityCustomProperty } = require('./lib/createuseractivitycp'); - -import getMasterDimension from './lib/cmd/getdim.js'; - -import deleteMasterDimension from './lib/cmd/deletedim.js'; -import getMasterMeasure from './lib/cmd/getmeasure.js'; -import deleteMasterMeasure from './lib/cmd/deletemeasure.js'; -import getVariable from './lib/cmd/getvariable.js'; -import deleteVariable from './lib/cmd/deletevariable.js'; -import getBookmark from './lib/cmd/getbookmark.js'; -import importMasterItemFromFile from './lib/cmd/import-masteritem-excel.js'; -import scrambleField from './lib/cmd/scramblefield.js'; -import getScript from './lib/cmd/getscript.js'; -import getTask from './lib/cmd/gettask.js'; -import setTaskCustomProperty from './lib/cmd/settaskcp.js'; -import importTaskFromFile from './lib/cmd/importtask.js'; -import importAppFromFile from './lib/cmd/importapp.js'; -import exportAppToFile from './lib/cmd/exportapp.js'; -import testConnection from './lib/cmd/testconnection.js'; -import visTask from './lib/cmd/vistask.js'; -import getSessions from './lib/cmd/getsessions.js'; -import deleteSessions from './lib/cmd/deletesessions.js'; - -import { - sharedParamAssertOptions, - masterItemImportAssertOptions, - masterItemMeasureDeleteAssertOptions, - masterItemDimDeleteAssertOptions, - masterItemGetAssertOptions, - variableGetAssertOptions, - variableDeleteAssertOptions, - getScriptAssertOptions, - getBookmarkAssertOptions, - getTaskAssertOptions, - setTaskCustomPropertyAssertOptions, - taskImportAssertOptions, - appImportAssertOptions, - appExportAssertOptions, - getSessionsAssertOptions, - deleteSessionsAssertOptions, -} from './lib/util/assert-options.js'; +// Import command setup functions +// QSEoW +import { setupQseowUserActivityCustomPropertyCommand } from './lib/cli/qseow-cp-user-activity-bucket.js'; +import { setupQseowMasterItemImportCommand } from './lib/cli/qseow-master-item-import.js'; +import { setupQseowGetMasterMeasureCommand } from './lib/cli/qseow-get-master-measure.js'; +import { setupQseowDeleteMasterMeasureCommand } from './lib/cli/qseow-delete-master-measure.js'; +import { setupQseowGetMasterDimensionCommand } from './lib/cli/qseow-get-master-dimension.js'; +import { setupQseowDeleteMasterDimensionCommand } from './lib/cli/qseow-delete-master-dimension.js'; +import { setpQseowGetVariableCommand } from './lib/cli/qseow-get-variable.js'; +import { setupQseowDeleteVariableCommand } from './lib/cli/qseow-delete-variable.js'; +import { setupQseowScrambleFieldCommand } from './lib/cli/qseow-scramble-field.js'; +import { setupGetScriptCommand } from './lib/cli/qseow-get-script.js'; +import { setupQseowGetBookmarkCommand } from './lib/cli/qseow-get-bookmark.js'; +import { setupGetTaskCommand } from './lib/cli/qseow-get-task.js'; +import { setupQseowSetTaskCustomPropertyCommand } from './lib/cli/qseow-set-task-cp.js'; +import { setupQseowImportTaskFromFileCommand } from './lib/cli/qseow-import-task-from-file.js'; +import { setupQseowImportAppFromFileCommand } from './lib/cli/qseow-import-app-from-file.js'; +import { setupQseowExportAppCommand } from './lib/cli/qseow-export-app-to-file.js'; +import { setupQseowTestConnectionCommand } from './lib/cli/qseow-test-connection.js'; +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'; const program = new Command(); @@ -65,6 +49,7 @@ program.configureHelp({ // Basic app info program .version(appVersion) + .name('ctrl-q') .description( `Ctrl-Q is a command line utility for interacting with client-managed Qlik Sense Enterprise on Windows servers.\nAmong other things the tool does bulk import of apps and tasks, manipulates master items and scrambles in-app data.\n\nVersion: ${appVersion}` ) @@ -82,947 +67,95 @@ program.configureHelp({ logger.verbose(`About to call action handler for subcommand: ${actionCommand.name()}`); }); - // Create custom properties for tracking user activity buckets, i.e. how long ago a user was last active (last login) in Sense - // program - // .command('user-activity-cp-create') - // .description( - // 'create custom property and populate it with values ("activity buckets") indicating how long ago users last logged into Sense' - // ) - // .action(async (options) => { - // try { - // let optionsLocal = options; - // await sharedParamAssertOptions(options); - // optionsLocal = userActivityCustomPropertyAssertOptions(options); - // createUserActivityCustomProperty(optionsLocal); - // } catch (err) { - // logger.error(`USER ACTIVITY CP: ${err}`); - // } - // }) - // .addOption( - // new Option('--log-level ', 'log level') - // .choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']) - // .default('info') - // ) - // .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - // .option('--port ', 'Qlik Sense repository API port', '4242') - // .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - // .requiredOption('--secure ', 'https connection to Qlik Sense must use correct certificate. Invalid certificates will result in rejected/failed connection.', true) - // .option('--auth-user-dir ', 'user directory for user to connect with', 'Internal') - // .option('--auth-user-id ', 'user ID for user to connect with', 'sa_repository') + // -------------------------------------------------------- + // Create a command for QSEoW-related sub-commands + function createQseowCommands() { + // Create a new command + // const qseow = program.command('qseow'); + const qseow = new Command('qseow'); - // .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') - // .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - // .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - // .option('--jwt ', 'JSON Web Token (JWT) to use for authenticating with Qlik Sense', '') + // Create custom properties for tracking user activity buckets, i.e. how long ago a user was last active (last login) in Sense + setupQseowUserActivityCustomPropertyCommand(qseow); - // .requiredOption('--user-directory ', 'name of user directory whose users will be updated with activity info') - // .requiredOption('--custom-property-name ', 'name of custom property that will hold user activity buckets') - // .addOption( - // new Option('--force ', 'forcibly overwrite and replace custom property and its values if it already exists') - // .choices(['true', 'false']) - // .default('false') - // ) - // .option('--activity-buckets ', 'custom property values/user activity buckets to be defined. In days.', [ - // '1', - // '7', - // '14', - // '30', - // '90', - // '180', - // '365', - // ]); + // QSEoW: Import dimensions/measures from definitions in Excel file + setupQseowMasterItemImportCommand(qseow); - // Import dimensions/measures from definitions in Excel file - program - .command('master-item-import') - .description('create master items based on definitions in a file on disk') - .action(async (options) => { - try { - await sharedParamAssertOptions(options); - masterItemImportAssertOptions(options); - importMasterItemFromFile(options); - } catch (err) { - catchLog('IMPORT EXCEL', err); - } - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') - .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .requiredOption('--app-id ', 'Qlik Sense app ID') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') + // QSEoW: Get measure command + setupQseowGetMasterMeasureCommand(qseow); - .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') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + // QSEoW: Delete measure command + setupQseowDeleteMasterMeasureCommand(qseow); - .addOption(new Option('-t, --file-type ', 'source file type').choices(['excel']).default('excel')) - .requiredOption('--file ', 'file containing master item definitions') - .requiredOption('--sheet ', 'name of Excel sheet where dim/measure flag column is found') - .addOption( - new Option( - '--col-ref-by ', - 'how to refer to columns in the source file. Options are by name or by position (zero based)' - ) - .choices(['name', 'position']) - .default('name') - ) - .requiredOption( - '--col-item-type ', - 'column where dim/measure flag is found. Use "dim-single" in that column to create dimension, "dim-drilldown" for drill-down dimension, "measure" for measure', - 'Master item type' - ) - .requiredOption( - '--col-master-item-name ', - 'column number (zero based) or name to use as master item name', - 'Master item name' - ) - .requiredOption( - '--col-master-item-descr ', - 'column number (zero based) or name to use as master item description', - 'Description' - ) - .requiredOption( - '--col-master-item-label ', - 'column number (zero based) or name to use as master item label', - 'Label' - ) - .requiredOption( - '--col-master-item-expr ', - 'column number (zero based) or name to use as master item expression', - 'Expression' - ) - .requiredOption( - '--col-master-item-tag ', - 'column number (zero based) or name to use as master item tags', - 'Tag' - ) - .requiredOption( - '--col-master-item-color ', - 'column number (zero based) or name to use as color for dimensions/measures', - 'Color' - ) - .requiredOption( - '--col-master-item-per-value-color ', - 'column number (zero based) or name to use as per-value/segment color for dimensions/measures', - 'Per value color' - ) + // QSEoW: Get dimension command + setupQseowGetMasterDimensionCommand(qseow); - .requiredOption('--sleep-between-imports ', 'sleep this many milliseconds between imports. Set to 0 to disable', 1000) - .requiredOption( - '--limit-import-count ', - 'import at most x number of master items from the Excel file. Defaults to 0 = no limit', - 0 - ) - .option('--dry-run', 'do a dry run, i.e. do not create or update anything - just show what would be done'); + // QSEoW: Delete dimension command + setupQseowDeleteMasterDimensionCommand(qseow); - // Get measure command - program - .command('master-item-measure-get') - .description('get info about one or more master measures') - .action(async (options) => { - await sharedParamAssertOptions(options); - masterItemGetAssertOptions(options); + // QSEoW: Get variable command + setpQseowGetVariableCommand(qseow); - getMasterMeasure(options); - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') - .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .requiredOption('--app-id ', 'Qlik Sense app ID') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') + // QSEoW: Delete variable command + setupQseowDeleteVariableCommand(qseow); - .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') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + // QSEoW: Scramble field command + setupQseowScrambleFieldCommand(qseow); - .addOption( - new Option('--id-type ', 'type of identifier passed in the --master-item option').choices(['id', 'name']).default('name') - ) - .option('--master-item ', 'master measure to retrieve. If not specified all measures will be retrieved') - .addOption(new Option('--output-format ', 'output format').choices(['json', 'table']).default('json')); + // QSEoW: Get script command + setupGetScriptCommand(qseow); - // Delete measure command - program - .command('master-item-measure-delete') - .description('delete master measure(s)') - .action(async (options) => { - await sharedParamAssertOptions(options); - masterItemMeasureDeleteAssertOptions(options); + // QSEoW: Get bookmark command + setupQseowGetBookmarkCommand(qseow); - deleteMasterMeasure(options); - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') - .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .requiredOption('--app-id ', 'Qlik Sense app ID') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') - - .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') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') - - .addOption(new Option('--id-type ', 'type of identifier passed in the --master-item option').choices(['id', 'name'])) - .option('--master-item ', 'names or IDs of master measures to be deleted. Multiple IDs should be space separated') - .option('--delete-all', 'delete all master measures') - .option('--dry-run', 'do a dry run, i.e. do not delete anything - just show what would be deleted'); - - // Get dimension command - program - .command('master-item-dim-get') - .description('get info about one or more master dimensions') - .action(async (options) => { - await sharedParamAssertOptions(options); - - getMasterDimension(options); - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') - .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .requiredOption('--app-id ', 'Qlik Sense app ID') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') - - .addOption(new Option('-a, --auth-type ', 'authentication type').choices(['cert', 'jwt']).default('cert')) - .requiredOption('--auth-cert-file ', 'Qlik Sense certificate file (exported from QMC)', './cert/client.pem') - .requiredOption('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .requiredOption('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + // QSEoW: Get tasks command + setupGetTaskCommand(qseow); - .addOption( - new Option('--id-type ', 'type of identifier passed in the --master-item option').choices(['id', 'name']).default('name') - ) - .option('--master-item ', 'master dimension to retrieve. If not specified all dimensions will be retrieved') - .addOption(new Option('--output-format ', 'output format').choices(['json', 'table']).default('json')); - - // Delete dimension command - program - .command('master-item-dim-delete') - .description('delete master dimension(s)') - .action(async (options) => { - await sharedParamAssertOptions(options); - masterItemDimDeleteAssertOptions(options); + // QSEoW: Set custom property on tasks command + setupQseowSetTaskCustomPropertyCommand(qseow); - deleteMasterDimension(options); - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') - .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .requiredOption('--app-id ', 'Qlik Sense app ID') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') + // QSEoW: Import tasks from definitions in Excel/CSV file + setupQseowImportTaskFromFileCommand(qseow); - .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') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + // QSEoW: Import apps from definitions in Excel file + setupQseowImportAppFromFileCommand(qseow); - .addOption(new Option('--id-type ', 'type of identifier passed in the --master-item option').choices(['id', 'name'])) - .option('--master-item ', 'names or IDs of master dimensions to be deleted. Multiple IDs should be space separated') - .option('--delete-all', 'delete all master dimensions') - .option('--dry-run', 'do a dry run, i.e. do not delete anything - just show what would be deleted'); + // QSEoW: Export apps to QVF files + setupQseowExportAppCommand(qseow); - // Get variable command - program - .command('variable-get') - .description('get variable definitions in one or more apps') - .action(async (options) => { - await sharedParamAssertOptions(options); - variableGetAssertOptions(options); + // QSEoW: Test connection command + setupQseowTestConnectionCommand(qseow); - getVariable(options); - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--engine-port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') - .option('--qrs-port ', 'Qlik Sense repository service (QRS) port (usually 4747 for cert auth, 443 for jwt auth)', '4242') - .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .option('--app-id ', 'Qlik Sense app ID(s) to get variables from') - .option('--app-tag ', 'Qlik Sense app tag(s) to get variables') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') + // QSEoW: Show version command + setupQseowShowVersionCommand(qseow); - .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') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + // QSEoW: Visualise task network + setupQseowVisualiseTaskCommand(qseow); - .addOption( - new Option('--id-type ', 'type of identifier passed in the --variable option').choices(['id', 'name']).default('name') - ) - .option('--variable ', 'variables to retrieve. If not specified all variables will be retrieved') - .addOption(new Option('--output-format ', 'output format').choices(['json', 'table']).default('json')); + // QSEoW: Get proxy sessions + setupQseowGetProxySessionsCommand(qseow); - // Delete variable command - program - .command('variable-delete') - .description('delete one or more variables in one or more apps') - .action(async (options) => { - await sharedParamAssertOptions(options); - variableDeleteAssertOptions(options); - - deleteVariable(options); - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--engine-port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') - .option('--qrs-port ', 'Qlik Sense repository service (QRS) port (usually 4242 for cert auth, 443 for jwt auth)', '4242') - .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .option('--app-id ', 'Qlik Sense app ID(s) to get variables from') - .option('--app-tag ', 'Qlik Sense app tag(s) to get variables') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') - - .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') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') - - .addOption( - new Option('--id-type ', 'type of identifier passed in the --variable option').choices(['id', 'name']).default('name') - ) - .option('--variable ', 'variables to retrieve. If not specified all variables will be retrieved') - .option('--delete-all', 'delete all variables') - .option('--dry-run', 'do a dry run, i.e. do not delete anything - just show what would be deleted'); - - // Scramble field command - program - .command('field-scramble') - .description('scramble one or more fields in an app. A new app with the scrambled data is created.') - .action(async (options) => { - await sharedParamAssertOptions(options); - - scrambleField(options); - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') - .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .requiredOption('--app-id ', 'Qlik Sense app ID') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') - - .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') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') - - .requiredOption('--field-name ', 'name of field(s) to be scrambled') - .requiredOption('--new-app-name ', 'name of new app that will contain scrambled data'); - - // Get script command - program - .command('script-get') - .description('get script from Qlik Sense app') - .action(async (options) => { - await sharedParamAssertOptions(options); - getScriptAssertOptions(options); - - getScript(options); - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') - .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .requiredOption('--app-id ', 'Qlik Sense app ID') - .addOption(new Option('--open-without-data ', 'open app without data').choices(['true', 'false']).default('true')) - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') - - .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') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server'); - - // Get bookmark command - program - .command('bookmark-get') - .description('get info about one or more bookmarks') - .action(async (options) => { - await sharedParamAssertOptions(options); - getBookmarkAssertOptions(options); - - getBookmark(options); - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') - .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .requiredOption('--app-id ', 'Qlik Sense app ID') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') - - .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') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') - - .addOption( - new Option('--id-type ', 'type of bookmark identifier passed in the --bookmark option') - .choices(['id', 'name']) - .default('name') - ) - .option('--bookmark ', 'bookmark to retrieve. If not specified all bookmarks will be retrieved') - .option('--output-format ', 'output format', 'json'); - - // Get tasks command - program - .command('task-get') - .description('get info about one or more tasks') - .action(async (options) => { - const newOptions = options; - // If options.tableDetails is true, it means --table-details was passed as options without any explicit value. - // This is allowed, but should be interpreted as "all" table details. - // Make options.tableDetails an array with all possible table details. - if (options.tableDetails === true) { - newOptions.tableDetails = ['common', 'lastexecution', 'tag', 'customproperty', 'schematrigger', 'compositetrigger']; - } - - await sharedParamAssertOptions(newOptions); - getTaskAssertOptions(newOptions); - - // If --output-format=table and --task-type is not specified, default to ['reload', 'ext-program'] - if (newOptions.outputFormat === 'table' && !newOptions.taskType) { - newOptions.taskType = ['reload', 'ext-program']; - } - - getTask(newOptions); - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--port ', 'Qlik Sense repository service (QRS) port (usually 4242 for cert auth, 443 for jwt auth)', '4242') - .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') - - .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') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') - - .addOption(new Option('--task-type ', 'type of tasks to include').choices(['reload', 'ext-program'])) - .option('--task-id ', 'use task IDs to select which tasks to retrieve. Only allowed when --output-format=table') - .option('--task-tag ', 'use tags to select which tasks to retrieve. Only allowed when --output-format=table') - - .addOption(new Option('--output-format ', 'output format').choices(['table', 'tree']).default('tree')) - .addOption(new Option('--output-dest ', 'where to send task info').choices(['screen', 'file']).default('screen')) - .addOption(new Option('--output-file-name ', 'file name to store task info in').default('')) - .addOption(new Option('--output-file-format ', 'file type/format').choices(['excel', 'csv', 'json']).default('excel')) - .option('--output-file-overwrite', 'overwrite output file without asking') - - .addOption(new Option('--text-color ', 'use colored text in task views').choices(['yes', 'no']).default('yes')) - - .option('--tree-icons', 'display task status icons in tree view') - .addOption( - new Option('--tree-details [detail...]', 'display details for each task in tree view') - .choices(['taskid', 'laststart', 'laststop', 'nextstart', 'appname', 'appstream']) - .default('') - ) + // QSEoW: Delete proxy sessions + setupQseowDeleteProxySessionsCommand(qseow); - .addOption( - new Option( - '--table-details [detail...]', - 'which aspects of tasks should be included in table view. Not choosing any details will show all' - ) - .choices(['common', 'lastexecution', 'tag', 'customproperty', 'schematrigger', 'compositetrigger']) - .default('') - ); + return qseow; + } - // Set custom property on tasks command - program - .command('task-custom-property-set') - .description('update a custom property of one or more tasks') - .action(async (options) => { - await sharedParamAssertOptions(options); - setTaskCustomPropertyAssertOptions(options); - - await setTaskCustomProperty(options); - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--port ', 'Qlik Sense repository service (QRS) port (usually 4242 for cert auth, 443 for jwt auth)', '4242') - .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') - - .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') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') - - .addOption( - new Option('--task-type ', 'type of tasks to list').choices(['reload']).default(['reload']) - // .choices(['reload', 'ext-program']) - // .default(['reload', 'ext-program']) - ) - .option('--task-id ', 'use task IDs to select which tasks to retrieve') - .option('--task-tag ', 'use tags to select which tasks to retrieve') - - .requiredOption('--custom-property-name ', 'name of custom property that will be updated') - .requiredOption('--custom-property-value ', 'one or more values name of custom property that will be updated') - .option('--overwrite', 'overwrite existing custom property values without asking') - .addOption( - new Option('--update-mode ', 'append or replace value(s) to existing custom property') - .choices(['append', 'replace']) - .default('append') - ) - .option('--dry-run', 'do a dry run, i.e. do not modify any reload tasks - just show what would be updated'); - - // Import tasks from definitions in Excel/CSV file - program - .command('task-import') - .description('create tasks based on definitions in a file on disk, optionally also importing apps from QVF files.') - .action(async (options) => { - try { - await sharedParamAssertOptions(options); - taskImportAssertOptions(options); - importTaskFromFile(options); - } catch (err) { - catchLog('IMPORT TASK 1', err); - } - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--port ', 'Qlik Sense repository service (QRS) port (usually 4242 for cert auth, 443 for jwt auth)', '4242') - .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') - - .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') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') - - .addOption(new Option('-t, --file-type ', 'source file type').choices(['excel', 'csv']).default('excel')) - .requiredOption('--file-name ', 'file containing task definitions') - .option('--sheet-name ', 'name of Excel sheet where task info is found') - - .addOption(new Option('--update-mode ', 'create new or update existing tasks').choices(['create']).default('create')) - - .requiredOption( - '--limit-import-count ', - 'import at most x number of tasks from the source file. Defaults to 0 = no limit', - 0 - ) - .requiredOption( - '--sleep-app-upload ', - 'Wait this long before continuing after each app has been uploaded to Sense. Defaults to 1000 = 1 second', - 1000 - ) - - .option('--import-app', 'import Sense app QVFs from specified directory') - .option('--import-app-sheet-name ', 'name of Excel sheet where app definitions are found') - - .option('--dry-run', 'do a dry run, i.e. do not create any reload tasks - just show what would be done'); - - // Import apps from definitions in Excel file - program - .command('app-import') - .description('import apps/upload QVF files on disk to Sense based on definitions in Excel file.') - .action(async (options) => { - try { - await sharedParamAssertOptions(options); - appImportAssertOptions(options); - importAppFromFile(options); - } catch (err) { - catchLog('IMPORT APP', err); - } - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--port ', 'Qlik Sense repository service (QRS) port (usually 4242 for cert auth, 443 for jwt auth)', '4242') - .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') - - .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') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') - - .addOption(new Option('-t, --file-type ', 'source file type').choices(['excel']).default('excel')) - .requiredOption('--file-name ', 'file containing app definitions') - .requiredOption('--sheet-name ', 'name of Excel sheet where app info is found') - - .requiredOption('--limit-import-count ', 'import at most x number of apps. Defaults to 0 = no limit', 0) - .requiredOption( - '--sleep-app-upload ', - 'Wait this long before continuing after each app has been uploaded to Sense. Defaults to 1000 = 1 second', - 1000 - ) - - .option('--dry-run', 'do a dry run, i.e. do not import any apps - just show what would be done'); - - // Export apps to QVF files - program - .command('app-export') - .description('export Qlik Sense apps to QVF files on disk.') - .action(async (options) => { - try { - await sharedParamAssertOptions(options); - await appExportAssertOptions(options); - exportAppToFile(options); - } catch (err) { - catchLog('EXPORT APP', err); - } - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--port ', 'Qlik Sense repository service (QRS) port (usually 4242 for cert auth, 443 for jwt auth)', '4242') - .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') - - .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') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') - - .option('--app-id ', 'use app IDs to select which apps to export') - .option('--app-tag ', 'use app tags to select which apps to export') - .requiredOption('--app-published', 'export all published apps ', false) - - .requiredOption('--output-dir ', 'relative or absolut path in which QVF files should be stored.', 'qvf-export') - .addOption( - new Option('--qvf-name-format ', 'structure of QVF file name format') - .choices(['app-id', 'app-name', 'export-date', 'export-time']) - .default(['app-name']) - ) - .addOption( - new Option('--qvf-name-separator ', 'character used to separate parts of the QVF file name') - .choices(['-', '--', '_', '__']) - .default('_') - ) - .option('--qvf-overwrite', 'overwrite existing QVF files without asking') - - .requiredOption('--exclude-app-data ', 'exclude or include app data in QVF file', true) - .requiredOption('--limit-export-count ', 'export at most x number of apps. Defaults to 0 = no limit', 0) - .requiredOption( - '--sleep-app-export ', - 'Wait this long before continuing after each app has been exported. Defaults to 1000 = 1 second', - 1000 - ) - - // Export of app metadata - .option('--metadata-file-create', 'create a separate file with information about all exported apps') - .addOption(new Option('--metadata-file-name ', 'file name to store app metadata in').default('app_export.xlsx')) - .addOption(new Option('--metadata-file-format ', 'file type/format').choices(['excel']).default('excel')) - .option('--metadata-file-overwrite', 'overwrite app metadata file without asking') - - .option('--dry-run', 'do a dry run, i.e. do not export any apps - just show what would be done'); - - // Test connection command - program - .command('connection-test') - .description('test connection to Qlik Sense server.') - .action(async (options) => { - try { - await sharedParamAssertOptions(options); - testConnection(options); - } catch (err) { - catchLog('CONNECTION TEST', err); - } - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--port ', 'Qlik Sense proxy service port', '4242') - .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') - - .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') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server'); - - // Show version command - program - .command('version') - .description('show version info') - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - // eslint-disable-next-line no-unused-vars - .action(async (options) => { - logger.verbose(`Version: ${appVersion}`); - }); - - // Visualise task network - program - .command('task-vis') - .description('visualise task network') - .action(async (options) => { - await sharedParamAssertOptions(options); - - await visTask(options); - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - .requiredOption('--host ', 'Qlik Sense server IP/FQDN') - .option('--port ', 'Qlik Sense repository service (QRS) port (usually 4242 for cert auth, 443 for jwt auth)', '4242') - // .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') - .requiredOption( - '--secure ', - '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') - - .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') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') - - // Options for visualisation host - .option('--vis-host ', 'host for visualisation server', 'localhost') - .option('--vis-port ', 'port for visualisation server', '3000'); - - // Get proxy sessions - program - .command('session-get') - .description('get info about proxy sessions on one or more virtual proxies') - .action(async (options) => { - await sharedParamAssertOptions(options); - await getSessionsAssertOptions(options); - - getSessions(options, null); - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - - .requiredOption('--host ', 'Qlik Sense host (IP/FQDN) where Qlik Repository Service (QRS) is running') - .option('--qrs-port ', 'Qlik Sense repository service (QRS) port (usually 4242)', '4242') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix to access QRS via', '') - .requiredOption( - '--secure ', - 'https connection to Qlik Sense must use correct certificate. Invalid certificates will result in rejected/failed connection.', - true - ) - - .option('--session-virtual-proxy ', 'one or more Qlik Sense virtual proxies to get sessions for') - .option( - '--host-proxy ', - 'Qlik Sense hosts/proxies (IP/FQDN) to get sessions from. Must match the host names of the Sense nodes' - ) - .option('--qps-port ', 'Qlik Sense proxy service (QPS) port (usually 4243)', '4243') - - .requiredOption('--auth-user-dir ', 'user directory for user to connect with') - .requiredOption('--auth-user-id ', 'user ID for user to connect with') - - .addOption(new Option('-a, --auth-type ', 'authentication type').choices(['cert']).default('cert')) - .option('--auth-cert-file ', 'Qlik Sense certificate file (exported from QMC)', './cert/client.pem') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') - - .option('--output-format ', 'output format', 'json') - - .addOption( - new Option('-s, --sort-by ', 'column to sort output table by') - .choices(['prefix', 'proxyhost', 'proxyname', 'userdir', 'userid', 'username']) - .default('prefix') - ); - - // Delete proxy sessions - program - .command('session-delete') - .description('delete proxy session(s) on a specific virtual proxy and proxy service') - .action(async (options) => { - await sharedParamAssertOptions(options); - await deleteSessionsAssertOptions(options); - - deleteSessions(options); - }) - .addOption( - new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') - ) - - .requiredOption('--host ', 'Qlik Sense host (IP/FQDN) where Qlik Repository Service (QRS) is running') - .option('--qrs-port ', 'Qlik Sense repository service (QRS) port (usually 4242)', '4242') - .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix to access QRS via', '') - .requiredOption( - '--secure ', - 'https connection to Qlik Sense must use correct certificate. Invalid certificates will result in rejected/failed connection.', - true - ) - - .option('--session-id ', 'session IDs to delete') - .requiredOption('--session-virtual-proxy ', 'Qlik Sense virtual proxy (prefix) to delete proxy session(s) on', '') - .requiredOption( - '--host-proxy ', - 'Qlik Sense proxy (IP/FQDN) where sessions should be deleted. Must match the host name of a Sense node' - ) - .option('--qps-port ', 'Qlik Sense proxy service (QPS) port (usually 4243)', '4243') + // -------------------------------------------------------- + // Create a command for QS Cloud-related sub-commands + function createQsCloudCommands() { + // Create a new command + const qsCloud = new Command('qscloud'); - .requiredOption('--auth-user-dir ', 'user directory for user to connect with') - .requiredOption('--auth-user-id ', 'user ID for user to connect with') + // QSEoW: Test connection command + setupQscloudTestConnectionCommand(qsCloud); - .addOption(new Option('-a, --auth-type ', 'authentication type').choices(['cert']).default('cert')) - .option('--auth-cert-file ', 'Qlik Sense certificate file (exported from QMC)', './cert/client.pem') - .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') - .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem'); + return qsCloud; + } - // .option('--dry-run', 'do a dry run, i.e. do not delete any sessions - just show what would be deleted') + // Add all command definitions + program.addCommand(createQseowCommands()); + program.addCommand(createQsCloudCommands()); - // Parse command line params + // Parse command definitions await program.parseAsync(process.argv); })(); diff --git a/src/lib/app/class_allapps.js b/src/lib/app/class_allapps.js index 99c2091..44c1e74 100644 --- a/src/lib/app/class_allapps.js +++ b/src/lib/app/class_allapps.js @@ -7,12 +7,12 @@ import fs2 from '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/qrs.js'; -import { getAppColumnPosFromHeaderRow } from '../util/lookups.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/tag.js'; -import { getAppById, deleteAppById } from '../util/app.js'; -import { getCustomPropertyDefinitionByName, doesCustomPropertyValueExist } from '../util/customproperties.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'; class QlikSenseApps { diff --git a/src/lib/cli/qscloud-test-connection.js b/src/lib/cli/qscloud-test-connection.js new file mode 100644 index 0000000..4da5b4e --- /dev/null +++ b/src/lib/cli/qscloud-test-connection.js @@ -0,0 +1,37 @@ +import { Option } from 'commander'; + +import { catchLog } from '../util/log.js'; +import { qscloudSharedParamAssertOptions } from '../util/qscloud/assert-options.js'; +import qscloudTestConnection from '../cmd/qscloud/testconnection.js'; + +export function setupQscloudTestConnectionCommand(qsCloud) { + qsCloud + .command('connection-test') + .description('test connection to Qlik Sense Cloud.') + .action(async (options) => { + // SHow app version + logger.info(`App version: ${appVersion}`); + + try { + await qscloudSharedParamAssertOptions(options); + const res = qscloudTestConnection(options); + logger.debug(`QS CLOUD CONNECTION TEST: Result: ${res}`); + } catch (err) { + catchLog('QS CLOUD CONNECTION TEST', err); + } + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption( + '--tenant-url ', + 'URL or host of Qlik Sense cloud tenant. Example: "https://tenant.eu.qlikcloud.com" or "tenant.eu.qlikcloud.com"' + ) + .addOption(new Option('-a, --auth-type ', 'authentication type').choices(['apikey']).default('apikey')) + .requiredOption('--apikey ', 'API key used to access the Sense APIs') + + .option('--auth-cert-file ', 'Qlik Sense certificate file (exported from QMC)', './cert/client.pem') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server'); +} diff --git a/src/lib/cli/qseow-cp-user-activity-bucket.js b/src/lib/cli/qseow-cp-user-activity-bucket.js new file mode 100644 index 0000000..4ebded9 --- /dev/null +++ b/src/lib/cli/qseow-cp-user-activity-bucket.js @@ -0,0 +1,59 @@ +import { Option } from 'commander'; + +import { catchLog } from '../util/log.js'; +// import { qseowSharedParamAssertOptions, customPropertyUserActivityBucketsAssertOptions } from '../util/qseow/assert-options.js'; +// import customPropertyUserActivityBuckets from '../cmd/qseow/custom-property-user-activity-buckets.js'; + +export function setupQseowUserActivityCustomPropertyCommand(qseow) { + // +} + +// program +// .command('user-activity-cp-create') +// .description( +// 'create custom property and populate it with values ("activity buckets") indicating how long ago users last logged into Sense' +// ) +// .action(async (options) => { +// try { +// let optionsLocal = options; +// await qseowSharedParamAssertOptions(options); +// optionsLocal = userActivityCustomPropertyAssertOptions(options); +// createUserActivityCustomProperty(optionsLocal); +// } catch (err) { +// logger.error(`USER ACTIVITY CP: ${err}`); +// } +// }) +// .addOption( +// new Option('--log-level ', 'log level') +// .choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']) +// .default('info') +// ) +// .requiredOption('--host ', 'Qlik Sense server IP/FQDN') +// .option('--port ', 'Qlik Sense repository API port', '4242') +// .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') +// .requiredOption('--secure ', 'https connection to Qlik Sense must use correct certificate. Invalid certificates will result in rejected/failed connection.', true) +// .option('--auth-user-dir ', 'user directory for user to connect with', 'Internal') +// .option('--auth-user-id ', 'user ID for user to connect with', 'sa_repository') + +// .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') +// .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') +// .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') +// .option('--jwt ', 'JSON Web Token (JWT) to use for authenticating with Qlik Sense', '') + +// .requiredOption('--user-directory ', 'name of user directory whose users will be updated with activity info') +// .requiredOption('--custom-property-name ', 'name of custom property that will hold user activity buckets') +// .addOption( +// new Option('--force ', 'forcibly overwrite and replace custom property and its values if it already exists') +// .choices(['true', 'false']) +// .default('false') +// ) +// .option('--activity-buckets ', 'custom property values/user activity buckets to be defined. In days.', [ +// '1', +// '7', +// '14', +// '30', +// '90', +// '180', +// '365', +// ]); diff --git a/src/lib/cli/qseow-delete-master-dimension.js b/src/lib/cli/qseow-delete-master-dimension.js new file mode 100644 index 0000000..66739f2 --- /dev/null +++ b/src/lib/cli/qseow-delete-master-dimension.js @@ -0,0 +1,43 @@ +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'; + +export function setupQseowDeleteMasterDimensionCommand(qseow) { + qseow + .command('master-item-dim-delete') + .description('delete master dimension(s)') + .action(async (options) => { + await qseowSharedParamAssertOptions(options); + masterItemDimDeleteAssertOptions(options); + + deleteMasterDimension(options); + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') + .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .requiredOption('--app-id ', 'Qlik Sense app ID') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .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') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + + .addOption(new Option('--id-type ', 'type of identifier passed in the --master-item option').choices(['id', 'name'])) + .option('--master-item ', 'names or IDs of master dimensions to be deleted. Multiple IDs should be space separated') + .option('--delete-all', 'delete all master dimensions') + .option('--dry-run', 'do a dry run, i.e. do not delete anything - just show what would be deleted'); +} diff --git a/src/lib/cli/qseow-delete-master-measure.js b/src/lib/cli/qseow-delete-master-measure.js new file mode 100644 index 0000000..cb5c045 --- /dev/null +++ b/src/lib/cli/qseow-delete-master-measure.js @@ -0,0 +1,43 @@ +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'; + +export function setupQseowDeleteMasterMeasureCommand(qseow) { + qseow + .command('master-item-measure-delete') + .description('delete master measure(s)') + .action(async (options) => { + await qseowSharedParamAssertOptions(options); + masterItemMeasureDeleteAssertOptions(options); + + deleteMasterMeasure(options); + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') + .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .requiredOption('--app-id ', 'Qlik Sense app ID') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .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') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + + .addOption(new Option('--id-type ', 'type of identifier passed in the --master-item option').choices(['id', 'name'])) + .option('--master-item ', 'names or IDs of master measures to be deleted. Multiple IDs should be space separated') + .option('--delete-all', 'delete all master measures') + .option('--dry-run', 'do a dry run, i.e. do not delete anything - just show what would be deleted'); +} diff --git a/src/lib/cli/qseow-delete-proxy-session.js b/src/lib/cli/qseow-delete-proxy-session.js new file mode 100644 index 0000000..4b23249 --- /dev/null +++ b/src/lib/cli/qseow-delete-proxy-session.js @@ -0,0 +1,45 @@ +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'; + +export function setupQseowDeleteProxySessionsCommand(qseow) { + qseow + .command('session-delete') + .description('delete proxy session(s) on a specific virtual proxy and proxy service') + .action(async (options) => { + await qseowSharedParamAssertOptions(options); + await deleteSessionsAssertOptions(options); + + deleteSessions(options); + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + + .requiredOption('--host ', 'Qlik Sense host (IP/FQDN) where Qlik Repository Service (QRS) is running') + .option('--qrs-port ', 'Qlik Sense repository service (QRS) port (usually 4242)', '4242') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix to access QRS via', '') + .requiredOption( + '--secure ', + 'https connection to Qlik Sense must use correct certificate. Invalid certificates will result in rejected/failed connection.', + true + ) + + .option('--session-id ', 'session IDs to delete') + .requiredOption('--session-virtual-proxy ', 'Qlik Sense virtual proxy (prefix) to delete proxy session(s) on', '') + .requiredOption( + '--host-proxy ', + 'Qlik Sense proxy (IP/FQDN) where sessions should be deleted. Must match the host name of a Sense node' + ) + .option('--qps-port ', 'Qlik Sense proxy service (QPS) port (usually 4243)', '4243') + + .requiredOption('--auth-user-dir ', 'user directory for user to connect with') + .requiredOption('--auth-user-id ', 'user ID for user to connect with') + + .addOption(new Option('-a, --auth-type ', 'authentication type').choices(['cert']).default('cert')) + .option('--auth-cert-file ', 'Qlik Sense certificate file (exported from QMC)', './cert/client.pem') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem'); +} diff --git a/src/lib/cli/qseow-delete-variable.js b/src/lib/cli/qseow-delete-variable.js new file mode 100644 index 0000000..6046f83 --- /dev/null +++ b/src/lib/cli/qseow-delete-variable.js @@ -0,0 +1,47 @@ +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'; + +export function setupQseowDeleteVariableCommand(qseow) { + qseow + .command('variable-delete') + .description('delete one or more variables in one or more apps') + .action(async (options) => { + await qseowSharedParamAssertOptions(options); + variableDeleteAssertOptions(options); + + deleteVariable(options); + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--engine-port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') + .option('--qrs-port ', 'Qlik Sense repository service (QRS) port (usually 4242 for cert auth, 443 for jwt auth)', '4242') + .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .option('--app-id ', 'Qlik Sense app ID(s) to get variables from') + .option('--app-tag ', 'Qlik Sense app tag(s) to get variables') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .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') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + + .addOption( + new Option('--id-type ', 'type of identifier passed in the --variable option').choices(['id', 'name']).default('name') + ) + .option('--variable ', 'variables to retrieve. If not specified all variables will be retrieved') + .option('--delete-all', 'delete all variables') + .option('--dry-run', 'do a dry run, i.e. do not delete anything - just show what would be deleted'); +} diff --git a/src/lib/cli/qseow-export-app-to-file.js b/src/lib/cli/qseow-export-app-to-file.js new file mode 100644 index 0000000..145646a --- /dev/null +++ b/src/lib/cli/qseow-export-app-to-file.js @@ -0,0 +1,73 @@ +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'; + +export function setupQseowExportAppCommand(qseow) { + qseow + .command('app-export') + .description('export Qlik Sense apps to QVF files on disk.') + .action(async (options) => { + try { + await qseowSharedParamAssertOptions(options); + await appExportAssertOptions(options); + exportAppToFile(options); + } catch (err) { + catchLog('EXPORT APP', err); + } + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--port ', 'Qlik Sense repository service (QRS) port (usually 4242 for cert auth, 443 for jwt auth)', '4242') + .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .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') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + + .option('--app-id ', 'use app IDs to select which apps to export') + .option('--app-tag ', 'use app tags to select which apps to export') + .requiredOption('--app-published', 'export all published apps ', false) + + .requiredOption('--output-dir ', 'relative or absolut path in which QVF files should be stored.', 'qvf-export') + .addOption( + new Option('--qvf-name-format ', 'structure of QVF file name format') + .choices(['app-id', 'app-name', 'export-date', 'export-time']) + .default(['app-name']) + ) + .addOption( + new Option('--qvf-name-separator ', 'character used to separate parts of the QVF file name') + .choices(['-', '--', '_', '__']) + .default('_') + ) + .option('--qvf-overwrite', 'overwrite existing QVF files without asking') + + .requiredOption('--exclude-app-data ', 'exclude or include app data in QVF file', true) + .requiredOption('--limit-export-count ', 'export at most x number of apps. Defaults to 0 = no limit', 0) + .requiredOption( + '--sleep-app-export ', + 'Wait this long before continuing after each app has been exported. Defaults to 1000 = 1 second', + 1000 + ) + + // Export of app metadata + .option('--metadata-file-create', 'create a separate file with information about all exported apps') + .addOption(new Option('--metadata-file-name ', 'file name to store app metadata in').default('app_export.xlsx')) + .addOption(new Option('--metadata-file-format ', 'file type/format').choices(['excel']).default('excel')) + .option('--metadata-file-overwrite', 'overwrite app metadata file without asking') + + .option('--dry-run', 'do a dry run, i.e. do not export any apps - just show what would be done'); +} diff --git a/src/lib/cli/qseow-get-bookmark.js b/src/lib/cli/qseow-get-bookmark.js new file mode 100644 index 0000000..1335766 --- /dev/null +++ b/src/lib/cli/qseow-get-bookmark.js @@ -0,0 +1,46 @@ +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'; + +export function setupQseowGetBookmarkCommand(qseow) { + qseow + .command('bookmark-get') + .description('get info about one or more bookmarks') + .action(async (options) => { + await qseowSharedParamAssertOptions(options); + getBookmarkAssertOptions(options); + + getBookmark(options); + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') + .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .requiredOption('--app-id ', 'Qlik Sense app ID') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .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') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + + .addOption( + new Option('--id-type ', 'type of bookmark identifier passed in the --bookmark option') + .choices(['id', 'name']) + .default('name') + ) + .option('--bookmark ', 'bookmark to retrieve. If not specified all bookmarks will be retrieved') + .option('--output-format ', 'output format', 'json'); +} diff --git a/src/lib/cli/qseow-get-master-dimension.js b/src/lib/cli/qseow-get-master-dimension.js new file mode 100644 index 0000000..424c1cf --- /dev/null +++ b/src/lib/cli/qseow-get-master-dimension.js @@ -0,0 +1,43 @@ +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'; + +export function setupQseowGetMasterDimensionCommand(qseow) { + qseow + .command('master-item-dim-get') + .description('get info about one or more master dimensions') + .action(async (options) => { + await qseowSharedParamAssertOptions(options); + + getMasterDimension(options); + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') + .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .requiredOption('--app-id ', 'Qlik Sense app ID') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .addOption(new Option('-a, --auth-type ', 'authentication type').choices(['cert', 'jwt']).default('cert')) + .requiredOption('--auth-cert-file ', 'Qlik Sense certificate file (exported from QMC)', './cert/client.pem') + .requiredOption('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .requiredOption('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + + .addOption( + new Option('--id-type ', 'type of identifier passed in the --master-item option').choices(['id', 'name']).default('name') + ) + .option('--master-item ', 'master dimension to retrieve. If not specified all dimensions will be retrieved') + .addOption(new Option('--output-format ', 'output format').choices(['json', 'table']).default('json')); +} diff --git a/src/lib/cli/qseow-get-master-measure.js b/src/lib/cli/qseow-get-master-measure.js new file mode 100644 index 0000000..0d229c8 --- /dev/null +++ b/src/lib/cli/qseow-get-master-measure.js @@ -0,0 +1,44 @@ +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'; + +export function setupQseowGetMasterMeasureCommand(qseow) { + qseow + .command('master-item-measure-get') + .description('get info about one or more master measures') + .action(async (options) => { + await qseowSharedParamAssertOptions(options); + masterItemGetAssertOptions(options); + + getMasterMeasure(options); + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') + .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .requiredOption('--app-id ', 'Qlik Sense app ID') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .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') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + + .addOption( + new Option('--id-type ', 'type of identifier passed in the --master-item option').choices(['id', 'name']).default('name') + ) + .option('--master-item ', 'master measure to retrieve. If not specified all measures will be retrieved') + .addOption(new Option('--output-format ', 'output format').choices(['json', 'table']).default('json')); +} diff --git a/src/lib/cli/qseow-get-proxy-session.js b/src/lib/cli/qseow-get-proxy-session.js new file mode 100644 index 0000000..766a0dd --- /dev/null +++ b/src/lib/cli/qseow-get-proxy-session.js @@ -0,0 +1,52 @@ +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'; + +export function setupQseowGetProxySessionsCommand(qseow) { + qseow + .command('session-get') + .description('get info about proxy sessions on one or more virtual proxies') + .action(async (options) => { + await qseowSharedParamAssertOptions(options); + await getSessionsAssertOptions(options); + + getSessions(options, null); + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + + .requiredOption('--host ', 'Qlik Sense host (IP/FQDN) where Qlik Repository Service (QRS) is running') + .option('--qrs-port ', 'Qlik Sense repository service (QRS) port (usually 4242)', '4242') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix to access QRS via', '') + .requiredOption( + '--secure ', + 'https connection to Qlik Sense must use correct certificate. Invalid certificates will result in rejected/failed connection.', + true + ) + + .option('--session-virtual-proxy ', 'one or more Qlik Sense virtual proxies to get sessions for') + .option( + '--host-proxy ', + 'Qlik Sense hosts/proxies (IP/FQDN) to get sessions from. Must match the host names of the Sense nodes' + ) + .option('--qps-port ', 'Qlik Sense proxy service (QPS) port (usually 4243)', '4243') + + .requiredOption('--auth-user-dir ', 'user directory for user to connect with') + .requiredOption('--auth-user-id ', 'user ID for user to connect with') + + .addOption(new Option('-a, --auth-type ', 'authentication type').choices(['cert']).default('cert')) + .option('--auth-cert-file ', 'Qlik Sense certificate file (exported from QMC)', './cert/client.pem') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + + .option('--output-format ', 'output format', 'json') + + .addOption( + new Option('-s, --sort-by ', 'column to sort output table by') + .choices(['prefix', 'proxyhost', 'proxyname', 'userdir', 'userid', 'username']) + .default('prefix') + ); +} diff --git a/src/lib/cli/qseow-get-script.js b/src/lib/cli/qseow-get-script.js new file mode 100644 index 0000000..d0a76d6 --- /dev/null +++ b/src/lib/cli/qseow-get-script.js @@ -0,0 +1,39 @@ +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'; + +export function setupGetScriptCommand(qseow) { + qseow + .command('script-get') + .description('get script from Qlik Sense app') + .action(async (options) => { + await qseowSharedParamAssertOptions(options); + getScriptAssertOptions(options); + + getScript(options); + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') + .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .requiredOption('--app-id ', 'Qlik Sense app ID') + .addOption(new Option('--open-without-data ', 'open app without data').choices(['true', 'false']).default('true')) + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .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') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server'); +} diff --git a/src/lib/cli/qseow-get-task.js b/src/lib/cli/qseow-get-task.js new file mode 100644 index 0000000..66d369b --- /dev/null +++ b/src/lib/cli/qseow-get-task.js @@ -0,0 +1,78 @@ +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'; + +export function setupGetTaskCommand(qseow) { + qseow + .command('task-get') + .description('get info about one or more tasks') + .action(async (options) => { + const newOptions = options; + // If options.tableDetails is true, it means --table-details was passed as options without any explicit value. + // This is allowed, but should be interpreted as "all" table details. + // Make options.tableDetails an array with all possible table details. + if (options.tableDetails === true) { + newOptions.tableDetails = ['common', 'lastexecution', 'tag', 'customproperty', 'schematrigger', 'compositetrigger']; + } + + await qseowSharedParamAssertOptions(newOptions); + getTaskAssertOptions(newOptions); + + // If --output-format=table and --task-type is not specified, default to ['reload', 'ext-program'] + if (newOptions.outputFormat === 'table' && !newOptions.taskType) { + newOptions.taskType = ['reload', 'ext-program']; + } + + getTask(newOptions); + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--port ', 'Qlik Sense repository service (QRS) port (usually 4242 for cert auth, 443 for jwt auth)', '4242') + .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .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') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + + .addOption(new Option('--task-type ', 'type of tasks to include').choices(['reload', 'ext-program'])) + .option('--task-id ', 'use task IDs to select which tasks to retrieve. Only allowed when --output-format=table') + .option('--task-tag ', 'use tags to select which tasks to retrieve. Only allowed when --output-format=table') + + .addOption(new Option('--output-format ', 'output format').choices(['table', 'tree']).default('tree')) + .addOption(new Option('--output-dest ', 'where to send task info').choices(['screen', 'file']).default('screen')) + .addOption(new Option('--output-file-name ', 'file name to store task info in').default('')) + .addOption(new Option('--output-file-format ', 'file type/format').choices(['excel', 'csv', 'json']).default('excel')) + .option('--output-file-overwrite', 'overwrite output file without asking') + + .addOption(new Option('--text-color ', 'use colored text in task views').choices(['yes', 'no']).default('yes')) + + .option('--tree-icons', 'display task status icons in tree view') + .addOption( + new Option('--tree-details [detail...]', 'display details for each task in tree view') + .choices(['taskid', 'laststart', 'laststop', 'nextstart', 'appname', 'appstream']) + .default('') + ) + + .addOption( + new Option( + '--table-details [detail...]', + 'which aspects of tasks should be included in table view. Not choosing any details will show all' + ) + .choices(['common', 'lastexecution', 'tag', 'customproperty', 'schematrigger', 'compositetrigger']) + .default('') + ); +} diff --git a/src/lib/cli/qseow-get-variable.js b/src/lib/cli/qseow-get-variable.js new file mode 100644 index 0000000..8667ddc --- /dev/null +++ b/src/lib/cli/qseow-get-variable.js @@ -0,0 +1,46 @@ +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'; + +export function setpQseowGetVariableCommand(qseow) { + qseow + .command('variable-get') + .description('get variable definitions in one or more apps') + .action(async (options) => { + await qseowSharedParamAssertOptions(options); + variableGetAssertOptions(options); + + getVariable(options); + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--engine-port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') + .option('--qrs-port ', 'Qlik Sense repository service (QRS) port (usually 4747 for cert auth, 443 for jwt auth)', '4242') + .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .option('--app-id ', 'Qlik Sense app ID(s) to get variables from') + .option('--app-tag ', 'Qlik Sense app tag(s) to get variables') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .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') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + + .addOption( + new Option('--id-type ', 'type of identifier passed in the --variable option').choices(['id', 'name']).default('name') + ) + .option('--variable ', 'variables to retrieve. If not specified all variables will be retrieved') + .addOption(new Option('--output-format ', 'output format').choices(['json', 'table']).default('json')); +} diff --git a/src/lib/cli/qseow-import-app-from-file.js b/src/lib/cli/qseow-import-app-from-file.js new file mode 100644 index 0000000..09f27a4 --- /dev/null +++ b/src/lib/cli/qseow-import-app-from-file.js @@ -0,0 +1,53 @@ +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'; + +export function setupQseowImportAppFromFileCommand(qseow) { + qseow + .command('app-import') + .description('import apps/upload QVF files on disk to Sense based on definitions in Excel file.') + .action(async (options) => { + try { + await qseowSharedParamAssertOptions(options); + appImportAssertOptions(options); + importAppFromFile(options); + } catch (err) { + catchLog('IMPORT APP', err); + } + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--port ', 'Qlik Sense repository service (QRS) port (usually 4242 for cert auth, 443 for jwt auth)', '4242') + .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .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') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + + .addOption(new Option('-t, --file-type ', 'source file type').choices(['excel']).default('excel')) + .requiredOption('--file-name ', 'file containing app definitions') + .requiredOption('--sheet-name ', 'name of Excel sheet where app info is found') + + .requiredOption('--limit-import-count ', 'import at most x number of apps. Defaults to 0 = no limit', 0) + .requiredOption( + '--sleep-app-upload ', + 'Wait this long before continuing after each app has been uploaded to Sense. Defaults to 1000 = 1 second', + 1000 + ) + + .option('--dry-run', 'do a dry run, i.e. do not import any apps - just show what would be done'); +} diff --git a/src/lib/cli/qseow-import-task-from-file.js b/src/lib/cli/qseow-import-task-from-file.js new file mode 100644 index 0000000..f0e6214 --- /dev/null +++ b/src/lib/cli/qseow-import-task-from-file.js @@ -0,0 +1,62 @@ +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'; + +export function setupQseowImportTaskFromFileCommand(qseow) { + qseow + .command('task-import') + .description('create tasks based on definitions in a file on disk, optionally also importing apps from QVF files.') + .action(async (options) => { + try { + await qseowSharedParamAssertOptions(options); + taskImportAssertOptions(options); + importTaskFromFile(options); + } catch (err) { + catchLog('IMPORT TASK 1', err); + } + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--port ', 'Qlik Sense repository service (QRS) port (usually 4242 for cert auth, 443 for jwt auth)', '4242') + .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .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') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + + .addOption(new Option('-t, --file-type ', 'source file type').choices(['excel', 'csv']).default('excel')) + .requiredOption('--file-name ', 'file containing task definitions') + .option('--sheet-name ', 'name of Excel sheet where task info is found') + + .addOption(new Option('--update-mode ', 'create new or update existing tasks').choices(['create']).default('create')) + + .requiredOption( + '--limit-import-count ', + 'import at most x number of tasks from the source file. Defaults to 0 = no limit', + 0 + ) + .requiredOption( + '--sleep-app-upload ', + 'Wait this long before continuing after each app has been uploaded to Sense. Defaults to 1000 = 1 second', + 1000 + ) + + .option('--import-app', 'import Sense app QVFs from specified directory') + .option('--import-app-sheet-name ', 'name of Excel sheet where app definitions are found') + + .option('--dry-run', 'do a dry run, i.e. do not create any reload tasks - just show what would be done'); +} diff --git a/src/lib/cli/qseow-master-item-import.js b/src/lib/cli/qseow-master-item-import.js new file mode 100644 index 0000000..44643b2 --- /dev/null +++ b/src/lib/cli/qseow-master-item-import.js @@ -0,0 +1,101 @@ +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'; + +export function setupQseowMasterItemImportCommand(qseow) { + qseow + .command('master-item-import') + .description('create master items based on definitions in a file on disk') + .action(async (options) => { + try { + await qseowSharedParamAssertOptions(options); + masterItemImportAssertOptions(options); + importMasterItemFromFile(options); + } catch (err) { + catchLog('IMPORT EXCEL', err); + } + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') + .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .requiredOption('--app-id ', 'Qlik Sense app ID') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .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') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + + .addOption(new Option('-t, --file-type ', 'source file type').choices(['excel']).default('excel')) + .requiredOption('--file ', 'file containing master item definitions') + .requiredOption('--sheet ', 'name of Excel sheet where dim/measure flag column is found') + .addOption( + new Option( + '--col-ref-by ', + 'how to refer to columns in the source file. Options are by name or by position (zero based)' + ) + .choices(['name', 'position']) + .default('name') + ) + .requiredOption( + '--col-item-type ', + 'column where dim/measure flag is found. Use "dim-single" in that column to create dimension, "dim-drilldown" for drill-down dimension, "measure" for measure', + 'Master item type' + ) + .requiredOption( + '--col-master-item-name ', + 'column number (zero based) or name to use as master item name', + 'Master item name' + ) + .requiredOption( + '--col-master-item-descr ', + 'column number (zero based) or name to use as master item description', + 'Description' + ) + .requiredOption( + '--col-master-item-label ', + 'column number (zero based) or name to use as master item label', + 'Label' + ) + .requiredOption( + '--col-master-item-expr ', + 'column number (zero based) or name to use as master item expression', + 'Expression' + ) + .requiredOption( + '--col-master-item-tag ', + 'column number (zero based) or name to use as master item tags', + 'Tag' + ) + .requiredOption( + '--col-master-item-color ', + 'column number (zero based) or name to use as color for dimensions/measures', + 'Color' + ) + .requiredOption( + '--col-master-item-per-value-color ', + 'column number (zero based) or name to use as per-value/segment color for dimensions/measures', + 'Per value color' + ) + + .requiredOption('--sleep-between-imports ', 'sleep this many milliseconds between imports. Set to 0 to disable', 1000) + .requiredOption( + '--limit-import-count ', + 'import at most x number of master items from the Excel file. Defaults to 0 = no limit', + 0 + ) + .option('--dry-run', 'do a dry run, i.e. do not create or update anything - just show what would be done'); +} diff --git a/src/lib/cli/qseow-scramble-field.js b/src/lib/cli/qseow-scramble-field.js new file mode 100644 index 0000000..a29e286 --- /dev/null +++ b/src/lib/cli/qseow-scramble-field.js @@ -0,0 +1,40 @@ +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'; + +export function setupQseowScrambleFieldCommand(qseow) { + qseow + .command('field-scramble') + .description('scramble one or more fields in an app. A new app with the scrambled data is created.') + .action(async (options) => { + await qseowSharedParamAssertOptions(options); + + scrambleField(options); + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--port ', 'Qlik Sense server engine port (usually 4747 for cert auth, 443 for jwt auth)', '4747') + .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .requiredOption('--app-id ', 'Qlik Sense app ID') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .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') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + + .requiredOption('--field-name ', 'name of field(s) to be scrambled') + .requiredOption('--new-app-name ', 'name of new app that will contain scrambled data'); +} diff --git a/src/lib/cli/qseow-set-task-cp.js b/src/lib/cli/qseow-set-task-cp.js new file mode 100644 index 0000000..afa5f8d --- /dev/null +++ b/src/lib/cli/qseow-set-task-cp.js @@ -0,0 +1,55 @@ +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'; + +export function setupQseowSetTaskCustomPropertyCommand(qseow) { + qseow + .command('task-custom-property-set') + .description('update a custom property of one or more tasks') + .action(async (options) => { + await qseowSharedParamAssertOptions(options); + setTaskCustomPropertyAssertOptions(options); + + await setTaskCustomProperty(options); + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--port ', 'Qlik Sense repository service (QRS) port (usually 4242 for cert auth, 443 for jwt auth)', '4242') + .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .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') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + + .addOption( + new Option('--task-type ', 'type of tasks to list').choices(['reload']).default(['reload']) + // .choices(['reload', 'ext-program']) + // .default(['reload', 'ext-program']) + ) + .option('--task-id ', 'use task IDs to select which tasks to retrieve') + .option('--task-tag ', 'use tags to select which tasks to retrieve') + + .requiredOption('--custom-property-name ', 'name of custom property that will be updated') + .requiredOption('--custom-property-value ', 'one or more values name of custom property that will be updated') + .option('--overwrite', 'overwrite existing custom property values without asking') + .addOption( + new Option('--update-mode ', 'append or replace value(s) to existing custom property') + .choices(['append', 'replace']) + .default('append') + ) + .option('--dry-run', 'do a dry run, i.e. do not modify any reload tasks - just show what would be updated'); +} diff --git a/src/lib/cli/qseow-show-version.js b/src/lib/cli/qseow-show-version.js new file mode 100644 index 0000000..3b6f259 --- /dev/null +++ b/src/lib/cli/qseow-show-version.js @@ -0,0 +1,16 @@ +import { Option } from 'commander'; + +import { catchLog } from '../util/log.js'; + +export function setupQseowShowVersionCommand(qseow) { + qseow + .command('version') + .description('show version info') + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + // eslint-disable-next-line no-unused-vars + .action(async (options) => { + logger.verbose(`Version: ${appVersion}`); + }); +} diff --git a/src/lib/cli/qseow-test-connection.js b/src/lib/cli/qseow-test-connection.js new file mode 100644 index 0000000..79109d4 --- /dev/null +++ b/src/lib/cli/qseow-test-connection.js @@ -0,0 +1,39 @@ +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'; + +export function setupQseowTestConnectionCommand(qseow) { + qseow + .command('connection-test') + .description('test connection to Qlik Sense server.') + .action(async (options) => { + try { + await qseowSharedParamAssertOptions(options); + testConnection(options); + } catch (err) { + catchLog('CONNECTION TEST', err); + } + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--port ', 'Qlik Sense proxy service port', '4242') + .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .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') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server'); +} diff --git a/src/lib/cli/qseow-visualise-task.js b/src/lib/cli/qseow-visualise-task.js new file mode 100644 index 0000000..1a9d8ff --- /dev/null +++ b/src/lib/cli/qseow-visualise-task.js @@ -0,0 +1,41 @@ +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'; + +export function setupQseowVisualiseTaskCommand(qseow) { + qseow + .command('task-vis') + .description('visualise task network') + .action(async (options) => { + await qseowSharedParamAssertOptions(options); + + await visTask(options); + }) + .addOption( + new Option('--log-level ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly']).default('info') + ) + .requiredOption('--host ', 'Qlik Sense server IP/FQDN') + .option('--port ', 'Qlik Sense repository service (QRS) port (usually 4242 for cert auth, 443 for jwt auth)', '4242') + // .option('--schema-version ', 'Qlik Sense engine schema version', '12.612.0') + .requiredOption('--virtual-proxy ', 'Qlik Sense virtual proxy prefix', '') + .requiredOption( + '--secure ', + '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') + + .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') + .option('--auth-cert-key-file ', 'Qlik Sense certificate key file (exported from QMC)', './cert/client_key.pem') + .option('--auth-root-cert-file ', 'Qlik Sense root certificate file (exported from QMC)', './cert/root.pem') + .option('--auth-jwt ', 'JSON Web Token (JWT) to use for authentication with Qlik Sense server') + + // Options for visualisation host + .option('--vis-host ', 'host for visualisation server', 'localhost') + .option('--vis-port ', 'port for visualisation server', '3000'); +} diff --git a/src/lib/cmd/qscloud/testconnection.js b/src/lib/cmd/qscloud/testconnection.js new file mode 100644 index 0000000..3186074 --- /dev/null +++ b/src/lib/cmd/qscloud/testconnection.js @@ -0,0 +1,35 @@ +import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; +import getAboutFromQseow from '../../util/qseow/about.js'; +import { catchLog } from '../../util/log.js'; + +const qscloudTestConnection = async (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 from ${execPath}`); + + logger.info(`Testing connection to Qlik Sense Cloud tenant "${options.tenantUrl}"`); + logger.debug(`Options: ${JSON.stringify(options, null, 2)}`); + + const tenantInfo = await getTenantInfoFromQscloud(options); + + if (!tenantInfo) { + logger.error(`Could not get tenant info from QS Cloud`); + return false; + } + + logger.info(`Successfully connected to Qlik Sense server ${options.host} on port ${options.port}`); + logger.info(`Qlik Sense repository build version: ${aboutInfo.buildVersion}`); + logger.info(`Qlik Sense repository build date: ${aboutInfo.buildDate}`); + + return aboutInfo; + } catch (err) { + catchLog(`Error testing connection to Qlik Sense server ${options.host} on port ${options.port}`, err); + logger.error(`EXPORT APP: ${err.stack}`); + return false; + } +}; + +export default qscloudTestConnection; diff --git a/src/lib/cmd/createdim.js b/src/lib/cmd/qseow/createdim.js similarity index 98% rename from src/lib/cmd/createdim.js rename to src/lib/cmd/qseow/createdim.js index f078d37..ceafe60 100644 --- a/src/lib/cmd/createdim.js +++ b/src/lib/cmd/qseow/createdim.js @@ -1,7 +1,7 @@ import enigma from 'enigma.js'; -import setupEnigmaConnection from '../util/enigma.js'; -import { logger, setLoggingLevel, isPkg, execPath } from '../../globals.js'; -import { catchLog } from '../util/log.js'; +import setupEnigmaConnection from '../../util/enigma.js'; +import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; +import { catchLog } from '../../util/log.js'; /** * diff --git a/src/lib/cmd/createuseractivitycp.js b/src/lib/cmd/qseow/createuseractivitycp.js similarity index 99% rename from src/lib/cmd/createuseractivitycp.js rename to src/lib/cmd/qseow/createuseractivitycp.js index be0673a..758e6e1 100644 --- a/src/lib/cmd/createuseractivitycp.js +++ b/src/lib/cmd/qseow/createuseractivitycp.js @@ -1,6 +1,6 @@ import qrsInteract from 'qrs-interact'; import path from 'path'; -import { logger, setLoggingLevel, isPkg, execPath } from '../../globals.js'; +import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; import { getUserActivityProfessional, @@ -10,7 +10,7 @@ import { getUserActivityUser, getUsersLastActivity, } from './useractivity.js'; -import { catchLog } from '../util/log.js'; +import { catchLog } from '../../util/log.js'; const _MS_PER_DAY = 1000 * 60 * 60 * 24; diff --git a/src/lib/cmd/deletedim.js b/src/lib/cmd/qseow/deletedim.js similarity index 97% rename from src/lib/cmd/deletedim.js rename to src/lib/cmd/qseow/deletedim.js index 91761b6..720041a 100644 --- a/src/lib/cmd/deletedim.js +++ b/src/lib/cmd/qseow/deletedim.js @@ -1,7 +1,7 @@ import enigma from 'enigma.js'; -import { setupEnigmaConnection, addTrafficLogging } from '../util/enigma.js'; -import { logger, setLoggingLevel, isPkg, execPath } from '../../globals.js'; -import { catchLog } from '../util/log.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; +import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; +import { catchLog } from '../../util/log.js'; // Variable to keep track of how many dimensions have been deleted let deleteCount = 0; diff --git a/src/lib/cmd/deletemeasure.js b/src/lib/cmd/qseow/deletemeasure.js similarity index 97% rename from src/lib/cmd/deletemeasure.js rename to src/lib/cmd/qseow/deletemeasure.js index 6bd61c4..40f097c 100644 --- a/src/lib/cmd/deletemeasure.js +++ b/src/lib/cmd/qseow/deletemeasure.js @@ -1,7 +1,7 @@ import enigma from 'enigma.js'; -import { setupEnigmaConnection, addTrafficLogging } from '../util/enigma.js'; -import { logger, setLoggingLevel, isPkg, execPath } from '../../globals.js'; -import { catchLog } from '../util/log.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; +import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; +import { catchLog } from '../../util/log.js'; // Variable to keep track of how many measures have been deleted let deleteCount = 0; diff --git a/src/lib/cmd/deletesessions.js b/src/lib/cmd/qseow/deletesessions.js similarity index 89% rename from src/lib/cmd/deletesessions.js rename to src/lib/cmd/qseow/deletesessions.js index 2addfc8..417d76b 100644 --- a/src/lib/cmd/deletesessions.js +++ b/src/lib/cmd/qseow/deletesessions.js @@ -1,6 +1,6 @@ -import { deleteSessionsFromQSEoWIds } from '../util/session.js'; -import { logger, setLoggingLevel, isPkg, execPath } from '../../globals.js'; -import { catchLog } from '../util/log.js'; +import { deleteSessionsFromQSEoWIds } from '../../util/qseow/session.js'; +import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; +import { catchLog } from '../../util/log.js'; /** * Delete Qlik Sense proxy sessions diff --git a/src/lib/cmd/deletevariable.js b/src/lib/cmd/qseow/deletevariable.js similarity index 97% rename from src/lib/cmd/deletevariable.js rename to src/lib/cmd/qseow/deletevariable.js index a119e8b..092edb3 100644 --- a/src/lib/cmd/deletevariable.js +++ b/src/lib/cmd/qseow/deletevariable.js @@ -2,10 +2,10 @@ /* eslint-disable no-await-in-loop */ import enigma from 'enigma.js'; -import { setupEnigmaConnection, addTrafficLogging } from '../util/enigma.js'; -import { getApps } from '../util/app.js'; -import { logger, setLoggingLevel, isPkg, execPath } from '../../globals.js'; -import { catchLog } from '../util/log.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.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/exportapp.js b/src/lib/cmd/qseow/exportapp.js similarity index 97% rename from src/lib/cmd/exportapp.js rename to src/lib/cmd/qseow/exportapp.js index dc18d7c..ead0693 100644 --- a/src/lib/cmd/exportapp.js +++ b/src/lib/cmd/qseow/exportapp.js @@ -3,9 +3,9 @@ import fs from 'fs'; import path from '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 { catchLog } from '../util/log.js'; +import { logger, setLoggingLevel, isPkg, execPath, mergeDirFilePath, verifyFileExists, isNumeric, sleep } from '../../../globals.js'; +import QlikSenseApps from '../../app/class_allapps.js'; +import { catchLog } from '../../util/log.js'; const exportAppToFile = async (options) => { try { diff --git a/src/lib/cmd/getbookmark.js b/src/lib/cmd/qseow/getbookmark.js similarity index 98% rename from src/lib/cmd/getbookmark.js rename to src/lib/cmd/qseow/getbookmark.js index 09fb95c..8a26103 100644 --- a/src/lib/cmd/getbookmark.js +++ b/src/lib/cmd/qseow/getbookmark.js @@ -1,8 +1,8 @@ import enigma from 'enigma.js'; import { table } from 'table'; -import { setupEnigmaConnection, addTrafficLogging } from '../util/enigma.js'; -import { logger, setLoggingLevel, isPkg, execPath } from '../../globals.js'; -import { catchLog } from '../util/log.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; +import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; +import { catchLog } from '../../util/log.js'; const consoleTableConfig = { border: { diff --git a/src/lib/cmd/getdim.js b/src/lib/cmd/qseow/getdim.js similarity index 98% rename from src/lib/cmd/getdim.js rename to src/lib/cmd/qseow/getdim.js index c62d91e..95f4b1b 100644 --- a/src/lib/cmd/getdim.js +++ b/src/lib/cmd/qseow/getdim.js @@ -3,9 +3,9 @@ import enigma from 'enigma.js'; import { table } from 'table'; -import { setupEnigmaConnection, addTrafficLogging } from '../util/enigma.js'; -import { logger, setLoggingLevel, isPkg, execPath } from '../../globals.js'; -import { catchLog } from '../util/log.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; +import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; +import { catchLog } from '../../util/log.js'; const consoleTableConfig = { border: { diff --git a/src/lib/cmd/getmeasure.js b/src/lib/cmd/qseow/getmeasure.js similarity index 98% rename from src/lib/cmd/getmeasure.js rename to src/lib/cmd/qseow/getmeasure.js index d530ab0..554eab2 100644 --- a/src/lib/cmd/getmeasure.js +++ b/src/lib/cmd/qseow/getmeasure.js @@ -2,9 +2,9 @@ import enigma from 'enigma.js'; import { table } from 'table'; -import { setupEnigmaConnection, addTrafficLogging } from '../util/enigma.js'; -import { logger, setLoggingLevel, isPkg, execPath } from '../../globals.js'; -import { catchLog } from '../util/log.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; +import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; +import { catchLog } from '../../util/log.js'; const consoleTableConfig = { border: { diff --git a/src/lib/cmd/getscript.js b/src/lib/cmd/qseow/getscript.js similarity index 95% rename from src/lib/cmd/getscript.js rename to src/lib/cmd/qseow/getscript.js index aaf9be6..e3941d2 100644 --- a/src/lib/cmd/getscript.js +++ b/src/lib/cmd/qseow/getscript.js @@ -1,7 +1,7 @@ import enigma from 'enigma.js'; -import { setupEnigmaConnection, addTrafficLogging } from '../util/enigma.js'; -import { logger, setLoggingLevel, isPkg, execPath } from '../../globals.js'; -import { catchLog } from '../util/log.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; +import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; +import { catchLog } from '../../util/log.js'; /** * diff --git a/src/lib/cmd/getsessions.js b/src/lib/cmd/qseow/getsessions.js similarity index 98% rename from src/lib/cmd/getsessions.js rename to src/lib/cmd/qseow/getsessions.js index 5c61dd1..f0b4faf 100644 --- a/src/lib/cmd/getsessions.js +++ b/src/lib/cmd/qseow/getsessions.js @@ -1,7 +1,7 @@ import { table } from 'table'; -import { getSessionsFromQseow } from '../util/session.js'; -import { logger, setLoggingLevel, isPkg, execPath } from '../../globals.js'; -import { catchLog } from '../util/log.js'; +import { getSessionsFromQseow } from '../../util/qseow/session.js'; +import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; +import { catchLog } from '../../util/log.js'; const consoleTableConfig = { border: { diff --git a/src/lib/cmd/gettask.js b/src/lib/cmd/qseow/gettask.js similarity index 99% rename from src/lib/cmd/gettask.js rename to src/lib/cmd/qseow/gettask.js index 2fdbb6a..7e111e0 100644 --- a/src/lib/cmd/gettask.js +++ b/src/lib/cmd/qseow/gettask.js @@ -4,11 +4,11 @@ import { promises as Fs } from '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 { mapEventType, mapIncrementOption, mapDaylightSavingTime, mapRuleState } from '../util/lookups.js'; -import { getTagsFromQseow } from '../util/tag.js'; -import { catchLog } from '../util/log.js'; +import { logger, setLoggingLevel, isPkg, execPath, verifyFileExists } 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'; const consoleTableConfig = { border: { diff --git a/src/lib/cmd/getvariable.js b/src/lib/cmd/qseow/getvariable.js similarity index 98% rename from src/lib/cmd/getvariable.js rename to src/lib/cmd/qseow/getvariable.js index d0df068..b66cef3 100644 --- a/src/lib/cmd/getvariable.js +++ b/src/lib/cmd/qseow/getvariable.js @@ -3,10 +3,10 @@ import enigma from 'enigma.js'; import { table } from 'table'; -import { setupEnigmaConnection, addTrafficLogging } from '../util/enigma.js'; -import { getApps } from '../util/app.js'; -import { logger, setLoggingLevel, isPkg, execPath } from '../../globals.js'; -import { catchLog } from '../util/log.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; +import { getApps } from '../../util/qseow/app.js'; +import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; +import { catchLog } from '../../util/log.js'; const consoleTableConfig = { border: { diff --git a/src/lib/cmd/import-masteritem-excel.js b/src/lib/cmd/qseow/import-masteritem-excel.js similarity index 99% rename from src/lib/cmd/import-masteritem-excel.js rename to src/lib/cmd/qseow/import-masteritem-excel.js index 4cec13a..cc62faf 100644 --- a/src/lib/cmd/import-masteritem-excel.js +++ b/src/lib/cmd/qseow/import-masteritem-excel.js @@ -4,9 +4,9 @@ import enigma from 'enigma.js'; import xlsx from 'node-xlsx'; import { v4 as uuidCreate } from 'uuid'; -import { setupEnigmaConnection, addTrafficLogging } from '../util/enigma.js'; -import { logger, setLoggingLevel, isPkg, execPath, verifyFileExists, sleep } from '../../globals.js'; -import { catchLog } from '../util/log.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; +import { logger, setLoggingLevel, isPkg, execPath, verifyFileExists, sleep } from '../../../globals.js'; +import { catchLog } from '../../util/log.js'; let importCount = 0; diff --git a/src/lib/cmd/importapp.js b/src/lib/cmd/qseow/importapp.js similarity index 90% rename from src/lib/cmd/importapp.js rename to src/lib/cmd/qseow/importapp.js index 7ad908e..8d6d416 100644 --- a/src/lib/cmd/importapp.js +++ b/src/lib/cmd/qseow/importapp.js @@ -2,12 +2,12 @@ import xlsx from 'node-xlsx'; -import { logger, setLoggingLevel, isPkg, execPath, verifyFileExists, isNumeric } from '../../globals.js'; -import QlikSenseApps from '../app/class_allapps.js'; -import { getAppColumnPosFromHeaderRow } from '../util/lookups.js'; -import { getTagsFromQseow } from '../util/tag.js'; -import { getCustomPropertiesFromQseow } from '../util/customproperties.js'; -import { catchLog } from '../util/log.js'; +import { logger, setLoggingLevel, isPkg, execPath, verifyFileExists, isNumeric } 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) => { try { diff --git a/src/lib/cmd/importtask.js b/src/lib/cmd/qseow/importtask.js similarity index 97% rename from src/lib/cmd/importtask.js rename to src/lib/cmd/qseow/importtask.js index fc4b4a6..0c4d7c9 100644 --- a/src/lib/cmd/importtask.js +++ b/src/lib/cmd/qseow/importtask.js @@ -8,13 +8,13 @@ import fs from 'fs'; import { finished } from 'stream/promises'; -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 { getTaskColumnPosFromHeaderRow } from '../util/lookups.js'; -import { getTagsFromQseow } from '../util/tag.js'; -import { getCustomPropertiesFromQseow } from '../util/customproperties.js'; -import { catchLog } from '../util/log.js'; +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 { getTaskColumnPosFromHeaderRow } 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 getHeaders = async (options) => { const records = []; diff --git a/src/lib/cmd/scramblefield.js b/src/lib/cmd/qseow/scramblefield.js similarity index 95% rename from src/lib/cmd/scramblefield.js rename to src/lib/cmd/qseow/scramblefield.js index 720c87d..3ba3d2b 100644 --- a/src/lib/cmd/scramblefield.js +++ b/src/lib/cmd/qseow/scramblefield.js @@ -1,7 +1,7 @@ import enigma from 'enigma.js'; -import { setupEnigmaConnection, addTrafficLogging } from '../util/enigma.js'; -import { logger, setLoggingLevel, isPkg, execPath } from '../../globals.js'; -import { catchLog } from '../util/log.js'; +import { setupEnigmaConnection, addTrafficLogging } from '../../util/qseow/enigma.js'; +import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; +import { catchLog } from '../../util/log.js'; /** * diff --git a/src/lib/cmd/settaskcp.js b/src/lib/cmd/qseow/settaskcp.js similarity index 98% rename from src/lib/cmd/settaskcp.js rename to src/lib/cmd/qseow/settaskcp.js index 556b7be..0d88e9f 100644 --- a/src/lib/cmd/settaskcp.js +++ b/src/lib/cmd/qseow/settaskcp.js @@ -1,7 +1,7 @@ import yesno from 'yesno'; -import { logger } from '../../globals.js'; -import { getCustomProperty, getTasksFromQseow, updateReloadTask } from '../task/task_qrs.js'; -import { catchLog } from '../util/log.js'; +import { logger } from '../../../globals.js'; +import { getCustomProperty, getTasksFromQseow, updateReloadTask } from '../../task/task_qrs.js'; +import { catchLog } from '../../util/log.js'; const updateTask = async (options, customPropertyDef, task) => new Promise(async (resolve, reject) => { diff --git a/src/lib/cmd/testconnection.js b/src/lib/cmd/qseow/testconnection.js similarity index 91% rename from src/lib/cmd/testconnection.js rename to src/lib/cmd/qseow/testconnection.js index 2588787..21b0ddb 100644 --- a/src/lib/cmd/testconnection.js +++ b/src/lib/cmd/qseow/testconnection.js @@ -1,6 +1,6 @@ -import { logger, setLoggingLevel, isPkg, execPath } from '../../globals.js'; -import getAboutFromQseow from '../util/about.js'; -import { catchLog } from '../util/log.js'; +import { logger, setLoggingLevel, isPkg, execPath } from '../../../globals.js'; +import getAboutFromQseow from '../../util/qseow/about.js'; +import { catchLog } from '../../util/log.js'; const testConnection = async (options) => { try { diff --git a/src/lib/cmd/useractivity.js b/src/lib/cmd/qseow/useractivity.js similarity index 98% rename from src/lib/cmd/useractivity.js rename to src/lib/cmd/qseow/useractivity.js index ea489bb..319cd42 100644 --- a/src/lib/cmd/useractivity.js +++ b/src/lib/cmd/qseow/useractivity.js @@ -1,5 +1,5 @@ -import { logger } from '../../globals.js'; -import { catchLog } from '../util/log.js'; +import { logger } from '../../../globals.js'; +import { catchLog } from '../../util/log.js'; export function getUserActivityProfessional(qrsInteractInstance) { // eslint-disable-next-line no-unused-vars, no-async-promise-executor diff --git a/src/lib/cmd/vistask.js b/src/lib/cmd/qseow/vistask.js similarity index 99% rename from src/lib/cmd/vistask.js rename to src/lib/cmd/qseow/vistask.js index fc33b05..ffb0d39 100644 --- a/src/lib/cmd/vistask.js +++ b/src/lib/cmd/qseow/vistask.js @@ -3,8 +3,8 @@ import path from 'path'; import fs from 'fs'; import handlebars from 'handlebars'; import { Readable } from 'stream'; -import { appVersion, logger, setLoggingLevel, isPkg, execPath, verifyFileExists } from '../../globals.js'; -import QlikSenseTasks from '../task/class_alltasks.js'; +import { appVersion, logger, setLoggingLevel, isPkg, execPath, verifyFileExists } from '../../../globals.js'; +import QlikSenseTasks from '../../task/class_alltasks.js'; // js: 'application/javascript', const MIME_TYPES = { diff --git a/src/lib/task/class_allcompositeevents.js b/src/lib/task/class_allcompositeevents.js index f6dab0e..cd06426 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 { logger, execPath, verifyFileExists } from '../../globals.js'; -import setupQRSConnection from '../util/qrs.js'; +import setupQRSConnection from '../util/qseow/qrs.js'; import QlikSenseCompositeEvent from './class_compositeevent.js'; import { catchLog } from '../util/log.js'; diff --git a/src/lib/task/class_allschemaevents.js b/src/lib/task/class_allschemaevents.js index b0a9866..f6966cd 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 { logger, execPath } from '../../globals.js'; -import setupQRSConnection from '../util/qrs.js'; +import setupQRSConnection from '../util/qseow/qrs.js'; import QlikSenseSchemaEvent from './class_schemaevent.js'; import { catchLog } from '../util/log.js'; diff --git a/src/lib/task/class_alltasks.js b/src/lib/task/class_alltasks.js index 5b40beb..99faa55 100644 --- a/src/lib/task/class_alltasks.js +++ b/src/lib/task/class_alltasks.js @@ -2,7 +2,7 @@ import axios from 'axios'; import path from 'path'; import { v4 as uuidv4, validate } from 'uuid'; import { logger, execPath } from '../../globals.js'; -import setupQRSConnection from '../util/qrs.js'; +import setupQRSConnection from '../util/qseow/qrs.js'; import { mapTaskType, @@ -11,15 +11,15 @@ import { mapIncrementOption, mapRuleState, getTaskColumnPosFromHeaderRow, -} from '../util/lookups.js'; +} from '../util/qseow/lookups.js'; import QlikSenseTask from './class_task.js'; import QlikSenseSchemaEvents from './class_allschemaevents.js'; import QlikSenseCompositeEvents from './class_allcompositeevents.js'; -import { getTagIdByName } from '../util/tag.js'; -import { getCustomPropertyIdByName } from '../util/customproperties.js'; -import { getAppById } from '../util/app.js'; -import { taskExistById, getTaskById } from '../util/task.js'; +import { getTagIdByName } from '../util/qseow/tag.js'; +import { getCustomPropertyIdByName } from '../util/qseow/customproperties.js'; +import { getAppById } from '../util/qseow/app.js'; +import { taskExistById, getTaskById } from '../util/qseow/task.js'; import { catchLog } from '../util/log.js'; class QlikSenseTasks { diff --git a/src/lib/task/class_task.js b/src/lib/task/class_task.js index a51bdfe..5e35cdf 100644 --- a/src/lib/task/class_task.js +++ b/src/lib/task/class_task.js @@ -1,7 +1,7 @@ import { Duration } from 'luxon'; // const { randomWords } = require('random-words'); import { logger } from '../../globals.js'; -import { mapTaskExecutionStatus } from '../util/lookups.js'; +import { mapTaskExecutionStatus } from '../util/qseow/lookups.js'; // const randomWords2 = (...args) => import('random-words').then(({ default: randomWords }) => randomWords(...args)); class QlikSenseTask { diff --git a/src/lib/task/task_qrs.js b/src/lib/task/task_qrs.js index 22209c3..98afa2a 100644 --- a/src/lib/task/task_qrs.js +++ b/src/lib/task/task_qrs.js @@ -5,7 +5,7 @@ import path from 'path'; import { logger, execPath } from '../../globals.js'; -import setupQRSConnection from '../util/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'); diff --git a/src/lib/util/qscloud/assert-options.js b/src/lib/util/qscloud/assert-options.js new file mode 100644 index 0000000..34f3c81 --- /dev/null +++ b/src/lib/util/qscloud/assert-options.js @@ -0,0 +1,23 @@ +import path from '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) { + logger.error('Mandatory option --auth-type is missing. Use it to specify how authorization with Qlik Sense Cloud will be done.'); + process.exit(1); + } + + // Debug + logger.debug(`Auth type: ${options.authType}`); + logger.debug(`execPath: ${execPath}`); + logger.debug(`authCertFile: ${options.authCertFile}`); + logger.debug(`authCertKeyFile: ${options.authCertKeyFile}`); + + // API key authentication + if (options.authType === 'apikey') { + // + } +}; diff --git a/src/lib/util/about.js b/src/lib/util/qseow/about.js similarity index 94% rename from src/lib/util/about.js rename to src/lib/util/qseow/about.js index d0803f7..4072500 100644 --- a/src/lib/util/about.js +++ b/src/lib/util/qseow/about.js @@ -1,8 +1,8 @@ import axios from 'axios'; import path from 'path'; -import { logger, execPath } from '../../globals.js'; +import { logger, execPath } from '../../../globals.js'; import setupQRSConnection from './qrs.js'; -import { catchLog } from './log.js'; +import { catchLog } from '../log.js'; function getAboutFromQseow(options) { return new Promise((resolve, reject) => { diff --git a/src/lib/util/app.js b/src/lib/util/qseow/app.js similarity index 98% rename from src/lib/util/app.js rename to src/lib/util/qseow/app.js index bab6816..8bee75b 100644 --- a/src/lib/util/app.js +++ b/src/lib/util/qseow/app.js @@ -1,9 +1,9 @@ import axios from 'axios'; import path from 'path'; import { validate } from 'uuid'; -import { logger, execPath, getCliOptions } from '../../globals.js'; +import { logger, execPath, getCliOptions } from '../../../globals.js'; import setupQRSConnection from './qrs.js'; -import { catchLog } from './log.js'; +import { catchLog } from '../log.js'; export async function getApps(options, idArray, tagArray) { try { diff --git a/src/lib/util/assert-options.js b/src/lib/util/qseow/assert-options.js similarity index 98% rename from src/lib/util/assert-options.js rename to src/lib/util/qseow/assert-options.js index 498fd25..45c60f7 100644 --- a/src/lib/util/assert-options.js +++ b/src/lib/util/qseow/assert-options.js @@ -1,8 +1,8 @@ import path from 'path'; import { version as uuidVersion, validate as uuidValidate } from 'uuid'; -import { logger, execPath, verifyFileExists } from '../../globals.js'; +import { logger, execPath, verifyFileExists } from '../../../globals.js'; -export const sharedParamAssertOptions = async (options) => { +export const qseowSharedParamAssertOptions = async (options) => { // Ensure that parameters common to all commands are valid if (options.authType === undefined || !options.authType) { logger.error('Mandatory option --auth-type is missing. Use it to specify how authorization with Qlik Sense will be done.'); diff --git a/src/lib/util/customproperties.js b/src/lib/util/qseow/customproperties.js similarity index 99% rename from src/lib/util/customproperties.js rename to src/lib/util/qseow/customproperties.js index 33a2d03..ce0b174 100644 --- a/src/lib/util/customproperties.js +++ b/src/lib/util/qseow/customproperties.js @@ -1,6 +1,6 @@ import axios from 'axios'; import path from 'path'; -import { logger, execPath } from '../../globals.js'; +import { logger, execPath } from '../../../globals.js'; import setupQRSConnection from './qrs.js'; export function getCustomPropertiesFromQseow(options) { diff --git a/src/lib/util/enigma.js b/src/lib/util/qseow/enigma.js similarity index 99% rename from src/lib/util/enigma.js rename to src/lib/util/qseow/enigma.js index f87f783..77212f6 100644 --- a/src/lib/util/enigma.js +++ b/src/lib/util/qseow/enigma.js @@ -4,7 +4,7 @@ import path from 'path'; import { readFileSync } from 'fs'; import { fileURLToPath } from 'url'; import upath from 'upath'; -import { logger, execPath, readCert } from '../../globals.js'; +import { logger, execPath, readCert } from '../../../globals.js'; export const setupEnigmaConnection = async (options, sessionId) => { logger.debug('Prepping for Enigma connection...'); diff --git a/src/lib/util/lookups.js b/src/lib/util/qseow/lookups.js similarity index 100% rename from src/lib/util/lookups.js rename to src/lib/util/qseow/lookups.js diff --git a/src/lib/util/proxy.js b/src/lib/util/qseow/proxy.js similarity index 92% rename from src/lib/util/proxy.js rename to src/lib/util/qseow/proxy.js index c7e76a8..7c27494 100644 --- a/src/lib/util/proxy.js +++ b/src/lib/util/qseow/proxy.js @@ -1,8 +1,8 @@ import axios from 'axios'; import path from 'path'; -import { logger, execPath } from '../../globals.js'; +import { logger, execPath } from '../../../globals.js'; import setupQRSConnection from './qrs.js'; -import { catchLog } from './log.js'; +import { catchLog } from '../log.js'; const getProxiesFromQseow = async (options, _sessionCookie) => { logger.verbose(`Getting all proxies from QSEoW...`); diff --git a/src/lib/util/qps.js b/src/lib/util/qseow/qps.js similarity index 97% rename from src/lib/util/qps.js rename to src/lib/util/qseow/qps.js index ee9dd7b..b066a96 100644 --- a/src/lib/util/qps.js +++ b/src/lib/util/qseow/qps.js @@ -1,5 +1,5 @@ import https from 'https'; -import { logger, generateXrfKey, readCert } from '../../globals.js'; +import { logger, generateXrfKey, readCert } from '../../../globals.js'; const setupQPSConnection = (options, param) => { // eslint-disable-next-line no-unused-vars diff --git a/src/lib/util/qrs.js b/src/lib/util/qseow/qrs.js similarity index 98% rename from src/lib/util/qrs.js rename to src/lib/util/qseow/qrs.js index 430d04f..4bf51bb 100644 --- a/src/lib/util/qrs.js +++ b/src/lib/util/qseow/qrs.js @@ -1,5 +1,5 @@ import https from 'https'; -import { logger, generateXrfKey, readCert } from '../../globals.js'; +import { logger, generateXrfKey, readCert } from '../../../globals.js'; const setupQRSConnection = (options, param) => { // eslint-disable-next-line no-unused-vars diff --git a/src/lib/util/session.js b/src/lib/util/qseow/session.js similarity index 99% rename from src/lib/util/session.js rename to src/lib/util/qseow/session.js index 0d481fc..26379e9 100644 --- a/src/lib/util/session.js +++ b/src/lib/util/qseow/session.js @@ -2,10 +2,10 @@ import axios from 'axios'; import path from 'path'; import { table } from 'table'; import yesno from 'yesno'; -import { logger, execPath } from '../../globals.js'; +import { logger, execPath } from '../../../globals.js'; import setupQPSConnection from './qps.js'; import setupQRSConnection from './qrs.js'; -import { catchLog } from './log.js'; +import { catchLog } from '../log.js'; import getProxiesFromQseow from './proxy.js'; const consoleProxiesTableConfig = { diff --git a/src/lib/util/tag.js b/src/lib/util/qseow/tag.js similarity index 97% rename from src/lib/util/tag.js rename to src/lib/util/qseow/tag.js index bde341d..1738d68 100644 --- a/src/lib/util/tag.js +++ b/src/lib/util/qseow/tag.js @@ -1,8 +1,8 @@ import axios from 'axios'; import path from 'path'; -import { logger, execPath } from '../../globals.js'; +import { logger, execPath } from '../../../globals.js'; import setupQRSConnection from './qrs.js'; -import { catchLog } from './log.js'; +import { catchLog } from '../log.js'; export function getTagsFromQseow(options) { return new Promise((resolve, _reject) => { diff --git a/src/lib/util/task.js b/src/lib/util/qseow/task.js similarity index 99% rename from src/lib/util/task.js rename to src/lib/util/qseow/task.js index f88ef9f..83b7edf 100644 --- a/src/lib/util/task.js +++ b/src/lib/util/qseow/task.js @@ -2,9 +2,9 @@ import axios from 'axios'; import fs from 'fs'; import path from 'path'; import { validate } from 'uuid'; -import { logger, execPath, getCliOptions } from '../../globals.js'; +import { logger, execPath, getCliOptions } from '../../../globals.js'; import setupQRSConnection from './qrs.js'; -import { catchLog } from './log.js'; +import { catchLog } from '../log.js'; // Check if a task with a given id exists // Look for all kinds of tasks, not just reload tasks