@gregoriusrippenstein/node-red-contrib-introspection 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/nodes/50-send-flow.html +126 -0
- package/nodes/50-send-flow.js +112 -0
- package/nodes/55-trigger-import.html +48 -0
- package/nodes/55-trigger-import.js +24 -0
- package/package.json +11 -9
package/README.md
CHANGED
|
@@ -85,6 +85,14 @@ GetFlows supports version selection of the flows and it has limited authenticati
|
|
|
85
85
|
|
|
86
86
|
Inspired by the [dsm](https://flows.nodered.org/node/node-red-contrib-dsm) package that has a [backup](https://github.com/cflurin/node-red-contrib-dsm/wiki/Backup) state machine.
|
|
87
87
|
|
|
88
|
+
### SendFlow
|
|
89
|
+
|
|
90
|
+
Send a flow to another Node-RED instance. This will replace **any existing** flows on the other server, use with care. Under the hood this uses the [Node-RED API](https://nodered.org/docs/api/admin/methods/post/flows/) to post a new flow to the server. This node also supports authentication if the other server happens to have some.
|
|
91
|
+
|
|
92
|
+
### TriggerImport
|
|
93
|
+
|
|
94
|
+
This node will open the Node-RED import dialog and insert the payload, i.e. a flow object, into the dialog. The flow can then be imported into the existing workspace. See the [RSS example](https://flowhub.org/f/e02ba6e534f7a0f4) for more details. Payload must be a valid flow object, i.e. a Javascript array with one object per node. The import dialog will complain should this not be the case.
|
|
95
|
+
|
|
88
96
|
## Node-RED Versions
|
|
89
97
|
|
|
90
98
|
These nodes have been tested and found to work on Node-RED 3.0.2 and 3.1.0.beta.4.
|
|
@@ -97,6 +105,8 @@ There are [example flows](/examples) contained in the package, examples can also
|
|
|
97
105
|
- [Sink and Seeker](https://flowhub.org/f/139a816449acd89f)
|
|
98
106
|
- [Screenshot](https://flowhub.org/f/07b2d0f3b0445ab5)
|
|
99
107
|
- [DrawSVG](https://flowhub.org/f/141037dcda5b69fd)
|
|
108
|
+
- [GetFlows](https://flowhub.org/f/0b1bfbf6e540be66)
|
|
109
|
+
- [TriggerImport](https://flowhub.org/f/e02ba6e534f7a0f4)
|
|
100
110
|
|
|
101
111
|
## License
|
|
102
112
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('SendFlow',{
|
|
3
|
+
color: '#e5e4ef',
|
|
4
|
+
icon: "icons/subflow.svg",
|
|
5
|
+
category: 'introspection',
|
|
6
|
+
defaults: {
|
|
7
|
+
name: {
|
|
8
|
+
value:"",
|
|
9
|
+
},
|
|
10
|
+
hostUrl: {
|
|
11
|
+
value: ""
|
|
12
|
+
},
|
|
13
|
+
flowVersion: {
|
|
14
|
+
value: "v1"
|
|
15
|
+
},
|
|
16
|
+
useAuthentication: {
|
|
17
|
+
value:false
|
|
18
|
+
},
|
|
19
|
+
apiUsername: {
|
|
20
|
+
value: "",
|
|
21
|
+
},
|
|
22
|
+
apiUsernameType: {
|
|
23
|
+
value: "env",
|
|
24
|
+
},
|
|
25
|
+
apiPassword: {
|
|
26
|
+
value: "",
|
|
27
|
+
},
|
|
28
|
+
apiPasswordType: {
|
|
29
|
+
value: "env",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
inputs:1,
|
|
33
|
+
outputs:1,
|
|
34
|
+
|
|
35
|
+
label: function() {
|
|
36
|
+
return (this.name || this._def.paletteLabel);
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
labelStyle: function() {
|
|
40
|
+
return this.name?"node_label_italic":"";
|
|
41
|
+
},
|
|
42
|
+
oneditprepare: function() {
|
|
43
|
+
$("#node-input-apiUsername").typedInput({
|
|
44
|
+
types:["env", "msg", "flow","global", "cred"],
|
|
45
|
+
typeField: "#node-input-apiUsernameType"
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
$("#node-input-apiPassword").typedInput({
|
|
49
|
+
types:["env", "msg", "flow","global", "env", "cred"],
|
|
50
|
+
typeField: "#node-input-apiPasswordType"
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if ( $('#node-input-useAuthentication').is(":checked") ) {
|
|
54
|
+
$('#useAuthentication-input-fields').show();
|
|
55
|
+
} else {
|
|
56
|
+
$('#useAuthentication-input-fields').hide()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
$('#node-input-useAuthentication').on( 'change', function() {
|
|
60
|
+
if ( $('#node-input-useAuthentication').is(":checked") ) {
|
|
61
|
+
$('#useAuthentication-input-fields').show();
|
|
62
|
+
} else {
|
|
63
|
+
$('#useAuthentication-input-fields').hide()
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<script type="text/html" data-template-name="SendFlow">
|
|
71
|
+
<div class="form-row">
|
|
72
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
73
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<div class="form-row">
|
|
77
|
+
<label for="node-input-hostUrl"><i class="fa fa-tag"></i> Host</label>
|
|
78
|
+
<input type="text" id="node-input-hostUrl" placeholder="Host URL">
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<div class="form-row">
|
|
82
|
+
<label for="node-input-flowVersion">
|
|
83
|
+
<i class="fa fa-tag"></i>
|
|
84
|
+
<span>Flow Version</span>
|
|
85
|
+
</label>
|
|
86
|
+
|
|
87
|
+
<select id="node-input-flowVersion">
|
|
88
|
+
<option value="v1" selected=selected>Version 1</option>
|
|
89
|
+
<option value="v2">Version 2</option>
|
|
90
|
+
</select>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div class="form-row">
|
|
94
|
+
<label for="node-input-useAuthentication">
|
|
95
|
+
<i class="fa "></i>
|
|
96
|
+
<span>Use Authentication?</span>
|
|
97
|
+
</label>
|
|
98
|
+
|
|
99
|
+
<input type="checkbox" id="node-input-useAuthentication"
|
|
100
|
+
style="display:inline-block; width:15px; vertical-align:baseline;">
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div id="useAuthentication-input-fields" class="hidden">
|
|
104
|
+
<div class="form-row">
|
|
105
|
+
<label for="node-input-apiUsername">
|
|
106
|
+
<i class="fa fa-tag"></i>
|
|
107
|
+
Username
|
|
108
|
+
</label>
|
|
109
|
+
<input type="text" id="node-input-apiUsername">
|
|
110
|
+
<input type="hidden" id="node-input-apiUsernameType">
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<div class="form-row">
|
|
114
|
+
<label for="node-input-apiPassword">
|
|
115
|
+
<i class="fa fa-tag"></i>
|
|
116
|
+
Password
|
|
117
|
+
</label>
|
|
118
|
+
<input type="text" id="node-input-apiPassword">
|
|
119
|
+
<input type="hidden" id="node-input-apiPasswordType">
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</script>
|
|
123
|
+
|
|
124
|
+
<script type="text/html" data-help-name="SendFlow">
|
|
125
|
+
<p>Send flow to another Node-RED instance.</p>
|
|
126
|
+
</script>
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
module.exports = function(RED) {
|
|
2
|
+
function SendFlowFunctionality(config) {
|
|
3
|
+
RED.nodes.createNode(this,config);
|
|
4
|
+
|
|
5
|
+
var node = this;
|
|
6
|
+
var cfg = config;
|
|
7
|
+
|
|
8
|
+
node.on('close', function() {
|
|
9
|
+
node.status({});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
node.on("input", function(msg, send, done) {
|
|
13
|
+
var sendFlow = (hdrs, got) => {
|
|
14
|
+
got.post( cfg.hostUrl + "/flows", {
|
|
15
|
+
headers: {
|
|
16
|
+
"Node-RED-API-Version": cfg.flowVersion,
|
|
17
|
+
"Content-type": "application/json",
|
|
18
|
+
"Node-RED-Deployment-Type": "full",
|
|
19
|
+
...hdrs
|
|
20
|
+
},
|
|
21
|
+
body: JSON.stringify(msg.payload)
|
|
22
|
+
}).then( res => {
|
|
23
|
+
var bodySize = res.body.length;
|
|
24
|
+
|
|
25
|
+
send({
|
|
26
|
+
...msg,
|
|
27
|
+
payload: res.body
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
node.status({fill:"green",shape:"dot",text:"Good"});
|
|
31
|
+
setTimeout( function() {
|
|
32
|
+
node.status({})
|
|
33
|
+
}, 450);
|
|
34
|
+
|
|
35
|
+
}).catch( err => {
|
|
36
|
+
node.status({fill:"red",shape:"dot",text:"Failed"});
|
|
37
|
+
node.error(err)
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Authentication
|
|
43
|
+
**/
|
|
44
|
+
if ( cfg.useAuthentication ) {
|
|
45
|
+
var username = undefined;
|
|
46
|
+
var password = undefined;
|
|
47
|
+
|
|
48
|
+
node.status({fill:"blue",shape:"dot",text:"Requesting token"});
|
|
49
|
+
|
|
50
|
+
RED.util.evaluateNodeProperty(cfg.apiUsername, cfg.apiUsernameType,
|
|
51
|
+
node, msg, (err, result) => {
|
|
52
|
+
if (err) {
|
|
53
|
+
node.status({fill:"red",shape:"dot",text:"Failed"});
|
|
54
|
+
node.error(err)
|
|
55
|
+
} else {
|
|
56
|
+
username = result;
|
|
57
|
+
|
|
58
|
+
RED.util.evaluateNodeProperty(cfg.apiPassword, cfg.apiPasswordType,
|
|
59
|
+
node, msg, (err, result) => {
|
|
60
|
+
if (err) {
|
|
61
|
+
node.status({fill:"red",shape:"dot",text:"Failed"});
|
|
62
|
+
node.error(err)
|
|
63
|
+
} else {
|
|
64
|
+
password = result;
|
|
65
|
+
|
|
66
|
+
var data = {
|
|
67
|
+
"client_id": "node-red-admin",
|
|
68
|
+
"grant_type": "password",
|
|
69
|
+
"scope": "*",
|
|
70
|
+
"username": username,
|
|
71
|
+
"password": password
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
import('got').then( (module) => {
|
|
75
|
+
module.got.post( cfg.hostUrl + "/auth/token", {
|
|
76
|
+
json: data
|
|
77
|
+
}).then( res => {
|
|
78
|
+
node.status({
|
|
79
|
+
fill:"blue",
|
|
80
|
+
shape:"dot",
|
|
81
|
+
text:"Sending flow"
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
var access_token = JSON.parse(res.body).access_token;
|
|
85
|
+
|
|
86
|
+
sendFlow({
|
|
87
|
+
"Authorization": "Bearer " + access_token
|
|
88
|
+
}, module.got);
|
|
89
|
+
|
|
90
|
+
}).catch((err) => {
|
|
91
|
+
node.status({fill:"red",shape:"dot",text:"Failed"});
|
|
92
|
+
node.error( err );
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
} else {
|
|
100
|
+
/*
|
|
101
|
+
* Authentication free zone...
|
|
102
|
+
*/
|
|
103
|
+
node.status({fill:"blue",shape:"dot",text:"Sending flow"});
|
|
104
|
+
import('got').then( (module) => {
|
|
105
|
+
sendFlow({}, module.got);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
RED.nodes.registerType("SendFlow", SendFlowFunctionality);
|
|
112
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.comms.subscribe('introspect:trigger-import-tripped', (event,data) => {
|
|
3
|
+
if ( data.msg == "import-flow" ) {
|
|
4
|
+
RED.clipboard.import();
|
|
5
|
+
|
|
6
|
+
setTimeout( () => {
|
|
7
|
+
var content = data.flowContent;
|
|
8
|
+
|
|
9
|
+
$('#red-ui-clipboard-dialog-import-text').val(
|
|
10
|
+
JSON.stringify(content)
|
|
11
|
+
).trigger("paste");
|
|
12
|
+
}, 300);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
RED.nodes.registerType('TriggerImport',{
|
|
17
|
+
color: '#e5e4ef',
|
|
18
|
+
icon: "icons/subflow.svg",
|
|
19
|
+
category: 'introspection',
|
|
20
|
+
paletteLabel: "TriggerImport",
|
|
21
|
+
defaults: {
|
|
22
|
+
name: {
|
|
23
|
+
value:"",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
inputs:1,
|
|
27
|
+
outputs:1,
|
|
28
|
+
|
|
29
|
+
label: function() {
|
|
30
|
+
return (this.name || this._def.paletteLabel);
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
labelStyle: function() {
|
|
34
|
+
return this.name?"node_label_italic":"";
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<script type="text/html" data-template-name="TriggerImport">
|
|
40
|
+
<div class="form-row">
|
|
41
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name">Name</span></label>
|
|
42
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
43
|
+
</div>
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<script type="text/html" data-help-name="TriggerImport">
|
|
47
|
+
<p>Trigger import opens the flow import dialog with data that came from the server.</p>
|
|
48
|
+
</script>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module.exports = function(RED) {
|
|
2
|
+
function TriggerImportFunctionality(config) {
|
|
3
|
+
RED.nodes.createNode(this,config);
|
|
4
|
+
|
|
5
|
+
var node = this;
|
|
6
|
+
var cfg = config;
|
|
7
|
+
|
|
8
|
+
node.on('close', function() {
|
|
9
|
+
node.status({});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
node.on("input", function(msg, send, done) {
|
|
13
|
+
RED.comms.publish("introspect:trigger-import-tripped",
|
|
14
|
+
RED.util.encodeObject({
|
|
15
|
+
flowContent: msg.payload,
|
|
16
|
+
msg: "import-flow",
|
|
17
|
+
})
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
send(msg);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
RED.nodes.registerType("TriggerImport", TriggerImportFunctionality);
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gregoriusrippenstein/node-red-contrib-introspection",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"got": "latest"
|
|
6
6
|
},
|
|
@@ -16,14 +16,16 @@
|
|
|
16
16
|
"node-red": {
|
|
17
17
|
"version": ">=2.0.0",
|
|
18
18
|
"nodes": {
|
|
19
|
-
"seeker":
|
|
20
|
-
"sink":
|
|
21
|
-
"screenshot":
|
|
22
|
-
"orphans":
|
|
23
|
-
"ismobile":
|
|
24
|
-
"navigator":
|
|
25
|
-
"drawsvg":
|
|
26
|
-
"getflows":
|
|
19
|
+
"seeker": "nodes/05-seeker.js",
|
|
20
|
+
"sink": "nodes/10-sink.js",
|
|
21
|
+
"screenshot": "nodes/15-screenshot.js",
|
|
22
|
+
"orphans": "nodes/20-orphans.js",
|
|
23
|
+
"ismobile": "nodes/25-ismobile.js",
|
|
24
|
+
"navigator": "nodes/30-navigator.js",
|
|
25
|
+
"drawsvg": "nodes/40-drawsvg.js",
|
|
26
|
+
"getflows": "nodes/45-get-flows.js",
|
|
27
|
+
"sendflow": "nodes/50-send-flow.js",
|
|
28
|
+
"triggerimport": "nodes/55-trigger-import.js"
|
|
27
29
|
}
|
|
28
30
|
},
|
|
29
31
|
"description": "Node-RED Editor-only nodes for introspecting flows.",
|