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 27ae044bfb7153ac70ea4e4d46fcbf95ac3b4fe7
Author: Alex Heneveld <[email protected]>
AuthorDate: Tue Apr 27 21:33:48 2021 +0100

    quick fix supports regexes to match params, and more
    
    * support regex to match existing params
    * always refer to root node as root
    * fix bug in parameter creation (newly caused)
    * fix bug in "Add parameter" button visibility (longstanding)
---
 docs/basic-with-constraint.bom                     |  25 ++--
 .../app/components/quick-fix/quick-fix.js          | 137 ++++++++++++---------
 .../spec-editor/spec-editor.template.html          |   9 +-
 .../app/components/util/model/entity.model.js      |   7 +-
 4 files changed, 108 insertions(+), 70 deletions(-)

diff --git a/docs/basic-with-constraint.bom b/docs/basic-with-constraint.bom
index 0f1a2db..a3297b1 100644
--- a/docs/basic-with-constraint.bom
+++ b/docs/basic-with-constraint.bom
@@ -36,21 +36,32 @@ brooklyn.catalog:
         fix: clear_config
         message-regex: cannot both be set
 
-      - key: post_code
+      - # set from existing key matching [.*_|]postal_code[|_.*] on ancestor
+        key: post_code
         fix: set_from_key
         message-regex: required
 
         source-key: postal_code
-        source-hierarchy: root
-        source-key-createable: true
+        source-key-regex: (_|^)postal_code(_|$)   # will match e.g. 
cust1_postal_code and postal_core_north but not postal_codex
+        source-hierarchy: ancestors               # on self or any ancestor
+
+      - # create key postal_code at root
+        key: post_code
+        fix: set_from_key
+        message-regex: required
+
+        source-key: postal_code
+        source-key-createable: true               # will create if not present
+        source-hierarchy: root                    # but only at root
         source-key-parameter-definition:
           constraints:
           - required
 
-      - key: post_code
+      - # use this key on any other entity of the same type
+        key: post_code
         fix: set_from_key
         message-regex: required
 
-        source-key: post_code
-        source-hierarchy: anywhere
-        source-types: [ basic-with-constraint ]
+        source-key: post_code                     # will match the key
+        source-hierarchy: anywhere                # anywhere
+        source-types: [ basic-with-constraint ]   # of this type
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 16cbac1..27f175a 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
@@ -114,9 +114,10 @@ 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);
+        let ckey_exact = qfdef['source-key'];
+        let ckey_regex = qfdef['source-key-regex'];
+        if (!ckey_exact && !ckey_regex) {
+            console.warn("Missing at least one of 'source-key' or 
'source-key-regex' on hint", qfdef);
             return;
         }
 
@@ -143,76 +144,96 @@ function proposeSetFrom() {
                 }
             }
 
-            if (sourceNode.entity._id === entity._id && ckey === issue.ref) {
-                // skip proposal for recursive definition
-                return;
-            }
+            let contenders = {};
+            if (ckey_exact) {
+                let exactKey = sourceNode.entity.config[ckey_exact] || 
(sourceNode.entity.miscData.get("config") || []).find(c => c.name === 
ckey_exact);
+                // don't think we need to check params -- 
sourceNode.entity.getParameterNamed(ckey) -- as they should be in config
 
-            let hasKey = sourceNode.entity.config[ckey] || 
(sourceNode.entity.miscData.get("config") || []).find(c => c && c.name === 
ckey);
-            let hasParam = sourceNode.entity.getParameterNamed(ckey);
+                if (exactKey) contenders[ckey_exact] = true;
+            }
+            let create = !Object.keys(contenders).length && createable && 
ckey_exact;
+            if (create) {
+                contenders[ckey_exact] = true;
+            }
 
-            let existing = hasKey || hasParam;
-            let create = !existing && createable;
+            if (ckey_regex) {
+                let r = new RegExp(ckey_regex);
+                Object.keys(sourceNode.entity.config).forEach(k => {
+                    if (r.test(k)) contenders[k] = true;
+                });
+                (sourceNode.entity.miscData.get("config") || []).forEach(c => {
+                    if (r.test(c.name)) contenders[c.name] = true;
+                });
+            }
+            console.log("result of regex at", ckey_regex, sourceNode, 
contenders);
 
-            if (!existing && !create) {
+            if (!Object.keys(contenders).length) {
                 // no proposal available (cannot create)
                 return;
             }
 
+            if (!sourceNode.entity.parent) {
+                sourceNode.id = sourceNode.id || 'root';
+                sourceNode.name = sourceNode.name || sourceNode.entity.name || 
'the application root node';
+            }
+
             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 (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
-                            + ". 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,
-                    };
+                ((sourceNode.entity.type || "Unnamed item") + " " + "(" + 
(sourceNode.entity.id || sourceNode.entity._id) + ")");
+
+            Object.keys(contenders).forEach(ckey => {
+                if (sourceNode.entity._id === entity._id && ckey === 
issue.ref) {
+                    // skip proposal for recursive definition
+                    return;
                 }
 
-                Object.assign(proposals[pkey], {
-                    issues: [],
-                    apply: (issue, entity) => {
-                        if (create) {
-                            // check again so we only create once
-                            let hasParam = 
sourceNode.entity.getParameterNamed(ckey);
-                            if (!hasParam) {
-                                
sourceNode.entity.addParameterDefinition(Object.assign(
-                                    {name: ckey,},
-                                    qfdef['source-key-parameter-definition'],
-                                ));
+                let pkey = 'set_from_' + sourceNode.id + '_' + ckey;
+                if (!proposals[pkey]) {
+                    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
+                                + ". 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 (create) {
+                                // check again so we only create once
+                                let hasParam = 
sourceNode.entity.getParameterNamed(ckey);
+                                if (!hasParam) {
+                                    
sourceNode.entity.addParameterDefinition(Object.assign(
+                                        {name: ckey,},
+                                        
qfdef['source-key-parameter-definition'],
+                                    ));
+                                }
+                            }
+                            if (!sourceNode.entity.id) {
+                                sourceNode.entity.id = sourceNode.entity._id;
                             }
-                        }
-                        if (!sourceNode.entity.id) {
-                            sourceNode.entity.id = sourceNode.entity._id;
-                        }
 
-                        entity = (entity || issue.entity);
-                        entity.addConfig(issue.ref, '$brooklyn:component("' + 
sourceNode.entity.id + '").config("' + ckey + '")');
-                    }
-                });
-            }
-            if (proposals[pkey]) {
-                proposals[pkey].issues.push(issue);
-            }
-        };
+                            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(),
-                    });
+            considerNode({ 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') {
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 6050219..bfe3616 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
@@ -97,16 +97,19 @@
             <div ng-if="parameters.length === 0">
                 <h4>No parameters</h4>
                 <p class="buttons">
-                    <button class="btn btn-sm btn-success" 
ng-click="specEditor.addParameter(state.parameters.search)">
-                        <i class="fa fa-plus"></i> Add parameter
+                    <button class="btn btn-sm btn-success" 
ng-click="specEditor.addParameter(state.parameters.search)" 
ng-if="state.parameters.search">
+                        <i class="fa fa-plus"></i> Add 
'{{state.parameters.search}}'
                     </button>
                 </p>
+                <p ng-if="!state.parameters.search">
+                    Search for a parameter to add one
+                </p>
             </div>
             <div ng-if="parameters.length > 0">
                 <h4>No matching parameters</h4>
                 <p class="buttons">
                     <button class="btn btn-sm btn-default" 
ng-if="state.parameters.search.length > 0" ng-click="state.parameters.search = 
''">Clear search</button>
-                    <button class="btn btn-sm btn-success" 
ng-click="specEditor.addParameter(state.parameters.search)">
+                    <button class="btn btn-sm btn-success" 
ng-click="specEditor.addParameter(state.parameters.search)" 
ng-if="state.parameters.search">
                         <i class="fa fa-plus"></i> Add 
'{{state.parameters.search}}'
                     </button>
                 </p>
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 71a5912..d61fa60 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
@@ -685,11 +685,14 @@ function addParameterDefinition(param, overwrite, 
skipUpdatesDuringBatch) {
     let allParams = this.miscDataOrDefault('parametersMap', {});
     if (param) {
         if (typeof param === 'string') {
-            param = {name: key, type: 'string'};
+            param = {name: param, type: 'string'};
             overwrite = false;
         }
         let key = (param || {}).name;
-        if (!key) throw new Error("'name' field must be included when adding 
parameter");
+        if (!key) {
+            console.warn("Invalid parameter definition to add", typeof param, 
param);
+            throw new Error("'name' field must be included when adding 
parameter");
+        }
 
         param = allParams[key] = Object.assign(allParams[key] || {}, param, 
overwrite ? null : allParams[key]);
 

Reply via email to