diff --git a/documentation/SolutionsRegistryFormat.md b/documentation/SolutionsRegistryFormat.md index c4b2b65c5..db163aa21 100644 --- a/documentation/SolutionsRegistryFormat.md +++ b/documentation/SolutionsRegistryFormat.md @@ -14,13 +14,13 @@ Each entry in the solution registry should have a unique ID (`Solution.id` in th "start": [ .. ], "stop": [ .. ], "isInstalled": [ .. ], - + "isConfigurable": [ ... ], + "makeConfigurable": [ ... ] + // Not yet supported. Post-Cloud4All features. "install": [ ... ], "uninstall": [ ... ], - "makeConfigurable": [ ... ], "isRunning": [ ... ], - "isConfigurable": [ ... ] } ``` @@ -143,6 +143,37 @@ This directive is used to detect whether a solution is installed. If any of thes ] ``` +####isConfigurable +This is run before configuration to ensure that the application is actually ready to be configured. This is relevant for applications where e.g. a configuration file needs to be present, a wizard needs to be run on the first launch, etc. Each of the blocks will be evaluated, if *any* of them evaluates to true, the solution is considered configurable. The absence of an `isConfigurable` block is also interpreted as the solution being configurable. + +If a solution is not configurable, the `makeConfigurable` block will be executed immediately following the `isConfigurable` check (see below). + +**Example Entry**: +``` +"isConfigurable": [ + { + "type": "gpii.lifecycleActions.pathExists", + "path": "/tmp/fakemag1.settings.json" + } +] +``` + +####makeConfigurable +Is the actions that need to be taken to make the application configurable (such as running a wizard, creating a default configuration file, adding a new system user, etc). This will be triggered by the system if `isConfigurable` has evaluated to false and run immediately following the `isConfigurable` check. + +**Example Entry**: +``` +"makeConfigurable": [ + { + "type": "gpii.lifecycleActions.createFile", + "options": { + "filename": "/tmp/fakemag1.settings.json", + "content": "{}" + } + } +] +``` + ***** ### UNIMPLEMENTED BLOCKS @@ -161,26 +192,7 @@ To detect whether a solution is running - this is planned to be integrated in th ] ``` -####isConfigurable -This is run before configuration to ensure that the application is actually ready to be configured. This is relevant for applications where e.g. a configuration file needs to be present, a tutorial needs to be run on the first launch, etc. - -**Example Entry**: -``` -"isConfigurable": [{ - "type": "gpii.reporter.fileExists", - "path": "${{environment}.XDG_DATA_HOME}/orca/user-settings.conf"" -}] -``` - -####makeConfigurable -Is the actions that need to be taken to make the application configurable (such as running a wizard, creating a default configuration file, adding a new system user, etc). -**Example Entry**: -``` -"makeConfigurable": [{ - "launch" // A special key meaning "start it, wait until isConfigurable is met, and then stop it" -}] -``` ####install: Used for describing the steps required for installing the application diff --git a/gpii/node_modules/lifecycleManager/src/LifecycleManager.js b/gpii/node_modules/lifecycleManager/src/LifecycleManager.js index 832fddbf8..685e3a874 100644 --- a/gpii/node_modules/lifecycleManager/src/LifecycleManager.js +++ b/gpii/node_modules/lifecycleManager/src/LifecycleManager.js @@ -82,6 +82,10 @@ var gpii = fluid.registerNamespace("gpii"); funcName: "gpii.lifecycleManager.invokeSettingsHandlers", args: ["{that}", "{arguments}.0", "{arguments}.1"] // solutionId, settingsHandlers + }, + isConfigurable: { + funcName: "gpii.lifecycleManager.isConfigurable", + args: [ "{that}", "{arguments}.0", "{arguments}.1" ] } } }); @@ -315,6 +319,25 @@ var gpii = fluid.registerNamespace("gpii"); return togo; }; + /* returns whether a solution is configurable or not. This is done by going through each of + * the blocks in the isConfigurable part of the solution entry. If *any* of them evaluates to + * true, the solution is considered configurable and true is returned, else false is returned. + * If no 'isConfigured' directive is found in the solutionRecord, true is returned + */ + gpii.lifecycleManager.isConfigurable = function (that, solutionRecord, sessionState) { + if (solutionRecord.isConfigurable !== undefined) { + return fluid.find(solutionRecord.isConfigurable, function (configurableCheck) { + var expanded = sessionState.localResolver(configurableCheck); + var result = gpii.lifecycleManager.invokeAction(expanded, that.nameResolver); + if (result === true) { + return result; + } + }, false); + } else { // if no isConfigurable block is given, we assume it's configurable + return true; + } + }; + /** Invoked on both "start" and "update" phases - in addition to forwarding to gpii.lifecycleManager.executeActions, * it is responsible for saving the settings that are being set (when the fullSnapshot is true) and storing * the list of applied solutions to the sessionState @@ -331,6 +354,14 @@ var gpii = fluid.registerNamespace("gpii"); */ gpii.lifecycleManager.applySolution = function (that, solutionId, solutionRecord, sessionState, lifecycleBlockKeys, fullSnapshot) { var promises = []; + + if (that.isConfigurable(solutionRecord, sessionState) === false) { + if (solutionRecord.makeConfigurable === undefined) { + fluid.fail("Found solution that is not configurable with no makeConfigurable directive"); + } else { + promises.push(that.executeActions(solutionId, solutionRecord, sessionState, "makeConfigurable")); + } + } fluid.each(lifecycleBlockKeys, function (key) { promises.push(that.executeActions(solutionId, solutionRecord, sessionState, key)); }); diff --git a/gpii/node_modules/lifecycleManager/test/js/LifecycleManagerTests.js b/gpii/node_modules/lifecycleManager/test/js/LifecycleManagerTests.js index 91cbb20f2..7dc034e6a 100644 --- a/gpii/node_modules/lifecycleManager/test/js/LifecycleManagerTests.js +++ b/gpii/node_modules/lifecycleManager/test/js/LifecycleManagerTests.js @@ -99,6 +99,35 @@ https://github.com/GPII/universal/blob/master/LICENSE.txt ] }; + gpii.tests.lifecycleManager.trueConfigurableDirective = { + "isConfigurable": [ + { + "type": "gpii.tests.lifecycleManager.mockIsConfigurable", + "toReturn": true + } + ] + }; + + gpii.tests.lifecycleManager.falseConfigurableDirective = { + "isConfigurable": [ + { + "type": "gpii.tests.lifecycleManager.mockIsConfigurable", + "toReturn": false + } + ] + }; + + gpii.tests.lifecycleManager.makeConfigurableDirective = { + "makeConfigurable": [ + { + "type": "gpii.tests.lifecycleManager.mockMakeConfigurable", + "options": { + "filename": "${{environment}.JAWS_DIR}configuration.json" + } + } + ] + }; + gpii.tests.lifecycleManager.configurationSpec = gpii.tests.lifecycleManager.buildLifecycleInstructions("org.gnome.desktop.a11y.magnifier", gpii.tests.lifecycleManager.buildSettingsHandlersEntry({ "cross-hairs-clip": true, "cross-hairs-color": "red" }), gpii.tests.lifecycleManager.noUpdateLifecycle, @@ -229,6 +258,31 @@ https://github.com/GPII/universal/blob/master/LICENSE.txt } }); + gpii.tests.lifecycleManager.mockIsConfigurable = function (toReturn) { + jqUnit.assertTrue("isConfigurable has been called", true); + return toReturn; + }; + + fluid.defaults("gpii.tests.lifecycleManager.mockIsConfigurable", { + gradeNames: "fluid.function", + argumentMap: { + toReturn: 0 + } + }); + + gpii.tests.lifecycleManager.mockMakeConfigurable = function (options) { + gpii.tests.lifecycleManager.staticRepository.configurableStash = { + options: options + }; + }; + + fluid.defaults("gpii.tests.lifecycleManager.mockMakeConfigurable", { + gradeNames: "fluid.function", + argumentMap: { + options: 0 + } + }); + gpii.tests.lifecycleManager.backingMockSettingsHandler = null; // initialised in every test that requires it gpii.tests.lifecycleManager.initBackingMock = function () { @@ -592,6 +646,60 @@ https://github.com/GPII/universal/blob/master/LICENSE.txt }); }; + gpii.tests.lifecycleManager.configurabilityTestDefs = [{ + name: "configurability test with a configurable solution", + expect: 5, + startPayload: fluid.extend(true, {}, gpii.tests.lifecycleManager.userOptions, + { + lifecycleInstructions: gpii.tests.lifecycleManager.buildLifecycleInstructions("org.gnome.desktop.a11y.magnifier", + gpii.tests.lifecycleManager.buildSettingsHandlersEntry({ "cross-hairs-clip": true, "cross-hairs-color": "red" }), + gpii.tests.lifecycleManager.noUpdateLifecycle, + gpii.tests.lifecycleManager.trueConfigurableDirective, + gpii.tests.lifecycleManager.makeConfigurableDirective) + }), + stashContent: undefined + }, { + name: "configurability test with a non-configurable solution to be made configurable", + expect: 5, + startPayload: fluid.extend(true, {}, gpii.tests.lifecycleManager.userOptions, + { + lifecycleInstructions: gpii.tests.lifecycleManager.buildLifecycleInstructions("org.gnome.desktop.a11y.magnifier", + gpii.tests.lifecycleManager.buildSettingsHandlersEntry({ "cross-hairs-clip": true, "cross-hairs-color": "red" }), + gpii.tests.lifecycleManager.noUpdateLifecycle, + gpii.tests.lifecycleManager.falseConfigurableDirective, + gpii.tests.lifecycleManager.makeConfigurableDirective) + }), + stashContent: { + "options": { + "filename": "e:\\Programs and Things\\Jaws\\configuration.json" + } + } + }]; + + // Tests focusing on the isConfigurable and makeConfigurable directives of the solution + gpii.tests.lifecycleManager.buildConfigurabilityTests = function () { + fluid.each(gpii.tests.lifecycleManager.configurabilityTestDefs, function (test) { + jqUnit.asyncTest("gpii.lifecycleManager configurability tests: " + test.name, function () { + gpii.tests.lifecycleManager.setup(); + jqUnit.expect(test.expect); + + var lifecycleManager = gpii.lifecycleManager(gpii.tests.lifecycleManager.testOptions); + gpii.tests.lifecycleManager.initBackingMock(); + + lifecycleManager.start(test.startPayload, function (success) { + jqUnit.assertTrue("gpii.lifecycleManager.start() succeeds", success); + gpii.tests.lifecycleManager.assertExpectedExec(); + var expectedFirstAppliedSettings = (test.expectedFirstAppliedSettings !== undefined) ? test.expectedFirstAppliedSettings : gpii.tests.lifecycleManager.settingsHandlerExpectedInputNewSettings; + gpii.tests.lifecycleManager.assertExpectedSettingsHandler(" - on start", expectedFirstAppliedSettings); + // make configurable shouldn't have been called: + jqUnit.assertDeepEq("checking whether makeConfigurable was run", test.stashContent, gpii.tests.lifecycleManager.staticRepository.configurableStash); + + jqUnit.start(); + }); + }); + }); + }; + gpii.tests.lifecycleManager.runTests = function () { jqUnit.module("Lifecycle Manager", function () { gpii.tests.staticRepository = {}; @@ -683,6 +791,8 @@ https://github.com/GPII/universal/blob/master/LICENSE.txt gpii.tests.lifecycleManager.buildUpdateTests(); + gpii.tests.lifecycleManager.buildConfigurabilityTests(); + jqUnit.asyncTest("gpii.lifecycleManager.update() tests for 'stop' reference", function () { // initial payload: var startPayload = fluid.extend(true, {}, gpii.tests.lifecycleManager.userOptions, {