Skip to content

Commit fb2d9ff

Browse files
authored
Merge pull request #97 from Samsung/add-aws-v3-node
add-aws-v3-node
2 parents 2128f36 + d84965d commit fb2d9ff

33 files changed

+5280
-14
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ module.exports = function (RED) {
3636
const bodyParser = require("body-parser");
3737
const cors = require('cors');
3838
const jsonParser = bodyParser.json();
39-
const SmartThingsAPI = require('./lib/SmartThingsAPI');
40-
const SmartThingsProfile = require('./lib/SmartThingsProfile');
39+
const SmartThingsAPI = require('../lib/SmartThingsAPI');
40+
const SmartThingsProfile = require('../lib/SmartThingsProfile');
4141
const corsHandler = cors({
4242
origin: "*",
4343
methods: "POST"
File renamed without changes.
File renamed without changes.
File renamed without changes.

301-AWS.html renamed to nodes/301-AWS.html

Lines changed: 206 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ <h3>Properties</h3>
105105
</script>
106106

107107
<script type="text/x-red" data-help-name="aws-sdk">
108-
<p>AWS SDK</p>
109-
<p>You can extend this flow by connecting it to your AWS using the AWS SDK node. Refer to <a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/index.html" target="_blank">AWS api documents.</a></p>
108+
<p>AWS SDK v2</p>
109+
<p>You can extend this flow by connecting it to your AWS using the AWS SDK v2 node. Refer to <a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/index.html" target="_blank">AWS api documents.</a></p>
110110
<h3>Properties</h3>
111111
<dl class="message-properties">
112112
<dt>Name</dt>
@@ -123,6 +123,49 @@ <h3>Properties</h3>
123123

124124
</script>
125125

126+
<script type="text/x-red" data-template-name="aws-sdk-v3">
127+
<div class="form-row">
128+
<label for="node-input-name"><i class="fa fa-tag"></i>Name</label>
129+
<input type="text" id="node-input-name" placeholder="Name"></input>
130+
</div>
131+
<div class="form-row">
132+
<label for="node-input-config"><i class="fa fa-user"></i> AWS Config</label>
133+
<select type="text" id="node-input-config"></select>
134+
</div>
135+
<div class="form-row">
136+
<label for="node-input-client"><i class="fa fa-cog"></i> Service</label>
137+
<select type="text" id="node-input-client"></select>
138+
</div>
139+
<div class="form-row">
140+
<label for="node-input-operation"><i class="fa fa-wrench"></i> Command</label>
141+
<select type="text" id="node-input-operation"></select>
142+
</div>
143+
<div class="form-row">
144+
<label for="node-input-params"><i class="fa fa-code"></i> Params</label>
145+
<input type="hidden" id="node-input-params" value="{}" autofocus="autofocus"/>
146+
<input type="hidden" id="node-input-noerr"/>
147+
<div class="form-row node-text-editor-row"><div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-param-editor" ></div></div>
148+
</div>
149+
</script>
150+
151+
<script type="text/x-red" data-help-name="aws-sdk-v3">
152+
<p>AWS SDK v3</p>
153+
<p>You can extend this flow by connecting it to your AWS using the AWS SDK v3 node. Refer to <a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/index.html" target="_blank">AWS api documents.</a></p>
154+
<h3>Properties</h3>
155+
<dl class="message-properties">
156+
<dt>Name</dt>
157+
<dd>You can tag or describe this aws-sdk-v3.</dd>
158+
<dt>AWS Config <span class="property-type">required</span></dt>
159+
<dd>Choose a AWS Config Node.</dd>
160+
<dt>Service <span class="property-type">required</span></dt>
161+
<dd>Choose a AWS Service</dd>
162+
<dt>Command <span class="property-type">required</span></dt>
163+
<dd>Choose a AWS Command.</dd>
164+
<dt>Params <span class="property-type">optional</span></dt>
165+
<dd>Insert a params of method. refer to each method description on AWS api documents. It's a JSON string. You can use <a href="http://mustache.github.io/mustache.5.html" target="_blank">mustache</a> templates.</dd>
166+
</dl>
167+
</script>
168+
126169
<script type="text/javascript">
127170
RED.nodes.registerType('aws-config', {
128171
category: 'Samsung AutomationStudio',
@@ -167,13 +210,13 @@ <h3>Properties</h3>
167210
},
168211
inputs: 1,
169212
outputs: 1,
170-
paletteLabel: "AWS SDK",
213+
paletteLabel: "AWS SDK v2",
171214
icon: "aws.png",
172215
align: "right",
173216
label: function () {
174217
if (this.name) return this.name;
175218
if (this.service && this.operation) return this.service + "." + this.operation;
176-
return "AWS SDK";
219+
return "AWS SDK v2";
177220
},
178221
oneditprepare: function () {
179222
var NODE = this;
@@ -235,6 +278,165 @@ <h3>Properties</h3>
235278
this.editor.resize();
236279
}
237280
});
281+
282+
let allClientsData = null;
283+
async function loadAWSClientsData() {
284+
if (allClientsData) {
285+
return allClientsData;
286+
}
287+
288+
allClientsData = await $.ajax({
289+
url: 'icons/node-red-contrib-samsung-automation-studio-nodes/aws_sdk_v3_clients.json',
290+
dataType: 'json',
291+
})
292+
293+
return allClientsData;
294+
}
295+
296+
function removeClientSuffix(clientName) {
297+
if (clientName.endsWith("Client")) {
298+
return clientName.slice(0, -6);
299+
}
300+
return clientName;
301+
}
302+
303+
RED.nodes.registerType('aws-sdk-v3', {
304+
category: 'Samsung AutomationStudio',
305+
color: '#FF9900',
306+
defaults: {
307+
config: {
308+
value: '', required: true,
309+
validate: function (val) {
310+
var isValidAwsConfig = false;
311+
RED.nodes.eachNode(function (node) {
312+
if (node.id == val) {
313+
isValidAwsConfig = true
314+
}
315+
})
316+
return !!val && isValidAwsConfig
317+
}
318+
},
319+
name: {value: ''},
320+
client: {value: '', required: true},
321+
operation: {value: '', required: true},
322+
params: {value: '{}'}
323+
},
324+
inputs: 1,
325+
outputs: 1,
326+
paletteLabel: "AWS SDK v3",
327+
icon: "aws.png",
328+
align: "right",
329+
label: function () {
330+
if (this.name) return this.name;
331+
if (this.operation) return this.operation;
332+
return "AWS SDK v3";
333+
},
334+
oneditprepare: function () {
335+
var NODE = this;
336+
$("#node-input-config").html('');
337+
RED.nodes.eachNode(function (node) {
338+
if (node.name && node.region && node.type == 'aws-config') {
339+
$("#node-input-config").append($("<option></option>").val(node.id).text(node.name + " (" + node.id + ")"));
340+
}
341+
});
342+
343+
console.log("data : ");
344+
loadAWSClientsData().then((data) => {
345+
console.log("data : ", data);
346+
const clientEl = $('#node-input-client');
347+
const operationEl = $('#node-input-operation');
348+
349+
const initialClient = NODE.client || null;
350+
const initialOperation = NODE.operation || null;
351+
352+
clientEl.on('change', (e) => {
353+
const selectedClient = e.target.value;
354+
const commands = data[selectedClient].commands;
355+
356+
operationEl.empty();
357+
358+
if (Array.isArray(commands)) {
359+
commands.forEach(command => {
360+
operationEl.append(
361+
$('<option></option>')
362+
.val(command)
363+
.text(command)
364+
);
365+
});
366+
367+
if (initialClient === selectedClient && initialOperation) {
368+
operationEl.val(initialOperation);
369+
}
370+
}
371+
});
372+
373+
Object.keys(data).forEach(packageName => {
374+
clientEl.append(
375+
$('<option></option>')
376+
.val(packageName)
377+
.text(removeClientSuffix(data[packageName].client))
378+
);
379+
});
380+
381+
if (initialClient && data[initialClient]) {
382+
clientEl.val(initialClient);
383+
}
384+
385+
clientEl.trigger('change');
386+
});
387+
388+
$("#node-input-config").val(NODE.config);
389+
this.editor = RED.editor.createEditor({
390+
id: 'node-input-param-editor',
391+
mode: 'ace/mode/mustache',
392+
value: $("#node-input-params").val(),
393+
globals: {
394+
msg: true,
395+
context: true,
396+
RED: true,
397+
util: true,
398+
flow: true,
399+
global: true,
400+
console: true,
401+
Buffer: true,
402+
setTimeout: true,
403+
clearTimeout: true,
404+
setInterval: true,
405+
clearInterval: true
406+
}
407+
});
408+
this.editor.focus();
409+
},
410+
oneditsave: function () {
411+
var annot = this.editor.getSession().getAnnotations();
412+
this.noerr = 0;
413+
$("#node-input-noerr").val(0);
414+
for (var k = 0; k < annot.length; k++) {
415+
if (annot[k].type === "error") {
416+
$("#node-input-noerr").val(annot.length);
417+
this.noerr = annot.length;
418+
}
419+
}
420+
$("#node-input-params").val(this.editor.getValue());
421+
this.editor.destroy();
422+
delete this.editor;
423+
},
424+
oneditcancel: function () {
425+
this.editor.destroy();
426+
delete this.editor;
427+
},
428+
oneditresize: function (size) {
429+
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
430+
var height = $("#dialog-form").height();
431+
for (var i = 0; i < rows.size(); i++) {
432+
height -= $(rows[i]).outerHeight(true);
433+
}
434+
var editorRow = $("#dialog-form>div.node-text-editor-row");
435+
height -= (parseInt(editorRow.css("marginTop")) + parseInt(editorRow.css("marginBottom")));
436+
$(".node-text-editor").css("height", height + "px");
437+
this.editor.resize();
438+
}
439+
});
238440
</script>
239441

240442

301-AWS.js renamed to nodes/301-AWS.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module.exports = function(RED) {
1717
"use strict";
1818
var AWS = require("aws-sdk");
1919
var mustache = require("mustache");
20+
const AWSClients = require('./lib/AWSClients.js');
2021

2122
function NodeContext(msg, nodeContext,parent) {
2223
this.msgContext = new mustache.Context(msg,parent);
@@ -136,11 +137,82 @@ module.exports = function(RED) {
136137
});
137138
}
138139

140+
function AwsSdkV3Node(n) {
141+
RED.nodes.createNode(this,n);
142+
var node = this;
143+
node.name = n.name;
144+
node.classes = n.client;
145+
node.methods = n.operation;
146+
node.params = n.params;
147+
node.configId = n.config;
148+
RED.nodes.eachNode(function (nn) {
149+
if(node.configId==nn.id) {
150+
node.config = nn;
151+
node.config.credentials = RED.nodes.getCredentials(nn.id)
152+
}
153+
});
154+
if(!node.config) {
155+
node.error("failed: Invalid AWS credentials");
156+
return;
157+
}
158+
if (node.config.proxyRequired){
159+
var proxy = require('proxy-agent');
160+
AWS.config.update({
161+
httpOptions: { agent: new proxy(node.config.proxy) }
162+
});
163+
}
164+
node.on("input", function(msg) {
165+
try {
166+
const Client = AWSClients.getClient(node.classes);
167+
const clientConfig = {
168+
credentials: {
169+
accessKeyId: node.config.credentials.accessKey,
170+
secretAccessKey: node.config.credentials.secretKey,
171+
},
172+
region: node.config.region,
173+
};
174+
175+
const client = new Client(clientConfig);
176+
const CommandClass = AWSClients.getCommand(node.classes, node.methods);
177+
let command;
178+
if (node.params) {
179+
const paramValue = JSON.parse(mustache.render(node.params, new NodeContext(msg, node.context())));
180+
command = new CommandClass(paramValue)
181+
} else {
182+
command = new CommandClass(msg.params);
183+
}
184+
node.status({fill:"blue",shape:"dot",text:node.methods});
185+
client.send(command)
186+
.then(response => {
187+
if (response.Body && typeof response.Body.transformToString === 'function') {
188+
return response.Body.transformToString();
189+
}
190+
return response;
191+
})
192+
.then(body => {
193+
msg.payload = body;
194+
node.status({});
195+
node.send(msg);
196+
})
197+
.catch((e) => {
198+
node.status({fill:"red",shape:"ring",text:"error"});
199+
node.error(e);
200+
node.send([null, { err: e }]);
201+
});
202+
}
203+
catch (e) {
204+
node.status({ fill: "red", shape: "dot", text: "error" });
205+
node.error(e);
206+
}
207+
});
208+
}
209+
139210
RED.nodes.registerType("aws-config", AwsConfig,{
140211
credentials: {
141212
accessKey: {type: "text", required: true},
142213
secretKey: {type: "password", required: true}
143214
}
144215
});
145216
RED.nodes.registerType("aws-sdk", AwsSdkNode);
217+
RED.nodes.registerType("aws-sdk-v3", AwsSdkV3Node);
146218
};
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)