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 2021/04/27 15:12:42 UTC
[brooklyn-ui] branch master updated (73e2334 -> 33b5d09)
This is an automated email from the ASF dual-hosted git repository.
heneveld pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-ui.git.
from 73e2334 Merge pull request #198 from apache/fix/removed-duplicate-plugin
new 70335dc undo the centering of palette, looks bad w few elements
new b51d3dc add a new bit in the composer to show errors and warnings
new 5a6b895 add example for constraint
new 8a0ad3f fix dsl detection edge case debug message for numbers and boolean values
new b1c9f07 add quick fix support
new 2315d3f remove pointless quick fix directive
new c419154 show quick fix on issues in spec editor
new ff755ee add quick fix tooltip and sketch set_from_ quick fixes
new 0749238 review with thomas - skip the intermediate small "x errors in x entities" step
new b7c38dd set_from_parameter minimally working!!!
new 0456306 set from key or parameter working, with root or anywhere (optionally with type) or ancestors (optionally with type)
new 4f159b8 cleanup how parameters and config are stored
new e189b37 update miscData.config on parameter refresh, and do validation after quick fixes
new a8fc797 also check supertypes for inclusion
new 33b5d09 This closes #200
The 15 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
...custom-widget.bom => basic-with-constraint.bom} | 53 ++--
.../catalog-selector/catalog-selector.less | 2 +-
.../catalog-selector.template.html | 4 +
.../providers/blueprint-service.provider.js | 297 ++++++++++++---------
.../app/components/quick-fix/quick-fix.js | 240 +++++++++++++++++
.../spec-editor/spec-editor.directive.js | 37 ++-
.../app/components/spec-editor/spec-editor.less | 11 +
.../spec-editor/spec-editor.template.html | 9 +-
.../app/components/util/model/dsl.model.js | 23 +-
.../app/components/util/model/entity.model.js | 101 ++++++-
.../app/components/util/model/issue.model.js | 14 +
ui-modules/blueprint-composer/app/index.js | 3 +-
.../app/views/main/graphical/graphical.state.html | 180 ++++++++++++-
.../app/views/main/graphical/graphical.state.js | 22 ++
.../app/views/main/graphical/graphical.state.less | 137 ++++++++--
.../blueprint-composer/app/views/main/main.less | 6 +-
.../app/views/main/yaml/yaml.state.js | 7 +
17 files changed, 958 insertions(+), 188 deletions(-)
copy docs/{vanilla-with-custom-widget.bom => basic-with-constraint.bom} (51%)
create mode 100644 ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
[brooklyn-ui] 04/15: fix dsl detection edge case debug message for
numbers and boolean values
Posted by he...@apache.org.
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 8a0ad3f5939b029c1cbaa1d690ce8a7f6d6eae07
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Wed Apr 21 15:28:59 2021 +0100
fix dsl detection edge case debug message for numbers and boolean values
---
.../app/components/providers/blueprint-service.provider.js | 2 +-
.../app/components/util/model/dsl.model.js | 13 ++++++++++---
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js b/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
index 7f78da1..1639577 100644
--- a/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
+++ b/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
@@ -485,7 +485,7 @@ function BlueprintService($log, $q, $sce, paletteApi, iconGenerator, dslService)
resolve(parsed);
}
} catch (ex) {
- $log.debug(ex);
+ $log.debug("Cannot detect whether this is a DSL expression; assuming not", ex);
reject(ex, input);
}
});
diff --git a/ui-modules/blueprint-composer/app/components/util/model/dsl.model.js b/ui-modules/blueprint-composer/app/components/util/model/dsl.model.js
index 7b78a0a..323d73d 100644
--- a/ui-modules/blueprint-composer/app/components/util/model/dsl.model.js
+++ b/ui-modules/blueprint-composer/app/components/util/model/dsl.model.js
@@ -37,6 +37,7 @@ export const KIND = {
ENTITY : {family: FAMILY.REFERENCE, name: 'entity object'},
STRING : {family: FAMILY.CONSTANT, name: 'constant string'},
NUMBER : {family: FAMILY.CONSTANT, name: 'constant number'},
+ OTHER : {family: FAMILY.CONSTANT, name: 'constant other'},
PORT : {family: FAMILY.CONSTANT, name: 'constant port'},
};
@@ -656,10 +657,16 @@ export class DslParser {
if (this.s instanceof String || typeof this.s === 'string') {
return this.parseString(this.s.toString().trim(), entity, entityResolver);
}
- // else ... TODO support JSON objects (for YAML syntax)
- else {
- throw new DslError("Unable to parse: " + typeof this.s);
+ // NUMBER and OTHER kinds are in the CONSTANT family which means they aren't DSL expressions
+ // (API here could be improved!)
+ if (typeof this.s === 'number') {
+ return new Dsl(KIND.NUMBER, this.s)
+ }
+ if (typeof this.s === 'boolean') {
+ return new Dsl(KIND.OTHER, this.s)
}
+ // TODO support JSON objects (for YAML syntax)
+ throw new DslError("Unable to parse: " + typeof this.s);
}
/**
[brooklyn-ui] 11/15: set from key or parameter working,
with root or anywhere (optionally with type) or ancestors
(optionally with type)
Posted by he...@apache.org.
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 0456306abca63ae1b69962d0f340b7febe23ace6
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Mon Apr 26 19:29:28 2021 +0100
set from key or parameter working, with root or anywhere (optionally with type) or ancestors (optionally with type)
---
docs/basic-with-constraint.bom | 17 +--
.../app/components/quick-fix/quick-fix.js | 166 ++++++++++++++-------
.../app/components/util/model/entity.model.js | 10 ++
3 files changed, 130 insertions(+), 63 deletions(-)
diff --git a/docs/basic-with-constraint.bom b/docs/basic-with-constraint.bom
index 3ca3e3c..0f1a2db 100644
--- a/docs/basic-with-constraint.bom
+++ b/docs/basic-with-constraint.bom
@@ -37,21 +37,20 @@ brooklyn.catalog:
message-regex: cannot both be set
- key: post_code
- fix: set_from_parameter
+ fix: set_from_key
message-regex: required
- source-mode: suggested
+ source-key: postal_code
source-hierarchy: root
- source-types: [ org.apache.brooklyn.api.entity.Application ]
- source-parameter: postal_code
- source-constraints:
- - required
+ source-key-createable: true
+ source-key-parameter-definition:
+ constraints:
+ - required
- key: post_code
- fix: set_from_config_key
+ fix: set_from_key
message-regex: required
- source-mode: enforced
- source-types: [ basic-with-constraint ]
source-key: post_code
source-hierarchy: anywhere
+ source-types: [ basic-with-constraint ]
diff --git a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
index 049a713..d703f11 100644
--- a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
+++ b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
@@ -90,76 +90,134 @@ const QUICK_FIX_PROPOSERS = {
proposals.clear_config.issues.push(issue);
},
},
- set_from_parameter: {
- propose: proposeSetFrom('parameter')
-
- // - key: post_code
- // fix: set_from_parameter
- // message-regex: required
- // source-mode: suggested
- // source-hierarchy: root
- // source-types: [ org.apache.brooklyn.api.entity.Application ]
- // source-parameter: postal_code
- // source-constraints:
- // - required
-
- },
- set_from_config_key: {
- propose: proposeSetFrom('config')
-
+ set_from_key: {
+ propose: proposeSetFrom()
// - key: post_code
- // fix: set_from_config_key
+ // fix: set_from_key
// message-regex: required
- // source-mode: enforced
- // source-types: [ basic-with-constraint ]
- // source-key: post_code
- // source-hierarchy: anywhere
+ //
+ // source-key: postal_code # required, custom
+ //
+ // source-hierarchy: root # optional, root|anywhere|ancestors -- default is root if no source-types given, anywhere if source-types are given
+ // source-types: [ org.apache.brooklyn.api.entity.Application ] # types to filter by
+ //
+ // source-key-createable: true # whether a parameter can be created or a key/param must already exist there (default the latter, ie false)
+ // source-key-parameter-definition: # if createable and did not exist, extra things to add to definition
+ // constraints:
+ // - required
+
+ // source-mode: suggested | enforced # (enhancement, not supported) could allow user to pick the type and/or key/param name
- }
+ },
};
-function proposeSetFrom(sourceType) {
+function proposeSetFrom() {
return function (qfdef, issue, entity, proposals) {
if (!issue.ref) return;
+
+ let ckey = qfdef['source-key'];
+ if (!ckey) {
+ console.warn("Missing required 'source-key' on hint", qfdef);
+ return;
+ }
+
if (!proposals) proposals = {};
- let sourceNode = {
- id: 'root',
- name: 'the application root node',
- entity: entity.getApplication(),
- };
+ let createable = qfdef['source-key-createable'];
- if (sourceType === 'parameter') {
- let params = sourceNode.entity.getParametersAsArray();
- console.log("params", params);
- }
+ // TODO if root param is required, show error
+ // TODO make default id containts type name
+ // TODO show default id if no id present
+ // TODO if id is changed, update all refs
+ // TODO allow graphically selectable
+
+ let considerNode = (sourceNode) => {
+ if (qfdef['source-types']) {
+ // TODO would be nice to store super-types of entity in miscData and filter based on those
+ if (!qfdef['source-types'].includes(sourceNode.entity.type)) {
+ // wrong type
+ return;
+ }
+ }
+
+ if (sourceNode.entity._id === entity._id && ckey === issue.ref) {
+ // skip proposal for recursive definition
+ return;
+ }
- let key = 'set_from_'+sourceType+'_'+sourceNode.id;
- if (!proposals[key]) {
- proposals[key] = {
- text: "Set from a new "+sourceType+" on "+sourceNode.name,
- tooltip: "This will create a "+sourceType+" on "+sourceNode.name+" and fix this error by setting it equal to that "+sourceType,
- issues: [],
- apply: (issue, entity) => {
- entity = (entity || issue.entity);
- console.log("TODO do this; NB issue invoked wrt", entity, qfdef);
- if (sourceType === 'parameter') {
- if (!sourceNode.entity.getParameterNamed(qfdef['source-parameter'])) {
- sourceNode.entity.addParameter({
- name: qfdef['source-parameter'],
- constraints: qfdef['source-constraints']
- });
+ let hasKey = sourceNode.entity.config[ckey] || (sourceNode.entity.miscData.get("config") || []).find(c => c && c.name === ckey);
+ let hasParam = sourceNode.entity.getParameterNamed(ckey);
+
+ let existing = hasKey || hasParam;
+ let create = !existing && createable;
+
+ if (!existing && !create) {
+ // no proposal available (cannot create)
+ return;
+ }
+
+ sourceNode.id = sourceNode.id || sourceNode.entity.id || sourceNode.entity._id;
+ sourceNode.name = sourceNode.name || sourceNode.entity.name ||
+ ((sourceNode.entity.type || "Unnamed item") + " " + "(" + (sourceNode.entity.id || sourceNode.entity._id) +")");
+
+ let pkey = 'set_from_' + sourceNode.id + '_' + ckey;
+ if (!proposals[pkey]) {
+ if (sourceNode.create_key) {
+ proposals[pkey] = {
+ text: "Set from new parameter '" + ckey + "' on " + sourceNode.name,
+ tooltip: "This will fix the error by setting the value here equal to the value of a new parameter '" + ckey + "' created on " + sourceNode.name
+ + ". The value of that parameter may need to be set in order to deploy this.",
+ };
+ } else {
+ proposals[pkey] = {
+ text: "Set from '" + ckey + "' on " + sourceNode.name,
+ tooltip: "This will fix the error by setting the value here equal to the value of " +
+ sourceNode.target_mode +
+ " '" + ckey + "' on " + sourceNode.name,
+ };
+ }
+
+ Object.assign(proposals[pkey], {
+ issues: [],
+ apply: (issue, entity) => {
+ if (sourceNode.create_key) {
+ // check again so we only create once
+ let hasParam = sourceNode.entity.getParameterNamed(ckey);
+ if (!hasParam) {
+ sourceNode.entity.addParameter(Object.assign(
+ {name: ckey,},
+ qfdef['source-key-parameter-definition'] || {}
+ ));
+ }
}
if (!sourceNode.entity.id) {
- // TODO
- sourceNode.entity.id = 'id_XXX';
+ sourceNode.entity.id = sourceNode.entity._id;
}
- entity.addConfig(issue.ref, '$brooklyn:component("'+sourceNode.entity.id+'").config("'+qfdef['source-parameter']+'")');
+
+ entity = (entity || issue.entity);
+ entity.addConfig(issue.ref, '$brooklyn:component("' + sourceNode.entity.id + '").config("' + ckey + '")');
}
- }
- };
+ });
+ }
+ if (proposals[pkey]) {
+ proposals[pkey].issues.push(issue);
+ }
+ };
+
+ if (qfdef['source-hierarchy']=='root' || (!qfdef['source-hierarchy'] && !qfdef['source-types'])) {
+ considerNode({
+ id: 'root',
+ name: 'the application root node',
+ entity: entity.getApplication(),
+ });
+ } else if (qfdef['source-hierarchy']=='anywhere' || (!qfdef['source-hierarchy'] && qfdef['source-types'])) {
+ entity.getApplication().visitWithDescendants(entity => considerNode({ entity }));
+ } else if (qfdef['source-hierarchy']=='ancestors') {
+ entity.visitWithAncestors(entity => considerNode({ entity }));
+ } else {
+ console.warn("Unsupported source-hierarchy in quick-fix", qfdef);
}
- proposals[key].issues.push(issue);
+
};
}
diff --git a/ui-modules/blueprint-composer/app/components/util/model/entity.model.js b/ui-modules/blueprint-composer/app/components/util/model/entity.model.js
index f8afc28..b844450 100644
--- a/ui-modules/blueprint-composer/app/components/util/model/entity.model.js
+++ b/ui-modules/blueprint-composer/app/components/util/model/entity.model.js
@@ -563,6 +563,16 @@ export class Entity {
toString() {
return 'Entity :: id = [' + this._id + ']' + (this.hasType() ? ' type = [' + this.type + ']' : '');
}
+
+ visitWithDescendants(fn) {
+ fn(this);
+ this.children.forEach(c => c.visitWithDescendants(fn));
+ }
+
+ visitWithAncestors(fn) {
+ fn(this);
+ if (this.parent) this.parent.visitWithAncestors(fn);
+ }
}
Entity.prototype.setEntityFromJson = setEntityFromJson;
[brooklyn-ui] 05/15: add quick fix support
Posted by he...@apache.org.
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 b1c9f0766482d83e0f0739af38bddb5db33b945d
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Thu Apr 22 22:12:51 2021 +0100
add quick fix support
in global view only; on entity coming soon
---
docs/basic-with-constraint.bom | 6 ++
.../providers/blueprint-service.provider.js | 11 +-
.../app/components/quick-fix/quick-fix.html | 21 ++++
.../app/components/quick-fix/quick-fix.js | 120 +++++++++++++++++++++
.../app/components/quick-fix/quick-fix.less | 21 ++++
.../app/components/util/model/issue.model.js | 14 +++
ui-modules/blueprint-composer/app/index.js | 3 +-
.../app/views/main/graphical/graphical.state.html | 61 ++++++++---
.../app/views/main/graphical/graphical.state.js | 25 +++--
.../app/views/main/graphical/graphical.state.less | 4 +
10 files changed, 259 insertions(+), 27 deletions(-)
diff --git a/docs/basic-with-constraint.bom b/docs/basic-with-constraint.bom
index 08ed2c6..b3f440a 100644
--- a/docs/basic-with-constraint.bom
+++ b/docs/basic-with-constraint.bom
@@ -27,4 +27,10 @@ brooklyn.catalog:
- name: zip_code
constraints:
- forbiddenIf: post_code
+ tags:
+ - ui-composer-hints:
+ config-quick-fixes:
+ - key: zip_code
+ fix: clear_config
+ message-regex: .*cannot both be set.*
diff --git a/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js b/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
index 1639577..c98cf69 100644
--- a/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
+++ b/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
@@ -65,6 +65,7 @@ function BlueprintService($log, $q, $sce, paletteApi, iconGenerator, dslService)
isReservedKey: isReservedKey,
getIssues: getIssues,
hasIssues: hasIssues,
+ clearAllIssues: clearAllIssues,
getAllIssues: getAllIssues,
populateEntityFromApi: populateEntityFromApiSuccess,
populateLocationFromApi: populateLocationFromApiSuccess,
@@ -177,6 +178,12 @@ function BlueprintService($log, $q, $sce, paletteApi, iconGenerator, dslService)
return issues;
}
+ // typically followed by a call to refresh
+ function clearAllIssues(entity = blueprint) {
+ entity.resetIssues();
+ entity.children.forEach(clearAllIssues);
+ }
+
function getAllIssues(entity = blueprint) {
return collectAllIssues({}, entity);
}
@@ -559,9 +566,9 @@ function BlueprintService($log, $q, $sce, paletteApi, iconGenerator, dslService)
return promises;
}, [])).then(results => {
results.forEach(result => {
- entity.clearIssues({ref: result.key});
+ entity.clearIssues({ref: result.key, phase: 'relationship'});
result.issues.forEach(issue => {
- entity.addIssue(Issue.builder().group('config').ref(result.key).message($sce.trustAsHtml(issue)).build());
+ entity.addIssue(Issue.builder().group('config').phase('relationship').ref(result.key).message($sce.trustAsHtml(issue)).build());
});
})
});
diff --git a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.html b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.html
new file mode 100644
index 0000000..e9bdb2e
--- /dev/null
+++ b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.html
@@ -0,0 +1,21 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<span class="quick-fix">
+ bob {{ foo }}.
+</span>
diff --git a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
new file mode 100644
index 0000000..184de2c
--- /dev/null
+++ b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import angular from 'angular';
+import template from './quick-fix.html';
+
+const MODULE_NAME = 'brooklyn.components.quick-fix.quick-fix';
+
+angular.module(MODULE_NAME, [])
+ .directive('quickFix', ['$rootScope', quickFixDirective]);
+
+export default MODULE_NAME;
+
+export function quickFixDirective($rootScope) {
+ return {
+ restrict: 'E',
+ template: template,
+ scope: {
+ issues: '=',
+ },
+ link: link,
+ };
+
+ function link(scope, element, attrs, specEditor) {
+ console.log("quick-fix", attrs);
+ scope.foo = "hello";
+ }
+}
+
+export function computeQuickFixes(allIssues) {
+ if (!allIssues) allIssues = {};
+ if (!allIssues.errors) allIssues.errors = {};
+
+ allIssues.errors.byMessage = {};
+ Object.values(allIssues.errors.byEntity).forEach(list => {
+ list.forEach(issue => {
+ // TODO key should be a tuple of group, ref, message
+ let key = issue.group+":"+issue.ref+":"+issue.message;
+ let v = allIssues.errors.byMessage[key];
+ if (!v) {
+ v = allIssues.errors.byMessage[key] = {
+ group: issue.group,
+ ref: issue.ref,
+ message: issue.message,
+ issues: [],
+ quickFixes: {},
+ };
+ }
+
+ let issueO = {
+ issue,
+ //quickFixes: {},
+ }
+ v.issues.push(issueO);
+
+ let qfs = getQuickFixHintsForIssue(issue);
+ (qfs || []).forEach(qf => {
+ let qfi = getQuickFixProposer(qf['fix']);
+ if (!qfi) {
+ console.log("Skipping unknown quick fix", qf);
+ } else {
+ qfi.propose(issue, v.quickFixes);
+ // we could offer the fix per-issue, but no need as they can get that by navigating to the entity
+ //qfi.propose(issue, issueO.quickFixes);
+ }
+ });
+ });
+ });
+ return allIssues;
+}
+
+const QUICK_FIX_PROPOSERS = {
+ clear_config: {
+ // the propose function updates the proposals object
+ propose: (issue, proposals) => {
+ if (!issue.ref) return;
+
+ if (!proposals) proposals = {};
+ if (!proposals.clear_config) {
+ proposals.clear_config = {
+ text: "Remove the current value (clear config \""+issue.ref+"\")",
+ apply: (issue) => issue.entity.removeConfig(issue.ref),
+ issues: [],
+ };
+ }
+ proposals.clear_config.issues.push(issue);
+ },
+ }
+};
+
+export function getQuickFixProposer(type) {
+ return QUICK_FIX_PROPOSERS[type];
+}
+
+export function getQuickFixHintsForIssue(issue) {
+ if (issue.group === 'config') {
+ let hints = (issue.entity.miscData.get('ui-composer-hints') || {})['config-quick-fixes'] || [];
+ hints = hints.filter(h => h.key === issue.ref);
+ if (!hints.length) return null;
+ hints = hints.filter(h => !h['message-regex'] || new RegExp(h['message-regex']).test(issue.message));
+ return hints;
+ }
+ return null;
+}
\ No newline at end of file
diff --git a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.less b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.less
new file mode 100644
index 0000000..77412b2
--- /dev/null
+++ b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.less
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+quick-fix, .quick-fix {
+}
diff --git a/ui-modules/blueprint-composer/app/components/util/model/issue.model.js b/ui-modules/blueprint-composer/app/components/util/model/issue.model.js
index badfab5..f00bd7d 100644
--- a/ui-modules/blueprint-composer/app/components/util/model/issue.model.js
+++ b/ui-modules/blueprint-composer/app/components/util/model/issue.model.js
@@ -18,6 +18,7 @@
*/
const MESSAGE = new WeakMap();
const GROUP = new WeakMap();
+const PHASE = new WeakMap();
const REF = new WeakMap();
const LEVEL = new WeakMap();
@@ -36,6 +37,7 @@ export class Issue {
constructor() {
MESSAGE.set(this, '');
GROUP.set(this, '');
+ PHASE.set(this, '');
REF.set(this, '');
LEVEL.set(this, ISSUE_LEVEL.ERROR);
}
@@ -56,6 +58,14 @@ export class Issue {
return GROUP.get(this);
}
+ set phase(group) {
+ PHASE.set(this, group);
+ }
+
+ get phase() {
+ return PHASE.get(this);
+ }
+
set ref(ref) {
REF.set(this, ref);
}
@@ -92,6 +102,10 @@ class Builder {
return this;
}
+ phase(phase) {
+ this.issue.phase = phase;
+ }
+
ref(ref) {
this.issue.ref = ref;
return this;
diff --git a/ui-modules/blueprint-composer/app/index.js b/ui-modules/blueprint-composer/app/index.js
index 7504627..944fbad 100755
--- a/ui-modules/blueprint-composer/app/index.js
+++ b/ui-modules/blueprint-composer/app/index.js
@@ -59,6 +59,7 @@ import paletteDragAndDropService from "./components/providers/palette-dragndrop.
import actionService from "./components/providers/action-service.provider";
import tabService from "./components/providers/tab-service.provider";
import composerOverrides from "./components/providers/composer-overrides.provider";
+import quickFix from "./components/quick-fix/quick-fix";
import {mainState} from "./views/main/main.controller";
import {yamlAutodetectState, yamlCampState, yamlState} from "./views/main/yaml/yaml.state";
import {graphicalState} from "./views/main/graphical/graphical.state";
@@ -82,7 +83,7 @@ angular.module('brooklynBlueprintComposer', [ngAnimate, ngResource, ngCookies, n
brServerStatus, brAutoFocus, brIconGenerator, brInterstitialSpinner, brooklynModuleLinks, brooklynUserManagement,
brYamlEditor, brUtils, brSpecEditor, brooklynCatalogSaver, brooklynApi, bottomSheet, stackViewer, brDragndrop, mdHelper,
customActionDirective, customConfigSuggestionDropdown, paletteApiProvider, paletteServiceProvider, blueprintLoaderApiProvider,
- breadcrumbs, catalogSelector, designer, objectCache, entityFilters, locationFilter, actionService, tabService, composerOverrides, blueprintService,
+ breadcrumbs, catalogSelector, designer, objectCache, entityFilters, locationFilter, actionService, tabService, composerOverrides, quickFix, blueprintService,
dslService, paletteDragAndDropService, recentlyUsedService, scriptTagDecorator, brandAngularJs])
.filter('dslParamLabel', ['$filter', dslParamLabelFilter])
.config(['$urlRouterProvider', '$stateProvider', '$logProvider', '$compileProvider', applicationConfig])
diff --git a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.html b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.html
index b6cf40e..dba01b1 100644
--- a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.html
+++ b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.html
@@ -87,9 +87,9 @@
<div class="errorsHeader">
<a ng-click="errorsPane.level = null" class="hand errors-close"><i class="fa fa-fw fa-times"></i></a>
- {{ vm.size(allIssues.errors.byEntity) }}
- <span ng-if="vm.size(allIssues.errors.byEntity) == 1">entity</span>
- <span ng-if="vm.size(allIssues.errors.byEntity) != 1">entities</span>
+ <b>{{ vm.size(allIssues.errors.byEntity) }}
+ <span ng-if="vm.size(allIssues.errors.byEntity) == 1">entity</span>
+ <span ng-if="vm.size(allIssues.errors.byEntity) != 1">entities</span></b>
with
@@ -123,9 +123,9 @@
<div class="errorsHeader">
<a ng-click="errorsPane.level = null" class="hand errors-close"><i class="fa fa-fw fa-times"></i></a>
- {{ allIssues.errors.count }}
- <span ng-if="allIssues.errors.count == 1">error</span>
- <span ng-if="allIssues.errors.count != 1">errors</span>
+ <b>{{ allIssues.errors.count }}
+ <span ng-if="allIssues.errors.count == 1">error</span>
+ <span ng-if="allIssues.errors.count != 1">errors</span></b>
<span ng-show="allIssues.errors.count != vm.size(allIssues.errors.byMessage)"> ({{ vm.size(allIssues.errors.byMessage) }} unique)</span>
in
@@ -143,24 +143,59 @@
<i class="fa fa-fw fa-times-circle"></i>
</div>
<div class="error-line-text">
- {{ itemK }}:
- <a class="hand" ng-click="errorsPane.focus = (errorsPane.focus == itemK ? null : itemK)">{{ itemV.length }} {{ itemV.length==1 ? 'error' : 'errors' }}</a>
- <div class="error-line-sub" ng-if="errorsPane.focus == itemK">
- <div ng-repeat="issue in itemV" class="error-line-sub-line">
+ <span>
+ <span ng-show="vm.messageNeedsPrefix(itemV)">
+ {{ itemV.group }} {{ itemV.ref }}<span ng-show="itemV.message">:</span>
+ </span>
+ <span ng-show="itemV.message">
+ <ng-bind-html ng-bind-html="itemV.message"></ng-bind-html>
+ </span>
+ </span>
+
+ (<a class="hand" ng-click="errorsPane.focus = (errorsPane.focus == 'errors:'+itemK ? null : 'errors:'+itemK)"
+ ng-class="{active: errorsPane.focus == 'errors:'+itemK }">{{
+ itemV.issues.length }}
+ {{ itemV.issues.length==1 ? 'entity' : 'entities'
+ }}</a><span ng-if="vm.size(itemV.quickFixes)">;
+ <a class="hand" ng-click="errorsPane.focus = (errorsPane.focus == 'fixes:'+itemK ? null : 'fixes:'+itemK)"
+ ng-class="{active: errorsPane.focus == 'fixes:'+itemK }">
+ {{ vm.size(itemV.quickFixes) }} quick fix<span ng-if="vm.size(itemV.quickFixes) != 1">es</span> available</a></span>)
+
+ <div class="error-line-sub" ng-if="errorsPane.focus == 'errors:'+itemK">
+ <div ng-repeat="issue in itemV.issues" class="error-line-sub-line">
<div class="error-line-marker">
<i class="fa fa-fw fa-circle"></i>
</div>
<div class="error-line-text">
- {{ issue.entity.id ? issue.entity.id + ' ('+issue.entity.type+')'
- : issue.entity.type }}
+ {{ issue.issue.entity.id ? issue.issue.entity.id + ' ('+issue.issue.entity.type+')'
+ : issue.issue.entity.type }}
+ <!-- could offer the issue-specific quick fixes; but clearer to navigate to entity and do there -->
</div>
<div class="error-line-action">
- <a class="hand" ui-sref="main.graphical.edit.entity({entityId: issue.entity._id})">
+ <a class="hand" ui-sref="main.graphical.edit.entity({entityId: issue.issue.entity._id})">
<i class="fa fa-fw fa-external-link"></i>
</a>
</div>
</div>
</div>
+
+ <div class="error-line-sub" ng-if="errorsPane.focus == 'fixes:'+itemK">
+ <div ng-repeat="fix in itemV.quickFixes" class="error-line-sub-line">
+ <div class="error-line-marker">
+ <i class="fa fa-fw fa-magic"></i>
+ </div>
+ <div class="error-line-text">
+ {{ fix.text }}
+ <a class="hand btn btn-xs btn-primary" style="float: right;" ng-click="vm.applyQuickFix(fix)"
+ >Apply
+ ({{ vm.size(fix.issues) }}
+ <span ng-if="vm.size(fix.issues) == 1">entity</span
+ ><span ng-if="vm.size(fix.issues) != 1">entities</span
+ >)
+ </a>
+ </div>
+ </div>
+ </div>
</div>
<!--
<div class="error-line-action">
diff --git a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.js b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.js
index 143ebb2..16cf7f8 100644
--- a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.js
+++ b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.js
@@ -19,6 +19,7 @@
import {graphicalEditEntityState} from './edit/entity/edit.entity.controller';
import {graphicalEditPolicyState} from './edit/policy/edit.policy.controller';
import {graphicalEditEnricherState} from './edit/enricher/edit.enricher.controller';
+import {computeQuickFixes} from '../../../components/quick-fix/quick-fix';
import {Entity, EntityFamily} from '../../../components/util/model/entity.model';
import template from './graphical.state.html';
@@ -36,6 +37,7 @@ export const graphicalState = {
};
function graphicalController($scope, $state, $filter, blueprintService, paletteService) {
+ let vm = this;
this.EntityFamily = EntityFamily;
this.sections = paletteService.getSections();
@@ -44,18 +46,11 @@ function graphicalController($scope, $state, $filter, blueprintService, paletteS
$scope.errorsPane = { level: null };
$scope.blueprint = blueprintService.get();
- $scope.$watch('blueprint', ()=> {
- $scope.allIssues = blueprintService.getAllIssues();
- $scope.allIssues.errors.byMessage = {};
- Object.values($scope.allIssues.errors.byEntity).forEach(list => {
- list.forEach(issue => {
- $scope.allIssues.errors.byMessage[issue.group+":"+issue.ref] = $scope.allIssues.errors.byMessage[issue.group+" "+issue.ref] || [];
- $scope.allIssues.errors.byMessage[issue.group+":"+issue.ref].push(issue);
- });
- });
- //console.log("blueprint update, get all issues", $scope.allIssues);
- }, true);
+ $scope.$watch('blueprint', () => vm.computeIssues(), true);
+ this.computeIssues = () => {
+ $scope.allIssues = computeQuickFixes(blueprintService.getAllIssues());
+ }
this.onCanvasSelection = (item) => {
$scope.canvasSelectedItem = item;
}
@@ -64,6 +59,7 @@ function graphicalController($scope, $state, $filter, blueprintService, paletteS
return Object.keys(obj).length;
}
+ this.messageNeedsPrefix = (itemV) => !itemV.message || (""+itemV.message).indexOf(itemV.ref)<0;
this.getOnSelectText = (selectableType) => $scope.canvasSelectedItem ? "Add to " + $filter('entityName')($scope.canvasSelectedItem) : "Add to application";
this.addSelectedTypeToTargetEntity = (selectedType, targetEntity) => {
@@ -96,4 +92,11 @@ function graphicalController($scope, $state, $filter, blueprintService, paletteS
$state.go(graphicalEditEntityState, {entityId: targetEntity._id});
}
};
+
+ this.applyQuickFix = (fix) => {
+ fix.issues.forEach(issue => fix.apply(issue));
+ // recompute errors
+ blueprintService.clearAllIssues();
+ blueprintService.refreshBlueprintMetadata()
+ }
}
diff --git a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.less b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.less
index eeada0b..3cb5fc1 100644
--- a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.less
+++ b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.less
@@ -118,6 +118,10 @@
}
max-height: 600px;
overflow-y: scroll;
+
+ a.active {
+ font-weight: bold;
+ }
}
.error-line {
display: flex;
[brooklyn-ui] 01/15: undo the centering of palette,
looks bad w few elements
Posted by he...@apache.org.
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 70335dc416c483b772f9f903c42a0101855f17db
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Wed Apr 21 09:29:32 2021 +0100
undo the centering of palette, looks bad w few elements
add a bit of spacing instead
---
.../catalog-selector/catalog-selector.template.html | 4 ++++
.../app/views/main/graphical/graphical.state.less | 14 +++++++++++---
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.template.html b/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.template.html
index a78c9fd..8419eff 100644
--- a/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.template.html
+++ b/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.template.html
@@ -96,6 +96,8 @@
</div>
</div>
+ <div class="palette-spacer"></div>
+
<div class="row grid">
<!-- here and below, col-xs-3 or -4 or -2 all work giving different densities;
this could be configurable ("compressed"=xs-2 w no labels, "normal"=xs-3, "big"=xs-4) -->
@@ -144,6 +146,8 @@
</div>
</div>
+ <div class="palette-spacer"></div>
+
<div class="catalog-palette-footer">
<div uib-pagination total-items="searchedItems.length" items-per-page="pagination.itemsPerPage" ng-model="pagination.page" boundary-link-numbers="true" rotate="false" max-size="4" ng-show="searchedItems.length > pagination.itemsPerPage" class="pagination-sm pull-right"></div>
<ng-include src="templateUrls.footer"/>
diff --git a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.less b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.less
index dfb6a30..915d352 100644
--- a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.less
+++ b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.less
@@ -143,13 +143,21 @@
margin-left: 0;
margin-right: 0;
}
-
+
+ .palette-spacer {
+ max-height: 12px;
+ height: 6px;
+ flex: 10 10 auto; // doesn't work to grow/shrink as hoped, but still looks better
+ }
+
&, .palette-full-height-wrapper {
display: flex;
flex-direction: column;
- justify-content: space-between;
- flex: 1 1 auto;
+ // this looks bad if just a few elements being shown
+ //justify-content: space-between;
+
+ flex: 0 0 auto;
margin-left: 0;
margin-right: 0;
}
[brooklyn-ui] 08/15: add quick fix tooltip and sketch set_from_
quick fixes
Posted by he...@apache.org.
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 ff755eea2e5957a9d28a9abdc60dda9cb03ea57c
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri Apr 23 18:00:15 2021 +0100
add quick fix tooltip and sketch set_from_ quick fixes
---
docs/basic-with-constraint.bom | 23 +++++++++++++++++-
.../app/components/quick-fix/quick-fix.js | 27 +++++++++++++++++++++-
.../spec-editor/spec-editor.template.html | 2 +-
.../app/views/main/graphical/graphical.state.html | 3 ++-
4 files changed, 51 insertions(+), 4 deletions(-)
diff --git a/docs/basic-with-constraint.bom b/docs/basic-with-constraint.bom
index b3f440a..3ca3e3c 100644
--- a/docs/basic-with-constraint.bom
+++ b/docs/basic-with-constraint.bom
@@ -24,6 +24,8 @@ brooklyn.catalog:
type: org.apache.brooklyn.entity.stock.BasicEntity
brooklyn.parameters:
- name: post_code
+ constraints:
+ - requiredUnless: zip-code
- name: zip_code
constraints:
- forbiddenIf: post_code
@@ -32,5 +34,24 @@ brooklyn.catalog:
config-quick-fixes:
- key: zip_code
fix: clear_config
- message-regex: .*cannot both be set.*
+ message-regex: cannot both be set
+
+ - key: post_code
+ fix: set_from_parameter
+ message-regex: required
+
+ source-mode: suggested
+ source-hierarchy: root
+ source-types: [ org.apache.brooklyn.api.entity.Application ]
+ source-parameter: postal_code
+ source-constraints:
+ - required
+
+ - key: post_code
+ fix: set_from_config_key
+ message-regex: required
+ source-mode: enforced
+ source-types: [ basic-with-constraint ]
+ source-key: post_code
+ source-hierarchy: anywhere
diff --git a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
index bd96735..a542742 100644
--- a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
+++ b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
@@ -81,13 +81,38 @@ const QUICK_FIX_PROPOSERS = {
if (!proposals) proposals = {};
if (!proposals.clear_config) {
proposals.clear_config = {
- text: "Remove the current value (clear config \""+issue.ref+"\")",
+ text: "Remove value",
+ tooltip: "This will clear the value currently set for config \""+issue.ref+"\".",
apply: (issue, entity) => (entity || issue.entity).removeConfig(issue.ref),
issues: [],
};
}
proposals.clear_config.issues.push(issue);
},
+ },
+ set_from_parameter: {
+ propose: (issue, proposals) => {}
+ // - key: post_code
+ // fix: set_from_parameter
+ // message-regex: required
+ // source-mode: suggested
+ // source-hierarchy: root
+ // source-types: [ org.apache.brooklyn.api.entity.Application ]
+ // source-parameter: postal_code
+ // source-constraints:
+ // - required
+
+ },
+ set_from_config_key: {
+ propose: (issue, proposals) => {}
+ // - key: post_code
+ // fix: set_from_config_key
+ // message-regex: required
+ // source-mode: enforced
+ // source-types: [ basic-with-constraint ]
+ // source-key: post_code
+ // source-hierarchy: anywhere
+
}
};
diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html
index d6ae606..6050219 100644
--- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html
+++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html
@@ -551,7 +551,7 @@
<span ng-bind-html="issue.message"></span>
<div ng-if="getObjectSize(issue.quickFixes)" class="quick-fix">
<div ng-repeat="fix in issue.quickFixes">
- <a ng-click="applyQuickFix(issue, fix)" class="btn btn-xs btn-primary">{{ fix.text }}</a>
+ <a ng-click="applyQuickFix(issue, fix)" class="btn btn-xs btn-primary" ng-attr-title="{{ fix.tooltip }}">{{ fix.text }}</a>
</div>
</div>
</small>
diff --git a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.html b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.html
index dba01b1..986279b 100644
--- a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.html
+++ b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.html
@@ -187,7 +187,8 @@
<div class="error-line-text">
{{ fix.text }}
<a class="hand btn btn-xs btn-primary" style="float: right;" ng-click="vm.applyQuickFix(fix)"
- >Apply
+ ng-attr-title="{{ fix.tooltip }}"
+ >Apply
({{ vm.size(fix.issues) }}
<span ng-if="vm.size(fix.issues) == 1">entity</span
><span ng-if="vm.size(fix.issues) != 1">entities</span
[brooklyn-ui] 13/15: update miscData.config on parameter refresh,
and do validation after quick fixes
Posted by he...@apache.org.
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 e189b375524677ed86f5863d784ffbd9397901d5
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Tue Apr 27 12:57:29 2021 +0100
update miscData.config on parameter refresh, and do validation after quick fixes
validation pathway working nicely now
---
.../providers/blueprint-service.provider.js | 35 +++++------
.../app/components/quick-fix/quick-fix.js | 1 -
.../spec-editor/spec-editor.directive.js | 2 +
.../app/components/util/model/entity.model.js | 71 ++++++++++++----------
4 files changed, 57 insertions(+), 52 deletions(-)
diff --git a/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js b/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
index 0132a85..47e7fa3 100644
--- a/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
+++ b/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
@@ -297,19 +297,23 @@ function BlueprintService($log, $q, $sce, paletteApi, iconGenerator, dslService)
}).catch(function (error) {
deferred.resolve(populateEntityFromApiError(entity, error));
});
- } else if (entity.parent) {
- entity.clearIssues({group: 'type'}).addIssue(Issue.builder().group('type').message('Entity needs a type').level(ISSUE_LEVEL.WARN).build());
- entity.miscData.set('sensors', []);
- entity.miscData.set('traits', []);
- deferred.resolve(entity);
- addUnlistedConfigKeysDefinitions(entity);
- addUnlistedParameterDefinitions(entity);
} else {
+ if (entity.parent) {
+ entity.clearIssues({group: 'type'}).addIssue(Issue.builder().group('type').message('Entity needs a type').level(ISSUE_LEVEL.WARN).build());
+ }
entity.miscData.set('sensors', []);
entity.miscData.set('traits', []);
- deferred.resolve(entity);
+
+ entity.clearIssues({group: 'config'});
+ entity.miscData.set('config', []);
+ entity.miscData.set('configMap', {});
+ entity.miscData.set('parameters', []);
+ entity.miscData.set('parametersMap', {});
+
addUnlistedConfigKeysDefinitions(entity);
addUnlistedParameterDefinitions(entity);
+
+ deferred.resolve(entity);
}
return deferred.promise;
@@ -585,7 +589,6 @@ function BlueprintService($log, $q, $sce, paletteApi, iconGenerator, dslService)
}
function addConfigKeyDefinition(entity, key) {
- // TODO return type, and below
entity.addConfigKeyDefinition(key, false);
}
@@ -597,24 +600,18 @@ function BlueprintService($log, $q, $sce, paletteApi, iconGenerator, dslService)
// copy config key definitions set on this entity into the miscData aggregated view
let allConfig = entity.miscDataOrDefault('configMap', {});
entity.config.forEach((value, key) => {
- if (!allConfig[key]) {
- entity.addConfigKeyDefinition(key);
- }
+ entity.addConfigKeyDefinition(key, false, true);
});
- entity.miscData.set('config', Object.values(allConfig));
-
+ entity.addConfigKeyDefinition(null, false, false);
}
function addUnlistedParameterDefinitions(entity) {
// copy parameter definitions set on this entity into the miscData aggregated view;
// see discussions in PR 112 about whether this is necessary and/or there is a better way; but note, this is much updated since
- let allParams = entity.miscDataOrDefault('parametersMap', {});
entity.parameters.forEach((param) => {
- if (!allParams[param.name]) {
- allParams[param.name] = param;
- }
+ entity.addParameterDefinition(param, false, true);
});
- entity.miscData.set('parameters', Object.values(allParams));
+ entity.addParameterDefinition(null, false, false);
}
function populateEntityFromApiSuccess(entity, data) {
diff --git a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
index ffcaa60..5204052 100644
--- a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
+++ b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
@@ -124,7 +124,6 @@ function proposeSetFrom() {
let createable = qfdef['source-key-createable'];
- // TODO if root param is required, show error
// TODO make default id contain type name
// TODO show default id if no id present
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 b1e556f..1f71e62 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
@@ -268,6 +268,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
scope.applyQuickFix = (issue, fix) => {
fix.apply(issue, scope.model);
+ blueprintService.refreshBlueprintMetadata();
}
scope.getObjectSize = (object) => {
@@ -1217,6 +1218,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
if (scope.state.parameters.focus === name) {
scope.state.parameters.focus = '';
}
+ blueprintService.refreshBlueprintMetadata(scope.model);
}
}
}
diff --git a/ui-modules/blueprint-composer/app/components/util/model/entity.model.js b/ui-modules/blueprint-composer/app/components/util/model/entity.model.js
index 1eb2343..71a5912 100644
--- a/ui-modules/blueprint-composer/app/components/util/model/entity.model.js
+++ b/ui-modules/blueprint-composer/app/components/util/model/entity.model.js
@@ -653,46 +653,53 @@ function addConfig(key, value) {
}
}
-function addConfigKeyDefinition(param, overwrite) {
- if (typeof param === 'string') {
- param = {
- "name": param,
- "label": param,
- "description": "",
- "priority": 1,
- "pinned": true,
- "type": "java.lang.String",
- "constraints": [],
- };
- overwrite = false;
- }
- let key = (param || {}).name;
- if (!key) throw new Error("'name' field must be included when adding parameter; was", param);
-
+function addConfigKeyDefinition(param, overwrite, skipUpdatesDuringBatch) {
let allConfig = this.miscDataOrDefault('configMap', {});
- allConfig[key] = Object.assign(allConfig[key] || {}, param, overwrite ? null : allConfig[key]);
- this.miscData.set('config', Object.values(allConfig));
+ if (param) {
+ if (typeof param === 'string') {
+ param = {
+ "name": param,
+ "label": param,
+ "description": "",
+ "priority": 1,
+ "pinned": true,
+ "type": "java.lang.String",
+ "constraints": [],
+ };
+ overwrite = false;
+ }
+ let key = (param || {}).name;
+ if (!key) throw new Error("'name' field must be included when adding parameter; was", param);
+
+ allConfig[key] = Object.assign(allConfig[key] || {}, param, overwrite ? null : allConfig[key]);
+ }
+ if (!skipUpdatesDuringBatch) {
+ this.miscData.set('config', Object.values(allConfig));
+ }
this.touch();
return this;
}
-function addParameterDefinition(param, overwrite) {
- if (typeof param === 'string') {
- param = {name: key, type: 'string'};
- overwrite = false;
- }
- let key = (param || {}).name;
- if (!key) throw new Error("'name' field must be included when adding parameter");
-
+function addParameterDefinition(param, overwrite, skipUpdatesDuringBatch) {
let allParams = this.miscDataOrDefault('parametersMap', {});
- allParams[key] = Object.assign(allParams[key] || {}, param, overwrite ? null : allParams[key]);
- this.miscData.set('parameters', Object.values(allParams));
+ if (param) {
+ if (typeof param === 'string') {
+ param = {name: key, type: 'string'};
+ overwrite = false;
+ }
+ let key = (param || {}).name;
+ if (!key) throw new Error("'name' field must be included when adding parameter");
+
+ param = allParams[key] = Object.assign(allParams[key] || {}, param, overwrite ? null : allParams[key]);
- let eps = PARAMETERS.get(this);
- this.updateParameter(key, allParams[key], true);
+ this.updateParameter(key, param, true);
+ }
+ if (!skipUpdatesDuringBatch) {
+ this.miscData.set('parameters', Object.values(allParams));
+ }
- this.addConfigKeyDefinition(allParams[key], overwrite);
+ this.addConfigKeyDefinition(param, overwrite, skipUpdatesDuringBatch);
this.touch();
return this;
@@ -721,7 +728,7 @@ function removeConfig(key) {
}
/**
- * Remove an entry from brooklyn.parameters
+ * Remove an entry from brooklyn.parameters; note the model needs an update after this
* @param {string} name
* @returns {Entity}
*/
[brooklyn-ui] 12/15: cleanup how parameters and config are stored
Posted by he...@apache.org.
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 4f159b8079b8454bd35b381003b58cb0b742e1fd
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Tue Apr 27 12:15:22 2021 +0100
cleanup how parameters and config are stored
ensure addition is preserved, and that parameters defined are displayed and interpreted as config
---
.../providers/blueprint-service.provider.js | 210 +++++++++++----------
.../app/components/quick-fix/quick-fix.js | 12 +-
.../spec-editor/spec-editor.directive.js | 12 +-
.../app/components/util/model/entity.model.js | 73 ++++++-
.../app/views/main/yaml/yaml.state.js | 7 +
5 files changed, 189 insertions(+), 125 deletions(-)
diff --git a/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js b/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
index c98cf69..0132a85 100644
--- a/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
+++ b/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
@@ -90,7 +90,7 @@ function BlueprintService($log, $q, $sce, paletteApi, iconGenerator, dslService)
}
setBlueprintFromJson(newBlueprint);
$log.debug(TAG + 'Blueprint set from YAML', blueprint);
- // TODO refresh the blueprint now? see comments in yaml.state.js and on refreshBlueprint
+ // do we need to refresh the blueprint now? see comments in yaml.state.js and on refreshBlueprint; think not.
}
function getBlueprint() {
@@ -285,7 +285,6 @@ function BlueprintService($log, $q, $sce, paletteApi, iconGenerator, dslService)
function refreshTypeMetadata(entity, family) {
let deferred = $q.defer();
-
if (entity.hasType()) {
entity.family = family.id;
@@ -350,86 +349,89 @@ function BlueprintService($log, $q, $sce, paletteApi, iconGenerator, dslService)
}
function refreshConfigConstraints(entity) {
+ function checkConstraints(config) {
+ for (let constraintO of config.constraints) {
+ let message = null;
+ let key = null, args = null;
+ if (constraintO instanceof String || typeof constraintO=='string') {
+ key = constraintO;
+ } else if (Object.keys(constraintO).length==1) {
+ key = Object.keys(constraintO)[0];
+ args = constraintO[key];
+ } else {
+ $log.warn("Unknown constraint object", typeof constraintO, constraintO, config);
+ key = constraintO;
+ }
+ let val = (k) => entity.config.get(k || config.name);
+ let isSet = (k) => entity.config.has(k || config.name) && angular.isDefined(val(k));
+ let isAnySet = (k) => {
+ if (!k || !Array.isArray(k)) return false;
+ return k.some(isSet);
+ }
+ let hasDefault = () => angular.isDefined(config.defaultValue);
+ switch (key) {
+ case 'Predicates.notNull()':
+ case 'Predicates.notNull':
+ if (!isSet() && !hasDefault()) {
+ message = `<samp>${config.name}</samp> is required`;
+ }
+ break;
+ case 'required':
+ if (!isSet() && !hasDefault() && val()!='') {
+ message = `<samp>${config.name}</samp> is required`;
+ }
+ break;
+ case 'regex':
+ if (isSet() && !(new RegExp(args).test(val))) {
+ message = `<samp>${config.name}</samp> does not match the required format: <samp>${args}</samp>`;
+ }
+ break;
+ case 'forbiddenIf':
+ if (isSet() && isSet(args)) {
+ message = `<samp>${config.name}</samp> and <samp>${args}</samp> cannot both be set`;
+ }
+ break;
+ case 'forbiddenUnless':
+ if (isSet() && !isSet(args)) {
+ message = `<samp>${config.name}</samp> can only be set when <samp>${args}</samp> is set`;
+ }
+ break;
+ case 'requiredIf':
+ if (!isSet() && isSet(args)) {
+ message = `<samp>${config.name}</samp> must be set if <samp>${args}</samp> is set`;
+ }
+ break;
+ case 'requiredUnless':
+ if (!isSet() && !isSet(args)) {
+ message = `<samp>${config.name}</samp> or <samp>${args}</samp> is required`;
+ }
+ break;
+ case 'requiredUnlessAnyOf':
+ if (!isSet() && !isAnySet(args)) {
+ message = `<samp>${config.name}</samp> or one of <samp>${args}</samp> is required`;
+ }
+ break;
+ case 'forbiddenUnlessAnyOf':
+ if (isSet() && !isAnySet(args)) {
+ message = `<samp>${config.name}</samp> cannot be set if any of <samp>${args}</samp> are set`;
+ }
+ break;
+ default:
+ $log.warn("Unknown constraint predicate", constraintO, config);
+ }
+ if (message !== null) {
+ entity.addIssue(Issue.builder().group('config').ref(config.name).message($sce.trustAsHtml(message)).build());
+ }
+ }
+ }
return $q((resolve) => {
if (entity.miscData.has('config')) {
entity.miscData.get('config')
.filter(config => config.constraints && config.constraints.length > 0)
- .forEach(config => {
- for (let constraintO of config.constraints) {
- let message = null;
- let key = null, args = null;
- if (constraintO instanceof String || typeof constraintO=='string') {
- key = constraintO;
- } else if (Object.keys(constraintO).length==1) {
- key = Object.keys(constraintO)[0];
- args = constraintO[key];
- } else {
- $log.warn("Unknown constraint object", typeof constraintO, constraintO, config);
- key = constraintO;
- }
- let val = (k) => entity.config.get(k || config.name);
- let isSet = (k) => entity.config.has(k || config.name) && angular.isDefined(val(k));
- let isAnySet = (k) => {
- if (!k || !Array.isArray(k)) return false;
- return k.some(isSet);
- }
- let hasDefault = () => angular.isDefined(config.defaultValue);
- switch (key) {
- case 'Predicates.notNull()':
- case 'Predicates.notNull':
- if (!isSet() && !hasDefault()) {
- message = `<samp>${config.name}</samp> is required`;
- }
- break;
- case 'required':
- if (!isSet() && !hasDefault() && val()!='') {
- message = `<samp>${config.name}</samp> is required`;
- }
- break;
- case 'regex':
- if (isSet() && !(new RegExp(args).test(val))) {
- message = `<samp>${config.name}</samp> does not match the required format: <samp>${args}</samp>`;
- }
- break;
- case 'forbiddenIf':
- if (isSet() && isSet(args)) {
- message = `<samp>${config.name}</samp> and <samp>${args}</samp> cannot both be set`;
- }
- break;
- case 'forbiddenUnless':
- if (isSet() && !isSet(args)) {
- message = `<samp>${config.name}</samp> can only be set when <samp>${args}</samp> is set`;
- }
- break;
- case 'requiredIf':
- if (!isSet() && isSet(args)) {
- message = `<samp>${config.name}</samp> must be set if <samp>${args}</samp> is set`;
- }
- break;
- case 'requiredUnless':
- if (!isSet() && !isSet(args)) {
- message = `<samp>${config.name}</samp> or <samp>${args}</samp> is required`;
- }
- break;
- case 'requiredUnlessAnyOf':
- if (!isSet() && !isAnySet(args)) {
- message = `<samp>${config.name}</samp> or one of <samp>${args}</samp> is required`;
- }
- break;
- case 'forbiddenUnlessAnyOf':
- if (isSet() && !isAnySet(args)) {
- message = `<samp>${config.name}</samp> cannot be set if any of <samp>${args}</samp> are set`;
- }
- break;
- default:
- $log.warn("Unknown constraint predicate", constraintO, config);
- }
- if (message !== null) {
- entity.addIssue(Issue.builder().group('config').ref(config.name).message($sce.trustAsHtml(message)).build());
- }
- }
- });
+ .forEach(checkConstraints);
}
+ // could do same as above to check parameters, but that doesn't make the parameters appear as config to set,
+ // so instead we merge parameters with config
resolve();
});
}
@@ -582,49 +584,51 @@ function BlueprintService($log, $q, $sce, paletteApi, iconGenerator, dslService)
return $q.all(promises);
}
- function addConfigKeyDefinition(config, key) {
- config.push({
- "name": key,
- "label": key,
- "description": "",
- "priority": 1,
- "pinned": true,
- "type": "java.lang.String",
- "constraints": [],
- });
+ function addConfigKeyDefinition(entity, key) {
+ // TODO return type, and below
+ entity.addConfigKeyDefinition(key, false);
}
- function addParameterDefinition(params, key) {
- params.push({
- "name": key,
- "type": "string",
- });
+ function addParameterDefinition(entity, key) {
+ entity.addParameterDefinition(key);
}
function addUnlistedConfigKeysDefinitions(entity) {
// copy config key definitions set on this entity into the miscData aggregated view
- let allConfig = entity.miscData.get('config') || [];
+ let allConfig = entity.miscDataOrDefault('configMap', {});
entity.config.forEach((value, key) => {
- if (!allConfig.some((e) => e.name === key)) {
- addConfigKeyDefinition(allConfig, key);
+ if (!allConfig[key]) {
+ entity.addConfigKeyDefinition(key);
}
});
- entity.miscData.set('config', allConfig);
+ entity.miscData.set('config', Object.values(allConfig));
+
}
function addUnlistedParameterDefinitions(entity) {
// copy parameter definitions set on this entity into the miscData aggregated view;
- // TODO see discussions in PR 112 about whether this is necessary and/or there is a better way
- let allParams = entity.miscData.get('parameters') || [];
+ // see discussions in PR 112 about whether this is necessary and/or there is a better way; but note, this is much updated since
+ let allParams = entity.miscDataOrDefault('parametersMap', {});
entity.parameters.forEach((param) => {
- if (!allParams.some((e) => e.name === param.name)) {
- allParams.push(param);
+ if (!allParams[param.name]) {
+ allParams[param.name] = param;
}
});
- entity.miscData.set('parameters', allParams);
+ entity.miscData.set('parameters', Object.values(allParams));
}
function populateEntityFromApiSuccess(entity, data) {
+ function mapped(list, field) {
+ let result = {};
+ if (list) {
+ list.forEach(l => {
+ if (l && l[field]) {
+ result[l[field]] = l;
+ }
+ });
+ }
+ return result;
+ }
entity.clearIssues({group: 'type'});
entity.type = data.symbolicName;
entity.icon = data.iconUrl || iconGenerator(data.symbolicName);
@@ -637,8 +641,12 @@ function BlueprintService($log, $q, $sce, paletteApi, iconGenerator, dslService)
entity.miscData.set('displayName', data.displayName);
entity.miscData.set('symbolicName', data.symbolicName);
entity.miscData.set('description', data.description);
+
entity.miscData.set('config', data.config || []);
+ entity.miscData.set('configMap', mapped(data.config, 'name'));
entity.miscData.set('parameters', data.parameters || []);
+ entity.miscData.set('parametersMap', mapped(data.parameters, 'name'));
+
entity.miscData.set('sensors', data.sensors || []);
entity.miscData.set('traits', data.supertypes || []);
entity.miscData.set('tags', data.tags || []);
diff --git a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
index d703f11..ffcaa60 100644
--- a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
+++ b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
@@ -33,7 +33,6 @@ export function computeQuickFixes(allIssues) {
allIssues.errors.byMessage = {};
Object.values(allIssues.errors.byEntity).forEach(list => {
list.forEach(issue => {
- // TODO key should be a tuple of group, ref, message
let key = issue.group+":"+issue.ref+":"+issue.message;
let v = allIssues.errors.byMessage[key];
if (!v) {
@@ -126,8 +125,9 @@ function proposeSetFrom() {
let createable = qfdef['source-key-createable'];
// TODO if root param is required, show error
- // TODO make default id containts type name
+ // TODO make default id contain type name
// TODO show default id if no id present
+
// TODO if id is changed, update all refs
// TODO allow graphically selectable
@@ -162,7 +162,7 @@ function proposeSetFrom() {
let pkey = 'set_from_' + sourceNode.id + '_' + ckey;
if (!proposals[pkey]) {
- if (sourceNode.create_key) {
+ if (create) {
proposals[pkey] = {
text: "Set from new parameter '" + ckey + "' on " + sourceNode.name,
tooltip: "This will fix the error by setting the value here equal to the value of a new parameter '" + ckey + "' created on " + sourceNode.name
@@ -180,13 +180,13 @@ function proposeSetFrom() {
Object.assign(proposals[pkey], {
issues: [],
apply: (issue, entity) => {
- if (sourceNode.create_key) {
+ if (create) {
// check again so we only create once
let hasParam = sourceNode.entity.getParameterNamed(ckey);
if (!hasParam) {
- sourceNode.entity.addParameter(Object.assign(
+ sourceNode.entity.addParameterDefinition(Object.assign(
{name: ckey,},
- qfdef['source-key-parameter-definition'] || {}
+ qfdef['source-key-parameter-definition'],
));
}
}
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 892167a..b1e556f 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
@@ -1052,8 +1052,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
specEditor.addConfigKey = addConfigKey;
function addConfigKey(name) {
if (name) {
- let allConfig = scope.model.miscData.get('config');
- blueprintService.addConfigKeyDefinition(allConfig, name);
+ blueprintService.addConfigKeyDefinition(scope.model, name);
scope.model.addConfig(name, '');
loadLocalConfigFromModel();
scope.state.config.add.value = '';
@@ -1204,14 +1203,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
}
function addParameter(name) {
- let allParams = scope.model.miscData.get('parameters');
- if (!allParams) {
- allParams = [];
- scope.model.miscData.set('parameters', allParams);
- }
- blueprintService.addParameterDefinition(allParams, name);
- let param = allParams.find(p => p.name === name);
- scope.model.addParameter(param);
+ scope.model.addParameterDefinition(name);
loadLocalParametersFromModel();
scope.state.parameters.search = '';
scope.state.parameters.add.value = '';
diff --git a/ui-modules/blueprint-composer/app/components/util/model/entity.model.js b/ui-modules/blueprint-composer/app/components/util/model/entity.model.js
index b844450..1eb2343 100644
--- a/ui-modules/blueprint-composer/app/components/util/model/entity.model.js
+++ b/ui-modules/blueprint-composer/app/components/util/model/entity.model.js
@@ -18,6 +18,7 @@
*/
import {Issue} from './issue.model';
import {Dsl, DslParser} from './dsl.model';
+import {blueprintServiceProvider} from "../../providers/blueprint-service.provider";
const MEMBERSPEC_REGEX = /^(\w+\.)*[mM]ember[sS]pec$/;
const FIRST_MEMBERSPEC_REGEX = /^(\w+\.)*first[mM]ember[sS]pec$/;
@@ -550,6 +551,17 @@ export class Entity {
return MISC_DATA.get(this);
}
+ miscDataOrDefault(key, defaultValue) {
+ let miscData = this.miscData;
+ let result = miscData.get(key);
+ if (result) {
+ return result;
+ } else {
+ miscData.set(key, defaultValue);
+ return defaultValue;
+ }
+ }
+
equals(value) {
if (value && value instanceof Entity) {
try {
@@ -593,10 +605,12 @@ Entity.prototype.setPoliciesFromJson = setPoliciesFromJson;
Entity.prototype.getData = getData;
Entity.prototype.addConfig = addConfig;
-Entity.prototype.addParameter = addParameter;
+Entity.prototype.addConfigKeyDefinition = addConfigKeyDefinition;
+Entity.prototype.addParameterDefinition = addParameterDefinition;
Entity.prototype.addMetadata = addMetadata;
Entity.prototype.removeConfig = removeConfig;
Entity.prototype.removeParameter = removeParameter;
+Entity.prototype.updateParameter = updateParameter;
Entity.prototype.removeMetadata = removeMetadata;
Entity.prototype.isCluster = isCluster;
Entity.prototype.isMemberSpec = isMemberSpec;
@@ -639,8 +653,47 @@ function addConfig(key, value) {
}
}
-function addParameter(param) {
- PARAMETERS.get(this).push(param);
+function addConfigKeyDefinition(param, overwrite) {
+ if (typeof param === 'string') {
+ param = {
+ "name": param,
+ "label": param,
+ "description": "",
+ "priority": 1,
+ "pinned": true,
+ "type": "java.lang.String",
+ "constraints": [],
+ };
+ overwrite = false;
+ }
+ let key = (param || {}).name;
+ if (!key) throw new Error("'name' field must be included when adding parameter; was", param);
+
+ let allConfig = this.miscDataOrDefault('configMap', {});
+ allConfig[key] = Object.assign(allConfig[key] || {}, param, overwrite ? null : allConfig[key]);
+ this.miscData.set('config', Object.values(allConfig));
+
+ this.touch();
+ return this;
+}
+
+function addParameterDefinition(param, overwrite) {
+ if (typeof param === 'string') {
+ param = {name: key, type: 'string'};
+ overwrite = false;
+ }
+ let key = (param || {}).name;
+ if (!key) throw new Error("'name' field must be included when adding parameter");
+
+ let allParams = this.miscDataOrDefault('parametersMap', {});
+ allParams[key] = Object.assign(allParams[key] || {}, param, overwrite ? null : allParams[key]);
+ this.miscData.set('parameters', Object.values(allParams));
+
+ let eps = PARAMETERS.get(this);
+ this.updateParameter(key, allParams[key], true);
+
+ this.addConfigKeyDefinition(allParams[key], overwrite);
+
this.touch();
return this;
}
@@ -685,17 +738,22 @@ function removeParameter(name) {
/**
- * Update an entry in brooklyn.parameters
+ * Update an entry in brooklyn.parameters if it exists
* @param {string} name
* @param {object} definition
* @returns {Entity}
*/
-function updateParameter(name, definition) {
- if (this.hasParameters()) {
+function updateParameter(name, definition, createIfMissing) {
+ if (createIfMissing || this.hasParameters()) {
let paramIndex = PARAMETERS.get(this).findIndex(e => e.name === name);
if (paramIndex != -1) {
PARAMETERS.get(this)[paramIndex] = definition;
this.touch();
+ } else {
+ if (createIfMissing) {
+ PARAMETERS.get(this).push(definition);
+ this.touch();
+ }
}
}
return this;
@@ -1065,7 +1123,7 @@ function setParametersFromJson(incomingModel) {
let self = this;
incomingModel.map((param)=> {
- self.addParameter(param);
+ self.addParameterDefinition(param);
});
this.touch();
}
@@ -1133,7 +1191,6 @@ function getParametersAsArray() {
}
function getParameterNamed(name) {
- // TODO confirm works
return PARAMETERS.get(this).find(p => p.name === name);
}
diff --git a/ui-modules/blueprint-composer/app/views/main/yaml/yaml.state.js b/ui-modules/blueprint-composer/app/views/main/yaml/yaml.state.js
index e4e3606..a3d6616 100644
--- a/ui-modules/blueprint-composer/app/views/main/yaml/yaml.state.js
+++ b/ui-modules/blueprint-composer/app/views/main/yaml/yaml.state.js
@@ -95,6 +95,9 @@ function yamlCampStateController($scope, blueprintService, brSnackbar) {
// blueprintService.refreshBlueprintMetadata();
} catch (err) {
+
+ // YAML exceptions detected separately so ignore those
+
if (!(err instanceof YAMLException)) {
issues.push({
from: CodeMirror.Pos(0, 0),
@@ -103,6 +106,10 @@ function yamlCampStateController($scope, blueprintService, brSnackbar) {
});
}
}
+ if (issues.length) {
+ // these should be displayed, but sometimes aren't.
+ console.log("Issues detected in blueprint", issues);
+ }
return issues;
});
[brooklyn-ui] 09/15: review with thomas - skip the intermediate
small "x errors in x entities" step
Posted by he...@apache.org.
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 074923807d5cbdef4717dfca574f77b96265c2aa
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri Apr 23 18:01:43 2021 +0100
review with thomas - skip the intermediate small "x errors in x entities" step
when expanding quick fixes from the graphical editor
---
.../blueprint-composer/app/views/main/graphical/graphical.state.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.html b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.html
index 986279b..b91e673 100644
--- a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.html
+++ b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.html
@@ -34,7 +34,7 @@
title="Click to show/hide details of errors"
ng-class="{ errorsActive: errorsPane.level }"
ng-show="allIssues.errors.count"
- ng-click="errorsPane.level = errorsPane.level ? null : 'summary'">
+ ng-click="errorsPane.level = errorsPane.level ? null : 'error-errors'">
<i class="fa fa-fw fa-times-circle"></i> {{ allIssues.errors.count }}
</a>
<a class="hand list-group-item warning-square"
[brooklyn-ui] 10/15: set_from_parameter minimally working!!!
Posted by he...@apache.org.
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 b7c38dd65286a638b4c0d78b9cdd65216a1a1349
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri Apr 23 18:54:39 2021 +0100
set_from_parameter minimally working!!!
---
.../app/components/quick-fix/quick-fix.js | 57 ++++++++++++++++++++--
.../spec-editor/spec-editor.directive.js | 2 +-
.../app/components/spec-editor/spec-editor.less | 2 +
.../app/components/util/model/entity.model.js | 11 +++++
4 files changed, 66 insertions(+), 6 deletions(-)
diff --git a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
index a542742..049a713 100644
--- a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
+++ b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
@@ -65,7 +65,7 @@ export function computeQuickFixesForIssue(issue, entity, proposalHolder) {
if (!qfi) {
console.log("Skipping unknown quick fix", qf);
} else {
- qfi.propose(issue, proposalHolder);
+ qfi.propose(qf, issue, entity, proposalHolder);
// we could offer the fix per-issue, but no need as they can get that by navigating to the entity
//qfi.propose(issue, issueO.quickFixes); // issueO from previous method
}
@@ -75,10 +75,10 @@ export function computeQuickFixesForIssue(issue, entity, proposalHolder) {
const QUICK_FIX_PROPOSERS = {
clear_config: {
// the propose function updates the proposals object
- propose: (issue, proposals) => {
+ propose: (qfdef, issue, entity, proposals) => {
if (!issue.ref) return;
-
if (!proposals) proposals = {};
+
if (!proposals.clear_config) {
proposals.clear_config = {
text: "Remove value",
@@ -91,7 +91,8 @@ const QUICK_FIX_PROPOSERS = {
},
},
set_from_parameter: {
- propose: (issue, proposals) => {}
+ propose: proposeSetFrom('parameter')
+
// - key: post_code
// fix: set_from_parameter
// message-regex: required
@@ -104,7 +105,8 @@ const QUICK_FIX_PROPOSERS = {
},
set_from_config_key: {
- propose: (issue, proposals) => {}
+ propose: proposeSetFrom('config')
+
// - key: post_code
// fix: set_from_config_key
// message-regex: required
@@ -116,6 +118,51 @@ const QUICK_FIX_PROPOSERS = {
}
};
+function proposeSetFrom(sourceType) {
+ return function (qfdef, issue, entity, proposals) {
+ if (!issue.ref) return;
+ if (!proposals) proposals = {};
+
+ let sourceNode = {
+ id: 'root',
+ name: 'the application root node',
+ entity: entity.getApplication(),
+ };
+
+ if (sourceType === 'parameter') {
+ let params = sourceNode.entity.getParametersAsArray();
+ console.log("params", params);
+ }
+
+ let key = 'set_from_'+sourceType+'_'+sourceNode.id;
+ if (!proposals[key]) {
+ proposals[key] = {
+ text: "Set from a new "+sourceType+" on "+sourceNode.name,
+ tooltip: "This will create a "+sourceType+" on "+sourceNode.name+" and fix this error by setting it equal to that "+sourceType,
+ issues: [],
+ apply: (issue, entity) => {
+ entity = (entity || issue.entity);
+ console.log("TODO do this; NB issue invoked wrt", entity, qfdef);
+ if (sourceType === 'parameter') {
+ if (!sourceNode.entity.getParameterNamed(qfdef['source-parameter'])) {
+ sourceNode.entity.addParameter({
+ name: qfdef['source-parameter'],
+ constraints: qfdef['source-constraints']
+ });
+ }
+ if (!sourceNode.entity.id) {
+ // TODO
+ sourceNode.entity.id = 'id_XXX';
+ }
+ entity.addConfig(issue.ref, '$brooklyn:component("'+sourceNode.entity.id+'").config("'+qfdef['source-parameter']+'")');
+ }
+ }
+ };
+ }
+ proposals[key].issues.push(issue);
+ };
+}
+
export function getQuickFixProposer(type) {
return QUICK_FIX_PROPOSERS[type];
}
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 43ac178..892167a 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
@@ -558,7 +558,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
return item.widgetMode;
};
specEditor.getParameterWidgetMode = (item) => {
- let type = item.type || item.typeName;
+ let type = item.type || item.typeName || 'string';
if (type === 'java.lang.Boolean') type = 'boolean';
else if (type === 'java.util.Map') type = 'map';
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 4fcefca..0396469 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
@@ -474,6 +474,8 @@ spec-editor {
.btn {
float: right;
color: @gray-lighter !important;
+ margin-left: 1em;
+ margin-bottom: 2px;
}
}
.param-fields {
diff --git a/ui-modules/blueprint-composer/app/components/util/model/entity.model.js b/ui-modules/blueprint-composer/app/components/util/model/entity.model.js
index 782da39..f8afc28 100644
--- a/ui-modules/blueprint-composer/app/components/util/model/entity.model.js
+++ b/ui-modules/blueprint-composer/app/components/util/model/entity.model.js
@@ -463,6 +463,11 @@ export class Entity {
}
}
+ getApplication() {
+ if (this.hasParent()) return this.parent.getApplication();
+ return this;
+ }
+
/**
* Has {Entity} got a parent
* @returns {boolean}
@@ -567,6 +572,7 @@ Entity.prototype.getConfigAsJson = getConfigAsJson;
Entity.prototype.setConfigFromJson = setConfigFromJson;
Entity.prototype.getParametersAsArray = getParametersAsArray;
+Entity.prototype.getParameterNamed = getParameterNamed;
Entity.prototype.setParametersFromJson = setParametersFromJson;
Entity.prototype.getMetadataAsJson = getMetadataAsJson;
@@ -1116,6 +1122,11 @@ function getParametersAsArray() {
return PARAMETERS.get(this);
}
+function getParameterNamed(name) {
+ // TODO confirm works
+ return PARAMETERS.get(this).find(p => p.name === name);
+}
+
/* "cleaning" here means: Dsl objects are toStringed, to the given depth (or infinite if depth<0);
* and entries in Map that are memberspec are unwrapped.
* previously we also stringified maps/lists but that seemed pointless, and it was lossy and buggy.
[brooklyn-ui] 14/15: also check supertypes for inclusion
Posted by he...@apache.org.
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 a8fc797a96374d8e4d95fae7581e0c431099c8cb
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Tue Apr 27 13:47:16 2021 +0100
also check supertypes for inclusion
---
.../blueprint-composer/app/components/quick-fix/quick-fix.js | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
index 5204052..16cbac1 100644
--- a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
+++ b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
@@ -132,10 +132,14 @@ function proposeSetFrom() {
let considerNode = (sourceNode) => {
if (qfdef['source-types']) {
- // TODO would be nice to store super-types of entity in miscData and filter based on those
if (!qfdef['source-types'].includes(sourceNode.entity.type)) {
- // wrong type
- return;
+ // wrong type; check traits (supertypes)
+ if ((sourceNode.entity.miscData.get("traits") || []).find(t => qfdef['source-types'].includes(t))) {
+ // there was a super-type which matched; we're okay
+ } else {
+ // also no supertype matches the specified source-types, so don't make a proposal for this node
+ return;
+ }
}
}
[brooklyn-ui] 02/15: add a new bit in the composer to show errors
and warnings
Posted by he...@apache.org.
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 b51d3dc1312754dd4b1e61178684f78a7ef0a492
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Wed Apr 21 09:30:09 2021 +0100
add a new bit in the composer to show errors and warnings
can navigate quickly to entities with problems;
and have a place we can logically add "quick fixes"
---
.../catalog-selector/catalog-selector.less | 2 +-
.../providers/blueprint-service.provider.js | 57 +++++++-
.../app/views/main/graphical/graphical.state.html | 144 ++++++++++++++++++++-
.../app/views/main/graphical/graphical.state.js | 19 +++
.../app/views/main/graphical/graphical.state.less | 119 ++++++++++++++---
.../blueprint-composer/app/views/main/main.less | 6 +-
6 files changed, 321 insertions(+), 26 deletions(-)
diff --git a/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.less b/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.less
index 1a2ac11..a1e5a61 100644
--- a/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.less
+++ b/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.less
@@ -22,7 +22,7 @@ catalog-selector {
display: block;
margin-top: 15px;
- height: 100%;
+ //height: 100%; //makes it too big
.grid {
margin-bottom: 15px;
diff --git a/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js b/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
index 19794ab..7f78da1 100644
--- a/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
+++ b/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
@@ -65,6 +65,7 @@ function BlueprintService($log, $q, $sce, paletteApi, iconGenerator, dslService)
isReservedKey: isReservedKey,
getIssues: getIssues,
hasIssues: hasIssues,
+ getAllIssues: getAllIssues,
populateEntityFromApi: populateEntityFromApiSuccess,
populateLocationFromApi: populateLocationFromApiSuccess,
addConfigKeyDefinition: addConfigKeyDefinition,
@@ -148,32 +149,74 @@ function BlueprintService($log, $q, $sce, paletteApi, iconGenerator, dslService)
return RESERVED_KEYS.indexOf(key) > -1;
}
- function getIssues(entity = blueprint) {
+ function getIssues(entity = blueprint, nonRecursive) {
let issues = [];
if (entity.hasIssues()) {
- issues = issues.concat(entity.issues.map((issue)=> {
+ issues = issues.concat(entity.issues.map((issue) => {
let newIssue = Issue.builder().message(issue.message).group(issue.group).ref(issue.ref).level(issue.level).build();
newIssue.entity = entity;
return newIssue;
}));
}
- entity.policies.forEach((policy)=> {
+ entity.policies.forEach((policy) => {
issues = issues.concat(getIssues(policy));
});
- entity.enrichers.forEach((enricher)=> {
+ entity.enrichers.forEach((enricher) => {
issues = issues.concat(getIssues(enricher));
});
- entity.children.forEach((child)=> {
- issues = issues.concat(getIssues(child));
- });
+ if (!nonRecursive) {
+ entity.children.forEach((child) => {
+ issues = issues.concat(getIssues(child));
+ });
+ }
return issues;
}
+ function getAllIssues(entity = blueprint) {
+ return collectAllIssues({}, entity);
+ }
+
+ function collectAllIssues(result, entity) {
+ if (!result.entities) {
+ result.entities = {};
+ result.byEntity = {};
+ result.count = 0;
+ result.errors = { byEntity: {}, count: 0 }
+ result.warnings = { byEntity: {}, count: 0 }
+ }
+ if (result.entities[entity._id]) {
+ // already visited, some sort of reference; ignore
+ } else {
+ result.entities[entity._id] = entity;
+
+ let issues = getIssues(entity, true);
+ if (issues.length) {
+ result.byEntity[entity._id] = issues;
+ result.count += issues.length;
+
+ let errors = issues.filter(i => i.level.id == 'error');
+ if (errors.length) {
+ result.errors.byEntity[entity._id] = errors;
+ result.errors.count += errors.length;
+ }
+
+ let warnings = issues.filter(i => i.level.id != 'error');
+ if (warnings.length) {
+ result.warnings.byEntity[entity._id] = warnings;
+ result.warnings.count += warnings.length;
+ }
+ }
+
+ entity.children.forEach((child)=> collectAllIssues(result, child));
+ }
+ return result;
+ }
+
function hasIssues() {
return this.getIssues().length > 0;
}
diff --git a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.html b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.html
index 711d4c1..b6cf40e 100644
--- a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.html
+++ b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.html
@@ -22,15 +22,157 @@
<div class="layout">
<div class="toolbar">
<div class="list-group">
- <a href class="list-group-item"
+ <a class="hand list-group-item"
ng-repeat="section in vm.sections track by $index"
ng-class="{'active': vm.selectedSection === section}"
ng-click="vm.selectedSection = section">
<i class="fa fa-fw" ng-class="section.icon"></i>
</a>
+
+ <div class="spacer"></div>
+ <a class="hand list-group-item errors-square"
+ title="Click to show/hide details of errors"
+ ng-class="{ errorsActive: errorsPane.level }"
+ ng-show="allIssues.errors.count"
+ ng-click="errorsPane.level = errorsPane.level ? null : 'summary'">
+ <i class="fa fa-fw fa-times-circle"></i> {{ allIssues.errors.count }}
+ </a>
+ <a class="hand list-group-item warning-square"
+ title="Click to show/hide details of warnings"
+ ng-class="{ errorsActive: errorsPane.level }"
+ ng-show="!allIssues.errors.count && allIssues.warnings.count"
+ ng-click="errorsPane.level = errorsPane.level ? null : 'summary'">
+ <i class="fa fa-fw fa-exclamation-triangle"></i> {{ allIssues.warnings.count }}
+ </a>
</div>
</div>
+ <div ng-if="errorsPane.level == 'summary'" class="errors-popout">
+ <div class="errors-body">
+
+ <a ng-click="errorsPane.level = null" class="hand errors-close"><i class="fa fa-fw fa-times"></i></a>
+
+ <div ng-if="allIssues.errors.count">
+ <a class="hand" ng-click="errorsPane.level = 'error-errors'">
+ {{ allIssues.errors.count }}
+ <span ng-if="allIssues.errors.count == 1">error</span>
+ <span ng-if="allIssues.errors.count != 1">errors</span>
+ </a>
+ in
+ <a class="hand" ng-click="errorsPane.level = 'error-entities'">
+ {{ vm.size(allIssues.errors.byEntity) }}
+ <span ng-if="vm.size(allIssues.errors.byEntity) == 1">entity</span>
+ <span ng-if="vm.size(allIssues.errors.byEntity) != 1">entities</span>
+ </a>
+ </div>
+
+ <!-- TODO add links and panels for warnings -->
+ <div ng-if="allIssues.warnings.count">
+ {{ allIssues.warnings.count }}
+ <span ng-if="allIssues.errors.count == 1">warning</span>
+ <span ng-if="allIssues.errors.count != 1">warnings</span>
+ in {{ vm.size(allIssues.warnings.byEntity) }}
+ <span ng-if="vm.size(allIssues.warnings.byEntity) == 1">entity</span>
+ <span ng-if="vm.size(allIssues.warnings.byEntity) != 1">entities</span>
+ </div>
+
+ <div ng-if="!allIssues.errors.count && !allIssues.warnings.count">
+ No errors or warnings.
+ </div>
+
+ </div>
+ </div>
+
+ <div ng-if="errorsPane.level == 'error-entities'" class="errors-popout">
+ <div class="errorsHeader">
+ <a ng-click="errorsPane.level = null" class="hand errors-close"><i class="fa fa-fw fa-times"></i></a>
+
+ {{ vm.size(allIssues.errors.byEntity) }}
+ <span ng-if="vm.size(allIssues.errors.byEntity) == 1">entity</span>
+ <span ng-if="vm.size(allIssues.errors.byEntity) != 1">entities</span>
+
+ with
+
+ <a class="hand" ng-click="errorsPane.level = 'error-errors'">
+ {{ allIssues.errors.count }}
+ <span ng-if="allIssues.errors.count == 1">error</span>
+ <span ng-if="allIssues.errors.count != 1">errors</span>
+ </a>
+ </div>
+
+ <div class="errors-body">
+ <div ng-repeat="(itemK,itemV) in allIssues.errors.byEntity" class="error-line">
+ <div class="error-line-marker">
+ <i class="fa fa-fw fa-times-circle"></i>
+ </div>
+ <div class="error-line-text">
+ {{ allIssues.entities[itemK].id ? allIssues.entities[itemK].id + ' ('+allIssues.entities[itemK].type+')'
+ : allIssues.entities[itemK].type }}:
+ {{ itemV.length }} {{ itemV.length==1 ? 'error' : 'errors' }}
+ </div>
+ <div class="error-line-action">
+ <a class="hand" ui-sref="main.graphical.edit.entity({entityId: itemK})">
+ <i class="fa fa-fw fa-external-link"></i>
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div ng-if="errorsPane.level == 'error-errors'" class="errors-popout">
+ <div class="errorsHeader">
+ <a ng-click="errorsPane.level = null" class="hand errors-close"><i class="fa fa-fw fa-times"></i></a>
+
+ {{ allIssues.errors.count }}
+ <span ng-if="allIssues.errors.count == 1">error</span>
+ <span ng-if="allIssues.errors.count != 1">errors</span>
+ <span ng-show="allIssues.errors.count != vm.size(allIssues.errors.byMessage)"> ({{ vm.size(allIssues.errors.byMessage) }} unique)</span>
+
+ in
+
+ <a class="hand" ng-click="errorsPane.level = 'error-entities'">
+ {{ vm.size(allIssues.errors.byEntity) }}
+ <span ng-if="vm.size(allIssues.errors.byEntity) == 1">entity</span>
+ <span ng-if="vm.size(allIssues.errors.byEntity) != 1">entities</span>
+ </a>
+ </div>
+
+ <div class="errors-body">
+ <div ng-repeat="(itemK,itemV) in allIssues.errors.byMessage" class="error-line">
+ <div class="error-line-marker">
+ <i class="fa fa-fw fa-times-circle"></i>
+ </div>
+ <div class="error-line-text">
+ {{ itemK }}:
+ <a class="hand" ng-click="errorsPane.focus = (errorsPane.focus == itemK ? null : itemK)">{{ itemV.length }} {{ itemV.length==1 ? 'error' : 'errors' }}</a>
+ <div class="error-line-sub" ng-if="errorsPane.focus == itemK">
+ <div ng-repeat="issue in itemV" class="error-line-sub-line">
+ <div class="error-line-marker">
+ <i class="fa fa-fw fa-circle"></i>
+ </div>
+ <div class="error-line-text">
+ {{ issue.entity.id ? issue.entity.id + ' ('+issue.entity.type+')'
+ : issue.entity.type }}
+ </div>
+ <div class="error-line-action">
+ <a class="hand" ui-sref="main.graphical.edit.entity({entityId: issue.entity._id})">
+ <i class="fa fa-fw fa-external-link"></i>
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ <!--
+ <div class="error-line-action">
+ <a class="hand" ui-sref="main.graphical.edit.entity({entityId: itemK})">
+ <i class="fa fa-fw fa-external-link"></i>
+ </a>
+ </div>
+ -->
+ </div>
+ </div>
+ </div>
+
<designer on-selection-change="vm.onCanvasSelection"></designer>
</div>
diff --git a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.js b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.js
index 0e7e0b2..143ebb2 100644
--- a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.js
+++ b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.js
@@ -41,10 +41,29 @@ function graphicalController($scope, $state, $filter, blueprintService, paletteS
this.sections = paletteService.getSections();
this.selectedSection = Object.values(this.sections).find(section => section.type === EntityFamily.ENTITY);
$scope.paletteState = {}; // share state among all sections
+ $scope.errorsPane = { level: null };
+
+ $scope.blueprint = blueprintService.get();
+ $scope.$watch('blueprint', ()=> {
+ $scope.allIssues = blueprintService.getAllIssues();
+ $scope.allIssues.errors.byMessage = {};
+ Object.values($scope.allIssues.errors.byEntity).forEach(list => {
+ list.forEach(issue => {
+ $scope.allIssues.errors.byMessage[issue.group+":"+issue.ref] = $scope.allIssues.errors.byMessage[issue.group+" "+issue.ref] || [];
+ $scope.allIssues.errors.byMessage[issue.group+":"+issue.ref].push(issue);
+ });
+ });
+ //console.log("blueprint update, get all issues", $scope.allIssues);
+ }, true);
this.onCanvasSelection = (item) => {
$scope.canvasSelectedItem = item;
}
+ this.size = (obj) => {
+ if (!obj) return 0;
+ return Object.keys(obj).length;
+ }
+
this.getOnSelectText = (selectableType) => $scope.canvasSelectedItem ? "Add to " + $filter('entityName')($scope.canvasSelectedItem) : "Add to application";
this.addSelectedTypeToTargetEntity = (selectedType, targetEntity) => {
diff --git a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.less b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.less
index 915d352..eeada0b 100644
--- a/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.less
+++ b/ui-modules/blueprint-composer/app/views/main/graphical/graphical.state.less
@@ -28,6 +28,7 @@
.toolbar {
position: relative;
height: calc(~"100vh - 105px");
+ width: 48px; // palette pane has left: 49px
background-color: #fff;
overflow-y: scroll;
z-index: 40;
@@ -43,27 +44,113 @@
background-color: @navbar-default-border;
}
- .list-group-item {
- @active-border-width: 4px;
- padding-top: 10px + @active-border-width;
- padding-bottom: 9px + @active-border-width;
- border-left: none;
- border-radius: 0;
+ .list-group {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ margin-bottom: 0;
+
+ .list-group-item {
+ @active-border-width: 4px;
+ padding-top: 10px + @active-border-width;
+ padding-bottom: 9px + @active-border-width;
+ border-left: none;
+ border-radius: 0;
+
+ &:first-child {
+ border-top: none;
+ }
+ &.active {
+ border-right: none;
+ border-color: @list-group-border;
+ border-bottom: @active-border-width solid @brand-primary;
+ background-color: #fff;
+ color: @list-group-link-color;
+ padding-bottom: 10px;
+ }
+ }
- &:first-child {
- border-top: none;
+
+ .spacer {
+ flex: 1 1 auto;
+ }
+
+ .errors-square, .warning-square {
+ padding-left: 0;
+ padding-right: 0;
+ text-align: center;
+ &.errorsActive {
+ background: @gray-lighter;
+ }
}
- &.active {
- border-right: none;
- border-color: @list-group-border;
- border-bottom: @active-border-width solid @brand-primary;
- background-color: #fff;
- color: @list-group-link-color;
- padding-bottom: 10px;
+ .errors-square {
+ color: @brand-danger;
+ }
+ .warning-square {
+ color: @brand-warning;
+ }
+ }
+ }
+ .errors-popout {
+ position: absolute;
+ left: 49px;
+ width: 440px;
+ bottom: 0;
+ z-index: 60;
+ border-top: 1px solid @gray-lighter;
+ background: @gray-lightest;
+
+ .errors-close {
+ float: right;
+ }
+
+ .errors-body {
+ padding: 8px;
+ }
+
+ .errorsHeader {
+ width: 100%;
+ background: @gray-lighter;
+ border-top: 1px solid @gray-light;
+ border-bottom: 1px solid @gray-light;
+ padding: 8px;
+ margin-bottom: 8px;
+ }
+ max-height: 600px;
+ overflow-y: scroll;
+ }
+ .error-line {
+ display: flex;
+ justify-self: stretch;
+ padding-bottom: 8px;
+ .error-line-marker, .error-line-action {
+ flex: 0 0 auto;
+ margin-top: 4px;
+ }
+ .error-line-text {
+ flex: 1 1 auto;
+ }
+ .error-line-marker {
+ color: @brand-danger;
+ padding-right: 0.5ex;
+ }
+ .error-line-action {
+ padding-left: 0.5ex;
+ }
+ .error-line-sub-line {
+ display: flex;
+ justify-self: stretch;
+ margin-bottom: 4px;
+ margin-top: 4px;
+ .error-line-marker {
+ color: inherit;
+ font-size: 50%;
+ padding-right: 1em;
+ margin-top: 6px;
}
}
}
-
+
#palette-controls-button {
margin-right: -2px; // not sure why the default -1px doesn't work to merge the borders?
}
diff --git a/ui-modules/blueprint-composer/app/views/main/main.less b/ui-modules/blueprint-composer/app/views/main/main.less
index 06e390b..8e38515 100644
--- a/ui-modules/blueprint-composer/app/views/main/main.less
+++ b/ui-modules/blueprint-composer/app/views/main/main.less
@@ -111,4 +111,8 @@
.flex {
display: flex;
-}
\ No newline at end of file
+}
+
+.hand {
+ cursor: pointer;
+}
[brooklyn-ui] 15/15: This closes #200
Posted by he...@apache.org.
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 33b5d091356c3af0651e635d98b7db52f89d14d6
Merge: 73e2334 a8fc797
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Tue Apr 27 16:12:28 2021 +0100
This closes #200
docs/basic-with-constraint.bom | 56 ++++
.../catalog-selector/catalog-selector.less | 2 +-
.../catalog-selector.template.html | 4 +
.../providers/blueprint-service.provider.js | 297 ++++++++++++---------
.../app/components/quick-fix/quick-fix.js | 240 +++++++++++++++++
.../spec-editor/spec-editor.directive.js | 37 ++-
.../app/components/spec-editor/spec-editor.less | 11 +
.../spec-editor/spec-editor.template.html | 9 +-
.../app/components/util/model/dsl.model.js | 23 +-
.../app/components/util/model/entity.model.js | 101 ++++++-
.../app/components/util/model/issue.model.js | 14 +
ui-modules/blueprint-composer/app/index.js | 3 +-
.../app/views/main/graphical/graphical.state.html | 180 ++++++++++++-
.../app/views/main/graphical/graphical.state.js | 22 ++
.../app/views/main/graphical/graphical.state.less | 137 ++++++++--
.../blueprint-composer/app/views/main/main.less | 6 +-
.../app/views/main/yaml/yaml.state.js | 7 +
17 files changed, 981 insertions(+), 168 deletions(-)
[brooklyn-ui] 03/15: add example for constraint
Posted by he...@apache.org.
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 5a6b895661b5188a0bce3fce6fc2ed17ae66fed1
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Wed Apr 21 15:13:25 2021 +0100
add example for constraint
---
docs/basic-with-constraint.bom | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/docs/basic-with-constraint.bom b/docs/basic-with-constraint.bom
new file mode 100644
index 0000000..08ed2c6
--- /dev/null
+++ b/docs/basic-with-constraint.bom
@@ -0,0 +1,30 @@
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+brooklyn.catalog:
+ id: basic-with-constraint
+ version: 1.0.0-SNAPSHOT
+ itemType: entity
+ item:
+ type: org.apache.brooklyn.entity.stock.BasicEntity
+ brooklyn.parameters:
+ - name: post_code
+ - name: zip_code
+ constraints:
+ - forbiddenIf: post_code
+
[brooklyn-ui] 07/15: show quick fix on issues in spec editor
Posted by he...@apache.org.
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 c41915466315d66e08be011b25561602af81f1c1
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Thu Apr 22 23:06:01 2021 +0100
show quick fix on issues in spec editor
also fix another dsl error logged as debug
---
.../app/components/quick-fix/quick-fix.js | 32 ++++++++++++----------
.../spec-editor/spec-editor.directive.js | 21 ++++++++++++++
.../app/components/spec-editor/spec-editor.less | 9 ++++++
.../spec-editor/spec-editor.template.html | 9 +++++-
.../app/components/util/model/dsl.model.js | 10 ++++++-
5 files changed, 65 insertions(+), 16 deletions(-)
diff --git a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
index 36e1ce0..bd96735 100644
--- a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
+++ b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
@@ -52,22 +52,26 @@ export function computeQuickFixes(allIssues) {
}
v.issues.push(issueO);
- let qfs = getQuickFixHintsForIssue(issue);
- (qfs || []).forEach(qf => {
- let qfi = getQuickFixProposer(qf['fix']);
- if (!qfi) {
- console.log("Skipping unknown quick fix", qf);
- } else {
- qfi.propose(issue, v.quickFixes);
- // we could offer the fix per-issue, but no need as they can get that by navigating to the entity
- //qfi.propose(issue, issueO.quickFixes);
- }
- });
+ computeQuickFixesForIssue(issue, issue.entity, v.quickFixes)
});
});
return allIssues;
}
+export function computeQuickFixesForIssue(issue, entity, proposalHolder) {
+ let qfs = getQuickFixHintsForIssue(issue, entity);
+ (qfs || []).forEach(qf => {
+ let qfi = getQuickFixProposer(qf['fix']);
+ if (!qfi) {
+ console.log("Skipping unknown quick fix", qf);
+ } else {
+ qfi.propose(issue, proposalHolder);
+ // we could offer the fix per-issue, but no need as they can get that by navigating to the entity
+ //qfi.propose(issue, issueO.quickFixes); // issueO from previous method
+ }
+ });
+}
+
const QUICK_FIX_PROPOSERS = {
clear_config: {
// the propose function updates the proposals object
@@ -78,7 +82,7 @@ const QUICK_FIX_PROPOSERS = {
if (!proposals.clear_config) {
proposals.clear_config = {
text: "Remove the current value (clear config \""+issue.ref+"\")",
- apply: (issue) => issue.entity.removeConfig(issue.ref),
+ apply: (issue, entity) => (entity || issue.entity).removeConfig(issue.ref),
issues: [],
};
}
@@ -91,9 +95,9 @@ export function getQuickFixProposer(type) {
return QUICK_FIX_PROPOSERS[type];
}
-export function getQuickFixHintsForIssue(issue) {
+export function getQuickFixHintsForIssue(issue, entity) {
if (issue.group === 'config') {
- let hints = (issue.entity.miscData.get('ui-composer-hints') || {})['config-quick-fixes'] || [];
+ let hints = (entity.miscData.get('ui-composer-hints') || {})['config-quick-fixes'] || [];
hints = hints.filter(h => h.key === issue.ref);
if (!hints.length) return null;
hints = hints.filter(h => !h['message-regex'] || new RegExp(h['message-regex']).test(issue.message));
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 9da1581..43ac178 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
@@ -29,6 +29,7 @@ import brooklynDslViewer from '../dsl-viewer/dsl-viewer';
import template from './spec-editor.template.html';
import {graphicalState} from '../../views/main/graphical/graphical.state';
import {SENSITIVE_FIELD_REGEX} from 'brooklyn-ui-utils/sensitive-field/sensitive-field';
+import {computeQuickFixesForIssue} from '../quick-fix/quick-fix';
const MODULE_NAME = 'brooklyn.components.spec-editor';
const ANY_MEMBERSPEC_REGEX = /(^.*[m,M]ember[s,S]pec$)/;
@@ -246,9 +247,29 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
return blueprintService.refreshConfigConstraints(scope.model);
}).then(() => {
refreshCustomValidation(scope.model);
+ }).then(() => {
+ scope.model.issuesWithFixes = [];
+ scope.model.issues.forEach(issue => {
+ // really messy copying the issue to add the fixes, but the easiest way
+ let issueCopy = {
+ ref: issue.ref,
+ group: issue.group,
+ message: issue.message,
+ };
+ let issueWithFixes = Object.assign({}, issueCopy, {
+ quickFixes: {},
+ });
+ computeQuickFixesForIssue(issueCopy, scope.model, issueWithFixes.quickFixes);
+ scope.model.issuesWithFixes.push(issueWithFixes);
+ });
});
+
}, true);
+ scope.applyQuickFix = (issue, fix) => {
+ fix.apply(issue, scope.model);
+ }
+
scope.getObjectSize = (object) => {
return specEditor.defined(object) && object !== null ? Object.keys(object).length : 0;
};
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 68fd6e3..4fcefca 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
@@ -459,6 +459,9 @@ spec-editor {
max-width: 0;
}
}
+ .quick-fix {
+ display: none;
+ }
}
.hide-masked-sensitive-value {
display: none !important;
@@ -467,6 +470,12 @@ spec-editor {
margin-top: 5px; //align with icons when expanded
}
}
+ .quick-fix {
+ .btn {
+ float: right;
+ color: @gray-lighter !important;
+ }
+ }
.param-fields {
width: 100%;
margin-top: 8px;
diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html
index 19b0371..d6ae606 100644
--- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html
+++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html
@@ -547,7 +547,14 @@
</div>
- <small ng-repeat="issue in model.issues | filter:{ref: item.name}:true | filter:{group: 'config'}:true" class="help-block issue" ng-bind-html="issue.message"></small>
+ <small ng-repeat="issue in model.issuesWithFixes | filter:{ref: item.name}:true | filter:{group: 'config'}:true" class="help-block issue">
+ <span ng-bind-html="issue.message"></span>
+ <div ng-if="getObjectSize(issue.quickFixes)" class="quick-fix">
+ <div ng-repeat="fix in issue.quickFixes">
+ <a ng-click="applyQuickFix(issue, fix)" class="btn btn-xs btn-primary">{{ fix.text }}</a>
+ </div>
+ </div>
+ </small>
<small ng-if="specEditor.customConfigWidgetError(item)" class="help-block issue">
Custom widget unavailable:
{{ specEditor.customConfigWidgetError(item) }}
diff --git a/ui-modules/blueprint-composer/app/components/util/model/dsl.model.js b/ui-modules/blueprint-composer/app/components/util/model/dsl.model.js
index 323d73d..0caa5e4 100644
--- a/ui-modules/blueprint-composer/app/components/util/model/dsl.model.js
+++ b/ui-modules/blueprint-composer/app/components/util/model/dsl.model.js
@@ -772,7 +772,9 @@ export class DslParser {
// a floating point number
return new Dsl(KIND.NUMBER, t.nextNumber());
}
- throw new DslError('Expected: CONSTANT but found: ' + t.toJSON());
+ return new Dsl(KIND.STRING, t.remainder());
+ // previously we did this, but it caused all kinds of errors, as non-json input is common
+ // throw new DslError('Expected: CONSTANT but found: ' + t.toJSON());
}
/**
@@ -973,6 +975,12 @@ export class Tokenizer {
this.s = this.s.substring(num);
}
+ remainder() {
+ let result = this.s;
+ this.s = "";
+ return result;
+ }
+
/**
* Return <code>true</code> if a call to <code>next(sym)</code> would succeed.
* @param {string} sym one or more characters, e.g.,
[brooklyn-ui] 06/15: remove pointless quick fix directive
Posted by he...@apache.org.
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 2315d3ffcbd8c53801be94b8be9ec2880e65eeeb
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Thu Apr 22 22:14:26 2021 +0100
remove pointless quick fix directive
---
.../app/components/quick-fix/quick-fix.html | 21 ---------------------
.../app/components/quick-fix/quick-fix.js | 19 +------------------
.../app/components/quick-fix/quick-fix.less | 21 ---------------------
3 files changed, 1 insertion(+), 60 deletions(-)
diff --git a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.html b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.html
deleted file mode 100644
index e9bdb2e..0000000
--- a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- KIND, either express or implied. See the License for the
- specific language governing permissions and limitations
- under the License.
--->
-<span class="quick-fix">
- bob {{ foo }}.
-</span>
diff --git a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
index 184de2c..36e1ce0 100644
--- a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
+++ b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.js
@@ -18,30 +18,13 @@
*/
import angular from 'angular';
-import template from './quick-fix.html';
const MODULE_NAME = 'brooklyn.components.quick-fix.quick-fix';
-angular.module(MODULE_NAME, [])
- .directive('quickFix', ['$rootScope', quickFixDirective]);
+angular.module(MODULE_NAME, []);
export default MODULE_NAME;
-export function quickFixDirective($rootScope) {
- return {
- restrict: 'E',
- template: template,
- scope: {
- issues: '=',
- },
- link: link,
- };
-
- function link(scope, element, attrs, specEditor) {
- console.log("quick-fix", attrs);
- scope.foo = "hello";
- }
-}
export function computeQuickFixes(allIssues) {
if (!allIssues) allIssues = {};
diff --git a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.less b/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.less
deleted file mode 100644
index 77412b2..0000000
--- a/ui-modules/blueprint-composer/app/components/quick-fix/quick-fix.less
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-quick-fix, .quick-fix {
-}