From 5ceeef0a8b4462e87eadc2685714f6a90215c88b Mon Sep 17 00:00:00 2001 From: Timotej Ecimovic <timotej.ecimovic@silabs.com> Date: Fri, 10 Jun 2022 09:16:18 -0400 Subject: [PATCH] Allow multiple --zcl arguments to load both Zigbee and Matter. 0. Proper exit if passed zcl.json file is broken. 1. Make zcl.json files an array. 2. Fix unit tests, which do not pass an array to zcl files. 3. Clean up the API for multi-file loading. 4. Properly propagate array and load the packages. --- Jenkinsfile | 1 + package.json | 6 +- src-electron/main-process/startup.js | 120 ++++++++++++--------------- src-electron/server/http-server.js | 8 +- src-electron/ui/main-ui.ts | 22 +++-- src-electron/util/args.ts | 2 +- src-electron/util/util.js | 18 ++-- src-electron/zcl/zcl-loader.js | 38 ++++++++- test/zcl-loader-consecutive.test.js | 4 +- 9 files changed, 124 insertions(+), 95 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 0b391b04..ed018f9b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -241,6 +241,7 @@ pipeline { sh 'npm --version' sh 'node --version' + sh 'npm config set strict-ssl false' sh 'npm ci' sh 'npm list || true' sh 'security unlock-keychain -u "/Library/Keychains/System.keychain"' diff --git a/package.json b/package.json index 5ed38ef6..b085ccad 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "type": "commonjs", "name": "zap", - "version": "2022.6.8", + "version": "2022.6.10", "description": "Configuration tool for the Zigbee Cluster Library", "productName": "zap", "cordovaId": "", @@ -49,7 +49,9 @@ "version-stamp": "node src-script/zap-versionstamp.js", "package-metadata": "node src-script/zap-package-metadata.js", "zap": "node src-script/zap-start.js --logToStdout --gen ./test/gen-template/zigbee/gen-templates.json", - "zapmatter": "node src-script/zap-start.js --logToStdout --zcl ./zcl-builtin/matter/zcl.json --gen ./test/gen-template/matter/gen-test.json", + "zapall": "node src-script/zap-start.js --logToStdout --zcl ./zcl-builtin/silabs/zcl.json --zcl ./zcl-builtin/matter/zcl.json --gen ./test/gen-template/zigbee/gen-templates.json", + "zapzigbee": "node src-script/zap-start.js --logToStdout --zcl ./zcl-builtin/silabs/zcl.json --gen ./test/gen-template/zigbee/gen-templates.json", + "zapmatter": "node src-script/zap-start.js --logToStdoutc --gen ./test/gen-template/matter/gen-test.json", "zapmeta": "node src-script/zap-start.js --logToStdout --zcl ./test/resource/meta/zcl.json --gen ./test/resource/meta/gen-test.json ./test/resource/test-meta.zap", "zaphelp": "node src-script/zap-start.js --help", "zap-dotdot": "node src-script/zap-start.js --logToStdout --zcl ./zcl-builtin/dotdot/library.xml", diff --git a/src-electron/main-process/startup.js b/src-electron/main-process/startup.js index c58eef5d..b6c00059 100644 --- a/src-electron/main-process/startup.js +++ b/src-electron/main-process/startup.js @@ -63,8 +63,8 @@ async function startNormal(quitFunction, argv) { mainDatabase = db try { - let ctx = await zclLoader.loadZcl(db, argv.zclProperties) - ctx = await generatorEngine.loadTemplates(ctx.db, argv.generationTemplate) + await zclLoader.loadZclMetafiles(db, argv.zclProperties) + let ctx = await generatorEngine.loadTemplates(db, argv.generationTemplate) if (ctx.error) { env.logWarning(ctx.error) @@ -186,7 +186,7 @@ async function startConvert(argv, options) { env.zapVersion() ) options.logger(' ðŸ database and schema initialized') - await zclLoader.loadZcl(db, argv.zclProperties) + await zclLoader.loadZclMetafiles(db, argv.zclProperties) options.logger(` ðŸ zcl package loaded: ${argv.zclProperties}`) if (argv.generationTemplate != null) { await generatorEngine.loadTemplates(db, argv.generationTemplate) @@ -265,34 +265,27 @@ async function startAnalyze(argv, options) { options.logger(' 👉 remove old database file') fs.unlinkSync(dbFile) } - let db - return dbApi - .initDatabaseAndLoadSchema(dbFile, env.schemaFile(), env.zapVersion()) - .then((d) => { - db = d - options.logger(' 👉 database and schema initialized') - return zclLoader.loadZcl(db, argv.zclProperties) - }) - .then((d) => { - return util.executePromisesSequentially(paths, (singlePath) => - importJs - .importDataFromFile(db, singlePath, { - defaultZclMetafile: argv.zclProperties, - postImportScript: argv.postImportScript, - }) - .then((importResult) => - util.sessionReport(db, importResult.sessionId) - ) - .then((report) => { - options.logger(`🤖 File: ${singlePath}\n`) - options.logger(report) - }) - ) - }) - .then(() => { - options.logger('😎 Analysis done!') - if (options.quitFunction != null) options.quitFunction() - }) + let db = await dbApi.initDatabaseAndLoadSchema( + dbFile, + env.schemaFile(), + env.zapVersion() + ) + options.logger(' 👉 database and schema initialized') + await zclLoader.loadZclMetafiles(db, argv.zclProperties) + await util.executePromisesSequentially(paths, (singlePath) => + importJs + .importDataFromFile(db, singlePath, { + defaultZclMetafile: argv.zclProperties, + postImportScript: argv.postImportScript, + }) + .then((importResult) => util.sessionReport(db, importResult.sessionId)) + .then((report) => { + options.logger(`🤖 File: ${singlePath}\n`) + options.logger(report) + }) + ) + options.logger('😎 Analysis done!') + if (options.quitFunction != null) options.quitFunction() } /** @@ -317,11 +310,9 @@ async function startServer(argv, quitFunction) { }) mainDatabase = db - return zclLoader - .loadZcl(db, argv.zclProperties) - .then((ctx) => - generatorEngine.loadTemplates(ctx.db, argv.generationTemplate) - ) + await zclLoader.loadZclMetafiles(db, argv.zclProperties) + return generatorEngine + .loadTemplates(db, argv.generationTemplate) .then((ctx) => { if (ctx.error) { env.logWarning(ctx.error) @@ -367,38 +358,29 @@ async function startSelfCheck( options.logger(' 👉 remove old database file') fs.unlinkSync(dbFile) } - let mainDb - return dbApi - .initDatabaseAndLoadSchema(dbFile, env.schemaFile(), env.zapVersion()) - .then((db) => { - mainDb = db - options.logger(' 👉 database and schema initialized') - return zclLoader.loadZcl(db, argv.zclProperties) - }) - .then((ctx) => { - options.logger(' 👉 zcl data loaded') - return generatorEngine.loadTemplates(ctx.db, argv.generationTemplate) - }) - .then(async (ctx) => { - if (ctx.error) { - options.logger(` âš ï¸ ${ctx.error}`) - } else { - options.logger(' 👉 generation templates loaded') - } + let mainDb = await dbApi.initDatabaseAndLoadSchema( + dbFile, + env.schemaFile(), + env.zapVersion() + ) + options.logger(' 👉 database and schema initialized') + await zclLoader.loadZclMetafiles(mainDb, argv.zclProperties) + options.logger(' 👉 zcl data loaded') + let ctx = await generatorEngine.loadTemplates(mainDb, argv.generationTemplate) + if (ctx.error) { + options.logger(` âš ï¸ ${ctx.error}`) + } else { + options.logger(' 👉 generation templates loaded') + } - // This is a hack to prevent too quick shutdown that causes core dumps. - dbApi.closeDatabaseSync(mainDb) - options.logger(' 👉 database closed') - await util.waitFor(2000) - options.logger('😎 Self-check done!') - if (options.quitFunction != null) { - options.quitFunction() - } - }) - .catch((err) => { - env.logError(err) - throw err - }) + // This is a hack to prevent too quick shutdown that causes core dumps. + dbApi.closeDatabaseSync(mainDb) + options.logger(' 👉 database closed') + await util.waitFor(2000) + options.logger('😎 Self-check done!') + if (options.quitFunction != null) { + options.quitFunction() + } } async function generateSingleFile( @@ -485,8 +467,8 @@ async function startGeneration(argv, options) { env.zapVersion() ) - let ctx = await zclLoader.loadZcl(mainDb, zclProperties) - ctx = await generatorEngine.loadTemplates(ctx.db, templateMetafile) + await zclLoader.loadZclMetafiles(mainDb, zclProperties) + let ctx = await generatorEngine.loadTemplates(mainDb, templateMetafile) if (ctx.error) { throw ctx.error } diff --git a/src-electron/server/http-server.js b/src-electron/server/http-server.js index 204fa281..f42ba680 100644 --- a/src-electron/server/http-server.js +++ b/src-electron/server/http-server.js @@ -171,7 +171,7 @@ async function initHttpServer( }) } -function userSessionHandler(db, metafiles) { +function userSessionHandler(db, options) { return (req, res, next) => { let sessionUuid = req.query[restApi.param.sessionId] let userKey = req.session.id @@ -201,11 +201,7 @@ function userSessionHandler(db, metafiles) { }) .then((result) => { if (result.newSession) { - return util.initializeSessionPackage( - db, - result.sessionId, - metafiles - ) + return util.initializeSessionPackage(db, result.sessionId, options) } }) .then(() => { diff --git a/src-electron/ui/main-ui.ts b/src-electron/ui/main-ui.ts index 8095d392..4d24bb10 100644 --- a/src-electron/ui/main-ui.ts +++ b/src-electron/ui/main-ui.ts @@ -38,15 +38,21 @@ function hookSecondInstanceEvents(argv: args.Arguments) { * Hook up all the events for the electron app object. */ function hookMainInstanceEvents(argv: args.Arguments) { - app.whenReady().then(() => - startup.startUpMainInstance( - { - quitFunction: app.quit, - uiEnableFunction: uiUtil.enableUi, - }, - argv + app + .whenReady() + .then(() => + startup.startUpMainInstance( + { + quitFunction: app.quit, + uiEnableFunction: uiUtil.enableUi, + }, + argv + ) ) - ) + .catch((err) => { + console.log(err) + app.quit() + }) if (!argv._.includes('server') && !argv.noServer) { app.on('window-all-closed', () => { diff --git a/src-electron/util/args.ts b/src-electron/util/args.ts index 9d48f9ff..eb88ff32 100644 --- a/src-electron/util/args.ts +++ b/src-electron/util/args.ts @@ -106,7 +106,7 @@ export function processCommandLineArguments(argv: string[]) { .option('zclProperties', { desc: 'zcl.properties file to read in.', alias: ['zcl', 'z'], - type: 'string', + type: 'array', default: env.builtinSilabsZclMetafile(), }) .option('generationTemplate', { diff --git a/src-electron/util/util.js b/src-electron/util/util.js index 6b8e683a..1df6624f 100644 --- a/src-electron/util/util.js +++ b/src-electron/util/util.js @@ -52,12 +52,18 @@ function checksum(data) { * * @param {*} db * @param {*} sessionId - * @param {*} metafiles: object containing 'zcl' and 'template' + * @param {*} options: object containing 'zcl' and 'template' * @returns Promise that resolves with the packages array. */ -async function initializeSessionPackage(db, sessionId, metafiles) { +async function initializeSessionPackage(db, sessionId, options) { let promises = [] + // This is the desired ZCL properties file. Because it is possible + // that an array is passed from the command line, we are simply taking + // the first one, if we pass multiple ones. + let zclFile = options.zcl + if (Array.isArray(zclFile)) zclFile = options.zcl[0] + // 1. Associate a zclProperties file. let zclPropertiesPromise = queryPackage .getPackagesByType(db, dbEnum.packageType.zclProperties) @@ -73,12 +79,12 @@ async function initializeSessionPackage(db, sessionId, metafiles) { packageId = null } else { rows.forEach((p) => { - if (path.resolve(metafiles.zcl) === p.path) { + if (path.resolve(zclFile) === p.path) { packageId = p.id } }) env.logWarning( - `${sessionId}, ${metafiles.zcl}: Multiple toplevel zcl.properties found. Using the first one from args: ${packageId}` + `${sessionId}, ${zclFile}: Multiple toplevel zcl.properties found. Using the first one from args: ${packageId}` ) } if (packageId != null) { @@ -103,8 +109,8 @@ async function initializeSessionPackage(db, sessionId, metafiles) { } else { rows.forEach((p) => { if ( - metafiles.template != null && - path.resolve(metafiles.template) === p.path + options.template != null && + path.resolve(options.template) === p.path ) { packageId = p.id } diff --git a/src-electron/zcl/zcl-loader.js b/src-electron/zcl/zcl-loader.js index 4fd8edaf..900aa6e7 100644 --- a/src-electron/zcl/zcl-loader.js +++ b/src-electron/zcl/zcl-loader.js @@ -28,6 +28,7 @@ const queryZcl = require('../db/query-zcl') const queryDeviceType = require('../db/query-device-type') const env = require('../util/env') const nativeRequire = require('../util/native-require') +const util = require('../util/util') const defaultValidator = (zclData) => { return [] @@ -64,12 +65,39 @@ async function recordVersion(db, packageId, version) { * * @export * @param {*} db + * @param {*} metadataFile array of paths + * @returns Array of loaded packageIds. + */ +async function loadZclMetafiles(db, metadataFiles) { + let packageIds = [] + if (Array.isArray(metadataFiles)) { + for (let f of metadataFiles) { + let ctx = await loadZcl(db, f) + packageIds.push(ctx.packageId) + } + } else { + let ctx = await loadZcl(db, metadataFiles) + packageIds.push(ctx.packageId) + } + return packageIds +} + +/** + * Loads individual zcl.json metafile. + * + * @param {*} db * @param {*} metadataFile - * @returns a Promise that resolves with the db. + * @returns Context object that contains .db and .packageId */ async function loadZcl(db, metadataFile) { let ext = path.extname(metadataFile) let resolvedMetafile = path.resolve(metadataFile) + + try { + await fsp.access(resolvedMetafile, fs.constants.R_OK) + } catch { + throw new Error(`Can't access file: ${metadataFile}`) + } if (ext == '.xml') { return dLoad.loadDotdotZcl(db, resolvedMetafile) } else if (ext == '.properties') { @@ -81,6 +109,13 @@ async function loadZcl(db, metadataFile) { } } +/** + * Load individual custom XML files. + * + * @param {*} db + * @param {*} filePath + * @param {*} sessionId + */ async function loadIndividualFile(db, filePath, sessionId) { let zclPropertiesPackages = await queryPackage.getSessionPackagesByType( db, @@ -264,6 +299,7 @@ async function getDiscriminatorMap(db, packageId) { } exports.loadZcl = loadZcl +exports.loadZclMetafiles = loadZclMetafiles exports.recordToplevelPackage = recordToplevelPackage exports.recordVersion = recordVersion exports.processZclPostLoading = processZclPostLoading diff --git a/test/zcl-loader-consecutive.test.js b/test/zcl-loader-consecutive.test.js index e8ebce95..975b768c 100644 --- a/test/zcl-loader-consecutive.test.js +++ b/test/zcl-loader-consecutive.test.js @@ -64,13 +64,13 @@ test( ctx = await zclLoader.loadZcl(db, env.builtinSilabsZclMetafile()) expect(ctx.packageId).toEqual(jsonPackageId) - let p = await queryPackage.getPackageByPackageId(ctx.db, ctx.packageId) + let p = await queryPackage.getPackageByPackageId(db, ctx.packageId) expect(p.version).toEqual('ZCL Test Data') await zclLoader.loadZcl(db, env.builtinDotdotZclMetafile()) ctx = await zclLoader.loadZcl(db, env.builtinDotdotZclMetafile()) dotdotPackageId = ctx.packageId expect(dotdotPackageId).not.toEqual(jsonPackageId) - p = await queryPackage.getPackageByPackageId(ctx.db, ctx.packageId) + p = await queryPackage.getPackageByPackageId(db, ctx.packageId) expect(p.version).toEqual('1.0') let rows = await queryPackage.getPackagesByType( -- GitLab