You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2022/08/22 20:37:18 UTC
[brooklyn-ui] 04/05: use yaml instead of json for complex fields in spec editor
This is an automated email from the ASF dual-hosted git repository.
heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-ui.git
commit 201229d897f6746d37071d3271a2ef434b8f37c6
Author: Alex Heneveld <al...@cloudsoft.io>
AuthorDate: Mon Aug 22 20:50:01 2022 +0100
use yaml instead of json for complex fields in spec editor
easily controllable with CODE_MODE="YAML" or "JSON"
---
.../spec-editor/spec-editor.directive.js | 51 +++++++++++++++++-----
.../app/components/spec-editor/spec-editor.less | 3 ++
2 files changed, 44 insertions(+), 10 deletions(-)
diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
index 1b74dafd..ab6df27e 100644
--- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
+++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
@@ -30,6 +30,7 @@ import template from './spec-editor.template.html';
import {isSensitiveFieldName} from 'brooklyn-ui-utils/sensitive-field/sensitive-field';
import {computeQuickFixesForIssue} from '../quick-fix/quick-fix';
import scriptTagDecorator from 'brooklyn-ui-utils/script-tag-non-overwrite/script-tag-non-overwrite';
+import jsYaml from "js-yaml";
const MODULE_NAME = 'brooklyn.components.spec-editor';
const REPLACED_DSL_ENTITYSPEC = '___brooklyn:entitySpec';
@@ -38,6 +39,8 @@ const SUBSECTION = {
PARAMETERS: 'parameters'
}
+const CODE_MODE = "YAML";
+
export const SUBSECTION_TEMPLATE_OTHERS_URL = 'blueprint-composer/component/spec-editor/section-others.html';
angular.module(MODULE_NAME, [onEnter, autoGrow, blurOnEnter, brooklynDslEditor, brooklynDslViewer, scriptTagDecorator])
@@ -640,7 +643,8 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
if (!specEditor.defined(val)) return false;
if (typeof val === 'string') {
try {
- // a YAML parse would be nicer, but JSON is simpler, and sufficient
+ // a YAML parse would be nicer, but JSON is easier to detect its presence, and sufficient;
+ // for convenience, we allow yaml to be _entered_
// (esp as we export a JSON stringify; preferable would be to export the exact YAML from model,
// including comments, but again lower priority)
let parsed = JSON.parse(val);
@@ -680,7 +684,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
try {
// if it's a parseable string, then parse it
if (typeof value === 'string' && value.length) {
- value = JSON.parse(value);
+ value = parseYamlOrJson(value);
if (value instanceof Array || value instanceof Object) {
// if result is not a primitive then don't allow user to leave code mode
// (if type is known as a map/list then likely we should allow leaving code mode,
@@ -716,7 +720,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
}
} catch (notJson) {
// if not parseable or not a string, then convert to json
- value = JSON.stringify(value);
+ value = toCode(value);
}
}
if (value !== null) {
@@ -767,7 +771,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
} catch (notJson) {
// if not parseable or not a string, then convert to json
// (as with other stringify, this loses any comments etc)
- value = JSON.stringify(value, null, " ");
+ value = toCode(value, true);
}
if (value !== null) {
scope.state.parameters.edit.json = value;
@@ -975,7 +979,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
// also supporting yaml and comments, but that is a bigger task!)
if (scope.config && typeof scope.config[key] === 'string') {
try {
- if (JSON.stringify(JSON.parse(scope.config[key])) === JSON.stringify(value)) {
+ if (JSON.stringify(parseYamlOrJson(scope.config[key])) === JSON.stringify(value)) {
return scope.config[key];
}
} catch (ignoredError) {
@@ -985,7 +989,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
// otherwise pretty print it, so they get decent multiline on first load and
// if they click off the entity then back on to the entity and this field
// (we'd like to respect what they actually typed but that needs the parse tree, as above)
- return JSON.stringify(value, null, " ");
+ return toCode(value, true);
}
// else treat as value, with array/map special
@@ -1026,6 +1030,8 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
// any map/array with complex json inside, or other value of complex json,
// will force the code editor
// (previously we did stringify on entries then tried to parse, but that was inconsistent)
+ console.log("Forcing code mode on ", key, "because", hasComplexJson);
+
scope.state.config.codeModeActive[key] = true;
// and the widget mode updated to be 'manual'
scope.getConfigWidgetMode(definition, value);
@@ -1090,13 +1096,15 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
// if JSON mode then parse
scope.state.config.codeModeError[keyRef] = null;
if (scope.state.config.codeModeActive && scope.state.config.codeModeActive[keyRef]) {
+ // first try a yaml parse
try {
- result[keyRef] = JSON.parse(v);
+ result[keyRef] = parseYamlOrJson(v);
+ continue;
} catch (ex) {
scope.state.config.codeModeError[keyRef] = "Invalid JSON";
result[keyRef] = localConfig[keyRef];
+ continue;
}
- continue;
}
// else return as is, or introspect for array/map
@@ -1147,7 +1155,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
model.addIssue(Issue.builder().group('config').ref(key).message('<code>' + $sanitize(value) + '</code> is not a number').build());
}
if (scope.state.config.codeModeError[key]) {
- model.addIssue(Issue.builder().group('config').ref(key).message('<code>' + $sanitize(value) + '</code> is not valid JSON').build());
+ model.addIssue(Issue.builder().group('config').ref(key).message('Content is not valid '+(CODE_MODE)).build());
}
});
}
@@ -1207,7 +1215,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
scope.state.parameters.edit.errors = [];
if (scope.state.parameters.codeModeActive[item.name]) {
try {
- let parsed = JSON.parse(scope.state.parameters.edit.json);
+ let parsed = parseYamlOrJson(scope.state.parameters.edit.json);
checkNameChange(item.name, parsed.name);
if (JSON.stringify(item)==JSON.stringify(parsed)) {
// no change; don't change else we get a digest cycle
@@ -1349,3 +1357,26 @@ export function specEditorTypeFilter() {
function templateCache($templateCache) {
$templateCache.put(TEMPLATE_URL, template);
}
+
+function parseYamlOrJson(v) {
+ // YAML mode experimental; if not working switch back to JSON
+ let error = null;
+ if (CODE_MODE=="YAML") {
+ try {
+ return jsYaml.safeLoad(v);
+ } catch (ex) {
+ error = ex;
+ }
+ }
+ try {
+ return JSON.parse(v);
+ } catch (ex) {
+ if (CODE_MODE=="YAML" && error) throw error;
+ throw ex;
+ }
+}
+
+function toCode(obj, pretty) {
+ if (CODE_MODE=="YAML") return jsYaml.dump(obj).trim();
+ return pretty ? JSON.stringify(obj, null, " ") : JSON.stringify(obj);
+}
diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.less b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.less
index b19d94bf..e02a3d61 100644
--- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.less
+++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.less
@@ -946,6 +946,9 @@ spec-editor {
.version-selection {
margin: 1em 0px;
+ button {
+ text-align: left;
+ }
.dropdown {
// preventing LESS from incorrectly parsing the calc expression
// using 30px offset for the icon