1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Merge branch 'dev' into new-export-dialog

This commit is contained in:
Nick O'Leary 2019-04-25 11:42:33 +01:00
commit 6d8ea2b6a4
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
51 changed files with 3088 additions and 311 deletions

View File

@ -1,3 +1,18 @@
#### 0.20.4: Maintenance Release
- Switch media-typer to content-type module Fixes #2122 #2123
- Use userObj.username and not .name for ssh key lookup Closes #2109
- Ensure mqtt message handlers are tidied up properly on partial deploy
- Update package dependencies
- Fix encoding menu in file node #2125
- Update ACE to 1.4.3-src-min-noconflict Fixes #2106
- Fix creating missing package.json when existing project imported Fixes #2115
- Allow subflow instance to override env var with falsey values Fixes #2113
- Prevent wire from normal node to link virtual port Fixes #2114
- Add explanation to the help text on the new feature to build query string from msg.payload #2116
- Bump bcrypt to latest
- Add Korean locales files for nodes #2100
#### 0.20.3: Maintenance Release #### 0.20.3: Maintenance Release
- Do not dynamically add/remove upgrade listener in ws nodes - Do not dynamically add/remove upgrade listener in ws nodes

View File

@ -30,24 +30,24 @@
"body-parser": "1.18.3", "body-parser": "1.18.3",
"cheerio": "0.22.0", "cheerio": "0.22.0",
"clone": "2.1.2", "clone": "2.1.2",
"content-type": "1.0.4",
"cookie": "0.3.1", "cookie": "0.3.1",
"cookie-parser": "1.4.4", "cookie-parser": "1.4.4",
"cors": "2.8.5", "cors": "2.8.5",
"cron": "1.7.0", "cron": "1.7.0",
"denque": "1.4.0", "denque": "1.4.1",
"express": "4.16.4", "express": "4.16.4",
"express-session": "1.15.6", "express-session": "1.15.6",
"fs-extra": "7.0.1", "fs-extra": "7.0.1",
"fs.notify": "0.0.4", "fs.notify": "0.0.4",
"hash-sum": "1.0.2", "hash-sum": "1.0.2",
"https-proxy-agent": "2.2.1", "https-proxy-agent": "2.2.1",
"i18next": "15.0.8", "i18next": "15.0.9",
"iconv-lite": "0.4.24", "iconv-lite": "0.4.24",
"is-utf8": "0.2.1", "is-utf8": "0.2.1",
"js-yaml": "3.13.0", "js-yaml": "3.13.0",
"json-stringify-safe": "5.0.1", "json-stringify-safe": "5.0.1",
"jsonata": "1.6.4", "jsonata": "1.6.4",
"media-typer": "1.0.1",
"memorystore": "1.6.1", "memorystore": "1.6.1",
"mime": "2.4.0", "mime": "2.4.0",
"mqtt": "2.18.8", "mqtt": "2.18.8",
@ -68,7 +68,7 @@
"raw-body": "2.3.3", "raw-body": "2.3.3",
"request": "2.88.0", "request": "2.88.0",
"semver": "6.0.0", "semver": "6.0.0",
"uglify-js": "3.5.2", "uglify-js": "3.5.3",
"when": "3.7.8", "when": "3.7.8",
"ws": "6.2.1", "ws": "6.2.1",
"xml2js": "0.4.19" "xml2js": "0.4.19"

View File

@ -315,7 +315,7 @@ module.exports = function(RED) {
} }
}; };
this.unsubscribe = function (topic, ref) { this.unsubscribe = function (topic, ref, removed) {
ref = ref||0; ref = ref||0;
var sub = node.subscriptions[topic]; var sub = node.subscriptions[topic];
if (sub) { if (sub) {
@ -323,10 +323,12 @@ module.exports = function(RED) {
node.client.removeListener('message',sub[ref].handler); node.client.removeListener('message',sub[ref].handler);
delete sub[ref]; delete sub[ref];
} }
if (Object.keys(sub).length === 0) { if (removed) {
delete node.subscriptions[topic]; if (Object.keys(sub).length === 0) {
if (node.connected) { delete node.subscriptions[topic];
node.client.unsubscribe(topic); if (node.connected) {
node.client.unsubscribe(topic);
}
} }
} }
} }
@ -430,10 +432,7 @@ module.exports = function(RED) {
} }
this.on('close', function(removed, done) { this.on('close', function(removed, done) {
if (node.brokerConn) { if (node.brokerConn) {
if (removed) { node.brokerConn.unsubscribe(node.topic,node.id, removed);
// This node has been removed so remove any subscriptions
node.brokerConn.unsubscribe(node.topic,node.id);
}
node.brokerConn.deregister(node,done); node.brokerConn.deregister(node,done);
} }
}); });

View File

@ -22,7 +22,7 @@ module.exports = function(RED) {
var getBody = require('raw-body'); var getBody = require('raw-body');
var cors = require('cors'); var cors = require('cors');
var onHeaders = require('on-headers'); var onHeaders = require('on-headers');
var typer = require('media-typer'); var typer = require('content-type');
var isUtf8 = require('is-utf8'); var isUtf8 = require('is-utf8');
var hashSum = require("hash-sum"); var hashSum = require("hash-sum");

11
packages/node_modules/@node-red/nodes/core/storage/50-file.html vendored Normal file → Executable file
View File

@ -22,7 +22,7 @@
<input type="checkbox" id="node-input-createDir" style="display: inline-block; width: auto; vertical-align: top;"> <input type="checkbox" id="node-input-createDir" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-createDir" style="width: 70%;"><span data-i18n="file.label.createdir"></span></label> <label for="node-input-createDir" style="width: 70%;"><span data-i18n="file.label.createdir"></span></label>
</div> </div>
<div class="form-row"> <div class="form-row form-row-file-encoding">
<label for="node-input-encoding"><i class="fa fa-flag"></i> <span data-i18n="file.label.encoding"></span></label> <label for="node-input-encoding"><i class="fa fa-flag"></i> <span data-i18n="file.label.encoding"></span></label>
<select type="text" id="node-input-encoding" style="width: 250px;"> <select type="text" id="node-input-encoding" style="width: 250px;">
</select> </select>
@ -246,8 +246,13 @@
}); });
encSel.val(node.encoding); encSel.val(node.encoding);
$("#node-input-overwriteFile").on("change",function() { $("#node-input-overwriteFile").on("change",function() {
if (this.value === "delete") { $(".form-row-file-write-options").hide(); } if (this.value === "delete") {
else { $(".form-row-file-write-options").show(); } $(".form-row-file-write-options").hide();
$(".form-row-file-encoding").hide();
} else {
$(".form-row-file-write-options").show();
$(".form-row-file-encoding").show();
}
}); });
} }
}); });

View File

@ -61,6 +61,7 @@
url to be constructed using values of the incoming message. For example, if the url is set to url to be constructed using values of the incoming message. For example, if the url is set to
<code>example.com/{{{topic}}}</code>, it will have the value of <code>msg.topic</code> automatically inserted. <code>example.com/{{{topic}}}</code>, it will have the value of <code>msg.topic</code> automatically inserted.
Using {{{...}}} prevents mustache from escaping characters like / & etc.</p> Using {{{...}}} prevents mustache from escaping characters like / & etc.</p>
<p>The node can optionally automatically encode <code>msg.payload</code> as query string parameters for a GET request, in which case <code>msg.payload</code> has to be an object.</p>
<p><b>Note</b>: If running behind a proxy, the standard <code>http_proxy=...</code> environment variable should be set and Node-RED restarted, or use Proxy Configuration. If Proxy Configuration was set, the configuration take precedence over environment variable.</p> <p><b>Note</b>: If running behind a proxy, the standard <code>http_proxy=...</code> environment variable should be set and Node-RED restarted, or use Proxy Configuration. If Proxy Configuration was set, the configuration take precedence over environment variable.</p>
<h4>Using multiple HTTP Request nodes</h4> <h4>Using multiple HTTP Request nodes</h4>
<p>In order to use more than one of these nodes in the same flow, care must be taken with <p>In order to use more than one of these nodes in the same flow, care must be taken with

View File

@ -859,6 +859,7 @@
"breaklines": "Break into lines", "breaklines": "Break into lines",
"filelabel": "file", "filelabel": "file",
"sendError": "Send message on error (legacy mode)", "sendError": "Send message on error (legacy mode)",
"encoding": "Encoding",
"deletelabel": "delete __file__", "deletelabel": "delete __file__",
"utf8String": "UTF8 string", "utf8String": "UTF8 string",
"binaryBuffer": "binary buffer" "binaryBuffer": "binary buffer"

View File

@ -143,7 +143,7 @@
"filterCurrent": "現在のフロー", "filterCurrent": "現在のフロー",
"debugNodes": "debugード", "debugNodes": "debugード",
"clearLog": "ログを削除", "clearLog": "ログを削除",
"filterLog": "ログのフィルタリング", "filterLog": "ログのフィルタリング",
"openWindow": "新しいウィンドウで開く" "openWindow": "新しいウィンドウで開く"
}, },
"messageMenu": { "messageMenu": {
@ -398,7 +398,7 @@
"status": "状態コード", "status": "状態コード",
"headers": "ヘッダ", "headers": "ヘッダ",
"other": "その他", "other": "その他",
"paytoqs" : "msg.payloadをクエリパラメータに追加", "paytoqs": "msg.payloadをクエリパラメータに追加",
"utf8String": "UTF8文字列", "utf8String": "UTF8文字列",
"binaryBuffer": "バイナリバッファ", "binaryBuffer": "バイナリバッファ",
"jsonObject": "JSONオブジェクト", "jsonObject": "JSONオブジェクト",
@ -857,6 +857,7 @@
"breaklines": "行へ分割", "breaklines": "行へ分割",
"filelabel": "file", "filelabel": "file",
"sendError": "エラーメッセージを送信(互換モード)", "sendError": "エラーメッセージを送信(互換モード)",
"encoding": "文字コード",
"deletelabel": "delete __file__", "deletelabel": "delete __file__",
"utf8String": "UTF8文字列", "utf8String": "UTF8文字列",
"binaryBuffer": "バイナリバッファ" "binaryBuffer": "バイナリバッファ"

View File

@ -0,0 +1,34 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="inject">
<p>수동, 혹은 일정간격으로 메세지를 플로우에 주입합니다. 메세지의 페이로드에는 문자열, JavaScript오브젝트, 현재시각 등 다양한 값을 지정할 수 있습니다.</p>
<h3>출력</h3>
<dl class="message-properties">
<dt>페이로드<span class="property-type">각종</span></dt>
<dd>지정한 메세지 페이로드</dd>
<dt class="optional">topic <span class="property-type">문자열</span></dt>
<dd>임의로 지정가능한 프로퍼티</dd>
</dl>
<h3>상세</h3>
<p>inject노드를 사용하여, 지정한 페이로드값을 이용하여 플로우를 시작할 수 있습니다. 페이로드의 기본값은 현재시각의 타임스탬프를 1970년 1월 1일부터 경과한 밀리초로 표현한 값입니다.</p>
<p>문자열, 수치, 논리값, JavaScript오브젝트, 플로우/글로벌 컨텍스트 값 등을 송출할 수도 있습니다.</p>
<p>기본값 설정으로는 에디터 내에 표시되는 버튼을 클릭하여, 노드를 수동으로 움직일 수 있습니다. 지정된 간격, 혹은 스케쥴에 따라 메세지를 송출하도록 설정할 수도 있습니다.</p>
<p>또한, 플로우를 시작할 때에 한 번만 메세지를 송출시킬 수도 있습니다.</p>
<p>'<i>시간간격</i>'으로 지정가능한 값의 최대치는, 약 596시간(혹은 24일)입니다. 24시간보다 긴 간격을 다루고 싶을 경우에는, 전원정지나 재시작에도 대응 가능한 스케쥴러노드의 이용을 검토하시는게 좋습니다.</p>
<p><b></b>:'<i>지정한 시간간격, 일시</i>'와 '<i>지정한 일시</i>'옵션은 표준적인 cron시스템을 내부에서 이용합니다. 따라서, '20분'으로 지정하면, 그 시점에서 20분 후가 아닌, 매 시 정확히 20분, 40분을 의미합니다. '현 시각에서 20분마다'를 지정하려면 '<i>지정한 시간간격</i>'' 옵션을 이용합니다.</p>
<p><b></b>: 문자열에 줄 바꿈을 포함하고 싶은 경우에는, function노드를 사용하여 페이로드를 지정 해 주세요.</p>
</script>

View File

@ -0,0 +1,36 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="catch">
<p>같은 탭내의 노드가 송출한 에러를 캐치합니다.</p>
<h3>출력</h3>
<dl class=message-properties>
<dt>error.message <span class="property-type">문자열</span></dt>
<dd>에러 메세지</dd>
<dt>error.source.id <span class="property-type">문자열</span></dt>
<dd>에러를 보낸 노드의 ID</dd>
<dt>error.source.type <span class="property-type">문자열</span></dt>
<dd>에러를 보낸 노드의 종류</dd>
<dt>error.source.name <span class="property-type">문자열</span></dt>
<dd>에러를 송출한 노드의 명칭(설정되어 있는 경우)</dd>
</dl>
<h3>상세</h3>
<p>메시지 처리중에 노드가 에러를 송출했을 경우, 플로우 실행은 기본적으로 정지합니다. 이 노드를 사용하면, 에러를 캐치하고 대응하는 플로우로 처리시킬 수 있습니다.
<p>기본값으로는, 같은 탭의 모든 노드가 송출한 에러를 캐치합니다. 특정 노드를 캐치대상으로 하거나 대상 catch노드에서 보충되지 않은 에러만 보충하도록 지정하는 것도 가능합니다.
<p>에러발생시에는 매치되는 모든 catch 노드가 메시지를 받습니다.
<p>서브플로우내에서 에러가 송출된 경우, 우선 서브플로우 내의 catch노드로 처리됩니다. 대응하는 노드가 존재하지 않을 경우에는 그 서브플로가 배치된 탭에 에러를 전파하여 처리합니다.
<p>메시지가<code>error</code>프로퍼티를 가지고 있는 경우, 원래의 <code>error</code><code>_error</code>로 복사합니다.
</script>

View File

@ -0,0 +1,33 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="status">
<p>같은 탭내 노드의 스테이터스 메시지를 취득합니다.</p>
<h3>출력</h3>
<dl class="message-properties">
<dt>status.text <span class="property-type">문자열</span></dt>
<dd>스테이터스 문자열</dd>
<dt>status.source.type <span class="property-type">문자열</span></dt>
<dd>스테이터스를 표시한 노드의 종류</dd>
<dt>status.source.id <span class="property-type">문자열</span></dt>
<dd>스테이터스를 표시한 노드의 ID</dd>
<dt>status.source.name <span class="property-type">문자열</span></dt>
<dd>스테이터스를 표시한 노드의 명칭(설정되어 있는 경우)</dd>
</dl>
<h3>상세</h3>
<p>이 노드는 <code>payload</code>를 설정하지 않습니다.</p>
<p>기본값으로는, 같은 워크스페이스 탭내의 모든 노드의 스테이터스를 취득합니다. 특정 노드의 스테이터스를 취득 대상으로 설정할 수도 있습니다.</p>
</script>

View File

@ -0,0 +1,25 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="debug">
<p>사이브바의 '디버그'탭에 선택한 메세지 프로퍼티의 값을 표시합니다. 설정에 의해, 랜덤로그로 출력도 가능합니다. 기본값의 표시대상은 <code>msg.payload</code>이지만, 설정에 의해 지정한 프로퍼티, 메세지 전체, 혹은 JSONata식의 평가결과를 출력할 수 있습니다.</p>
<h3>상세</h3>
<p>'디버그'사이드바는 받은 메시지의 계층구조를 표시하는 기능을 갖추고 있습니다. 이 기능으로 메시지의 구조를 쉽게 이해할 수 있습니다.</p>
<p>JavaScript오브젝트와 배열은 필요에 따라 접거나 펼칠 수 있습니다. 버퍼 오브젝트 데이터를 표시하거나, 표현가능한 경우에는 문자열로 표시할 수도 있습니다.</p>
<p>메세지를 받은 시각, 송신 노드, 메시지 타입에 관한 정보를 '디버그'사이드바에 표시된 메세지에 부수적으로 표시합니다. 송신한 노드의 ID를 선택하면, 워크스페이스내의 대응하는 노드를 확인할 수 있습니다.</p>
<p>출력의 유효/무효는 노드상의 버튼으로 전환할 수 있습니다. 플로우상에서 미사용중인 debug노드는, 무효화 하거나 삭제할 것을 권장합니다.</p>
<p>모든 메세지를 런타임 로그에 보내거나, 짧은(32자) 데이터를 debug노드 아래의 스테이터스텍스트에 표시할 수 있습니다.</p>
</script>

View File

@ -0,0 +1,23 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="link in">
<p>플로우간에 가상의 링크를 작성합니다.</p>
<h3>상세</h3>
<p>임의의 탭상에 존재하는 <code>link out</code>노드에 접속할 수 있습니다. 이 접속은 마치 직접 링크한 것 처럼 작동합니다.</p>
<p>link노드간의 링크는 link노드를 선택한 경우에만 표시됩니다. 다른 탭으로의 링크가 있을 경우에는, 가상의 노드를 표시합니다. 이 가상의 노드를 클릭하면, 대응되는 탭으로 이동할 수 있습니다.</p>
<p><b>주: </b>서브플로우 밖에서 안으로, 혹은 안에서 밖으로의 링크는 작성할 수 없습니다.</p>
</script>

View File

@ -0,0 +1,74 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="exec">
<p>시스템 커맨드를 실행하여 출력을 반환합니다.</p>
<p>커맨드 완료까지 기다릴지, 커맨드가 출력을 실행할 때 마다 메세지를 출력할지 지정할 수 있습니다.</p>
<p>실행대상의 커맨드는, 노드의 설정 혹은 수신메세지에서 지정합니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt class="optional">payload <span class="property-type">문자열</span></dt>
<dd>실행할 커맨의 마지막에 추가하도록 설정할 수 있습니다</dd>
<dt class="optional">kill <span class="property-type">문자열</span></dt>
<dd>exec노드의 프로세스에 보낼 시그널 종류를 지정합니다</dd>
<dt class="optional">pid <span class="property-type">수치|문자열</span></dt>
<dd>시그널 송신대상의 exec노드의 프로세스 ID를 지정합니다</dd>
</dl>
<h3>출력</h3>
<ol class="node-ports">
<li>표준출력(stdout)
<dl class="message-properties">
<dt>payload <span class="property-type">문자열</span></dt>
<dd>커맨드의 표준출력</dd>
</dl>
<dl class="message-properties">
<dt>rc <span class="property-type">문자열</span></dt>
<dd>반환코드 문자열(3번째 단자에서도 취득가능)의 복사(exec모드에서 만)</dd>
</dl>
</li>
<li>표준에러 출력(stderr)
<dl class="message-properties">
<dt>payload <span class="property-type">문자열</span></dt>
<dd>커맨드의 표준에러 출력</dd>
</dl>
<dl class="message-properties">
<dt>rc <span class="property-type">문자열</span></dt>
<dd>반환코드 문자열(3번째 단자에서도 취득가능)의 복사(exec모드에서 만)</dd>
</dl>
</li>
<li>반환코드(return code)
<dl class="message-properties">
<dt>payload <span class="property-type">오브젝트</span></dt>
<dd>리턴코드, <code>message</code>, <code>signal</code>프로퍼티를 포함하는 오브젝트(<code>message</code>, <code>signal</code>는 이용가능한 경우)</dd>
</dl>
</li>
</ol>
<h3>상세</h3>
<p>기본값으로는, <code>exec</code>시스템콜을 사용하여 커맨드를 호출하여 완료를 기다리고, 출력을 반환합니다. Ω예를 들면, 커맨드의 실행이 성공한 경우에는, <code>{ code: 0 }</code>라는 반환값을 반환합니다.</p>
<p><code>spawn</code>를 사용하여 커맨드를 실행하고, 표준출력 및 표준에러출력을 반환하도록 할 수도 있습니다. 이 경우, 통상 1행 마다 값을 반환합니다. 커맨드의 실행이 완료되면, 3번째 단자에 오브젝트를 출력합니다. 예를 들면, 커맨드의 실행이 성공한 경우에는, <code>{ code: 0 }</code>라는 값을 반환합니다.</p>
<p>에러 발생시에는, 3번째 단자의 <code>msg.payload</code><code>message</code>, <code>signal</code>등 부가정보를 반환합니다.</p>
<p>실행대상의 커맨드는 노드설정에서 정의합니다. <code>msg.payload</code>나 추가인수를 커맨드에 추가할 수도 있습니다.</p>
<p>커맨드 혹은 파라미터가 공백을 포함하는 경우에는, 앞뒤에 인용부를 붙입니다. - <code>"이것은 하나의 파라미터 입니다"</code></p>
<p>반환할 <code>payload</code>는 보통 <i>문자열</i>이자만, UTF8문자가 아닌 문자가 존재하면<i>버퍼</i>가 됩니다.</p>
<p>노드가 실행중인 경우, 스테이터스 아이콘과 PID를 표시합니다. 이 상태변화는 <code>Status</code>노드에서 감시할 수 있습니다.</p>
<h4>프로세스 정지</h4>
<p><code>msg.kill</code>을 수신하면, 실행중인 프로세스를 정지할 수 있습니다. <code>msg.kill</code>에는 송출할 시그널 종류를 지정합니다. 예를 들면, <code>SIGINT</code>, <code>SIGQUIT</code>, <code>SIGHUP</code>등 입니다. 빈 문자열을 지정한 경우에는, <code>SIGTERM</code>을 지정한 것으로 간주됩니다.</p>
<p>노드가 1개이상의 프로세스를 실행하고 있는 경우, <code>msg.pid</code>에 정지대상의 PID를 지정해야 합니다.</p>
<p><code>타임아웃</code>필드에 값을 지정하면, 지정한 초 이내에 커맨드가 완료되지 않는 경우, 프로세스를 자동적으로 정지합니다.</p>
<p>힌트: Python어플리케이션을 실행할 경우, <code>-u</code>를 지정하면 출력이 버퍼되는 것을 방지할 수 있습니다.</p>
</script>

View File

@ -0,0 +1,51 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="function">
<p>수신 메시지에 대해서 처리를 실시하는 JavaScript코드(함수의 본체)를 정의합니다.</p>
<p>입력 메시지는 <code>msg</code>라는 명칭의 JavaScript 객체로 전달됩니다.</p>
<p><code>msg</code>오브젝트는<code>msg.payload</code>프로퍼티에 메시지 본체를 유지하는 것이 관례입니다.</p>
<p>보통 코드는 메시지 오브젝트(혹은 여러 메시지 객체)를 반환합니다.후속 플로우의 실행을 정지하고 싶은 경우에는, 오브젝트를 반환하지 않아도 상관없습니다.</p>
<h3>상세</h3>
<p>코드 쓰는 방식에 대한 자세한 내용은, <a target="_blank" href="http://nodered.org/docs/writing-functions.html">공식 홈페이지</a>를 참조해 주세요.</p>
<h4>메세지 송신</h4>
<p>플로우 내의 다음 노드에 메세지를 전달하기 위해서는, 메세지를 반환하거나, <code>node.send(messages)</code>를 호출합니다.</p>
<p>반환/send 대상은 다음과 같습니다:</p>
<ul>
<li>단일 메세지 오브젝트 - 첫 출력에 접속된 노드에 전해집니다</li>
<li>메세지 오브젝트의 배열 - 대응하는 출력에 접속된 노드에 전해집니다</li>
</ul>
<p>배열요소가 배열인 경우에는, 복수의 메세지에 대응하는 출력에 송출합니다.</p>
<p>반환방법이 단일값인지 배열오소인지 상관없이, 반환값이 null인 경우 메세지를 송출하지 않습니다.</p>
<h4>로그 출력과 에러처리</h4>
<p>로그 정보의 출력, 에러출력을 실행하려면 아래의 함수를 이용합니다:</p>
<ul>
<li><code>node.log("로그메세지")</code></li>
<li><code>node.warn("경고")</code></li>
<li><code>node.error("에러")</code></li>
</ul>
</p>
<p>catch노드를 이용하여 에러를 처리할 수 있습니다. catch노드로 처리하기 위해서는, <code>msg</code><code>node.error</code>의 제2 인수로 전달합니다:</p>
<pre>node.error("에러",msg);</pre>
<h4>노드 정보 참조</h4>
<p>코드안에서 노드의 ID및 이름을 아래의 프로퍼티로 참조할 수 있습니다:</p>
<ul>
<li><code>node.id</code> - 노드 ID</li>
<li><code>node.name</code> - 노드의 별칭</li>
</ul>
<h4>환경변수의 이용</h4>
<p>환경변수는 <code>env.get("MY_ENV_VAR")</code>로 참조할 수 있습니다.</p>
</script>

View File

@ -0,0 +1,49 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="template">
<p>템플릿에 기초하여 프로퍼티를 설정합니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt>msg <span class="property-type">오브젝트</span></dt>
<dd>템플릿을 생성하기 위한 정보를 포함한 메세지 오브젝트</dd>
<dt class="optional">template <span class="property-type">문자열</span></dt>
<dd><code>msg.payload</code>를 생성하기 위한 템플릿. 편집패널에서 템플릿을 설정하지 않은 경우에는, 출력메세지로 이용합니다.</dd>
</dl>
<h3>출력</h3>
<dl class="message-properties">
<dt>msg <span class="property-type">오브젝트</span></dt>
<dd>지정한 템플릿과 입력메세지의 프로퍼티로 생성된 값을 설정한 메세지</dd>
</dl>
<h3>상세</h3>
<p>이 노드는 <i><a href="http://mustache.github.io/mustache.5.html" target="_blank">mustache</a></i>형식을 기본값으로 이용하지만, 사용하지 않도록 설정할 수도 있습니다.</p>
<p>예를 들면,
<pre>안녕하세요, {{payload.name}}씨. 오늘은 {{date}}입니다.</pre>
<p>라는 템플릿에 대해,
<pre>{
date: "월요일"
payload: {
name: "홍길동",
}
}</pre>
<p>이라는 메세지를 수신한 경우,</p>
<pre>안녕하세요, 홍길동씨. 오늘은 월요일입니다.</pre>
<p>라는 프로퍼티가 생성됩니다.</p>
<p>플로우 컨텍스트 혹은 글로벌 컨텍스트의 프로퍼티값을 사용할 수도 있습니다. 각각, <code>{{flow.이름}}</code>혹은 <code>{{global.이름}}</code>을 사용합니다. 또는, 퍼시스터블스토어(<code>store</code>)에 대해서는, <code>{{flow[store].이름}}</code>혹은
<code>{{global[store].이름}}</code>을 사용합니다.
</p>
<p><b>주: </b>기본값으로는, <i>mustache</i>형식은 치환대상인 HTML요소를 이스케이프합니다. 이것을 방지하기 위해서는 <code>{{{3중}}}</code>괄호형식을 사용해야 합니다.</p>
</script>

View File

@ -0,0 +1,30 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="delay">
<p>노드를 통과하는 메세지를 지연, 혹은 유량을 제한합니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt class="optional">delay <span class="property-type">수치</span></dt>
<dd>메세지의 지연시간을 밀리초 단위로 설정합니다. 이것은 노드 설정에서 기본값의 지연시간을 덮어쓸 수 있도록 노드를 설정한 경우에만 적용됩니다.</dd>
<dt class="optional">reset</dt>
<dd>수신메세지에서 이 프로퍼티를 임의의 값으로 설정하면, 노드가 유지하는 모든 미송신 메세지를 클리어합니다.</dd>
</dl>
<h3>상세</h3>
<p>메세지를 지연시키도록 설정한 경우, 지연시간은 고정값, 범위내의 난수값, 메세지 마다의 동적인 지정값 중 하나를 지정할 수 있습니다.</p>
<p>유량을 제한할 경우, 메세지는 지정된 시간간격내에 분산되어 송신합니다. 큐에 남은 메세지 수는 노드의 스테이터스에 표시됩니다. 받은 중간 메세지를 파기할 수도 있습니다.</p>
<p>유량제한은 모든 메세지에 적용할 수도, <code>msg.topic</code>값으로 그룹화하여 적용할 수도 있습니다. 그룹화하면, 중간메세지는 자동적으로 파기됩니다. 시간간격별로 모든 토픽의 최신메세지를 송신할지, 다음 토픽의 최신메세지를 송신할지 지정할 수 있습니다.</p>
</script>

View File

@ -0,0 +1,33 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="trigger">
<p>메세지를 송신하면, 다른 메세지를 송신합니다. 지연 혹은 초기화가 지정되있지 않은 경우에는, 다음 2번째 메세지를 송신할 수 도 있습니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt class="optional">reset</dt>
<dd>이 프로퍼티를 갖는 메세지를 받으면, 준비중인 대기나 반복을 클리어하여 메세지 송신은 실행되지 않습니다.</dd>
</dl>
<h3>상세</h3>
<p>플로우 내에서 타임아웃을 작성하는데 이용합니다. 메세지를 받으면 기본값으로는 <code>payload</code><code>1</code>을 설정하여 송신합니다. 송신후 250ms 대기하고 <code>payload</code><code>0</code>에 설정한 2번째 메세지를 송신합니다. 이 기능은, 예를 들어 Raspberry Pi의 GPIO핀에 접속한 LED를 조작할때에 활동할 수 있습니다.</p>
<p>각 송신메세지의 페이로드는 다양한 종류의 값으로 설정할 수 있습니다. 재송신 데이터없음으로 하는 것도 가능합니다. 예를 들면, 재송신 데이터를 '<i>없음</i>'으로 하고, 메세지를 받았을 때에 지연을 연장하는 것으로 선택한 경우, trigger노드는 감시타이머로써 작동합니다. 즉, 지정한 간격내에 메세지를 수신하지 않은 경우에 메세지를 송신합니다.</p>
<p>페이로드에 <i>문자열</i>을 지정한 경우, mustache형식의 템플릿을 이용할 수 있습니다.</p>
<p><code>reset</code>프로퍼티를 갖는 메세지를 수신한 경우, 혹은 <code>payload</code>가 기정한 값에 매치하는 경우, 준비중인 대기나 반복을 클리어 하여 메세지 송신은 실행되지 않습니다.</p>
<p>수신메세지에서 리셋할 때까지 일정간격으로 메세지를 재송신하도록 지정할 수도 있습니다.</p>
<p><code>msg.topic</code>마다 다른 스트림으로 취급되도록 설정하는 것도 가능합니다.</p>
</script>

View File

@ -0,0 +1,21 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="comment">
<p>플로우에 코멘트를 기술하기 위해 이용합니다.</p>
<h3>상세</h3>
<p>편집패널은 Markdown형식으로 기입가능 합니다. 입력한 텍스트는 '정보'사이드패널에 표시됩니다.</p>
</script>

View File

@ -0,0 +1,24 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="unknown">
<p>설치된 Node-RED가 인식할 수 없는 종류의 노드입니다.</p>
<h3>상세</h3>
<p><i>이 종류의 노드를 배포한 경우, 설정은 유지되지만 부족한 노드를 설치할 때 까지 플로우를 시작할 수 없습니다.</i></p>
<p><code>메뉴 - 팔렛트 관리</code>를 사용하여 노드를 검색하여 설치하거나, <b>npm install &lt;모듈&gt;</b>으로 부족한 모듈을 설치하고 Node-RED를 재시작한 후, 노드를 다시 가져와야 합니다.</p>
<p>이 종류의 노드가 이미 설치되었지만, 라이브러리가 설치되지 않은 케이스도 있습니다. Node-RED의 로그를 참조하여 부족한 노드에 관련된 에러메세지를 확인하는게 좋습니다.</p>
<p>그래도 해결되지 않는 경우, 플로우 제작자에게 의뢰하여 부족한 노드의 복사본을 입수하여 주십시오.</p>
</script>

View File

@ -0,0 +1,71 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="rpi-gpio in">
<p>Raspberry Pi의 입력노드. 입력 핀의 상태에 따라, 0 또는 1의 값을 갖는 <code>msg.payload</code>을 생성합니다.</p>
<h3>출력</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">수치</span></dt>
<dd>페이로드에는, 0 또는 1이 설정됩니다.</dd>
<dt>topic <span class="property-type">문자열</span></dt>
<dd>토픽에는, <code>pi/{핀 번호}</code>가 설정됩니다.</dd>
</dl>
<h3>상세</h3>
<p>입력의 풀 업 저항 또는, 풀 다운 저항을 유효화 할 수도 있습니다.</p>
<p>작동하려면 RPi.GPIO python라이브러리 버젼0.5.10 (또는 그 이상)이 필요합니다.</p>
</script>
<script type="text/x-red" data-help-name="rpi-gpio out">
<p>Raspberry Pi의 출력노드. 디지털모드 또는 PWM모드에서 이용할 수 있습니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">수치 | 문자열 | 진위값</span></dt>
</dl>
<h3>상세</h3>
<p>디지털모드 - <code>msg.payload</code>에 0 또는 1 (혹은 true 또는 false) 을 지정하면, 입력값에 따라 선택된 물리핀에 high 또는 low를 설정합니다.</p>
<p>배포시에 핀의 초기값으로 0 또는 1을 설정할 수도 있습니다.</p>
<p>PWM모드 - 입력값에 0에서 100의 수치를 지정할수 있고, 소수값도 지정할수 있습니다.</p>
<p>서보제어에 PWM모드를 이용할수 있으며, 입력에 소수값을 포함한 10에서 20의 값을 지정할 수 있습니다.
PWM를 실행하는 하드웨어를 이용하여, PWM모드의 지정에는 GPIO2핀이 가장 적합합니다.
보다 좋은 서보제어를 원하는 경우에는, node-red-node-pi-gpiod 노드를 이용할 것을 검토해 주십시오.</p>
<p>작동하려면 RPi.GPIO python라이브러리 버젼0.5.10 (또는 그 이상)이 필요합니다.</p>
</script>
<script type="text/x-red" data-help-name="rpi-mouse">
<p>Raspberry Pi 의 마우스버튼노드. USB마우스가 필요합니다.</p>
<h3>출력</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">수치</span></dt>
<dd>선택된 마우스의 버튼이 눌려지거나 떨어졌을 경우에 1 또는 0이 설정됩니다.</dd>
<dt>button <span class="property-type">수치</span></dt>
<dd>좌, 우, 중앙 버튼에 따라 1, 2, 4 가 설정되어, 버튼 혹은 버튼의 조합에 따른 처리를 할 수 있습니다.</dd>
<dt>topic <span class="property-type">문자열</span></dt>
<dd><code>pi/mouse</code>이 설정됩니다.</dd>
</dl>
</script>
<script type="text/x-red" data-help-name="rpi-keyboard">
<p>Raspberry Pi 의 키보드를 제어하는 노드. USB키보드가 필요합니다.</p>
<h3>출력</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">수치</span></dt>
<dd>키 코드를 포함합니다.</dd>
<dt>action <span class="property-type">문자열</span></dt>
<dd>"up", "down", 또는 "repeat" 이 설정됩니다.</dd>
<dt>topic <span class="property-type">문자열</span></dt>
<dd><code>pi/key</code>가 설정됩니다.</dd>
</dl>
</script>

View File

@ -0,0 +1,19 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="tls-config">
<p>TLS접속을 위한 옵션 설정</p>
</script>

View File

@ -0,0 +1,22 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="http proxy">
<p>프록시를 위한 옵션 설정</p>
<h3>상세</h3>
<p>예외 호스트에 설정된 호스트에 액세스할 때에는, 프록시를 사용하지 않습니다.</p>
</script>

View File

@ -0,0 +1,74 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="mqtt in">
<p>MQTT브로커에 접속하여, 지정한 토픽의 메세지를 서브스크랩(구독)합니다.</p>
<h3>출력</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">문자열 | 버퍼</span></dt>
<dd>바이너리버퍼가 아닌 경우에는 문자열</dd>
<dt>topic <span class="property-type">문자열</span></dt>
<dd>MQTT의 토픽. /로 계층을 구분한다</dd>
<dt>qos <span class="property-type">수치</span> </dt>
<dd>0: 최대 1번 도착, 1: 1번 이상 도착, 2: 1번만 도착</dd>
<dt>retain <span class="property-type">진위값</span></dt>
<dd>true일 경우, 메세지를 유지. 메세지가 오래된 값인 경우가 있습니다.</dd>
</dl>
<h3>상세</h3>
<p>구독토픽에는 MQTT의 와일드카드(+: 1레벨, #: 복수레벨)을 포함할 수 있습니다.</p>
<p>이 노드를 이용하기 위해서는 MQTT브로커로의 접속설정이 필요합니다. 연필아이콘을 클릭하여 설정할 수 있습니다.</p>
<p>MQTT(in 및 out)노드는 브로커로의 접속설정을 필요에 따라 공유할 수 있습니다.</p>
</script>
<script type="text/x-red" data-help-name="mqtt out">
<p>MQTT브로커에 접속하여, 메세지를 퍼블리쉬(발행) 합니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">문자열 | 버퍼</span></dt>
<dd>보통 단순한 텍스트형식의 페이로드가 사용되지만, 바이너리버퍼를 발행하는 것도 가능합니다.</dd>
<dt class="optional">topic <span class="property-type">문자열</span></dt>
<dd>발생대상의 MQTT토픽</dd>
<dt class="optional">qos <span class="property-type">수치</span></dt>
<dd>0: 최대 1번 도착, 1: 1번 이상 도착, 2: 1번만 도착. 기본값은 0입니다.</dd>
<dt class="optional">retain <span class="property-type">진위값</span></dt>
<dd>true일 경우, 메세지를 브로커에 유지합니다. 기본값은 false입니다.</dd>
</dl>
<h3>상세</h3>
<p><code>msg.payload</code>를 발행하는 메세지의 페이로드로 이용됩니다. 페이로드가 오브젝트인 경우, 송신할 때에 JSON문자열로 변환합니다. 페이로드가 바이너리버퍼인 경우, 그대로 송신합니다.</p>
<p>발행에 이용하는 토픽은 노드에 설정하거나, <code>msg.topic</code>으로 지정합니다.</p>
<p>이와 같이, QoS와 retain도 노드 설정, 혹은 노드의 설정이 공란일 경우에는, 각각 <code>msg.qos</code><code>msg.retain</code>으로 지정할 수 있습니다. 앞서 브로커에 보존한 토픽을 클리어 하려면, retain플래그를 설정하여 해당 토픽에 빈 메세지를 발행합니다.</p>
<p>이 노드를 이용하기 위해서는 MQTT브로커로의 접속설정이 필요합니다. 연필아이콘을 클릭하여 설정할 수 있습니다.</p>
<p>MQTT(in 및 out)노드는 브로커로의 접속설정을 필요에 따라 공유할 수 있습니다.</p>
</script>
<script type="text/x-red" data-help-name="mqtt-broker">
<p>MQTT브로커로의 접속설정</p>
<p>브로커로의 접속설정을 작성합니다. 설정은 <code>MQTT In</code><code>MQTT Out</code>노드로 재이용할 수 있습니다.</p>
<p>노드에 클라이언트 ID를 설정하지 않고 세션의 초기화를 설정하고 있는 경우, 랜덤한 클라이언트 ID를 생성합니다. 클라이언트 ID를 설정할 경우, 접속처의 브로커로 통일되게 해주세요.</p>
<h4>Birth메세지</h4>
<p>접속을 확립했을 때, 설정한 토픽에 대하여 발행되는 메세지</p>
<h4>Close메세지</h4>
<p>접속이 정상적으로 종료되기 전에, 노드의 재배포 혹은 셧다운 되었을 경우에, 설정된 토픅에 대하여 발행하는 메세지</p>
<h4>Will메세지</h4>
<p>예기치 못한 접속이 절단되었을 경우에, 브로커가 발행하는 메세지</p>
<h4>WebSocket</h4>
<p>WebSocket에 의한 접속을 실행하도록 설정할 수 있습니다. WebSocket을 이용하려면, 서버필드에 접속처 URI를 완전한 형식으로 기입합니다. 아래에 그 예시를 표시합니다.</p>
<pre>ws://example.com:4000/mqtt</pre>
</script>

View File

@ -0,0 +1,81 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="http in">
<p>HTTP 엔드 포인트를 작성하여 Web 서비스를 구성합니다.</p>
<h3>출력</h3>
<dl class="message-properties">
<dt>payload</dt>
<dd>GET리퀘스트의 경우, 쿼리 파라미터로 구성된 오브젝트. 그 이외의 경우, HTTP 리퀘스트의 본체를 가리킵니다.</dd>
<dt>req<span class="property-type">오브젝트</span></dt>
<dd>HTTP 리퀘스트 오브젝트. 오브젝트는 리퀘스트의 정보에 관한 복수의 속성을 포함합니다.
<ul>
<li><code>body</code> - リ리퀘스트 본체. 형식은 리퀘스트에 의존합니다</li>
<li><code>headers</code> - HTTP 리퀘스트 헤더를 포함한 오브젝트</li>
<li><code>query</code> - 쿼리 파라미터를 포함한 오브젝트</li>
<li><code>params</code> - 루팅 파라미터를 포함한 오브젝트</li>
<li><code>cookies</code> - 리퀘스트 쿠키를 포함한 오브젝트</li>
<li><code>files</code> - POST리퀘스트에서 파일의 업로드가 설정으로 유효화 되어 있는 경우, 업로드 대상 파일</li>
</ul>
</dd>
<dt>res<span class="property-type">오브젝트</span></dt>
<dd>HTTP 리스폰스 오브젝트. 이 프로퍼티를 직접 이용하는 것은 권장하지 않습니다. 리퀘스트의 처리 방법에 대해서는 <code>HTTP Response</code>ノ노드의 문서를 참조해 주세요.이 프로퍼티는 response노드에 전하는 메세지에 붙인 상태로 두십시오.</dd>
</dl>
<h3>상세</h3>
<p>이 노드는 설정으로 지정한 패스와 리퀘스트종류로 리퀘스트를 기다립니다. 패스 지정은 완전하게 지정하는 형식(예: <code>/user</code>), 혹은 임의의 값을 대기하는 이름있는 파라미터를 포함한 형식(예: <code>/user/:name</code>) 어떤 것도 상관없습니다. 이름있는 파라미터를 지정한 경우, 리퀘스트에서 지정한 실제 값은 <code>msg.req.params</code>로 참조할 수 있습니다.</p>
<p>POST나 PUT처럼 리퀘스트바디를 포함한 리퀘스트의 경우, 리퀘스트 내용은 <code>msg.payload</code>로 참조할 수 있습니다.</p>
<p>리퀘스트의 요소타입이 식별가능한 경우에는, 리퀘스트바디를 적절한 형식으로 변환합니다. 예를 들면, <code>application/json</code>은 JavaScript오브젝트로 변환합니다.</p>
<p><b>주:</b> 이 노드는 리퀘스트에 대한 레스폰스 송신을 하지 않습니다. 리퀘스트를 처리하기 위해서는 플로우에 HTTP Response노드를 포함해 주십시오.</p>
</script>
<script type="text/x-red" data-help-name="http response">
<p>HTTP In노드에서 받은 리퀘스트에 대한 레스폰스를 반환합니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">문자열</span></dt>
<dd>레스폰스 본체</dd>
<dt class="optional">statusCode <span class="property-type">수치</span></dt>
<dd>설정하면 레스폰스의 스테이터스 코드로 합니다. 기본값 : 200</dd>
<dt class="optional">headers <span class="property-type">오브젝트</span></dt>
<dd>설정하면 레스폰스의 HTTP헤더로 합니다.</dd>
<dt class="optional">cookies <span class="property-type">오브젝트</span></dt>
<dd>설정하면 쿠키를 설정 혹은 삭제하기 위해 사용합니다.</dd>
</dl>
<h3>상세</h3>
<p><code>statusCode</code><code>headers</code>는 노드의 설정에서 지정할 수도 있습니다. 노드설정에서 프로퍼티를 지정한 경우에는, 대응하는 메세지 프로퍼티는 사용하지 않습니다.</p>
<h4>쿠키 처리</h4>
<p><code>cookies</code>는 키/값 으로 구성된 오브젝트 입니다. 값에는 기본옵션을 사용하여 쿠키값으로 설장할 문자열, 혹은 옵션을 포함한 오브젝트를 지정할 수 있습니다.</p>
<p>아래의 예에서는 2개의 쿠키를 설정하고 있습니다. 1번째는 <code>name</code>으로 그 값은 <code>nick</code>, 2번째는 <code>session</code>으로 값은 <code>1234</code>, 유효기간으로 15분을 지정하고 있습니다.</p>
<pre>
msg.cookies = {
name: 'nick',
session: {
value: '1234',
maxAge: 900000
}
}</pre>
<p>유효한 옵션은 아래와 같습니다.</p>
<ul>
<li><code>domain</code> - (문자열) 쿠키의 도메인 지정</li>
<li><code>expires</code> - (일시) GMT표현으로 된 유효기한. 미지정 혹은 0으로 지정한 경우, 세션종료시 까지 유효</li>
<li><code>maxAge</code> - (문자열) 현 시각으로 부터 경과된 밀리초로 표현된 유효기한</li>
<li><code>path</code> - (문자열) 쿠키의 패스. 기본값은 '/'</li>
<li><code>value</code> - (문자열) 쿠키 값</li>
</ul>
<p>쿠키를 삭제하려면, <code>value</code><code>null</code>을 설정합니다.</p>
</script>

View File

@ -0,0 +1,75 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="http request">
<p>HTTP리퀘스트를 송신하여, 리스폰스를 반환합니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt class="optional">url <span class="property-type">문자열</span></dt>
<dd>노드 설정해서 지정하지 않은 경우, 이 프로퍼티로 리퀘스트의 url을 설정합니다.</dd>
<dt class="optional">method <span class="property-type">문자열</span></dt>
<dd>노드 설정해서 지정하지 않은 경우, 이 프로퍼티로 리퀘스트에 사용할 HTTP메소드를 설정합니다. <code>GET</code>, <code>PUT</code>, <code>POST</code>, <code>PATCH</code>, <code>DELETE</code>중 하나를 지정해 주세요.</dd>
<dt class="optional">headers <span class="property-type">오브젝트</span></dt>
<dd>리퀘스트의 HTTP헤더를 지정합니다.</dd>
<dt class="optional">cookies <span class="property-type">오브젝트</span></dt>
<dd>설정하면, 리퀘스트와 함께 쿠키를 보낼 수 있습니다.</dd>
<dt class="optional">payload</dt>
<dd>리퀘스트바디로써 보내는 데이터데이터</dd>
<dt class="optional">rejectUnauthorized</dt>
<dd><code>false</code>를 셋팅하면, 자기서명증명서를 사용하는 https사이트로의 리퀘스트를 허가합니다.</dd>
<dt class="optional">followRedirects</dt>
<dd><code>false</code>를 셋팅하면, 리다이렉트 하지않습니다. 기본값은 <code>true</code>입니다.</dd>
</dl>
<h3>출력</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">문자열 | 오브젝트 | 버퍼</span></dt>
<dd>리스폰스바디. 반환할 바디데이터를 문자열, JSON문자열로써 해석한 결과, 바이너리버퍼 그대로, 어떤 것으로 할지를 노드설정에 의해 지정할 수 있습니다.</dd>
<dt>statusCode <span class="property-type">수치</span></dt>
<dd>리스폰스의 스테이터스코드 혹은 리퀘스트가 완료되지 않은 경우의 에러코드.</dd>
<dt>headers <span class="property-type">오브젝트</span></dt>
<dd>리스폰스헤더를 포함하는 오브젝트</dd>
<dt>responseUrl <span class="property-type">문자열</span></dt>
<dd>리퀘스트 처리시에 리다이렉트가 발생한 경우, 이 프로퍼티가 마지막으로 리다이렉트된 URL을 표시합니다. 리다이렉트가 일어나지 않았을 경우, 첫 리퀘스트URL을 표시합니다.</dd>
<dt>responseCookies <span class="property-type">오브젝트</span></dt>
<dd>리스폰스가 쿠키르 포함하는 경우, 이 프로퍼티는 각 쿠키의 이름/값을 포함하는 오브젝트를 표시합니다.</dd>
<dt>redirectList <span class="property-type">배열</span></dt>
<dd>리퀘스트가 1번이상 리다이렉트된 경우, 이 프로퍼티에 정보가 축적됩니다. `location`은, 리다이렉트처를 나타냅니다. `cookies`는, 리다이렉트가 시작된 곳에서 반환된 쿠키정보입니다.</dd>
</dl>
<h3>상세</h3>
<p>노드 설정에서 url프로퍼티를 지정할 경우, <a href="http://mustache.github.io/mustache.5.html" target="_blank">mustache형식</a>의 태그를 포함할 수 있습니다. 이로 인해, URL을 입력메세지 값으로 구성할 수 있습니다. 예를 들면, url이 <code>example.com/{{{topic}}}</code>인 경우, <code>msg.topic</code>의 값에 의한 치환작업을 자동적으로 실행합니다. {{{...}}}표기를 사용하면, /, &이라는 문자를 mustache가 이스케이프 하는 것을 막을 수 있습니다.</p>
<p><b></b>: proxy서버를 이용하고 있는 경우, 환경변수 <code>http_proxy=...</code>를 설정하여 Node-RED를 재시작하거나, 혹은 노드 설정에서 프록시를 설정해 주십시오. 만약 노드 설정에서 프록시를 설정한 경우, 환경변수 보다 이쪽의 설정이 우선시 됩니다.</p>
<h4>복수의 HTTP리퀘스트 노드 이용</h4>
<p>같은 플로우에서 이 노드를 여러개 사용하기 위해서는, <code>msg.headers</code>프로퍼티의 취급에 주의해야 합니다. 예를 들면, 첫 노드가 리스폰스헤더에 이 프로퍼티를 설정하고, 다음 노드가 이 프로퍼티를 리퀘스트헤더에 이용하는 것은 일반적으로 기대되는 작동법이 아닙니다. <code>msg.headers</code>프로퍼티를 노드간에서 변경하지 않았다면, 2번째 노드에서 무시됩니다. 커스텀헤더를 설정하기 위해서는, <code>msg.headers</code>를 먼저 삭제 혹은 빈 오브젝트<code>{}</code>로 리셋합니다.
<h4>쿠키의 취급</h4>
<p>노드에 <code>cookies</code>프로퍼티를 전달할 경우, 그 값은 키/값 쌍으로 된 오브젝트로 해 주십시오. 값에는 쿠키값으로 설정할 문자열, 혹은 단일의 <code>value</code>프로퍼티를 포함하는 오브젝트를 지정할 수 있습니다.</p>
<p>리퀘스트에 대해 반환된 쿠키는 <code>responseCookies</code>프로퍼티에 격납됩니다.</p>
<h4>요소 타입의 취급</h4>
<p><code>msg.payload</code>가 오브젝트인 경우, 리퀘스트의 요소 타입을 <code>msg.payload</code>에 자동적으로 설정하고, 바디ー를 JSON으로 변환합니다.</p>
<p>리퀘스트를 양식데이터로 인코딩 할 때에는, <code>msg.headers["content-type"]</code><code>application/x-www-form-urlencoded</code>으로 설정합니다.</p>
<h4>파일 업로드</h4>
<p><code>msg.headers["content-type"]</code><code>multipart/form-data</code>을 지정하면 파이러을 업로드 할 수 있습니다. 이 때, 노드의 <code>msg.payload</code>에 전달되는 데이터는 아래의 구조를 갖는 오브젝트로 합니다:</p>
<pre><code>{
"KEY": {
"value": FILE_CONTENTS,
"options": {
"filename": "FILENAME"
}
}
}</code></pre>
<p><code>KEY</code>, <code>FILE_CONTENTS</code><code>FILENAME</code>에는 적절한 값을 설정해 주세요.</p>
</script>

View File

@ -0,0 +1,36 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="websocket in">
<p>WebSocket입력 노드</p>
<p>기본값으로는, WebSocket에 의해 수신한 데이터는 <code>msg.payload</code>에 격납됩니다. 소켓은 JSON형식의 문자열을 대기하도록 설정할 수 있습니다. JSON형식의 문자열을 받으면, 오브젝트로 변환하여 메세지 전체로서 송신합니다.</p>
</script>
<script type="text/x-red" data-help-name="websocket out">
<p>WebSocket출력 노드</p>
<p>기본값으로는, <code>msg.payload</code>를 WebSocket경유로 송신합니다. 소켓은 <code>msg</code>전체를 JSON문자열로 인코딩하여 WebSocket을 통해 송신할 수 있습니다.</p>
<p>이 노드가 수신한 메세지가 WebSocket In노드가 생성한 것일 경우, 메세지는 플로우를 작동한 클라이언트에게 반환됩니다. 그 이외의 경우, 메세지는 접속하고 있는 모든 클라이언트에게 브로드캐스트 됩니다.</p>
<p>WebSocket In노드가 생성한 메세지를 브로드캐스트하고 싶은 경우에는, 플로우 안에서 <code>msg._session</code>프로퍼티를 삭제합니다.</p>
</script>
<script type="text/x-red" data-help-name="websocket-listener">
<p>이 설정노드는 패스를 지정하여 WebSocket서버의 엔드포인트를 작성합니다.</p>
</script>
<script type="text/x-red" data-help-name="websocket-client">
<p>이 설정노드는 지정한 URL에 WebSocket클라이언트를 접속합니다.</p>
</script>

View File

@ -0,0 +1,25 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="watch">
<p>디렉토리 혹은 파일의 변화를 감지합니다.</p>
<p>콤마로 나누어 디렉토리 및 파일 리스트를 지정합니다. 공백을 포함하는 경우, 인용부를 이용해 "..." 형태로 만들어 주세요.</p>
<p>Windows에서는, 2중 백슬래쉬\\를 디렉토리 이름으로 사용합니다.</p>
<p>실제로 변경된 파일의 풀패스명을 <code>msg.payload</code>으로, 감지대상 리스트의 문자열을 <code>msg.topic</code>으로 반환합니다.</p>
<p><code>msg.file</code>은 변경된 파일의 파일명을 나타냅니다. <code>msg.type</code>은 변경된 대상의 종류(<i>file</i> 혹은 <i>directory</i>)를, <code>msg.size</code>는 파일사이즈(바이트 수)를 나타냅니다.</p>
<p>Linux에서는 파일로 표현되는 <i>전부</i>를 감지대상으로 할 수 있습니다.</p>
<p><b>주: </b>감지대상인 디렉토리 혹은 파일은 존재해야 합니다. 대상 파일 혹은 디렉토리가 삭제된 경우, 다시 작성되어도 감지대상에서 제외됩니다.</p>
</script>

View File

@ -0,0 +1,35 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="tcp in">
<p>TCP로 부터의 입력을 수행합니다. 리모트 TCP포트에 접속하거나, 외부로부터의 접속을 접수합니다.</p>
<p><b>주: </b>1024번 보다 작은 번호의 포트를 액세스 하기 위해서는 root 혹은 administrator권한이 필요한 시스템도 있습니다.</p>
</script>
<script type="text/x-red" data-help-name="tcp out">
<p>TCP로의 출력을 수행합니다. 리모트 TCP포트로 접속, 외부로부터의 접속을 접수, 혹은 TCP In노드에서 받은 메세지로의 리플라이를 수행합니다.</p>
<p><code>msg.payload</code>만이 송신대상입니다.</p>
<p><code>msg.payload</code>가 바이너리데이터를 Base64인코딩의 문자열로 변환한 것일 경우, Base64디코딩 옵션을 지정하면 데이터를 바이너리로 변환하여 송신합니다.</p>
<p><code>msg._session</code>가 존재하지 않는 경우, 접속하고 있는 <b>모든</b> 클라이언트에게 송신합니다.</p>
<p><b>주: </b>1024번 보다 작은 번호의 포트를 액세스 하기 위해서는 root 혹은 administrator권한이 필요한 시스템도 있습니다.</p>
</script>
<script type="text/x-red" data-help-name="tcp request">
<p>간단한 TCP리퀘스트 노드. <code>msg.payload</code>를 서버의 TCP포트에 송신하여, 리스폰스를 기다립니다.</p>
<p>서버접속, "리퀘스트"송신, "리스폰스"수신을 수행합니다. 고정장의 문자수나 지정문자로의 매치, 첫 리플라이의 도착으로 부터 지정된 시간동안 대기, 데이터 도착 대기, 데이터 송신을 수행하여 리플라이를 기다리지 않고 접속을 즉시 삭제, 등의 동작을 선택할 수 있습니다.</p>
<p>리스폰스는 버퍼형식으로 <code>msg.payload</code>에 출력됩니다. 문자열로서 취급하기 위해서는, .toString()을 사용해 주세요.</p>
<p>TCP호스트의 포트번호설정을 공백으로 설정한 경우, <code>msg.host</code><code>msg.port</code>프로퍼티를 설정해야 합니다.</p>
</script>

View File

@ -0,0 +1,28 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="udp in">
<p>UDP입력 노드. <code>msg.payload</code>에 버퍼, 문자열, 혹은 Base64인코딩 문자열을 생성합니다. 멀티캐스트를 지원합니다.</p>
<p><code>msg.ip</code><code>msg.port</code>에 수신된 메세지의 IP주소와 포트를 설정합니다.</p>
<p><b>주: </b>1024번 보다 작은 번호의 포트를 액세스, 브로드캐스트 하기 위해서는 root 혹은 administrator권한이 필요한 시스템도 있습니다.</p>
</script>
<script type="text/x-red" data-help-name="udp out">
<p><code>msg.payload</code>를 지정된 UDP 호스트와 포트에 송신합니다. 멀티캐스트를 지원합니다.</p>
<p><code>msg.ip</code><code>msg.port</code>에 접속처를 설정할수 있지만, 노드설정이 우선시 됩니다.</p>
<p>브로드캐스트를 수행하기 위해서는, 주소를 로컬 브로드캐스트 IP주소로 설정하거나, 글로벌 브로드캐스트인 255.255.255.255를 시험해 주세요.</p>
<p><b>주: </b>1024번 보다 작은 번호의 포트를 액세스, 브로드캐스트 하기 위해서는 root 혹은 administrator권한이 필요한 시스템도 있습니다.</p>
</script>

View File

@ -0,0 +1,39 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="switch">
<p>프로퍼티 값에 의해 메세지를 분류합니다.</p>
<h3>상세</h3>
<p>수신한 메세지에 대해, 지정된 룰을 순서대로 평가하여 매치된 룰에 대응하는 출력포트에 메세지를 송출합니다.</p>
<p>처음에 룰이 매치된 시점에서 평가를 멈추는 것도 가능합니다.</p>
<p>평가 룰에는, 메세지 프로퍼티, 플로우/글로벌 컨텍스트 프로퍼티, JSONata식의 평가결과를 이용할 수 있습니다.</p>
<h4></h4>
<p>분류 룰은 아래의 4개로 나뉩니다.</p>
<ol>
<li><b>값(value)</b>룰 - 지정된 프로퍼티에 대해 평가</li>
<li><b>열(sequence)</b>룰 - 메세지 열에 대해 적용(메세지열을 split노드 등으로 생성)</li>
<li><b>JSONata식</b> - 메세지 전체에 대해 평가를 수행하여, 결과가 true인 경우에 매치</li>
<li><b>그 외</b> - 이 전의 룰에 매치되는 것이 없는 경우에 적용</li>
</ol>
<h4>주석</h4>
<p><code>is true/false</code><code>is null</code> 룰은, 타입에 대해 엄밀한 비교를 수행합니다. 타입 변환한 상태에서 비교는 하지 않습니다.</p>
<p><code>is empty</code> 룰은, 길이 0의 문자열/배열/버퍼/프로퍼티를 갖지않는 오브젝트를 출력합니다. <code>null</code><code>undefined</code>는 출력하지 않습니다.</p>
<h4>메세지 열의 취급</h4>
<p>switch노드는 입력메세지 열에 관한 정보를 유지하는 <code>msg.parts</code>를 기본값으로는 변경하지 않습니다.</p>
<p>'<b>메세지 열의 보정</b>' 옵션을 지정하면, 매치한 각 룰에 대해 새로운 메세지를 생성합니다. 이 모드에서는, switch노드는 새로운 메세지열을 송신하기 전에, 입력메세지를 내부에 축적합니다. <b>settings.js</b><code>nodeMessageBufferMaxLength</code>를 설장하면, 축적할 메세지 수를 제한할 수 있습니다.</p>
</script>

View File

@ -0,0 +1,33 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="change">
<p>메세지, 플로우컨텍스트, 글로벌컨텍스트의 프로퍼티를 변경, 삭제, 이동합니다.</p>
<p>룰을 여러개 지정한 경우, 정의된 순으로 적용됩니다.</p>
<h3>상세</h3>
<p>이용가능한 처리</p>
<dl class="message-properties">
<dt>대입</dt>
<dd>프로퍼티를 셋팅합니다. 설정값에는, 다양한 타입의 값, 메세지나 컨텍스트의 기존 프로퍼티를 이용할 수 있습니다.<dd>
<dt>치환</dt>
<dd>프로퍼티에 대해 검색과 치환을 수행합니다. 정규표현을 지정한 경우, '치환 후의 문자열'에는 <code>$1</code>와 같은 캡쳐그룹을 지정할 수 있습니다. 치환처리에서는, 룰이 완전히 매치된 경우에만 프로퍼티를 변경할 수 있습니다.</dd>
<dt>삭제</dt>
<dd>프로퍼티를 삭제합니다.</dd>
<dt>이동</dt>
<dd>프로퍼티의 이동 또는 이름변경을 수행합니다.</dd>
</dl>
<p>'JSONata식'에는 <a href="http://jsonata.org/" target="_new">JSONata</a>언어를 지정할 수 있습니다.</p>
</script>

View File

@ -0,0 +1,33 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="range">
<p>수치를 다른 범위 값으로 변환합니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">수치</span></dt>
<dd>수치를 지정합니다. 수치이외를 지정한 경우에는, 수치로 변환합니다. 변환할 수 없는 경우에는 에러가 발생합니다.</dd>
</dl>
<h3>출력</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">수치</span></dt>
<dd>새로운 범위로 변환한 결과 값.</dd>
</dl>
<h3>상세</h3>
<p>이 노드는 받은 수치를 선형스케일링 합니다. 기본값으로는, 결과 값은 노드에 설정한 범위내로 한정하지 않습니다.</p>
<p>'<i>입력값의 범위외의 값을 최소값/최대값으로 확대/축소</i>'를 지정하면, 값이 지정된 범위 밖의 값으로 변하지 않도록 합니다.</p>
<p>'<i>입력값의 범위외의 값을 범위폭으로 나눈 나머지로 해서 확대/축소</i>'를 지정하면, 결과를 범위폭으로 나눕니다.</p>
</script>

View File

@ -0,0 +1,138 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="split">
<p>메세지를 메세지열로 분할합니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">오브젝트 | 문자열 | 배열 | 버퍼</span></dt>
<dd><code>msg.payload</code>の타입에 의해 노드의 동작이 다릅니다.
<ul>
<li><b>문자열</b>/<b>버퍼</b> - 지정된 문자열 (기본값: <code>\n</code>), 버퍼열, 혹은 고정된 길이에 의해 메세지를 분할합니다.</li>
<li><b>배열</b> - 메세지을 배열요소마다 혹은 고정된 길이의 배열로 분할합니다.</li>
<li><b>오브젝트</b> - 키/값의 각 조합에 대해 메세지를 송신합니다.</li>
</ul>
</dd>
</dl>
<h3>출력</h3>
<dl class="message-properties">
<dt>parts<span class="property-type">오브젝트</span></dt>
<dd>원래 메세지를 어떻게 분할했는지에 관한 정보를 이 프로퍼티에 유지합니다. 예를 들면, <b>join</b>노드에 전하여, 메세지열을 하나의 메세지로 재구성할 수 있습니다. <code>parts</code>프로퍼티는 다음 프로퍼티를 포함합니다.
<ul>
<li><code>id</code> - 메세지그룹의 식별자</li>
<li><code>index</code> - 그룹 내의 위치</li>
<li><code>count</code> - 이미 알고 있는 경우, 그룹 내의 메세지수를 설정. 아래의 '스트림모드'를 참조</li>
<li><code>type</code> - 메세지 타입 - 문자열/배열/오브젝트/버퍼</li>
<li><code>ch</code> - 문자열 혹은 버퍼인 경우, 메세지를 분할하기 위해 사용한 문자열 혹은 바이트배열</li>
<li><code>key</code> - 오브젝트인 경우, 메세지가 생성된 부분의 키.
노드의 설정에 의해 키를 <code>msg.topic</code>등 다른 메세지프로퍼티로 복사할 수도 있습니다.</li>
<li><code>len</code> - 메세지를 고정된 길이로 분할한 경우의 길이.</li>
</ul>
</dd>
</dl>
<h3>상세</h3>
<p>이 노드는, 메세지열을 구성하는 메세지에 대해 공통 처리를 수행하고, <b>join</b>노드에서 하나의 메세지로 정리하는 플로우를 작성할 때에 유용합니다.</p>
<p><code>msg.parts</code>프로퍼티를 사용하여 원래의 메세지와 메세지열과의 대응관계를 기억합니다.</p>
<h4>스트림모드</h4>
<p>이 노드는 메세지열을 재구성하여 송신할 때에도 유용합니다. 예를 들면, 줄 바꿈 마지막의 커맨드를 송신하는 시리얼 디바이스에서는, 메세지의 마지막 커맨드부분이 끊어진 메세지를 송출하는 경우가 있습니다. '스트림모드'를 사용하여, 완결된 개별 커맨드에 메세지를 분할할 수 있습니다. 입력메세지 마지막에 미완성 부분이 있는 경우, <b>split</b>노드는 미완성부분을 기억하고, 다음에 수신한 메세지 앞부분에 추가합니다.</p>
<p>이 모드에서 처리할 때에는, 메세지 수를 미리 알 수 없기 때문에, <code>msg.parts.count</code>프로퍼티는 설정되지 않습니다. 따라서, <b>join</b>노드의 '자동모드'와 조합할 수 없습니다.</p>
</script>
<script type="text/x-red" data-help-name="join">
<p>메세지열를 결합하여 하나의 메세지로 만듭니다.</p>
<p>메세지의 결합에는 다음 3개의 모드를 이용할 수 있습니다.</p>
<dl>
<dt>자동</dt>
<dd><b>split</b>노드와 조합하여, split과 역으로 메세지를 결합하는 처리를 수행합니다.</dd>
<dt>수동</dt>
<dd>메세지열을 다양한 방법으로 결합합니다.</dd>
<dt>열의 집약</dt>
<dd>메세지열에 대해 지정한 식을 적용하여, 하나의 메세지로 집약합니다.</dd>
</dl>
<h3>입력</h3>
<dl class="message-properties">
<dt class="optional">parts<span class="property-type">오브젝트</span></dt>
<dd>자동적으로 메세지열을 결합하기 위해서는, 모든 메세지가 이 프로퍼티를 갖고 있어야합니다. <b>split</b>노드로 이 프로퍼티를 생성할 수 있지만, 독자적으로 생성해도 무관합니다. <code>parts</code>프로퍼티는 아래의 프로퍼티를 포함합니다.
<ul>
<li><code>id</code> - 메세지그룹의 식별자</li>
<li><code>index</code> - 그룹 내의 순서</li>
<li><code>count</code> - 그룹을 구성하는 메세지 수</li>
<li><code>type</code> - 메세지 타입 - string/array/object/buffer</li>
<li><code>ch</code> - 문자열 혹은 버퍼인 경우, 메세지를 분할할 때 사용한 문자열 혹은 바이트배열</li>
<li><code>key</code> - 오브젝트인 경우, 메세지가 생성된 부분의 키</li>
<li><code>len</code> - 메세지를 고정된 길이로 분할한 경우의 길이</li>
</ul>
</dd>
<dt class="optional">complete</dt>
<dd>설정되어 있는 경우, 유지하고 있는 메세지를 결합하여 송신합니다.</dd>
</dl>
<h3>상세</h3>
<h4>자동모드</h4>
<p>자동모드에서는, 입력메세지의 <code>parts</code>프로퍼티를 이용하여 메세지열을 결합합니다. 이 모드는 <b>split</b>노드의 처리의 역을 자동적으로 수행합니다.</p>
<h4>수동모드</h4>
<p>수동모드에서는, 메세지열을 다양한 결과로 결합할 수 있습니다.</p>
<ul>
<li><b>문자열</b>혹은 <b>버퍼</b> - 지정한 문자열 혹은 버퍼값을 기준으로 나누어 각 메세지의 지정된 프로퍼티를 결합</li>
<li><b>배열</b> - 지정프로퍼티 혹은 메세지 전체를 요소로 하는 배열</li>
<li><b>key/value오브젝트</b> - 입력메세지의 지정된 프로퍼티 값을 키로 하여, 프로퍼티값을 스토어한 오브젝트</li>
<li><b>통합오브젝트</b> - 각 메세지의 프로퍼티를 하나의 오브젝트로 통합</li>
</ul>
<p>출력메세지의 그 외의 프로퍼티는 메세지를 송신하기 직전의 메세지를 복사합니다.</p>
<p>'<i>합계값</i>'에는 출력메세지를 송신하기 전에 수신해야 할 메세지 수를 지정합니다. 오브젝트 출력인 경우, 이 합계값에 달하면 후속 메세지가 도착할 때 마다 메세지를 출력하도록 설정할 수도 있습니다.</p>
<p>'<i></i>'에는 신규 메세지를 송신하기 까지의 경과시간을설정합니다.</p>
<p><code>msg.complete</code>프로퍼티를 설정한 메세지를 수신하면, 출력메세지를 송신합니다. 이 때, 메세지열 수를 리셋합니다.</p>
<p><code>msg.reset</code>프로퍼티를 설정한 메세지를 수신하면, 부분적으로 수신된 메세지를 파기합니다. 이러한 메세지는 송신되지 않습니다. 이 때, 메세지열 수를 리셋합니다.</p>
<h4>열의 집약모드</h4>
<p>열의 집약모드를 선택하면, 메세지열을 구성하는 각각의 메세지에 대해 식을 적용햐여, 집약한 값을 이용하여 하나의 메세지를 구성합니다.</p>
<dl class="message-properties">
<dt>초기값</dt>
<dd>
집약의 초기값(<code>$A</code>)
</dd>
<dt>집약식</dt>
<dd>메세지그룹을 구성하는 각 메세지에 적용되는 JSONata식.
식의 평가결과는 다음 호출때에 집약값으로 전달합니다.
<ul>
<li><code>$A</code> 집약값</li>
<li><code>$I</code> 그룹 내의 메세지 순서</li>
<li><code>$N</code> 그룹 내의 메세지 수</li>
</ul>
</dd>
<dt>최종조정식</dt>
<dd>메세지그룹의 집약이 완료된 후에 적용되는 JSONata식. 임의로 지정가능 합니다. 식에는 아래의 특수변수를 참조할 수 있습니다.
<ul>
<li><code>$A</code> 집약값</li>
<li><code>$N</code> 그룹 내의 메세지 수</li>
</ul>
</dd>
<p>메세지그룹의 메세지에 대해, 기본값으로는 집약식은 처음 메세지로 부터 마지막 메세지에 대해 순서대로 적용됩니다. 적용을 역순으로 하는 것도 가능합니다.</p>
</dl>
<p><b>예:</b> 아래의 설정으로, 수 값의 메세지열에 대해 평균값을 계산합니다.
<ul>
<li><b>집약식</b>: <code>$A+payload</code></li>
<li><b>초기값</b>: <code>0</code></li>
<li><b>최종조정식</b>: <code>$A/$N</code></li>
</ul>
</p>
<h4>메세지의 축적</h4>
<p>이 노드의 처리에서는 메세지열의 처리를 위해 메세지를 내부에 축적합니다. <code>nodeMessageBufferMaxLength</code>를 지정하여 축적할 메세지의 최대값을 제한할 수 있습니다.</p>
</script>

View File

@ -0,0 +1,40 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="sort">
<p>메세지 열 혹은 배열형의 페이로드를 정렬합니다.</p>
<p><b>split</b>노드와 조합하여 메세지의 순서를 정렬할 수 있습니다.</p>
<p>아래의 정렬순서를 지정할 수 있습니다.</p>
<ul>
<li><b>오름차순</b></li>
<li><b>내림차순</b></li>
</ul>
<p>수치에 의한 정렬을 선택할 수도 있습니다.</p>
<p>메세지의 정렬을 수행하기 위한 정렬키는 <code>payload</code>프로퍼티 혹은 JSONata식을 이용할 수 있습니다. 배열형 페이로드의 정렬키에는, 요소값 혹은 JSONata식을 이용할 수 있습니다.</p>
<p>sort노드의 처리에서는 수신한 메세지가 <code>msg.parts</code>프로퍼티를 갖고있는 것을 상정하고 있습니다. split노드로 이 프로퍼티를 생성할 수 있지만, 독자적으로 생성해도 무관합니다. <code>parts</code>프로퍼티는 아래의 프로퍼티를 포함합니다.</p>
<p>
<ul>
<li><code>id</code> - 메세지 그룹의 식별자</li>
<li><code>index</code> - 그룹내의 순서</li>
<li><code>count</code> - 그룹을 구성하는 메세지의 수</li>
</ul>
</p>
<p><b>주:</b> 이 노드의 처리에서는 메세지를 내부에 축적합니다. 축적할 메세지의 최대값을 지정하여, 예기치 못한 메모리사용량의 증대를 막을수 있습니다. 기본값으로는 메세지 수를 제한하지 않습니다.
<ul>
<li><b>settings.js</b><code>nodeMessageBufferMaxLength</code>프로퍼티</li>
</ul>
</p>
</script>

View File

@ -0,0 +1,34 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="batch">
<p>지정한 룰에 의해 메세지열을 생성합니다.</p>
<h3>상세</h3>
<p>메세지열의 생성에는 아래의 3가지 모드를 이용할 수 있습니다.</p>
<dl>
<dt>입력 메세지 수로 그룹화</dt>
<dd>입력 메세지를 지정한 길이의 메세지열로 그룹화 합니다. 메세지열의 마지막 부분을 다음 메세지열의 앞에서 반복하는 메세지 수를 '<b>오버랩</b>'으로 지정할 수 있습니다.</dd>
<dt>입력간격(초)로 그룹화</dt>
<dd>지정한 시간간격내에 수신한 입력 메세지를 메세지열로 그룹화 합니다. 지정한 시간내에 메세지를 수신하지 않은 경우에, 공백의 메세지를 송신하도록 설정할 수도 있습니다.</dd>
<dt>메세지 그룹의 결합</dt>
<dd>입력 메세지를 결합하여, 하나의 메세지열로 만듭니다. 메세지열의 식별을 위해, 각 메세지는 <code>msg.topic</code>프로퍼티와 <code>msg.parts</code>프로퍼티를 갖고 있어야 합니다. 메세지열의 결합순은, <code>topic</code>값의 리스트로 하여 batch노드에 지정합니다.
</dd>
</dl>
<h4>메세지의 축적</h4>
<p>이 노드의 처리에서는 메세지열의 처리를 위해 메세지를 내부에 축적합니다. <b>settings.js</b><code>nodeMessageBufferMaxLength</code>를 지정하여 축적할 메세지의 최대수를 제한할 수 있습니다.</p>
</script>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,44 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="csv">
<p>CSV형식의 문자열과 그 JavaScript오브젝트 표현의 사이에서 쌍방향의 변환을 수행합니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">오브젝트 | 배열 | 문자열</span></dt>
<dd>JavaScript오브젝트, 배열, CSV문자열 중 하나</dd>
</dl>
<h3>출력</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">오브젝트 | 배열 | 문자열</span></dt>
<dd>
<ul>
<li>입력이 문자열인 경우, CSV로서 해석하여 CSV의 각 행을 키/값으로 구성된 JavaScript오브젝트를 생성합니다.
각 행마다 메세지를 송신할지, 오브젝트의 배열로 된 하나의 메세지를 송신할지를 선택할 수 있습니다.</li>
<li>입력이 JavaScript오브젝트인 경우, CSV문자열로의 변환을 수행합니다.</li>
<li>입력이 기본형인 배열의 경우, 1행의 CSV문자열로 변환합니다.</li>
<li>입력이 배열의 배열, 혹은 오브젝트의 배열인 경우, 복수행의 CSV문자열로 변환합니다.</li>
</ul>
</dd>
</dl>
<h3>상세</h3>
<p>'열 이름'에 컬럼명의 리스트를 지정할 수 있습니다. CSV에서 오브젝트로 변환을 수행할 때, 컬럼명을 프로퍼티명으로 사용합니다. '열 이름'대신에 CSV데이터의 첫번째 행에 컬럼명을 포함시킬 수 도 있습니다.</p>
<p>CSV로의 변환을 수행할 때에는, 오브젝트에서 취득해야 할 프로퍼티와 그 순서를 '열 이름'을 참조하여 결정합니다.</p>
<p>입력이 배열인 경우에는, '열 이름'은 컬럼명을 나타내는 행의 출력이 지정된 경우에만 사용합니다.</p>
<p><code>parts</code>프로퍼티가 정확하게 설정되어 있는 경우, 메세지열을 입력으로 접수합니다.</p>
<p>CSV를 복수의 메세지로 변환하여 출력할 경우, 출력이 메세지열이 되도록 <code>parts</code>프로퍼티를 설정합니다.</p>
<p><b>주:</b> 콤마 이외의 구분문자를 설정한 경우여도, '열 이름'은 콤마로 구분해 주세요.</p>
</script>

View File

@ -0,0 +1,33 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="html">
<p><code>msg.payload</code>에 격납한 HTML다큐멘트에서 CSS셀렉터를 사용하여 요소를 추출합니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">문자열</span></dt>
<dd>요소를 추출할 HTML문자열</dd>
<dt class="optional">select <span class="property-type">문자열</span></dt>
<dd>편집패널에서 셀렉터를 지정하지 않은 경우, 메세지의 프로퍼티로서 설정할 수 있습니다.</dd>
</dl>
<h3>출력</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">배열 | 문자열</span></dt>
<dd>결과는, 페이로드에 매치된 요소의 배열을 포함하는 단일 메세지, 혹은 매치된 요소 마다의 메세지 중 하나를 선택할 수 있습니다. 복수의 메세지를 송신하는 경우, 메세지에는 <code>parts</code>를 설정합니다.</dd>
</dl>
<h3>상세</h3>
<p>이 노드는 CSS 및 jQuery셀렉터의 조합을 지원합니다. 이용가능한 구문의 자세한 사항은 <a href="https://github.com/fb55/CSSselect#user-content-supported-selectors" target="_blank">css-select documentation</a>를 참조해 주세요.</p>
</script>

View File

@ -0,0 +1,43 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="json">
<p>JSON문자열과 JavaScript오브젝트과의 사이에서 상호변환을 수행합니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">오브젝트 | 문자열</span></dt>
<dd>JavaScript오브젝트 혹은 JSON문자열</dd>
<dt>schema<span class="property-type">오브젝트</span></dt>
<dd>JSON의 검증에 이용할 JSON스키머. 설정되지 않은 경우에는 검증을 수행하지 않습니다.</dd>
</dl>
<h3>출력</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">오브젝트 | 문자열</span></dt>
<dd>
<ul>
<li>입력이 문자열인 경우, JSON으로 해석하여, JavaScript오브젝틀로 변환합니다.</li>
<li>입력이 JavaScript오브젝트인 경우, JSON문자열로 변환합니다. JSON문자열은 성형하는 것도 가능합니다.</li>
</ul>
</dd>
<dt>schemaError<span class="property-type">배열</span></dt>
<dd>JSON의 검증에서 에러가 발생한 경우, Catch노드를 이용하여 에러를 배열로서 <code>schemaError</code>프로퍼티에서 취득할 수 있습니다.</dd>
</dl>
<h3>상세</h3>
<p>기본값의 변환대상은 <code>msg.payload</code>이지만, 다른 메세지 프로퍼티를 변환대상으로 할 수도 있습니다.</p>
<p>쌍방향의 변환을 자동선택하는 것이 아닌, 특정한 변환만 수행하도록 설정할 수 있습니다. 예를 들면, <code>HTTP In</code>노드에 대한 리퀘스트가 content-type을 정확하게 설정하고 있지 않는 경우여도, JSON노드에 의한 변환결과가 JavaScript오브젝트인 것을 보증하기위해 이용합니다.</p>
<p>JSON문자열로의 변환이 지정되지 않았을 경우, 수신된 문자열에 대해 더 이상의 체크를 하지 않습니다. 즉, 문자열이 JSON으로써 정확한지에 대한 검사나, 성형옵션을 지정하였더라도 성형처리를 실시하지 않습니다.</p>
<p>JSON스키머의 자세한 사항은, <a href="http://json-schema.org/latest/json-schema-validation.html">여기</a>를 참조해주세요.</p>
</script>

View File

@ -0,0 +1,49 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="xml">
<p>XML문자열과 JavaScript오브젝트와의 사이에서 상호변환을 수행합니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">오브젝트 | 문자열</span></dt>
<dd>JavaScript오브젝트 혹은 XML문자열</dd>
</dl>
<h3>출력</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">오브젝트 | 문자열</span></dt>
<dd>
<ul>
<li>입력이 문자열인 경우, XML로써 해석하여 JavaScript오브젝트로 변환합니다.</li>
<li>입력이 JavaScript오브젝트인 경우, XML문자열로 변환합니다.</li>
</ul>
</dd>
<dt class="optional">options <span class="property-type">오브젝트</span></dt>
<dd>내부에서 사용중인 XML로의 변환 라이브러리에 대해 옵션을 전달할 수 있습니다. 자세한 사항은 <a href="https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md#options" target="_blank">the xml2js docs</a>를 참조해 주세요.</dd>
</dl>
<h3>상세</h3>
<p>XML와 오브젝트의 사이에서의 변환을 수행할 경우, 기본값으로는 XML속성은 <code>$</code>이라는 명칭의 프로퍼티에 추가합니다.
텍스트의 내용은 <code>_</code>라는 이름의 프로퍼티에 추가합니다. 이러한 프로퍼티명은 노드의 설정에서 변경가능합니다.</p>
<p>예로 아래의 XML의 변환결과를 표시합니다.</p>
<pre>&lt;p class="tag"&gt;Hello World&lt;/p&gt;</pre>
<pre>{
"p": {
"$": {
"class": "tag"
},
"_": "Hello World"
}
}</pre>
</script>

View File

@ -0,0 +1,34 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="yaml">
<p>YAML형식의 문자열과 JavaScript오브젝트 사이에서 상호변환을 수행합니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">오브젝트 | 문자열</span></dt>
<dd>JavaScript오브젝트 혹은 YAML형식문자열</dd>
</dl>
<h3>출력</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">오브젝트 | 문자열</span></dt>
<dd>
<ul>
<li>입력이 YAML형식의 문자열인 경우, JavaScript오브젝트로 변환합니다.</li>
<li>입력이 JavaScript오브젝트인 경우, YAML형식의 문자열로 변환합니다.</li>
</ul>
</dd>
</dl>
</script>

View File

@ -0,0 +1,59 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed 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.
-->
<script type="text/x-red" data-help-name="file">
<p><code>msg.payload</code>를 파일로 내보냅니다. 내보내기는, 파일의 마지막에 추기 혹은 기존 내용의 치환을 선택할 수 있습니다. 또한, 파일을 삭제하는 것도 가능합니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt class="optional">filename <span class="property-type">문자열</span></dt>
<dd>대상 파일명을 노드에 설정하지않은 경우, 이 프로퍼티로 파일을 지정할 수 있습니다</dd>
</dl>
<h3>출력</h3>
<p>작성 완료시, 입력메세지를 출력단자로 송출합니다합니다.</p>
<h3>상세</h3>
<p>입력메세지의 페이로드를 파일의 마지막에 추가합니다. 줄 바꿈(\n)을 각 데이터의 마지막에 추가할 수도 있습니다.</p>
<p><code>msg.filename</code>을 사용하는 경우, 작성을 수행할 때 마다 파일을 클로즈 합니다. 보다 좋은 성능을 얻기 위해서는 파일명을 노드에 설정해 주세요.</p>
<p>추가하는 대신에, 파일전체를 덮어쓰기 하도록 설정할 수도 있습니다. 예를 들면, 이미지와 같은 바이너리 데이터를 파일로 작성하는 경우에는, 이 옵션을 지정하여, 줄 바꿈을 추가하는 옵션을 지정하지 않도록 합니다.</p>
<p>파일을 출력할 때의 인코딩은, 인코딩리스트에서 선택할 수 있습니다.</p>
<p>또한, 파일을 삭제할 수도 있습니다.</p>
</script>
<script type="text/x-red" data-help-name="file in">
<p>파일 내용을 문자열 혹은 바이너리버퍼로 불러옵니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt class="optional">filename <span class="property-type">문자열</span></dt>
<dd>불러올 대상 파일명을 노드에 설정하지않은 경우, 이 프로퍼티에서 파일을 지정할 수 있습니다</dd>
</dl>
<h3>출력</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">문자열 | 버퍼</span></dt>
<dd>파일 내용을 문자열 혹은 버퍼로 표현합니다</dd>
<dt class="optional">filename <span class="property-type">문자열</span></dt>
<dd>불러올 대상 파일명을 노드에 설정하지않은 경우, 이 프로퍼티에서 파일을 지정합니다</dd>
<dt class="optional">error <span class="property-type">오브젝트</span></dt>
<dd><i>비권장</i>: 설정에서 유효로 한 경우, 파일을 불러올 때에 에러가 발생하면 <code>payload</code>을 갖지 않고 <code>error</code>프로퍼티에 에러의 상세정보를 설정한 메세지를 송신합니다. 이 동작모드는 권장하지 않으며, 새로운 노드를 사용할 때의 기본값으로는 무효로 하고 있습니다. 자세한 사항은 아래를 참조해 주세요.</dd>
</dl>
<h3>상세</h3>
<p>파일이름은 절대패스에서 지정할 것을 권장합니다. 절대패스를 지정하지 않는 경우에는, Node-RED프로세스의 워킹디렉토리로부터의 상대패스로서 취급합니다.</p>
<p>Windows에서는 패스를 나누는 문자를(예를 들면, <code>\\유저\\이름</code>과 같이)이스케이프 할 필요가 있습니다.</p>
<p>텍스트파일인 경우, 행 마다 분할하여 각각 메세지를 송신할 수 있습니다. 또, 바이너리파일인 경우, 작은 덩어리의 버퍼로 분할하여 송신할 수 있습니다. 버퍼의 분할단위는 오퍼레이팅시스템에 의존하지만, 일반적으로 64k(Linux/Mac) 혹은 41k(Windows)입니다.</p>
<p>복수의 메세지로 분할하는 경우, 각 메세지에는 <code>parts</code>프로퍼티가 설정되어, 메세지열을 구성합니다.</p>
<p>주력형식이 문자열인 경우, 입력데이터의 인코딩을 인코딩리스트로부터 선택할 수 있습니다.</p>
<h4>구식의 에러 처리</h4>
<p>Node-RED 0.17보다 이전의 버젼에서는, 파일을 불러올 때에 에러가 발생하면 <code>payload</code>를 갖지않고 <code>error</code>프로퍼티에 에러의 상세정보를 설정한 메세지를 송신합니다. 이 동작모드는 권장하지 않으며, 새로운 노드를 사용할 때의 기본값으로는 무효로 하고 있습니다. 노드설정으로, 필요에 따라 이 모드를 유효화 할 수 있습니다.</p>
<p>에러는 Catch노드로 보충하여 처리할것을 권장합니다.</p>
</script>

View File

@ -18,18 +18,18 @@
"ajv": "6.10.0", "ajv": "6.10.0",
"body-parser": "1.18.3", "body-parser": "1.18.3",
"cheerio": "0.22.0", "cheerio": "0.22.0",
"content-type": "1.0.4",
"cookie-parser": "1.4.4", "cookie-parser": "1.4.4",
"cookie": "0.3.1", "cookie": "0.3.1",
"cors": "2.8.5", "cors": "2.8.5",
"cron": "1.7.0", "cron": "1.7.0",
"denque": "1.4.0", "denque": "1.4.1",
"fs-extra": "7.0.1", "fs-extra": "7.0.1",
"fs.notify": "0.0.4", "fs.notify": "0.0.4",
"hash-sum": "1.0.2", "hash-sum": "1.0.2",
"https-proxy-agent": "2.2.1", "https-proxy-agent": "2.2.1",
"is-utf8": "0.2.1", "is-utf8": "0.2.1",
"js-yaml": "3.13.0", "js-yaml": "3.13.0",
"media-typer": "1.0.1",
"mqtt": "2.18.8", "mqtt": "2.18.8",
"multer": "1.4.1", "multer": "1.4.1",
"mustache": "3.0.1", "mustache": "3.0.1",

View File

@ -18,7 +18,7 @@
"dependencies": { "dependencies": {
"@node-red/util": "0.21.0-alpha.0", "@node-red/util": "0.21.0-alpha.0",
"semver": "6.0.0", "semver": "6.0.0",
"uglify-js": "3.5.2", "uglify-js": "3.5.3",
"when": "3.7.8" "when": "3.7.8"
} }
} }

View File

@ -35,8 +35,8 @@ var authCache = require("./git/authCache");
// TODO: DRY - red/api/editor/sshkeys ! // TODO: DRY - red/api/editor/sshkeys !
function getSSHKeyUsername(userObj) { function getSSHKeyUsername(userObj) {
var username = '__default'; var username = '__default';
if ( userObj && userObj.name ) { if ( userObj && userObj.username ) {
username = userObj.name; username = userObj.username;
} }
return username; return username;
} }

View File

@ -76,6 +76,7 @@ function runGitCommand(args,cwd,env,emit) {
}) })
} }
function runGitCommandWithAuth(args,cwd,auth,emit) { function runGitCommandWithAuth(args,cwd,auth,emit) {
log.trace("runGitCommandWithAuth "+JSON.stringify(auth).replace(/("pass.*?"\s*:\s*").+?"/g,'$1[hidden]"'));
return authResponseServer(auth).then(function(rs) { return authResponseServer(auth).then(function(rs) {
var commandEnv = clone(process.env); var commandEnv = clone(process.env);
commandEnv.GIT_ASKPASS = path.join(__dirname,"node-red-ask-pass.sh"); commandEnv.GIT_ASKPASS = path.join(__dirname,"node-red-ask-pass.sh");
@ -93,6 +94,7 @@ function runGitCommandWithAuth(args,cwd,auth,emit) {
} }
function runGitCommandWithSSHCommand(args,cwd,auth,emit) { function runGitCommandWithSSHCommand(args,cwd,auth,emit) {
log.trace("runGitCommandWithSSHCommand "+JSON.stringify(auth).replace(/("pass.*?"\s*:\s*").+?"/g,'$1[hidden]"'));
return sshResponseServer(auth).then(function(rs) { return sshResponseServer(auth).then(function(rs) {
var commandEnv = clone(process.env); var commandEnv = clone(process.env);
commandEnv.SSH_ASKPASS = path.join(__dirname,"node-red-ask-pass.sh"); commandEnv.SSH_ASKPASS = path.join(__dirname,"node-red-ask-pass.sh");

View File

@ -16,7 +16,7 @@
], ],
"dependencies": { "dependencies": {
"clone": "2.1.2", "clone": "2.1.2",
"i18next": "15.0.8", "i18next": "15.0.9",
"json-stringify-safe": "5.0.1", "json-stringify-safe": "5.0.1",
"jsonata": "1.6.4", "jsonata": "1.6.4",
"when": "3.7.8" "when": "3.7.8"

View File

@ -19,22 +19,15 @@ var request = require('supertest');
var express = require('express'); var express = require('express');
var bodyParser = require('body-parser'); var bodyParser = require('body-parser');
var sinon = require('sinon'); var sinon = require('sinon');
var when = require('when');
var NR_TEST_UTILS = require("nr-test-utils"); var NR_TEST_UTILS = require("nr-test-utils");
var context = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/context"); var context = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/context");
// var Context = require("../../../../red/runtime/nodes/context");
// var Util = require("../../../../red/runtime/util");
describe("api/admin/context", function() { describe("api/admin/context", function () {
it.skip("NEEDS TESTS WRITING",function() {});
});
/*
var app = undefined; var app = undefined;
before(function (done) { before(function () {
var node_context = undefined;
app = express(); app = express();
app.use(bodyParser.json()); app.use(bodyParser.json());
app.get("/context/:scope(global)", context.get); app.get("/context/:scope(global)", context.get);
@ -42,196 +35,196 @@ describe("api/admin/context", function() {
app.get("/context/:scope(node|flow)/:id", context.get); app.get("/context/:scope(node|flow)/:id", context.get);
app.get("/context/:scope(node|flow)/:id/*", context.get); app.get("/context/:scope(node|flow)/:id/*", context.get);
context.init({ app.delete("/context/:scope(global)/*", context.delete);
settings: { app.delete("/context/:scope(node|flow)/:id/*", context.delete);
},
log:{warn:function(){},_:function(){},audit:function(){}},
nodes: {
listContextStores: Context.listStores,
getContext: Context.get,
getNode: function(id) {
if (id === 'NID') {
return {
id: 'NID',
context: function () {
return node_context;
}
};
}
return null;
}
},
util: Util
});
Context.init({
contextStorage: {
memory0: {
module: "memory"
},
memory1: {
module: "memory"
}
}
});
Context.load().then(function () {
var ctx = Context.get("NID", "FID");
node_context = ctx;
ctx.set("foo", "n_v00", "memory0");
ctx.set("bar", "n_v01", "memory0");
ctx.set("baz", "n_v10", "memory1");
ctx.set("bar", "n_v11", "memory1");
ctx.flow.set("foo", "f_v00", "memory0");
ctx.flow.set("bar", "f_v01", "memory0");
ctx.flow.set("baz", "f_v10", "memory1");
ctx.flow.set("bar", "f_v11", "memory1");
ctx.global.set("foo", "g_v00", "memory0");
ctx.global.set("bar", "g_v01", "memory0");
ctx.global.set("baz", "g_v10", "memory1");
ctx.global.set("bar", "g_v11", "memory1");
done();
});
}); });
after(function () { describe("get", function () {
Context.clean({allNodes:{}}); var gContext = {
Context.close(); default: { abc: { msg: '111', format: 'number' } },
}); file: { abc: { msg: '222', format: 'number' } }
};
var fContext = {
default: { bool: { msg: 'true', format: 'boolean' } },
file: { string: { msg: 'aaaa', format: 'string[7]' } }
};
var nContext = { msg: "1", format: "number" };
var stub = sinon.stub();
function check_mem(body, mem, name, val) { before(function () {
var mem0 = body[mem]; context.init({
mem0.should.have.property(name); context: {
mem0[name].should.deepEqual(val); getValue: stub
} }
function check_scope(scope, prefix, id) {
describe('# '+scope, function () {
var xid = id ? ("/"+id) : "";
it('should return '+scope+' contexts', function (done) {
request(app)
.get('/context/'+scope+xid)
.set('Accept', 'application/json')
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
var body = res.body;
body.should.have.key('memory0', 'memory1');
check_mem(body, 'memory0',
'foo', {msg:prefix+'_v00', format:'string[5]'});
check_mem(body, 'memory0',
'bar', {msg:prefix+'_v01', format:'string[5]'});
check_mem(body, 'memory1',
'baz', {msg:prefix+'_v10', format:'string[5]'});
check_mem(body, 'memory1',
'bar', {msg:prefix+'_v11', format:'string[5]'});
done();
});
});
it('should return a value from default '+scope+' context', function (done) {
request(app)
.get('/context/'+scope+xid+'/foo')
.set('Accept', 'application/json')
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
var body = res.body;
body.should.deepEqual({msg: prefix+'_v00', format: 'string[5]'});
done();
});
});
it('should return a value from specified '+scope+' context', function (done) {
request(app)
.get('/context/'+scope+xid+'/bar?store=memory1')
.set('Accept', 'application/json')
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
var body = res.body;
body.should.deepEqual({msg: prefix+'_v11', format: 'string[5]', store: 'memory1'});
done();
});
});
it('should return specified '+scope+' store', function (done) {
request(app)
.get('/context/'+scope+xid+'?store=memory1')
.set('Accept', 'application/json')
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
var body = res.body;
body.should.deepEqual({
memory1: {
baz: { msg: prefix+'_v10', format: 'string[5]' },
bar: { msg: prefix+'_v11', format: 'string[5]' }
}
});
done();
});
});
it('should return undefined for unknown key of default '+scope+' store', function (done) {
request(app)
.get('/context/'+scope+xid+'/unknown')
.set('Accept', 'application/json')
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
var body = res.body;
body.should.deepEqual({msg:'(undefined)', format:'undefined'});
done();
});
});
it('should cause error for unknown '+scope+' store', function (done) {
request(app)
.get('/context/'+scope+xid+'?store=unknown')
.set('Accept', 'application/json')
.expect(200)
.end(function (err, res) {
if (err) {
return done();
}
done("unexpected");
});
}); });
}); });
}
check_scope("global", "g", undefined); afterEach(function () {
check_scope("node", "n", "NID"); stub.reset();
check_scope("flow", "f", "FID"); });
describe("# errors", function () { it('should call context.getValue to get global contexts', function (done) {
it('should cause error for unknown scope', function (done) { stub.returns(Promise.resolve(gContext));
request(app) request(app)
.get('/context/scope') .get('/context/global')
.set('Accept', 'application/json') .set('Accept', 'application/json')
.expect(200) .expect(200)
.end(function (err, res) { .end(function (err, res) {
if (err) { if (err) {
return done(); return done(err);
} }
done("unexpected"); stub.args[0][0].should.have.property('user', undefined);
stub.args[0][0].should.have.property('scope', 'global');
stub.args[0][0].should.have.property('id', undefined);
stub.args[0][0].should.have.property('key', undefined);
stub.args[0][0].should.have.property('store', undefined);
var body = res.body;
body.should.eql(gContext);
done();
}); });
}); });
it('should call context.getValue to get flow contexts', function (done) {
stub.returns(Promise.resolve(fContext));
request(app)
.get('/context/flow/1234/')
.set('Accept', 'application/json')
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
stub.args[0][0].should.have.property('user', undefined);
stub.args[0][0].should.have.property('scope', 'flow');
stub.args[0][0].should.have.property('id', '1234');
stub.args[0][0].should.have.property('key', undefined);
stub.args[0][0].should.have.property('store', undefined);
var body = res.body;
body.should.eql(fContext);
done();
});
});
it('should call context.getValue to get a node context', function (done) {
stub.returns(Promise.resolve(nContext));
request(app)
.get('/context/node/5678/foo?store=file')
.set('Accept', 'application/json')
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
stub.args[0][0].should.have.property('user', undefined);
stub.args[0][0].should.have.property('scope', 'node');
stub.args[0][0].should.have.property('id', '5678');
stub.args[0][0].should.have.property('key', 'foo');
stub.args[0][0].should.have.property('store', 'file');
var body = res.body;
body.should.eql(nContext);
done();
});
});
it('should handle error which context.getValue causes', function (done) {
stub.returns(Promise.reject('error'));
request(app)
.get('/context/global')
.set('Accept', 'application/json')
.expect(400)
.end(function (err, res) {
if (err) {
return done(err);
}
res.body.should.has.a.property('code', 'unexpected_error');
res.body.should.has.a.property('message', 'error');
done();
});
});
}); });
describe("delete", function () {
var stub = sinon.stub();
before(function () {
context.init({
context: {
delete: stub
}
});
});
afterEach(function () {
stub.reset();
});
it('should call context.delete to delete a global context', function (done) {
stub.returns(Promise.resolve());
request(app)
.delete('/context/global/abc?store=default')
.expect(204)
.end(function (err, res) {
if (err) {
return done(err);
}
stub.args[0][0].should.have.property('user', undefined);
stub.args[0][0].should.have.property('scope', 'global');
stub.args[0][0].should.have.property('id', undefined);
stub.args[0][0].should.have.property('key', 'abc');
stub.args[0][0].should.have.property('store', 'default');
done();
});
});
it('should call context.delete to delete a flow context', function (done) {
stub.returns(Promise.resolve());
request(app)
.delete('/context/flow/1234/abc?store=file')
.expect(204)
.end(function (err, res) {
if (err) {
return done(err);
}
stub.args[0][0].should.have.property('user', undefined);
stub.args[0][0].should.have.property('scope', 'flow');
stub.args[0][0].should.have.property('id', '1234');
stub.args[0][0].should.have.property('key', 'abc');
stub.args[0][0].should.have.property('store', 'file');
done();
});
});
it('should call context.delete to delete a node context', function (done) {
stub.returns(Promise.resolve());
request(app)
.delete('/context/node/5678/foo?store=file')
.expect(204)
.end(function (err, res) {
if (err) {
return done(err);
}
stub.args[0][0].should.have.property('user', undefined);
stub.args[0][0].should.have.property('scope', 'node');
stub.args[0][0].should.have.property('id', '5678');
stub.args[0][0].should.have.property('key', 'foo');
stub.args[0][0].should.have.property('store', 'file');
done();
});
});
it('should handle error which context.delete causes', function (done) {
stub.returns(Promise.reject('error'));
request(app)
.delete('/context/global/abc?store=default')
.expect(400)
.end(function (err, res) {
if (err) {
return done(err);
}
res.body.should.has.a.property('code', 'unexpected_error');
res.body.should.has.a.property('message', 'error');
done();
});
});
});
}); });
*/

View File

@ -26,6 +26,7 @@ var auth = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth");
var nodes = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/nodes"); var nodes = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/nodes");
var flows = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/flows"); var flows = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/flows");
var flow = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/flow"); var flow = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/flow");
var context = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/context");
/** /**
* Ensure all API routes are correctly mounted, with the expected permissions checks * Ensure all API routes are correctly mounted, with the expected permissions checks
@ -34,8 +35,8 @@ describe("api/admin/index", function() {
describe("Ensure all API routes are correctly mounted, with the expected permissions checks", function() { describe("Ensure all API routes are correctly mounted, with the expected permissions checks", function() {
var app; var app;
var mockList = [ var mockList = [
flows,flow,nodes flows,flow,nodes,context
] ];
var permissionChecks = {}; var permissionChecks = {};
var lastRequest; var lastRequest;
var stubApp = function(req,res,next) { var stubApp = function(req,res,next) {
@ -50,7 +51,7 @@ describe("api/admin/index", function() {
return function(req,res,next) { return function(req,res,next) {
permissionChecks[permission] = (permissionChecks[permission]||0)+1; permissionChecks[permission] = (permissionChecks[permission]||0)+1;
next(); next();
} };
}); });
sinon.stub(flows,"get",stubApp); sinon.stub(flows,"get",stubApp);
@ -70,6 +71,9 @@ describe("api/admin/index", function() {
sinon.stub(nodes,"putSet",stubApp); sinon.stub(nodes,"putSet",stubApp);
sinon.stub(nodes,"getModuleCatalog",stubApp); sinon.stub(nodes,"getModuleCatalog",stubApp);
sinon.stub(nodes,"getModuleCatalogs",stubApp); sinon.stub(nodes,"getModuleCatalogs",stubApp);
sinon.stub(context,"get",stubApp);
sinon.stub(context,"delete",stubApp);
}); });
after(function() { after(function() {
mockList.forEach(function(m) { mockList.forEach(function(m) {
@ -92,15 +96,19 @@ describe("api/admin/index", function() {
nodes.putSet.restore(); nodes.putSet.restore();
nodes.getModuleCatalog.restore(); nodes.getModuleCatalog.restore();
nodes.getModuleCatalogs.restore(); nodes.getModuleCatalogs.restore();
context.get.restore();
context.delete.restore();
}); });
before(function() { before(function() {
app = adminApi.init({}); app = adminApi.init({});
}); });
beforeEach(function() { beforeEach(function() {
permissionChecks = {}; permissionChecks = {};
}) });
it('GET /flows', function(done) { it('GET /flows', function(done) {
request(app).get("/flows").expect(200).end(function(err,res) { request(app).get("/flows").expect(200).end(function(err,res) {
if (err) { if (err) {
@ -108,8 +116,9 @@ describe("api/admin/index", function() {
} }
permissionChecks.should.have.property('flows.read',1); permissionChecks.should.have.property('flows.read',1);
done(); done();
}) });
}); });
it('POST /flows', function(done) { it('POST /flows', function(done) {
request(app).post("/flows").expect(200).end(function(err,res) { request(app).post("/flows").expect(200).end(function(err,res) {
if (err) { if (err) {
@ -117,7 +126,7 @@ describe("api/admin/index", function() {
} }
permissionChecks.should.have.property('flows.write',1); permissionChecks.should.have.property('flows.write',1);
done(); done();
}) });
}); });
it('GET /flow/1234', function(done) { it('GET /flow/1234', function(done) {
@ -126,10 +135,11 @@ describe("api/admin/index", function() {
return done(err); return done(err);
} }
permissionChecks.should.have.property('flows.read',1); permissionChecks.should.have.property('flows.read',1);
lastRequest.params.should.have.property('id','1234') lastRequest.params.should.have.property('id','1234');
done(); done();
}) });
}); });
it('POST /flow', function(done) { it('POST /flow', function(done) {
request(app).post("/flow").expect(200).end(function(err,res) { request(app).post("/flow").expect(200).end(function(err,res) {
if (err) { if (err) {
@ -137,27 +147,29 @@ describe("api/admin/index", function() {
} }
permissionChecks.should.have.property('flows.write',1); permissionChecks.should.have.property('flows.write',1);
done(); done();
}) });
}); });
it('DELETE /flow/1234', function(done) { it('DELETE /flow/1234', function(done) {
request(app).del("/flow/1234").expect(200).end(function(err,res) { request(app).del("/flow/1234").expect(200).end(function(err,res) {
if (err) { if (err) {
return done(err); return done(err);
} }
permissionChecks.should.have.property('flows.write',1); permissionChecks.should.have.property('flows.write',1);
lastRequest.params.should.have.property('id','1234') lastRequest.params.should.have.property('id','1234');
done(); done();
}) });
}); });
it('PUT /flow/1234', function(done) { it('PUT /flow/1234', function(done) {
request(app).put("/flow/1234").expect(200).end(function(err,res) { request(app).put("/flow/1234").expect(200).end(function(err,res) {
if (err) { if (err) {
return done(err); return done(err);
} }
permissionChecks.should.have.property('flows.write',1); permissionChecks.should.have.property('flows.write',1);
lastRequest.params.should.have.property('id','1234') lastRequest.params.should.have.property('id','1234');
done(); done();
}) });
}); });
it('GET /nodes', function(done) { it('GET /nodes', function(done) {
@ -167,8 +179,9 @@ describe("api/admin/index", function() {
} }
permissionChecks.should.have.property('nodes.read',1); permissionChecks.should.have.property('nodes.read',1);
done(); done();
}) });
}); });
it('POST /nodes', function(done) { it('POST /nodes', function(done) {
request(app).post("/nodes").expect(200).end(function(err,res) { request(app).post("/nodes").expect(200).end(function(err,res) {
if (err) { if (err) {
@ -176,27 +189,29 @@ describe("api/admin/index", function() {
} }
permissionChecks.should.have.property('nodes.write',1); permissionChecks.should.have.property('nodes.write',1);
done(); done();
}) });
}); });
it('GET /nodes/module', function(done) { it('GET /nodes/module', function(done) {
request(app).get("/nodes/module").expect(200).end(function(err,res) { request(app).get("/nodes/module").expect(200).end(function(err,res) {
if (err) { if (err) {
return done(err); return done(err);
} }
permissionChecks.should.have.property('nodes.read',1); permissionChecks.should.have.property('nodes.read',1);
lastRequest.params.should.have.property(0,'module') lastRequest.params.should.have.property(0,'module');
done(); done();
}) });
}); });
it('GET /nodes/@scope/module', function(done) { it('GET /nodes/@scope/module', function(done) {
request(app).get("/nodes/@scope/module").expect(200).end(function(err,res) { request(app).get("/nodes/@scope/module").expect(200).end(function(err,res) {
if (err) { if (err) {
return done(err); return done(err);
} }
permissionChecks.should.have.property('nodes.read',1); permissionChecks.should.have.property('nodes.read',1);
lastRequest.params.should.have.property(0,'@scope/module') lastRequest.params.should.have.property(0,'@scope/module');
done(); done();
}) });
}); });
it('PUT /nodes/module', function(done) { it('PUT /nodes/module', function(done) {
@ -205,19 +220,20 @@ describe("api/admin/index", function() {
return done(err); return done(err);
} }
permissionChecks.should.have.property('nodes.write',1); permissionChecks.should.have.property('nodes.write',1);
lastRequest.params.should.have.property(0,'module') lastRequest.params.should.have.property(0,'module');
done(); done();
}) });
}); });
it('PUT /nodes/@scope/module', function(done) { it('PUT /nodes/@scope/module', function(done) {
request(app).put("/nodes/@scope/module").expect(200).end(function(err,res) { request(app).put("/nodes/@scope/module").expect(200).end(function(err,res) {
if (err) { if (err) {
return done(err); return done(err);
} }
permissionChecks.should.have.property('nodes.write',1); permissionChecks.should.have.property('nodes.write',1);
lastRequest.params.should.have.property(0,'@scope/module') lastRequest.params.should.have.property(0,'@scope/module');
done(); done();
}) });
}); });
it('DELETE /nodes/module', function(done) { it('DELETE /nodes/module', function(done) {
@ -226,19 +242,20 @@ describe("api/admin/index", function() {
return done(err); return done(err);
} }
permissionChecks.should.have.property('nodes.write',1); permissionChecks.should.have.property('nodes.write',1);
lastRequest.params.should.have.property(0,'module') lastRequest.params.should.have.property(0,'module');
done(); done();
}) });
}); });
it('DELETE /nodes/@scope/module', function(done) { it('DELETE /nodes/@scope/module', function(done) {
request(app).del("/nodes/@scope/module").expect(200).end(function(err,res) { request(app).del("/nodes/@scope/module").expect(200).end(function(err,res) {
if (err) { if (err) {
return done(err); return done(err);
} }
permissionChecks.should.have.property('nodes.write',1); permissionChecks.should.have.property('nodes.write',1);
lastRequest.params.should.have.property(0,'@scope/module') lastRequest.params.should.have.property(0,'@scope/module');
done(); done();
}) });
}); });
it('GET /nodes/module/set', function(done) { it('GET /nodes/module/set', function(done) {
@ -247,21 +264,22 @@ describe("api/admin/index", function() {
return done(err); return done(err);
} }
permissionChecks.should.have.property('nodes.read',1); permissionChecks.should.have.property('nodes.read',1);
lastRequest.params.should.have.property(0,'module') lastRequest.params.should.have.property(0,'module');
lastRequest.params.should.have.property(2,'set') lastRequest.params.should.have.property(2,'set');
done(); done();
}) });
}); });
it('GET /nodes/@scope/module/set', function(done) { it('GET /nodes/@scope/module/set', function(done) {
request(app).get("/nodes/@scope/module/set").expect(200).end(function(err,res) { request(app).get("/nodes/@scope/module/set").expect(200).end(function(err,res) {
if (err) { if (err) {
return done(err); return done(err);
} }
permissionChecks.should.have.property('nodes.read',1); permissionChecks.should.have.property('nodes.read',1);
lastRequest.params.should.have.property(0,'@scope/module') lastRequest.params.should.have.property(0,'@scope/module');
lastRequest.params.should.have.property(2,'set') lastRequest.params.should.have.property(2,'set');
done(); done();
}) });
}); });
it('PUT /nodes/module/set', function(done) { it('PUT /nodes/module/set', function(done) {
@ -270,21 +288,22 @@ describe("api/admin/index", function() {
return done(err); return done(err);
} }
permissionChecks.should.have.property('nodes.write',1); permissionChecks.should.have.property('nodes.write',1);
lastRequest.params.should.have.property(0,'module') lastRequest.params.should.have.property(0,'module');
lastRequest.params.should.have.property(2,'set') lastRequest.params.should.have.property(2,'set');
done(); done();
}) });
}); });
it('PUT /nodes/@scope/module/set', function(done) { it('PUT /nodes/@scope/module/set', function(done) {
request(app).put("/nodes/@scope/module/set").expect(200).end(function(err,res) { request(app).put("/nodes/@scope/module/set").expect(200).end(function(err,res) {
if (err) { if (err) {
return done(err); return done(err);
} }
permissionChecks.should.have.property('nodes.write',1); permissionChecks.should.have.property('nodes.write',1);
lastRequest.params.should.have.property(0,'@scope/module') lastRequest.params.should.have.property(0,'@scope/module');
lastRequest.params.should.have.property(2,'set') lastRequest.params.should.have.property(2,'set');
done(); done();
}) });
}); });
it('GET /nodes/messages', function(done) { it('GET /nodes/messages', function(done) {
@ -293,10 +312,10 @@ describe("api/admin/index", function() {
return done(err); return done(err);
} }
permissionChecks.should.have.property('nodes.read',1); permissionChecks.should.have.property('nodes.read',1);
done(); done();
}) });
}); });
it('GET /nodes/module/set/messages', function(done) { it('GET /nodes/module/set/messages', function(done) {
request(app).get("/nodes/module/set/messages").expect(200).end(function(err,res) { request(app).get("/nodes/module/set/messages").expect(200).end(function(err,res) {
if (err) { if (err) {
@ -305,8 +324,9 @@ describe("api/admin/index", function() {
permissionChecks.should.have.property('nodes.read',1); permissionChecks.should.have.property('nodes.read',1);
lastRequest.params.should.have.property(0,'module/set'); lastRequest.params.should.have.property(0,'module/set');
done(); done();
}) });
}); });
it('GET /nodes/@scope/module/set/messages', function(done) { it('GET /nodes/@scope/module/set/messages', function(done) {
request(app).get("/nodes/@scope/module/set/messages").expect(200).end(function(err,res) { request(app).get("/nodes/@scope/module/set/messages").expect(200).end(function(err,res) {
if (err) { if (err) {
@ -315,7 +335,124 @@ describe("api/admin/index", function() {
permissionChecks.should.have.property('nodes.read',1); permissionChecks.should.have.property('nodes.read',1);
lastRequest.params.should.have.property(0,'@scope/module/set'); lastRequest.params.should.have.property(0,'@scope/module/set');
done(); done();
}) });
});
it('GET /context/global', function(done) {
request(app).get("/context/global").expect(200).end(function(err,res) {
if (err) {
return done(err);
}
permissionChecks.should.have.property('context.read',1);
lastRequest.params.should.have.property('scope','global');
done();
});
});
it('GET /context/global/key?store=memory', function(done) {
request(app).get("/context/global/key?store=memory").expect(200).end(function(err,res) {
if (err) {
return done(err);
}
permissionChecks.should.have.property('context.read',1);
lastRequest.params.should.have.property('scope','global');
lastRequest.params.should.have.property(0,'key');
lastRequest.query.should.have.property('store','memory');
done();
});
});
it('GET /context/flow/1234', function(done) {
request(app).get("/context/flow/1234").expect(200).end(function(err,res) {
if (err) {
return done(err);
}
permissionChecks.should.have.property('context.read',1);
lastRequest.params.should.have.property('scope','flow');
lastRequest.params.should.have.property('id','1234');
done();
});
});
it('GET /context/flow/1234/key?store=memory', function(done) {
request(app).get("/context/flow/1234/key?store=memory").expect(200).end(function(err,res) {
if (err) {
return done(err);
}
permissionChecks.should.have.property('context.read',1);
lastRequest.params.should.have.property('scope','flow');
lastRequest.params.should.have.property('id','1234');
lastRequest.params.should.have.property(0,'key');
lastRequest.query.should.have.property('store','memory');
done();
});
});
it('GET /context/node/5678', function(done) {
request(app).get("/context/node/5678").expect(200).end(function(err,res) {
if (err) {
return done(err);
}
permissionChecks.should.have.property('context.read',1);
lastRequest.params.should.have.property('scope','node');
lastRequest.params.should.have.property('id','5678');
done();
});
});
it('GET /context/node/5678/foo?store=memory', function(done) {
request(app).get("/context/node/5678/foo?store=memory").expect(200).end(function(err,res) {
if (err) {
return done(err);
}
permissionChecks.should.have.property('context.read',1);
lastRequest.params.should.have.property('scope','node');
lastRequest.params.should.have.property('id','5678');
lastRequest.params.should.have.property(0,'foo');
lastRequest.query.should.have.property('store','memory');
done();
});
});
it('DELETE /context/global/key?store=memory', function(done) {
request(app).del("/context/global/key?store=memory").expect(200).end(function(err,res) {
if (err) {
return done(err);
}
permissionChecks.should.have.property('context.write',1);
lastRequest.params.should.have.property('scope','global');
lastRequest.params.should.have.property(0,'key');
lastRequest.query.should.have.property('store','memory');
done();
});
});
it('DELETE /context/flow/1234/key?store=memory', function(done) {
request(app).del("/context/flow/1234/key?store=memory").expect(200).end(function(err,res) {
if (err) {
return done(err);
}
permissionChecks.should.have.property('context.write',1);
lastRequest.params.should.have.property('scope','flow');
lastRequest.params.should.have.property('id','1234');
lastRequest.params.should.have.property(0,'key');
lastRequest.query.should.have.property('store','memory');
done();
});
});
it('DELETE /context/node/5678/foo?store=memory', function(done) {
request(app).del("/context/node/5678/foo?store=memory").expect(200).end(function(err,res) {
if (err) {
return done(err);
}
permissionChecks.should.have.property('context.write',1);
lastRequest.params.should.have.property('scope','node');
lastRequest.params.should.have.property('id','5678');
lastRequest.params.should.have.property(0,'foo');
lastRequest.query.should.have.property('store','memory');
done();
});
}); });
}); });
}); });

View File

@ -19,7 +19,7 @@ var should = require("should");
var sinon = require("sinon"); var sinon = require("sinon");
var NR_TEST_UTILS = require("nr-test-utils"); var NR_TEST_UTILS = require("nr-test-utils");
var context = NR_TEST_UTILS.require("@node-red/runtime/lib/api/context") var context = NR_TEST_UTILS.require("@node-red/runtime/lib/api/context");
var mockLog = () => ({ var mockLog = () => ({
log: sinon.stub(), log: sinon.stub(),
@ -29,8 +29,8 @@ var mockLog = () => ({
info: sinon.stub(), info: sinon.stub(),
metric: sinon.stub(), metric: sinon.stub(),
audit: sinon.stub(), audit: sinon.stub(),
_: function() { return "abc"} _: function() { return "abc";}
}) });
var mockContext = function(contents) { var mockContext = function(contents) {
return { return {
@ -41,51 +41,65 @@ var mockContext = function(contents) {
callback(null,undefined); callback(null,undefined);
} }
}, },
keys: function(store,callback) { set: function (key, value, store, callback) {
if (contents.hasOwnProperty(store)) { if (contents.hasOwnProperty(store)) {
callback(null,Object.keys(contents[store])); if (!value) {
delete contents[store][key];
callback(null);
}
} else {
callback("err store");
}
},
keys: function (store, callback) {
if (contents.hasOwnProperty(store)) {
callback(null, Object.keys(contents[store]));
} else { } else {
callback("err store"); callback("err store");
} }
} }
} };
} };
describe("runtime-api/context", function() { describe("runtime-api/context", function() {
describe("getValue", function() { var globalContext, flowContext, nodeContext, contexts;
var contexts = {
global: mockContext({ default: {abc:111}, file: {abc:222}}),
flow1: mockContext({ default: {abc:333}, file: {abc:444}})
}
var nodeContext = mockContext({ default: {abc:555}, file: {abc:666}})
beforeEach(function() { beforeEach(function() {
context.init({ globalContext = { default: { abc: 111 }, file: { abc: 222 } };
nodes: { flowContext = { default: { abc: 333 }, file: { abc: 444 } };
listContextStores: function() { nodeContext = { default: { abc: 555 }, file: { abc: 666 } };
return { default: 'default', stores: [ 'default', 'file' ] } contexts = {
}, global: mockContext(globalContext),
getContext: function(id) { flow1: mockContext(flowContext)
return contexts[id] };
}, context.init({
getNode: function(id) { nodes: {
if (id === 'known') { listContextStores: function() {
return { return { default: 'default', stores: [ 'default', 'file' ] };
context: function() { return nodeContext }
}
} else {
return null;
}
}
}, },
settings: { getContext: function(id) {
functionGlobalContext: { return contexts[id];
fgc:1234
}
}, },
log: mockLog() getNode: function(id) {
}) if (id === 'known') {
return {
context: function() { return mockContext(nodeContext); }
};
} else {
return null;
}
}
},
settings: {
functionGlobalContext: {
fgc:1234
}
},
log: mockLog()
}); });
});
describe("getValue", function() {
it('gets global value of default store', function() { it('gets global value of default store', function() {
return context.getValue({ return context.getValue({
scope: 'global', scope: 'global',
@ -95,8 +109,9 @@ describe("runtime-api/context", function() {
}).then(function(result) { }).then(function(result) {
result.should.have.property('msg','111'); result.should.have.property('msg','111');
result.should.have.property('format','number'); result.should.have.property('format','number');
}) });
}) });
it('gets global value of specified store', function() { it('gets global value of specified store', function() {
return context.getValue({ return context.getValue({
scope: 'global', scope: 'global',
@ -106,8 +121,9 @@ describe("runtime-api/context", function() {
}).then(function(result) { }).then(function(result) {
result.should.have.property('msg','222'); result.should.have.property('msg','222');
result.should.have.property('format','number'); result.should.have.property('format','number');
}) });
}) });
it('gets flow value of default store', function() { it('gets flow value of default store', function() {
return context.getValue({ return context.getValue({
scope: 'flow', scope: 'flow',
@ -117,8 +133,9 @@ describe("runtime-api/context", function() {
}).then(function(result) { }).then(function(result) {
result.should.have.property('msg','333'); result.should.have.property('msg','333');
result.should.have.property('format','number'); result.should.have.property('format','number');
}) });
}) });
it('gets flow value of specified store', function() { it('gets flow value of specified store', function() {
return context.getValue({ return context.getValue({
scope: 'flow', scope: 'flow',
@ -128,8 +145,9 @@ describe("runtime-api/context", function() {
}).then(function(result) { }).then(function(result) {
result.should.have.property('msg','444'); result.should.have.property('msg','444');
result.should.have.property('format','number'); result.should.have.property('format','number');
}) });
}) });
it('gets node value of default store', function() { it('gets node value of default store', function() {
return context.getValue({ return context.getValue({
scope: 'node', scope: 'node',
@ -139,8 +157,9 @@ describe("runtime-api/context", function() {
}).then(function(result) { }).then(function(result) {
result.should.have.property('msg','555'); result.should.have.property('msg','555');
result.should.have.property('format','number'); result.should.have.property('format','number');
}) });
}) });
it('gets node value of specified store', function() { it('gets node value of specified store', function() {
return context.getValue({ return context.getValue({
scope: 'node', scope: 'node',
@ -150,8 +169,8 @@ describe("runtime-api/context", function() {
}).then(function(result) { }).then(function(result) {
result.should.have.property('msg','666'); result.should.have.property('msg','666');
result.should.have.property('format','number'); result.should.have.property('format','number');
}) });
}) });
it('404s for unknown store', function(done) { it('404s for unknown store', function(done) {
context.getValue({ context.getValue({
@ -162,12 +181,11 @@ describe("runtime-api/context", function() {
}).then(function(result) { }).then(function(result) {
done("getValue for unknown store should not resolve"); done("getValue for unknown store should not resolve");
}).catch(function(err) { }).catch(function(err) {
err.should.have.property('code','not_found') err.should.have.property('code','not_found');
err.should.have.property('status',404); err.should.have.property('status',404);
done(); done();
}) });
}) });
it('gets all global value properties', function() { it('gets all global value properties', function() {
return context.getValue({ return context.getValue({
@ -180,8 +198,9 @@ describe("runtime-api/context", function() {
default: { abc: { msg: '111', format: 'number' } }, default: { abc: { msg: '111', format: 'number' } },
file: { abc: { msg: '222', format: 'number' } } file: { abc: { msg: '222', format: 'number' } }
}); });
}) });
}) });
it('gets all flow value properties', function() { it('gets all flow value properties', function() {
return context.getValue({ return context.getValue({
scope: 'flow', scope: 'flow',
@ -193,8 +212,9 @@ describe("runtime-api/context", function() {
default: { abc: { msg: '333', format: 'number' } }, default: { abc: { msg: '333', format: 'number' } },
file: { abc: { msg: '444', format: 'number' } } file: { abc: { msg: '444', format: 'number' } }
}); });
}) });
}) });
it('gets all node value properties', function() { it('gets all node value properties', function() {
return context.getValue({ return context.getValue({
scope: 'node', scope: 'node',
@ -206,8 +226,128 @@ describe("runtime-api/context", function() {
default: { abc: { msg: '555', format: 'number' } }, default: { abc: { msg: '555', format: 'number' } },
file: { abc: { msg: '666', format: 'number' } } file: { abc: { msg: '666', format: 'number' } }
}); });
}) });
}) });
}) it('gets empty object when specified context doesn\'t exist', function() {
return context.getValue({
scope: 'node',
id: 'non-existent',
store: 'file',
key: 'abc'
}).then(function(result) {
result.should.be.an.Object();
result.should.be.empty();
});
});
});
describe("delete", function () {
it('deletes global value of default store', function () {
return context.delete({
scope: 'global',
id: undefined,
store: undefined, // use default
key: 'abc'
}).then(function () {
globalContext.should.eql({
default: {}, file: { abc: 222 }
});
});
});
it('deletes global value of specified store', function () {
return context.delete({
scope: 'global',
id: undefined,
store: 'file',
key: 'abc'
}).then(function () {
globalContext.should.eql({
default: { abc: 111 }, file: {}
});
});
});
it('deletes flow value of default store', function () {
return context.delete({
scope: 'flow',
id: 'flow1',
store: undefined, // use default
key: 'abc'
}).then(function () {
flowContext.should.eql({
default: {}, file: { abc: 444 }
});
});
});
it('deletes flow value of specified store', function () {
return context.delete({
scope: 'flow',
id: 'flow1',
store: 'file',
key: 'abc'
}).then(function () {
flowContext.should.eql({
default: { abc: 333 }, file: {}
});
});
});
it('deletes node value of default store', function () {
return context.delete({
scope: 'node',
id: 'known',
store: undefined, // use default
key: 'abc'
}).then(function () {
nodeContext.should.eql({
default: {}, file: { abc: 666 }
});
});
});
it('deletes node value of specified store', function () {
return context.delete({
scope: 'node',
id: 'known',
store: 'file',
key: 'abc'
}).then(function () {
nodeContext.should.eql({
default: { abc: 555 }, file: {}
});
});
});
it('does nothing when specified context doesn\'t exist', function() {
return context.delete({
scope: 'node',
id: 'non-existent',
store: 'file',
key: 'abc'
}).then(function(result) {
should.not.exist(result);
nodeContext.should.eql({
default: { abc: 555 }, file: { abc: 666 }
});
});
});
it('404s for unknown store', function (done) {
context.delete({
scope: 'global',
id: undefined,
store: 'unknown',
key: 'abc'
}).then(function () {
done("delete for unknown store should not resolve");
}).catch(function (err) {
err.should.have.property('code', 'not_found');
err.should.have.property('status', 404);
done();
});
});
});
}); });