diff --git a/electron/app/js/ipcRendererPreload.js b/electron/app/js/ipcRendererPreload.js index c4666a6f4..6243dce2f 100644 --- a/electron/app/js/ipcRendererPreload.js +++ b/electron/app/js/ipcRendererPreload.js @@ -9,6 +9,7 @@ const uuid = require('uuid'); const k8sUtils = require('./k8sUtils'); const fsUtils = require('./fsUtils'); +const errorUtils = require('./errorUtils'); const WktApp = require('./wktApp'); const osUtils = require('./osUtils'); const i18n = require('./i18next.webui.config'); @@ -292,6 +293,7 @@ contextBridge.exposeInMainWorld( 'utils': { generateUuid: () => uuid.v4(), compareVersions: (version, otherVersion) => compareVersions(version, otherVersion), + getErrorMessage: (err) => errorUtils.getErrorMessage(err), mainModule: mainModule, wrcFrontendCompatibilityVersion: wrcFrontendCompatibilityVersion, } diff --git a/electron/app/locales/en/webui.json b/electron/app/locales/en/webui.json index cef236705..0e028bfe4 100644 --- a/electron/app/locales/en/webui.json +++ b/electron/app/locales/en/webui.json @@ -1285,7 +1285,7 @@ "k8s-domain-undeployer-operator-installed-check-failed-error-message": "Unable to undeploy WebLogic domain because an error occurred while detecting if the WebLogic Kubernetes Operator {{operatorName}} is installed in Kubernetes namespace {{operatorNamespace}}: {{error}}.", "k8s-domain-undeployer-update-operator-config-in-progress": "Updating WebLogic Kubernetes Operator {{operatorName}} in Kubernetes namespace {{operatorNamespace}} to stop managing WebLogic Domains in Kubernetes namespace {{domainNamespace}}", "k8s-domain-undeployer-k8s-namespace-delete-failed-error-message": "Unable to undeploy the domain because an error occurred while trying to delete the Kubernetes namespace {{namespace}}: {{error}}.", - "k8s-domain-undeployer-k8s-object-delete-failed-error-message": "Unable to undeploy the domain because an error occurred while trying to delete the Kubernetes {{type}} object {{name}} from the Kubernetes namespace {{namespace}}: {{error}}.", + "k8s-domain-undeployer-k8s-object-delete-failed-error-message": "Unable to undeploy the domain because an error occurred while trying to delete the Kubernetes {{type}} {{name}} from the Kubernetes namespace {{namespace}}: {{error}}.", "k8s-domain-undeployer-remove-domain-error-message": "Unable to update WebLogic Kubernetes Operator to remove Kubernetes namespace {{domainNamespace}} from its list of managed namespaces due to an error while upgrading the WebLogic Kubernetes Operator {{operatorName}} in namespace {{operatorNamespace}}: {{error}}.", "k8s-domain-undeployer-undeploy-complete-title": "WebLogic Domain Undeployment from Kubernetes Complete", "k8s-domain-undeployer-undeploy-domain-complete-message": "WebLogic domain {{domainName}} successfully undeployed from Kubernetes namespace {{domainNamespace}}.", @@ -1854,9 +1854,8 @@ "quickstart-page5-list1-item-1": "Model - Defines the domain configuration.", "quickstart-page5-list1-item-2": "Variables - Defines a set of tokens to be used in the model and their values.", "quickstart-page5-list1-item-3": "Archive - Contains application-related and domain-related files and directories to be added to the domain.", - "quickstart-page5-paragraph-2-pre-url": "See the", - "quickstart-page5-wdt-url-label": "WebLogic Deploy Tooling documentation", - "quickstart-page5-paragraph-2-post-url": "for more information.", + "quickstart-page5-paragraph-2": "For more information, please see the documentation: ", + "quickstart-page5-wdt-url-label": "WebLogic Deploy Tooling", "quickstart-page5-list2-heading": "The WKT UI application supports creating, discovering, importing, and editing WDT metadata models.", "quickstart-page5-list2-item-1": "Use the File menu's Add Model menu to discover a model from a domain or import existing model files.", "quickstart-page5-list2-item-2": "Use the Model section to create new or edit existing model-related files.", @@ -1959,5 +1958,7 @@ "wrc-wdt-archive-add-bad-wrc-type-error": "Failed to add to archive because the WebLogic Remote Console entry type {{wrcType}} was not known.", "wrc-wdt-archive-add-empty-file-path-error": "Failed to add to archive because WebLogic Remote Console entry type {{wrcType}} requires a non-empty file path.", "wrc-wdt-archive-add-failed-error": "Failed to add WebLogic Remote Console entry type {{wrcType}} to archive: {{error}}", - "wrc-wdt-archive-remove-empty-path-error": "Failed to remove from archive because the archive path was empty." + "wrc-wdt-archive-remove-empty-path-error": "Failed to remove from archive because the archive path was empty.", + "wrc-wdt-archive-remove-failed-to-match-path-error": "Failed to remove {{archivePath}} from the archive because an error occurred while trying to match the path to its archive entry: {{error}}.", + "wrc-wdt-archive-remove-no-matching-node-error": "Failed to match WRC archive path {{archivePath}}'" } diff --git a/webui/package-lock.json b/webui/package-lock.json index 91b9da598..678f47050 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -6,7 +6,7 @@ "": { "dependencies": { "@oracle/oraclejet": "^13.1.7", - "@oracle/wrc-jet-pack": "~2.4.2", + "@oracle/wrc-jet-pack": "^2.4.3-develop", "ace-builds": "^1.15.0", "i18next": "^22.4.9", "jquery": "^3.6.3", @@ -921,9 +921,9 @@ "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==" }, "node_modules/@oracle/wrc-jet-pack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@oracle/wrc-jet-pack/-/wrc-jet-pack-2.4.2.tgz", - "integrity": "sha512-JLuBr1ObbEkKpRfzPThHygHJ039tdvqk8TOaMVwtyQvqzqyfDnWLSYVbsaA6Dn/xDlDItk+QBed1dnjGu66SdA==", + "version": "2.4.3-develop.202303212222", + "resolved": "https://registry.npmjs.org/@oracle/wrc-jet-pack/-/wrc-jet-pack-2.4.3-develop.202303212222.tgz", + "integrity": "sha512-cfUaogP1M2y4Jq4xjHRYrbvPZ9x73cFiCy8w+D8Dna+Ui60IQ8Kv9lWf2Fpv79u4KxrmCwmNiP7NhuWaO1B49A==", "engines": { "node": ">=4.0.0" } @@ -7818,9 +7818,9 @@ } }, "@oracle/wrc-jet-pack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@oracle/wrc-jet-pack/-/wrc-jet-pack-2.4.2.tgz", - "integrity": "sha512-JLuBr1ObbEkKpRfzPThHygHJ039tdvqk8TOaMVwtyQvqzqyfDnWLSYVbsaA6Dn/xDlDItk+QBed1dnjGu66SdA==" + "version": "2.4.3-develop.202303212222", + "resolved": "https://registry.npmjs.org/@oracle/wrc-jet-pack/-/wrc-jet-pack-2.4.3-develop.202303212222.tgz", + "integrity": "sha512-cfUaogP1M2y4Jq4xjHRYrbvPZ9x73cFiCy8w+D8Dna+Ui60IQ8Kv9lWf2Fpv79u4KxrmCwmNiP7NhuWaO1B49A==" }, "@tootallnate/once": { "version": "2.0.0", diff --git a/webui/package.json b/webui/package.json index 48ec292d7..fcc00556b 100644 --- a/webui/package.json +++ b/webui/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@oracle/oraclejet": "^13.1.7", - "@oracle/wrc-jet-pack": "~2.4.2", + "@oracle/wrc-jet-pack": "^2.4.3-develop", "ace-builds": "^1.15.0", "i18next": "^22.4.9", "jquery": "^3.6.3", diff --git a/webui/src/js/utils/wdt-archive-helper.js b/webui/src/js/utils/wdt-archive-helper.js index a465e733f..036261277 100644 --- a/webui/src/js/utils/wdt-archive-helper.js +++ b/webui/src/js/utils/wdt-archive-helper.js @@ -10,8 +10,8 @@ * Returns a singleton. */ -define(['knockout', 'models/wkt-project'], - function (ko, project) { +define(['knockout', 'models/wkt-project', 'utils/wkt-logger'], + function (ko, project, wktLogger) { function WdtArchiveHelper() { this.project = project; @@ -61,16 +61,20 @@ define(['knockout', 'models/wkt-project'], this.addToArchive = async (archiveEntryTypeName, options) => { const result = await window.api.ipc.invoke('add-archive-entry', archiveEntryTypeName, options); + wktLogger.debug('add-to-archive IPC call returned: %s', result); // no archivePath means selection was cancelled, no need to notify or continue if (!!result.archivePath) { this._addArchiveUpdate(result.filePath, result.archiveUpdatePath); this._addToArchiveModel(result.archivePath, options.fileType || 'emptyDir', result.childPaths); } - return result.archivePath; + return result ? result.archiveUpdatePath : result; }; this.removeFromArchive = (archivePath) => { - this.project.wdtModel.addArchiveUpdate('remove', archivePath); + if (archivePath) { + this._removeFromArchiveModel(archivePath, this.project.wdtModel.archiveRoots); + this._removeArchiveUpdate(archivePath); + } }; // add an archive update that will be applied to the file on the next save. @@ -78,6 +82,10 @@ define(['knockout', 'models/wkt-project'], this.project.wdtModel.addArchiveUpdate('add', archivePath, filePath); }; + this._removeArchiveUpdate = (archivePath) => { + this.project.wdtModel.addArchiveUpdate('remove', archivePath); + }; + // add the archive entry to the model, so it will be displayed in the tree. this._addToArchiveModel = (archivePath, leafEntryFileType, leafChildPaths) => { const childList = this.project.wdtModel.archiveRoots; @@ -150,6 +158,22 @@ define(['knockout', 'models/wkt-project'], } } }; + + this._removeFromArchiveModel = (archivePath, nodesObservable) => { + for (const node of nodesObservable()) { + if (node.id === archivePath) { + nodesObservable.remove(node); + + // this shouldn't be required, but resolves tree view problems with emptied lists + nodesObservable.sort(); + break; + } + + if (node.children) { + this._removeFromArchiveModel(archivePath, node.children); + } + } + }; } return new WdtArchiveHelper(); diff --git a/webui/src/js/utils/wrc-wdt-archive.js b/webui/src/js/utils/wrc-wdt-archive.js index 7dd0a55d1..a9735f871 100644 --- a/webui/src/js/utils/wrc-wdt-archive.js +++ b/webui/src/js/utils/wrc-wdt-archive.js @@ -5,14 +5,15 @@ */ 'use strict'; -define(['models/wkt-project', 'utils/i18n', 'utils/wdt-archive-helper'], - function(project, i18n, archiveHelper) { +define(['models/wkt-project', 'utils/i18n', 'utils/wdt-archive-helper', 'utils/wkt-logger'], + function(project, i18n, archiveHelper, wktLogger) { class WrcWdtArchive { constructor() { this.project = project; } async addToArchive(wrcEntryTypeName, filePath, otherArgs={}) { + wktLogger.debug('entering WRC API addToArchive(%s, %s, %s)', wrcEntryTypeName, filePath, otherArgs); const typeInfo = this._convertWrcType(wrcEntryTypeName); if (typeof typeInfo === 'undefined') { return Promise.reject(new Error(i18n.t('wrc-wdt-archive-add-empty-wrc-type-error'))); @@ -34,8 +35,11 @@ define(['models/wkt-project', 'utils/i18n', 'utils/wdt-archive-helper'], const options = archiveHelper.buildAddToArchiveOptions(archiveEntryTypeName, archiveEntry, filePath, filePathType, otherArgs); + wktLogger.debug('preparing to call archiveHelper.addToArchive(%s, %s)', archiveEntryTypeName, options); return new Promise((resolve, reject) => { archiveHelper.addToArchive(archiveEntryTypeName, options).then((archivePath) => { + wktLogger.debug('WRC API addToArchive(%s, %s, %s) returned %s', wrcEntryTypeName, filePath, + otherArgs, archivePath); resolve(archivePath); }).catch(err => { const errMessage = err instanceof Error ? err.message : err; @@ -46,11 +50,27 @@ define(['models/wkt-project', 'utils/i18n', 'utils/wdt-archive-helper'], }); } + // WRC does not have the ability to remember the archivePath returned from addToArchive(). + // This causes a problem for removing directories, where the underlying code depends on + // directory entries having a trailing slash. As such, we have to go determine if the + // archivePath argument is a directory and, if so, make sure it ends with a trailing slash + // before passing it on. + // async removeFromArchive(archivePath) { + wktLogger.debug('entering WRC API removeFromToArchive(%s)', archivePath); return new Promise((resolve, reject) => { if (archivePath) { - archiveHelper.removeFromArchive(archivePath); - resolve(); + try { + const updatedArchivePath = this._convertWrcArchivePath(archivePath); + + wktLogger.debug('calling archiveHelper.removeFromArchive(%s)', updatedArchivePath); + archiveHelper.removeFromArchive(updatedArchivePath); + resolve(); + } catch (err) { + const errorMessage = window.api.utils.getErrorMessage(err); + reject(new Error(i18n.t('wrc-wdt-archive-remove-failed-to-match-path-error', + { archivePath, error: errorMessage}))); + } } else { reject(new Error(i18n.t('wrc-wdt-archive-remove-empty-path-error'))); } @@ -83,6 +103,35 @@ define(['models/wkt-project', 'utils/i18n', 'utils/wdt-archive-helper'], return result; } + + _convertWrcArchivePath(wrcArchivePath, nodesObservable = this.project.wdtModel.archiveRoots) { + wktLogger.debug('Entering _convertWrcArchivePath(%s, %s)', wrcArchivePath, JSON.stringify(nodesObservable())); + const wrcPath = wrcArchivePath.endsWith('/') ? wrcArchivePath.slice(0, -1) : wrcArchivePath; + let result; + for (const node of nodesObservable()) { + if (wrcPath === node.id || wrcPath + '/' === node.id) { + wktLogger.debug('Found matching node id = %s', node.id); + return node.id; + } + + if (node.children) { + wktLogger.debug('node %s has children', node.id); + result = this._convertWrcArchivePath(wrcArchivePath, node.children); + if (result) { + wktLogger.debug('return nested _convertWrcArchivePath() call from node %s: %s', node.id, result); + return result; + } + wktLogger.debug('No match found for %s in children of node %s', wrcArchivePath, node.id); + } + } + + // If we are back at the topmost level and have no result, throw an error. + if (nodesObservable === this.project.wdtModel.archiveRoots && !result) { + throw new Error(i18n.t('wrc-wdt-archive-remove-no-matching-node-error', {archivePath: wrcArchivePath})); + } + wktLogger.debug('Exiting _convertWrcArchivePath() returning %s', result); + return result; + } } return new WrcWdtArchive(); diff --git a/webui/src/js/viewModels/model-archive-view.js b/webui/src/js/viewModels/model-archive-view.js index 6eaa5dc53..fdc33c4f1 100644 --- a/webui/src/js/viewModels/model-archive-view.js +++ b/webui/src/js/viewModels/model-archive-view.js @@ -41,32 +41,8 @@ function(accUtils, ko, i18n, project, dialogHelper, archiveHelper, ArrayTreeData this.deleteSelected = () => { const path = this.selectedItem(); - - // delete the path from the UI - this._deleteArchiveNode(path, project.wdtModel.archiveRoots); - - // add a 'remove' operation to be applied on save archiveHelper.removeFromArchive(path); }; - - // recursively search the archive tree for a node matching the ID, - // then delete that element from its parent list. - this._deleteArchiveNode = (id, list) => { - for (let item of list()) { - if(item.id === id) { - list.remove(item); - - // this shouldn't be required, but resolves tree view problems with emptied lists - list.sort(); - break; - } - - const children = item['children']; - if(children) { - this._deleteArchiveNode(id, children); - } - } - }; } /* diff --git a/webui/src/js/viewModels/model-design-view.js b/webui/src/js/viewModels/model-design-view.js index 19470329a..291f9736a 100644 --- a/webui/src/js/viewModels/model-design-view.js +++ b/webui/src/js/viewModels/model-design-view.js @@ -136,7 +136,7 @@ function(accUtils, i18n, ko, project, urlCatalog, wrcArchiveHelper, viewHelper, // Set model properties provider option providerOptions['modelProperties'] = JSON.parse(JSON.stringify(this.project.wdtModel.getModelPropertiesObject().observable())); // Set model archive provider option - providerOptions['modelArchive'] = this.project.wdtModel.archiveRoots; + // providerOptions['modelArchive'] = this.project.wdtModel.archiveRoots; return providerOptions; }; diff --git a/webui/src/js/views/quickstart/page5-view.html b/webui/src/js/views/quickstart/page5-view.html index 6c2a5c797..5a2c65c51 100644 --- a/webui/src/js/views/quickstart/page5-view.html +++ b/webui/src/js/views/quickstart/page5-view.html @@ -18,11 +18,10 @@

- + -