diff --git a/electron/app/js/ipcRendererPreload.js b/electron/app/js/ipcRendererPreload.js index 051f186a2..dc1782036 100644 --- a/electron/app/js/ipcRendererPreload.js +++ b/electron/app/js/ipcRendererPreload.js @@ -203,7 +203,8 @@ contextBridge.exposeInMainWorld( 'get-wrc-home-directory', 'get-wrc-app-image', 'wrc-get-home-default-value', - 'wrc-set-home-and-start' + 'wrc-set-home-and-start', + 'get-wrc-port' ]; return new Promise((resolve, reject) => { if (validChannels.includes(channel)) { diff --git a/electron/app/js/wlRemoteConsoleUtils.js b/electron/app/js/wlRemoteConsoleUtils.js index fd1363ca9..111f1587c 100644 --- a/electron/app/js/wlRemoteConsoleUtils.js +++ b/electron/app/js/wlRemoteConsoleUtils.js @@ -21,7 +21,7 @@ const { spawnDaemonChildProcess } = require('./childProcessExecutor'); const { wlRemoteConsoleFrontendVersion } = require('../webui.json'); let _wlRemoteConsoleChildProcess; - +let _wlRemoteConsolePort; async function startWebLogicRemoteConsoleBackend(currentWindow, skipVersionCheck = false) { if (_wlRemoteConsoleChildProcess) { @@ -45,6 +45,8 @@ async function startWebLogicRemoteConsoleBackend(currentWindow, skipVersionCheck }); _wlRemoteConsoleChildProcess.on('exit', (code) => { getLogger().info('WebLogic Remote Console backend process exited with code %s', code); + _wlRemoteConsoleChildProcess = undefined; + _wlRemoteConsolePort = undefined; }); const stdoutLines = readline.createInterface({ input: _wlRemoteConsoleChildProcess.stdout }); @@ -58,6 +60,9 @@ async function startWebLogicRemoteConsoleBackend(currentWindow, skipVersionCheck const matcher = line.match(portRegex); if (matcher) { foundPort = true; + // The exported getWebLogicRemoteConsolePort function returns + // the current port, so we need to save it. + _wlRemoteConsolePort = matcher[1]; sendToWindow(currentWindow, 'set-wrc-backend-port', matcher[1]); } } @@ -150,6 +155,10 @@ function getDefaultDirectoryForOpenDialog(isAppImage = false) { return result; } +function getWebLogicRemoteConsolePort() { + return _wlRemoteConsolePort; +} + async function _getWebLogicRemoteConsoleHome(skipVersionCheck = false) { const rcHome = userSettings.getWebLogicRemoteConsoleHome(); if (!rcHome) { @@ -236,7 +245,7 @@ async function _getWebLogicRemoteConsoleExecutableData(rcHome) { _getDevExecutablePath(rcHome, pathToDirectoryWithExecutable).then(exeResult => { if (exeResult.exists) { results['executable'] = exeResult.executable; - results['arguments'] = ['.', 'dev', '--showPort', '--stdin', '--quiet', '--headless']; + results['arguments'] = ['.', 'dev', '--showPort', `--check-pid=${process.pid}`, '--quiet', '--headless', '--useTokenNotCookie']; results['options'] = { cwd: rcHome }; } else { const message = i18n.t('wrc-dev-executable-existence-check-failed', @@ -254,7 +263,7 @@ async function _getWebLogicRemoteConsoleExecutableData(rcHome) { _getInstalledExecutablePath(rcHome).then(exeResult => { if (exeResult['exists']) { results['executable'] = exeResult['executable']; - results['arguments'] = ['--showPort', '--stdin', '--quiet', '--headless']; + results['arguments'] = ['--showPort', '--useTokenNotCookie', `--check-pid=${process.pid}`, '--quiet', '--headless']; resolve(results); } else { const message = i18n.t('wrc-executable-not-exists', { rcHome: rcHome, executable: results['executable'] }); @@ -495,5 +504,6 @@ module.exports = { getDefaultDirectoryForOpenDialog, getDefaultWebLogicRemoteConsoleHome, setWebLogicRemoteConsoleHomeAndStart, - startWebLogicRemoteConsoleBackend + startWebLogicRemoteConsoleBackend, + getWebLogicRemoteConsolePort }; diff --git a/electron/app/main.js b/electron/app/main.js index cdeab6917..25cb0b746 100644 --- a/electron/app/main.js +++ b/electron/app/main.js @@ -35,7 +35,7 @@ const openSSLUtils = require('./js/openSSLUtils'); const osUtils = require('./js/osUtils'); const { initializeAutoUpdater, registerAutoUpdateListeners, installUpdates, getUpdateInformation } = require('./js/appUpdater'); const { startWebLogicRemoteConsoleBackend, getDefaultDirectoryForOpenDialog, setWebLogicRemoteConsoleHomeAndStart, - getDefaultWebLogicRemoteConsoleHome } = require('./js/wlRemoteConsoleUtils'); + getDefaultWebLogicRemoteConsoleHome, getWebLogicRemoteConsolePort } = require('./js/wlRemoteConsoleUtils'); const { getHttpsProxyUrl, getBypassProxyHosts } = require('./js/userSettings'); const { sendToWindow } = require('./js/windowUtils'); @@ -931,6 +931,11 @@ class Main { ipcMain.handle('wrc-get-home-default-value', async (event) => { return getDefaultWebLogicRemoteConsoleHome(); }); + + // eslint-disable-next-line no-unused-vars + ipcMain.handle('get-wrc-port', async (event) => { + return getWebLogicRemoteConsolePort(); + }); } async getLatestWdtInstaller(targetWindow) { diff --git a/webui/src/css/app.css b/webui/src/css/app.css index 0877bc67a..bbea5f2aa 100644 --- a/webui/src/css/app.css +++ b/webui/src/css/app.css @@ -194,16 +194,32 @@ } /* container for model design view, currently text */ +.wkt-model-edit-design, +.wkt-model-code-view +{ + display: flex; + height: 100%; + width: 100%; +} + +.wkt-model-edit-design > a > img { + margin: 0 3px 0 0; + height: 24px; + width: 18px; +} + +/* +///MLW .wkt-model-edit-design { margin: 10px; } -/* container for model design view, currently text */ .wkt-model-code-view { display: flex; height: 100%; width: 100%; } +*/ /* the ace editor pane */ #model-editor { diff --git a/webui/src/js/main.js b/webui/src/js/main.js index 1bdcc5a4f..73af8ffb4 100644 --- a/webui/src/js/main.js +++ b/webui/src/js/main.js @@ -40,9 +40,22 @@ 'corejs' : 'libs/corejs/shim', 'chai': 'libs/chai/chai-4.2.0', 'regenerator-runtime' : 'libs/regenerator-runtime/runtime', - 'ace': 'libs/ace/ace' + 'ace': 'libs/ace/ace', + 'wrc-translations': 'resources', + 'wrc-frontend': 'jet-composites/wrc-frontend/1.0.0', + 'wdt-model-designer': 'jet-composites/wdt-model-designer/1.0.0', + 'cfe-navtree': 'jet-composites/cfe-navtree/1.0.0', + 'cfe-multi-select': 'jet-composites/cfe-multi-select/1.0.0', + 'cfe-property-list-editor': 'jet-composites/cfe-property-list-editor/1.0.0' } // endinjector + , config: { + ojL10n: { + merge: { + 'ojtranslations/nls/ojtranslations': 'resources/nls/frontend' + } + } + } } ); }()); diff --git a/webui/src/js/path_mapping.json b/webui/src/js/path_mapping.json index fc550ee03..21bf65747 100644 --- a/webui/src/js/path_mapping.json +++ b/webui/src/js/path_mapping.json @@ -332,6 +332,97 @@ "path": "libs/ace/ace.js", "cdnPath": "" } + }, + + "wrc-translations": { + "cdn": "3rdparty", + "cwd": "node_modules/@oracle/wrc-jet-pack/dist/resources", + "debug": { + "src": ["**"], + "path": "resources", + "cdnPath": "" + }, + "release": { + "src": ["**"], + "path": "resources", + "cdnPath": "" + } + }, + + "wrc-frontend": { + "cdn": "3rdparty", + "cwd": "node_modules/@oracle/wrc-jet-pack/dist/jet-composites/wrc-frontend", + "debug": { + "src": ["**"], + "path": "jet-composites/wrc-frontend/1.0.0", + "cdnPath": "" + }, + "release": { + "src": ["**"], + "path": "jet-composites/wrc-frontend/1.0.0", + "cdnPath": "" + } + }, + + "wdt-model-designer": { + "cdn": "3rdparty", + "cwd": "node_modules/@oracle/wrc-jet-pack/dist/jet-composites/wdt-model-designer", + "debug": { + "src": ["**"], + "path": "jet-composites/wdt-model-designer/1.0.0", + "cdnPath": "" + }, + "release": { + "src": ["**"], + "path": "jet-composites/wdt-model-designer/1.0.0", + "cdnPath": "" + } + }, + + "cfe-navtree": { + "cdn": "3rdparty", + "cwd": "node_modules/@oracle/wrc-jet-pack/dist/jet-composites/cfe-navtree", + "debug": { + "src": ["**"], + "path": "jet-composites/cfe-navtree/1.0.0", + "cdnPath": "" + }, + "release": { + "src": ["**"], + "path": "jet-composites/cfe-navtree/1.0.0", + "cdnPath": "" + } + }, + + "cfe-multi-select": { + "cdn": "3rdparty", + "cwd": "node_modules/@oracle/wrc-jet-pack/dist/jet-composites/cfe-multi-select", + "debug": { + "src": ["**"], + "path": "jet-composites/cfe-multi-select/1.0.0", + "cdnPath": "" + }, + "release": { + "src": ["**"], + "path": "jet-composites/cfe-multi-select/1.0.0", + "cdnPath": "" + } + }, + + "cfe-property-list-editor": { + "cdn": "3rdparty", + "cwd": "node_modules/@oracle/wrc-jet-pack/dist/jet-composites/cfe-property-list-editor", + "debug": { + "src": ["**"], + "path": "jet-composites/cfe-property-list-editor/1.0.0", + "cdnPath": "" + }, + "release": { + "src": ["**"], + "path": "jet-composites/cfe-property-list-editor/1.0.0", + "cdnPath": "" + } } + } } diff --git a/webui/src/js/viewModels/model-design-view.js b/webui/src/js/viewModels/model-design-view.js index 5cac655ba..c6bb21b5c 100644 --- a/webui/src/js/viewModels/model-design-view.js +++ b/webui/src/js/viewModels/model-design-view.js @@ -3,81 +3,241 @@ * Copyright (c) 2021, 2022, Oracle and/or its affiliates. * Licensed under The Universal Permissive License (UPL), Version 1.0 as shown at https://oss.oracle.com/licenses/upl/ */ -define(['accUtils', 'utils/i18n', 'knockout', 'models/wkt-project', 'utils/url-catalog', 'utils/wkt-logger', - 'ojs/ojinputtext', 'ojs/ojlabel', 'ojs/ojbutton', 'ojs/ojformlayout' ], -function(accUtils, i18n, ko, project, urlCatalog) { - function ModelDesignViewModel() { - this.connected = () => { - accUtils.announce('Model design view loaded.', 'assertive'); - // Implement further logic if needed - }; +define(['accUtils', 'utils/i18n', 'knockout', 'models/wkt-project', 'utils/url-catalog', 'utils/wkt-logger', 'wrc-frontend/core/parsers/yaml', 'wrc-frontend/integration/viewModels/utils', + 'wdt-model-designer/loader', 'ojs/ojinputtext', 'ojs/ojlabel', 'ojs/ojbutton', 'ojs/ojformlayout'], + function(accUtils, i18n, ko, project, urlCatalog, wktLogger, YamlParser, ViewModelUtils) { + function ModelDesignViewModel() { + const self = this; - this.labelMapper = (labelId, payload) => { - return i18n.t(`model-design-${labelId}`, payload); - }; + this.project = project; + this.designer; + this.dataProvider = {}; - this.isLinux = () => { - return window.api.process.isLinux(); - }; + this.connected = function () { + accUtils.announce('Model design view loaded.', 'assertive'); + // Set up a change subscription on wlRemoteConsolePort observable, + // which will primarily be used in the "WRC Not Installed Yet" and + // "WRC Process Was Killed or Died" use cases. + this.wlRemoteConsolePortSubscription = project.wdtModel.internal.wlRemoteConsolePort.subscribe((newValue) => { + wktLogger.debug('connected: newValue=%s', newValue); + showWdtModelDesigner(newValue); + }); + // Get WRC backend port, asynchronously + window.api.ipc.invoke('get-wrc-port') + .then(backendPort => { + if (backendPort !== project.wdtModel.internal.wlRemoteConsolePort()) { + // Trigger the change subscription + project.wdtModel.internal.wlRemoteConsolePort(backendPort); + } else { + // Call showWdtModelDesigner directly + showWdtModelDesigner(backendPort); + } + }) + .catch(err => { + ViewModelUtils.failureResponseDefaultHandling(err); + }); + }; - this.project = project; - this.i18n = i18n; + this.disconnected = function() { + self.wlRemoteConsolePortSubscription.dispose(); + if (self.designer) { + self.designer.deactivateProvider(self.dataProvider); + } + }; - this.disableStartButton = ko.observable(false); + function showWdtModelDesigner(backendPort) { + wktLogger.info('showWdtModelDesigner: backendPort=%s', backendPort); + if (!backendPort) { + return; + } + // Lookup designer in the DOM, because we mainly need to + // interact with it programmatically. + self.designer = document.getElementById('WdtModelDesigner'); + // We cannot use to control the visibility of + // the JET composite, because it prevents + // JET from fully baking a JET composite. Being "half-baked" + // means that the JET composite exists, but custom methods + // on it are not callable, because of the . So, + // the wdt-model-designer JET composite has a visible property + // that controls it's visibility. The default value for that + // property is false. + self.designer.visible = self.showRemoteConsoleComponent(); + // Tell designer the port, so it can construct the backend + // URL it's internal module(s) uses when making REST calls + // to the WRC backend. + self.designer.setBackendUrlPort(backendPort); + // ResizeObserver needs to be set on the parent element + // of the tag. + const parentElement = self.designer.parentElement; + new ResizeObserver(() => { + self.designer.resize(); + }).observe(parentElement); - const wrcInitialText = this.labelMapper('wrc-install-description'); - const wrcInstallLocation = '-models/model.yaml + // + // We'll use that call to get the name, for now. + providerOptions['name'] = project.wdtModel.getDefaultModelFile(); + // The WRC JET Pack includes a YamlParser and JsonParser module. For the + // demo, fileContents is YAML, so the YamlParser module is used. + YamlParser.parse(providerOptions.fileContents) + .then(data => { + // Use promise fulfillment value for second argument of the + // designer's createProvider() method. The newly created + // (and activated) provider, needs to be captured in your + // event listener for the "providerActivated" custom event. + self.designer.createProvider(providerOptions.name, data); + }) + .catch(err => { + ViewModelUtils.failureResponseDefaultHandling(err); + }); + } + + /** + * @param {CustomEvent} event - Triggered when WDT Model File provider has been activated with the WRC backend. + */ + this.providerActivated = (event) => { + self.dataProvider = event.detail.value; + self.designer.selectLastVisitedSlice(); + }; + + /** + * @param {CustomEvent} event - Triggered when changes have been downloaded from the WRC backend, for the active WDT Model File provider. + */ + this.changesAutoDownloaded = (event) => { + project.wdtModel.modelContent(event.detail.value); + }; + + /** + * @param {CustomEvent} event - Triggered when WDT Model File provider has been deactivated with the WRC backend. + */ + this.providerDeactivated = (event) => { + const result = event.detail.value; + delete result.data; + self.dataProvider = {state: 'disconnected'}; + }; + + /** + * @param {CustomEvent} event - Triggered when WDT Model Designer has lost it's connection to the WRC backend. + */ + this.connectionLostRefused = (event) => { + wktLogger.debug('connectionLostRefused: backendUrl=%s', event.detail.value); + if (self.designer) self.designer.visible = false; + project.wdtModel.internal.wlRemoteConsolePort(undefined); + }; + + this.labelMapper = (labelId, payload) => { + return i18n.t(`model-design-${labelId}`, payload); + }; + + this.isLinux = () => { + return window.api.process.isLinux(); + }; + + this.i18n = i18n; + + this.disableStartButton = ko.observable(false); + + const wrcInitialText = this.labelMapper('wrc-install-description'); + const wrcInstallLocation = '' + this.labelMapper('wrc-link-text') + ''; - const wrcInstallParagraph = this.labelMapper('wrc-install-location-prefix') + ' ' + wrcInstallLocation + - ' ' + this.labelMapper('wrc-install-location-postfix'); - const wrcStartParagraph = this.labelMapper('wrc-start-description'); - this.wrcInstallInstructions = wrcInitialText + '

' + wrcInstallParagraph + '

' + wrcStartParagraph; - - this.showRemoteConsoleConfigForm = () => { - return !project.wdtModel.internal.wlRemoteConsolePort(); - }; - - this.showRemoteConsoleComponent = () => { - return !!project.wdtModel.internal.wlRemoteConsolePort(); - }; - - this.getWebLogicRemoteConsoleHomeHelpText = () => { - let helpKey; - if (window.api.process.isMac()) { - helpKey = 'user-settings-dialog-wrc-home-macos-help'; - } else if (window.api.process.isWindows()) { - helpKey = 'user-settings-dialog-wrc-home-windows-help'; - } else { - helpKey = 'user-settings-dialog-wrc-home-linux-help'; - } - return i18n.t(helpKey); - }; + const wrcInstallParagraph = this.labelMapper('wrc-install-location-prefix') + ' ' + wrcInstallLocation + + ' ' + this.labelMapper('wrc-install-location-postfix'); + const wrcStartParagraph = this.labelMapper('wrc-start-description'); + this.wrcInstallInstructions = wrcInitialText + '

' + wrcInstallParagraph + '

' + wrcStartParagraph; - this.chooseWebLogicRemoteConsoleHomeDirectory = async () => { - const rcHome = await window.api.ipc.invoke('get-wrc-home-directory'); - if (rcHome) { - this.project.wdtModel.internal.wlRemoteConsoleHome.observable(rcHome); - } - }; + this.showRemoteConsoleConfigForm = () => { + return !project.wdtModel.internal.wlRemoteConsolePort(); + }; - this.chooseWebLogicRemoteConsoleAppImage = async () => { - const rcHome = await window.api.ipc.invoke('get-wrc-app-image'); - if (rcHome) { - this.project.wdtModel.internal.wlRemoteConsoleHome.observable(rcHome); - } - }; + this.showRemoteConsoleComponent = () => { + return !!project.wdtModel.internal.wlRemoteConsolePort(); + }; - this.startWebLogicRemoteConsole = async () => { - const rcHome = this.project.wdtModel.internal.wlRemoteConsoleHome.observable(); - // TODO - do we need a busy dialog? - return window.api.ipc.invoke('wrc-set-home-and-start', rcHome); - }; - } + this.getWebLogicRemoteConsoleHomeHelpText = () => { + let helpKey; + if (window.api.process.isMac()) { + helpKey = 'user-settings-dialog-wrc-home-macos-help'; + } else if (window.api.process.isWindows()) { + helpKey = 'user-settings-dialog-wrc-home-windows-help'; + } else { + helpKey = 'user-settings-dialog-wrc-home-linux-help'; + } + return i18n.t(helpKey); + }; + + this.chooseWebLogicRemoteConsoleHomeDirectory = async () => { + const rcHome = await window.api.ipc.invoke('get-wrc-home-directory'); + if (rcHome) { + this.project.wdtModel.internal.wlRemoteConsoleHome.observable(rcHome); + } + }; + + this.chooseWebLogicRemoteConsoleAppImage = async () => { + const rcHome = await window.api.ipc.invoke('get-wrc-app-image'); + if (rcHome) { + this.project.wdtModel.internal.wlRemoteConsoleHome.observable(rcHome); + } + }; - /* - * Returns a constructor for the ViewModel. - */ - return ModelDesignViewModel; -}); + this.startWebLogicRemoteConsole = async () => { + const rcHome = this.project.wdtModel.internal.wlRemoteConsoleHome.observable(); + // TODO - do we need a busy dialog? + // Set cursor to BUSY and let self.designer + // set it back to default + document.querySelector('oj-button#start-wrc-button span').style.cursor = 'wait'; + return window.api.ipc.invoke('wrc-set-home-and-start', rcHome) + .then(() => { + return window.api.ipc.invoke('get-wrc-port'); + }) + .then(backendPort => { + if (self.designer) self.designer.setBackendUrlPort(backendPort); + // Trigger the change subscription + project.wdtModel.internal.wlRemoteConsolePort(backendPort); + }); + }; + } + + /* + * Returns a constructor for the ViewModel. + */ + return ModelDesignViewModel; + } +); diff --git a/webui/src/js/views/model-design-view.html b/webui/src/js/views/model-design-view.html index bcac442b9..009a8d734 100644 --- a/webui/src/js/views/model-design-view.html +++ b/webui/src/js/views/model-design-view.html @@ -2,12 +2,15 @@ Copyright (c) 2021, 2022, Oracle and/or its affiliates. Licensed under The Universal Permissive License (UPL), Version 1.0 as shown at https://oss.oracle.com/licenses/upl/ --> -

- -
-

-
-
+
+ +