Initial commit

This commit is contained in:
zhongjin 2018-07-21 15:01:25 +08:00
commit f645903d19
22 changed files with 4728 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
node_modules
.idea
tmp
admin/i18n/flat.txt
admin/i18n/*/flat.txt
iob_npm.done
package-lock.json

9
.npmignore Normal file
View File

@ -0,0 +1,9 @@
gulpfile.js
tasks
tmp
test
.travis.yml
appveyor.yml
admin/i18n
iob_npm.done
package-lock.json

23
.travis.yml Normal file
View File

@ -0,0 +1,23 @@
os:
- linux
- osx
language: node_js
node_js:
- '4'
- '6'
- '8'
- '10'
before_script:
- export NPMVERSION=$(echo "$($(which npm) -v)"|cut -c1)
- 'if [[ $NPMVERSION == 5 ]]; then npm install -g npm@5; fi'
- npm -v
- npm install winston@2.3.1
- 'npm install https://git.spacen.net/yunkong2/yunkong2.js-controller/tarball/master --production'
env:
- CXX=g++-4.8
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015-2017 bluefox <dogafox@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

498
README.md Normal file
View File

@ -0,0 +1,498 @@
![Logo](admin/simple-api.png)
yunkong2 simple-api adapter
=================
[![NPM version](http://img.shields.io/npm/v/yunkong2.simple-api.svg)](https://www.npmjs.com/package/yunkong2.simple-api)
[![Downloads](https://img.shields.io/npm/dm/yunkong2.simple-api.svg)](https://www.npmjs.com/package/yunkong2.simple-api)
[![Tests](https://travis-ci.org/yunkong2/yunkong2.simple-api.svg?branch=master)](https://travis-ci.org/yunkong2/yunkong2.simple-api)
[![NPM](https://nodei.co/npm/yunkong2.simple-api.png?downloads=true)](https://nodei.co/npm/yunkong2.simple-api/)
This is RESTFul interface to read the objects and states from yunkong2 and to write/control the states over HTTP Get/Post requests.
## Usage
Call in browser ```http://ipaddress:8087/help``` to get the help about API. The result is:
```
{
"getPlainValue": "http://ipaddress:8087/getPlainValue/stateID",
"get": "http://ipaddress:8087/get/stateID/?prettyPrint",
"getBulk": "http://ipaddress:8087/getBulk/stateID1,stateID2/?prettyPrint",
"set": "http://ipaddress:8087/set/stateID?value=1&prettyPrint",
"toggle": "http://ipaddress:8087/toggle/stateID&prettyPrint",
"setBulk": "http://ipaddress:8087/setBulk?stateID1=0.7&stateID2=0&prettyPrint",
"objects": "http://ipaddress:8087/objects?pattern=system.adapter.admin.0*&prettyPrint",
"objects": "http://ipaddress:8087/objects?pattern=system.adapter.admin.0*&type=adapter&prettyPrint",
"states": "http://ipaddress:8087/states?pattern=system.adapter.admin.0*&prettyPrint"
}
```
### getPlainValue
Call e.g.:
```
http://ipaddress:8087/getPlainValue/system.adapter.admin.0.alive
```
Result:
```
true
```
### get
Call e.g.:
```
http://ipaddress:8087/get/system.adapter.admin.0.alive
```
Result:
```
{"val":true,"ack":true,"ts":1442432193,"from":"system.adapter.admin.0","lc":1442431190,"expire":23437,"_id":"system.adapter.admin.0.alive","type":"state","common":{"name":"admin.0.alive","type":"boolean","role":"indicator.state"},"native":{}}
```
or call e.g.:
```
http://ipaddress:8087/get/system.adapter.admin.0.alive?prettyPrint
```
Result:
```
{
"val": true,
"ack": true,
"ts": 1442432238,
"from": "system.adapter.admin.0",
"lc": 1442431190,
"expire": 28494,
"_id": "system.adapter.admin.0.alive",
"type": "state",
"common": {
"name": "admin.0.alive",
"type": "boolean",
"role": "indicator.state"
},
"native": {}
}
```
### getBulk
get many states with one request, returned as object with ID as key and val/ts as subobject
### set
Call e.g.:
```
http://ipaddress:8087/set/javascript.0.test?value=1
```
Result:
```
{"id":"javascript.0.test","value":1}
```
or call e.g.:
```
http://ipaddress:8087/set/javascript.0.test?value=1&prettyPrint
```
Result:
```
{
"id": "javascript.0.test",
"value": 1
}
```
Of course the data point *javascript.0.test* must exist.
### toggle
toggles value:
- boolean: true => false, false => true
- number without limits: x => 100-x
- number with limits: x => max - (x - min)
### setBulk
Set many states with one request. This request supports POST method too, for POST data should be in body and not URL.
### setValueFromBody
Allows to set the value of a given State be set by the POST body content.
### objects
### states
### help
Gives [this](#usage) output back
## Install
```node yunkong2.js add simple-api```
## Usage
Assume, we have no security and the server runs on default port 8087.
For all queries the name or id of the state can be specified.
For every requiest that returns JSON you can set parameter *prettyPrint* to get the output in human readable form.
If authentication is enabled, two other fields are mandatory: <pre>?user=admin&pass=yunkong2</pre>
### getPlainValue
Read state value as text. You can specify more ids divided by semicolon
<pre>http://ip:8087/getPlainValue/admin.0.memHeapTotal</pre>
<pre>
31.19
</pre>
<pre>http://ip:8087/getPlainValue/admin.0.memHeapTotal, admin.0.memHeapUsed</pre>
<pre>
31.19
17.52
</pre>
### get
Read state and object data of state as json. You can specify more ids divided by semicolon.
If more than one ID requested, the JSON array will be returned.
<pre>http://localhost:8087/get/admin.0.memHeapTotal/?prettyPrint</pre>
<pre>
{
"val": 31.19,
"ack": true,
"ts": 1423154619,
"from": "system.adapter.admin.0",
"lc": 1423153989,
"_id": "system.adapter.admin.0.memHeapTotal",
"type": "state",
"common": {
"name": "admin.0.memHeapTotal",
"type": "number",
"role": "indicator.state",
"unit": "MB",
"history": {
"enabled": true,
"changesOnly": true,
"minLength": 480,
"maxLength": 960,
"retention": 604800,
"debounce": 10000
}
},
"native": {}
}
</pre>
<pre>http://ip:8087/get/admin.0.memHeapTotal,admin.0.memHeapUsed/?prettyPrint</pre>
<pre>
[
{
"val": 31.19,
"ack": true,
"ts": 1423154544,
"from": "system.adapter.admin.0",
"lc": 1423153989,
"_id": "system.adapter.admin.0.memHeapTotal",
"type": "state",
"common": {
"name": "admin.0.memHeapTotal",
"type": "number",
"role": "indicator.state",
"unit": "MB",
"history": {
"enabled": true,
"changesOnly": true,
"minLength": 480,
"maxLength": 960,
"retention": 604800,
"debounce": 10000
}
},
"native": {}
},
{
"val": 16.25,
"ack": true,
"ts": 1423154544,
"from": "system.adapter.admin.0",
"lc": 1423154544,
"_id": "system.adapter.admin.0.memHeapUsed",
"type": "state",
"common": {
"name": "admin.0.memHeapUsed",
"type": "number",
"role": "indicator.state",
"unit": "MB",
"history": {
"enabled": true,
"changesOnly": true,
"minLength": 480,
"maxLength": 960,
"retention": 604800,
"debounce": 10000
}
},
"native": {}
}
]
</pre>
### getBulk
Read the states of more IDs with timestamp. You can specify more ids divided by semicolon.
Always the JSON array will be returned.
<pre>http://ip:8087/getBulk/admin.0.memHeapTotal,admin.0.memHeapUsed/?prettyPrint</pre>
<pre>
{
"admin.0.memHeapTotal": {
"val": 31.19,
"ts": 1423154754
},
"admin.0.memHeapUsed": {
"val": 15.6,
"ts": 1423154754
}
}
</pre>
### set
Write the states with specified IDs. You can specifiy *wait* option in milliseconds to wait for answer from driver.
<pre>http://ip:8087/set/hm-rpc.0.IEQ12345.LEVEL?value=1&prettyPrint</pre>
<pre>{
"id": "hm-rpc.0.IEQ12345.LEVEL",
"value": 1
}
</pre>
<pre>http://ip:8087/set/hm-rpc.0.IEQ12345.LEVEL?value=1&wait=5000&prettyPrint</pre>
<pre>{
"val": 1,
"ack": true,
"ts": 1423155399,
"from": "hm-rpc.0.IEQ12345.LEVEL",
"lc": 1423155399
}
</pre>
If no answer will be recieved in specified time, the *null* value will be returned.
In the first case the answer will be returned immediately and *ack* is false. In the second case *ack* is true. That means it was response from driver.
### setBulk
- write bulk of IDs in one request.
<pre>http://ip:8087/setBulk?hm-rpc.0.FEQ1234567:1.LEVEL=0.7&Anwesenheit=0&prettyPrint</pre>
<pre>
[
{
"id": "hm-rpc.0.FEQ1234567:1.LEVEL",
"val": "0.7"
},
{
"error": "error: datapoint \"Anwesenheit\" not found"
}
]
</pre>
You can send this request as POST too.
### objects
Get the list of all objects for pattern. If no pattern specified all objects as JSON array will be returned.
<pre>http://ip:8087/objects?prettyPrint</pre>
<pre>
{
"system.adapter.admin.0.uptime": {
"_id": "system.adapter.admin.0.uptime",
"type": "state",
"common": {
"name": "admin.0.uptime",
"type": "number",
"role": "indicator.state",
"unit": "seconds"
},
"native": {}
},
"system.adapter.admin.0.memRss": {
"_id": "system.adapter.admin.0.memRss",
"type": "state",
"common": {
"name": "admin.0.memRss",
"desc": "Resident set size",
"type": "number",
"role": "indicator.state",
"unit": "MB",
"history": {
"enabled": true,
"changesOnly": true,
"minLength": 480,
"maxLength": 960,
"retention": 604800,
"debounce": 10000
}
},
"native": {}
},
...
</pre>
Get all control objects of adapter system.adapter.admin.0:
<pre>http://ip:8087/objects?pattern=system.adapter.admin.0*&prettyPrint</pre>
<pre>
{
"system.adapter.admin.0.uptime": {
"_id": "system.adapter.admin.0.uptime",
"type": "state",
"common": {
"name": "admin.0.uptime",
"type": "number",
"role": "indicator.state",
"unit": "seconds"
},
"native": {}
},
...
</pre>
### states
Get the list of all states for pattern. If no pattern specified all states as JSON array will be returned.
<pre>http://ip:8087/states?prettyPrint</pre>
<pre>
{
"system.adapter.admin.0.uptime": {
"val": 32176,
"ack": true,
"ts": 1423156164,
"from": "system.adapter.admin.0",
"lc": 1423156164
},
"system.adapter.admin.0.memRss": {
"val": 41.14,
"ack": true,
"ts": 1423156164,
"from": "system.adapter.admin.0",
"lc": 1423156119
},
"system.adapter.admin.0.memHeapTotal": {
"val": 31.19,
"ack": true,
"ts": 1423156164,
"from": "system.adapter.admin.0",
"lc": 1423155084
},
...
</pre>
Get all control objects of adapter system.adapter.admin.0:
<pre>http://ip:8087/states?pattern=system.adapter.admin.0*&prettyPrint</pre>
<pre>
{
"system.adapter.admin.0.uptime": {
"val": 32161,
"ack": true,
"ts": 1423156149,
"from": "system.adapter.admin.0",
"lc": 1423156149
},
"system.adapter.admin.0.memRss": {
"val": 41.14,
"ack": true,
"ts": 1423156149,
"from": "system.adapter.admin.0",
"lc": 1423156119
},
"system.adapter.admin.0.memHeapTotal": {
"val": 31.19,
"ack": true,
"ts": 1423156149,
"from": "system.adapter.admin.0",
"lc": 1423155084
},
"system.adapter.admin.0.memHeapUsed": {
"val": 19.07,
"ack": true,
"ts": 1423156149,
"from": "system.adapter.admin.0",
"lc": 1423156149
},
"system.adapter.admin.0.connected": {
"val": true,
"ack": true,
"ts": 1423156149,
"from": "system.adapter.admin.0",
"lc": 1423128324,
"expire": 28100
},
"system.adapter.admin.0.alive": {
"val": true,
"ack": true,
"ts": 1423156149,
"from": "system.adapter.admin.0",
"lc": 1423128324,
"expire": 28115
}
}
</pre>
## Changelog
### 2.0.0 (2018-06-29)
* (Giermann) BREAKING CHANGE: getBulk is returning data in a different structure
### 1.6.3 (2018-04-15)
* (Apollon77) Return used character encoding (UTF-8)
### 1.6.2 (2017-11-27)
* (Apollon77) Fix decoding problems
### 1.6.1 (2017-09-25)
* (Apollon77) Fix statuscode for setBulk and optimize permission errors
### 1.6.0 (2017-07-10)
* (Apollon77) Fix handling of URL-encoded values, they are now decoded properly
* (Apollon77) Optimize Permission handling
* (Apollon77) add possibility to only allow access to states where user is also owner, finally works correct with js-controller 1.1.1!
### 1.5.0 (2017-03-10)
* (greyhound) Add new POST method setValueFromBody
### 1.4.0 (2017-01-05)
* (bluefox) new web server plugin support
### 1.3.0 (2016-08-30)
* (bluefox) сompatible only with new admin
### 1.2.0 (2016-08-27)
* (bluefox) support of letsencrypt certificates
### 1.1.1 (2016-07-06)
* (bluefox) support of chained certificates
### 1.1.0 (2016-02-09)
* (bluefox) fix toggle, objects, states, setBulk, POST
* (bluefox) add tests
### 1.0.0 (2015-09-30)
* (bluefox) stop adapter before update
### 0.1.2 (2015-06-28)
* (bluefox) add description in readme.md
* (bluefox) change "toggle" for boolean and numbers
### 0.1.1 (2015-06-28)
* (bluefox) change setForeignState api
* (bluefox) add type to io-package.json
* (bluefox) enable run from "web"
* (bluefox) add default user
### 0.1.0 (2015-06-10)
* (bluefox) change setForeignState api
* (bluefox) support of user permissions
### 0.0.4 (2015-03-11)
* (bluefox) remove socket.io from file
### 0.0.3 (2015-02-13)
* (bluefox) remove socket.io from dependencies
### 0.0.2 (2015-02-12)
* (bluefox) enable be a part of "web"
### 0.0.1 (2015-02-06)
* (bluefox) initial commit

176
admin/index.html Normal file
View File

@ -0,0 +1,176 @@
<html>
<link rel="stylesheet" type="text/css" href="../../lib/css/themes/jquery-ui/redmond/jquery-ui.min.css"/>
<script type="text/javascript" src="../../lib/js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="../../socket.io/socket.io.js"></script>
<script type="text/javascript" src="../../lib/js/jquery-ui-1.10.3.full.min.js"></script>
<link rel="stylesheet" type="text/css" href="../../css/adapter.css"/>
<script type="text/javascript" src="../../js/translate.js"></script>
<script type="text/javascript" src="../../js/adapter-settings.js"></script>
<style>
.number {
width: 70px
}
</style>
<script type="text/javascript" src="words.js"></script>
<script type="text/javascript">
function showHideSettings() {
if ($('#webInstance').val()) {
$('.no-if-extend').hide();
} else {
$('.no-if-extend').show();
if ($('#secure').prop('checked')) {
$('#_certPublic').show();
$('#_certPrivate').show();
$('#_certChained').show();
$('.le-settings').show();
if ($('#leEnabled').prop('checked')) {
$('.le-sub-settings').show();
if ($('#leUpdate').prop('checked')) {
$('.le-sub-settings-update').show();
} else {
$('.le-sub-settings-update').hide();
}
} else {
$('.le-sub-settings').hide();
}
} else {
$('#_certPublic').hide();
$('#_certPrivate').hide();
$('#_certChained').hide();
$('#auth').prop('checked', false);
$('.le-settings').hide();
}
if ($('#auth').prop('checked')) {
$('#defaultUser').val('admin');
$('.defaultUser').hide();
} else {
$('.defaultUser').show();
}
}
}
// the function loadSettings has to exist ...
function load(settings, onChange) {
if (!settings) return;
if (!settings.lePort) settings.lePort = 80;
getIPs(function(ips) {
for (var i = 0; i < ips.length; i++) {
$('#bind').append('<option value="' + ips[i].address + '">' + ips[i].name + '</option>');
}
$('#bind.value').val(settings.bind);
});
$('.value').each(function () {
var key = $(this).attr('id');
// example: select elements with id=key and class=value and insert value
if ($('#' + key + '.value').attr('type') == 'checkbox') {
$('#' + key + '.value').prop('checked', settings[key]).change(function() {
if ($('#auth').prop('checked')) {
$('#secure').prop('checked', true);
}
showHideSettings();
onChange();
});
} else {
$('#' + key + '.value').val(settings[key]).change(function() {
if (key === 'webInstance') showHideSettings();
onChange();
}).keyup(function() {
onChange();
});
}
});
fillSelectCertificates('#certPublic', 'public', settings.certPublic);
fillSelectCertificates('#certPublic', 'public', settings.certPublic);
fillSelectCertificates('#certPrivate', 'private', settings.certPrivate);
fillSelectCertificates('#certChained', 'chained', settings.certChained);
fillUsers('#defaultUser', settings.defaultUser);
if (typeof getExtendableInstances !== 'undefined') {
getExtendableInstances(function (result) {
if (result) {
var text = '';
for (var r = 0; r < result.length; r++) {
var name = result[r]._id.substring('system.adapter.'.length);
text += '<option value="' + name + '" ' + (settings.webInstance === name ? 'selected' : '') + '>' + name + '</option>';
}
$('#webInstance').append(text);
showHideSettings();
}
});
} else {
showHideSettings();
}
onChange(false);
}
// ... and the function save has to exist.
// you have to make sure the callback is called with the settings object as first param!
function save(callback) {
// example: select elements with class=value and build settings object
var obj = {};
$('.value').each(function () {
var $this = $(this);
if ($this.attr('type') == 'checkbox') {
obj[$this.attr('id')] = $this.prop('checked');
} else {
obj[$this.attr('id')] = $this.val();
}
});
if ($('#secure').prop('checked') && (!$('#certPrivate').val() || !$('#certPublic').val())) {
showMessage(_('Set certificates or load it first in the system settings (right top).'));
return;
}
callback(obj);
}
</script>
<!-- you have to put your config page in a div with id adapter-container -->
<div id="adapter-container">
<table><tr><td><img src="simple-api.png"></td><td><h3 class="translate">simpleAPI adapter settings</h3></td></tr></table>
<table>
<tr><td><label for="webInstance" class="translate">Extend WEB adapter:</label></td><td><select class="value" id="webInstance">
<option value="" class="translate">none</option>
<option value="*" class="translate">all</option>
</select></td></tr>
<tr class="no-if-extend"><td><label for="bind" class="translate">IP:</label></td><td> <select class="value" id="bind"></select></td></tr>
<tr class="no-if-extend"><td><label for="port" class="translate">Port:</label></td><td> <input class="value" id="port" size="5" maxlength="5"/></td></tr>
<tr class="no-if-extend"><td><label for="secure" class="translate">Secure(HTTPS):</label></td><td> <input class="value" id="secure" type="checkbox" /></td></tr>
<tr class="no-if-extend" id="_certPublic">
<td><label for="certPublic" class="translate">Public certificate:</label></td>
<td><select id="certPublic" class="value"></select></td>
</tr>
<tr class="no-if-extend" id="_certPrivate">
<td><label for="certPrivate" class="translate">Private certificate:</label></td>
<td><select id="certPrivate" class="value"></select></td>
</tr>
<tr class="no-if-extend" id="_certChained">
<td><label for="certChained" class="translate">Chained certificate:</label></td>
<td><select id="certChained" class="value"></select></td>
</tr>
<tr class="no-if-extend"><td><label for="auth" class="translate">Authentication:</label></td><td><input class="value" id="auth" type="checkbox" /></td></tr>
<tr class="no-if-extend defaultUser"><td><label for="defaultUser" class="translate">Run as:</label></td><td><select class="value" id="defaultUser"></select></td></tr>
<tr class="no-if-extend defaultUser"><td><label for="onlyAllowWhenUserIsOwner" class="translate">Allow only when User is Owner:</label></td><td><input class="value" id="onlyAllowWhenUserIsOwner" type="checkbox" /></td></tr>
<tr class="no-if-extend"><td colspan="2">&nbsp;</td></tr>
<tr class="no-if-extend le-settings"><td colspan="2"><h3 class="translate">Let's Encrypt settings</h3></tr>
<tr class="no-if-extend le-settings"><td><label for="leEnabled" class="translate">Use Lets Encrypt certificates:</label></td><td><input class="value" id="leEnabled" type="checkbox" /></td></tr>
<tr class="no-if-extend le-settings le-sub-settings"><td><label for="leUpdate" class="translate">Use this instance for automatic update:</label></td><td><input class="value" id="leUpdate" type="checkbox" /></td></tr>
<tr class="no-if-extend le-settings le-sub-settings le-sub-settings-update"><td><label for="lePort" class="translate">Port to check the domain:</label></td><td><input class="value number" id="lePort" type="number" size="5" maxlength="5" /></td></tr>
</table>
</div>
</html>

BIN
admin/simple-api.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

43
admin/words.js Normal file
View File

@ -0,0 +1,43 @@
systemDictionary = {
"simpleAPI adapter settings": {
"de": "simpleAPI adapter settings",
"ru": "simpleAPI adapter settings"
},
"Run as:": {"de": "Laufen unter Anwender:", "ru": "Запустить от пользователя:"},
"Allow only when User is Owner:": {"de": "Nue erlauben wenn Anwender auch Besitzer ist:", "ru": "Allow only when User is Owner:"},
"IP:": {"de": "IP:", "ru": "IP:"},
"Port:": {"de": "Port:", "ru": "Порт:"},
"Secure(HTTPS):": {"de": "Verschlüsselung(HTTPS):", "ru": "Шифрование(HTTPS):"},
"Authentication:": {"de": "Authentifikation:", "ru": "Аутентификация:"},
"Public certificate:": {"en": "Public certificate:", "de": "Publikzertifikat:", "ru": "'Public' сертификат:"},
"Private certificate:": {"en": "Private certificate:", "de": "Privatzertifikat:", "ru": "'Private' сертификат:"},
"Chained certificate:": {"en": "Chained certificate:", "de": "Kettenzertifikat:", "ru": "'Chained' сертификат:"},
"Extend WEB adapter:": {"en": "Extend WEB adapter:", "de": "Erweitere WEB Adapter:", "ru": "Подключить к WEB драйверу:"},
"all": {"en": "all", "de": "alle", "ru": "все"},
"Listen on all IPs": {"en": "Listen on all IPs", "de": "Alle IPs zulassen", "ru": "Открыть для всех IP адресов"},
"Let's Encrypt settings": {
"en": "Let's Encrypt settings",
"de": "Einstellungen Let's Encrypt",
"ru": "Настройкт Let's Encrypt"
},
"Use Lets Encrypt certificates:": {
"en": "Use Let's Encrypt certificates:",
"de": "Benutzen Let's Encrypt Zertifikate:",
"ru": "Использовать сертификаты Let's Encrypt:"
},
"Use this instance for automatic update:": {
"en": "Use this instance for automatic update:",
"de": "Benutze diese Instanz für automatische Updates:",
"ru": "Обновлять сертификаты в этом драйвере:"
},
"Port to check the domain:": {
"en": "Port to check the domain:",
"de": "Port um die Domain zu prüfen:",
"ru": "Порт для проверки доменного имени:"
},
"Set certificates or load it first in the system settings (right top).": {
"en": "Set certificates or load it first in the system settings (right top).",
"de": "Setze Zertificate oder lade die erst unter System/Einstellungen (oben rechts).",
"ru": "Нужно выбрать сертификаты или сначала загрузить их в системных настройках (вверху справа)."
}
};

25
appveyor.yml Normal file
View File

@ -0,0 +1,25 @@
version: 'test-{build}'
environment:
matrix:
- nodejs_version: '4'
- nodejs_version: '6'
- nodejs_version: '8'
- nodejs_version: '10'
platform:
- x86
- x64
clone_folder: 'c:\projects\%APPVEYOR_PROJECT_NAME%'
install:
- ps: 'Install-Product node $env:nodejs_version $env:platform'
- ps: '$NpmVersion = (npm -v).Substring(0,1)'
- ps: 'if($NpmVersion -eq 5) { npm install -g npm@5 }'
- ps: npm --version
- npm install
- npm install winston@2.3.1
- 'npm install https://git.spacen.net/yunkong2/yunkong2.js-controller/tarball/master --production'
test_script:
- echo %cd%
- node --version
- npm --version
- npm test
build: 'off'

401
gulpfile.js Normal file
View File

@ -0,0 +1,401 @@
'use strict';
var gulp = require('gulp');
var fs = require('fs');
var pkg = require('./package.json');
var iopackage = require('./io-package.json');
var version = (pkg && pkg.version) ? pkg.version : iopackage.common.version;
/*var appName = getAppName();
function getAppName() {
var parts = __dirname.replace(/\\/g, '/').split('/');
return parts[parts.length - 1].split('.')[0].toLowerCase();
}
*/
const fileName = 'words.js';
var languages = {
en: {},
de: {},
ru: {},
pt: {},
nl: {},
fr: {},
it: {},
es: {},
pl: {}
};
function lang2data(lang, isFlat) {
var str = isFlat ? '' : '{\n';
var count = 0;
for (var w in lang) {
if (lang.hasOwnProperty(w)) {
count++;
if (isFlat) {
str += (lang[w] === '' ? (isFlat[w] || w) : lang[w]) + '\n';
} else {
var key = ' "' + w.replace(/"/g, '\\"') + '": ';
str += key + '"' + lang[w].replace(/"/g, '\\"') + '",\n';
}
}
}
if (!count) return isFlat ? '' : '{\n}';
if (isFlat) {
return str;
} else {
return str.substring(0, str.length - 2) + '\n}';
}
}
function readWordJs(src) {
try {
var words;
if (fs.existsSync(src + 'js/' + fileName)) {
words = fs.readFileSync(src + 'js/' + fileName).toString();
} else {
words = fs.readFileSync(src + fileName).toString();
}
var lines = words.split(/\r\n|\r|\n/g);
var i = 0;
while (!lines[i].match(/^systemDictionary = {/)) {
i++;
}
lines.splice(0, i);
// remove last empty lines
i = lines.length - 1;
while (!lines[i]) {
i--;
}
if (i < lines.length - 1) {
lines.splice(i + 1);
}
lines[0] = lines[0].replace('systemDictionary = ', '');
lines[lines.length - 1] = lines[lines.length - 1].trim().replace(/};$/, '}');
words = lines.join('\n');
var resultFunc = new Function('return ' + words + ';');
return resultFunc();
} catch (e) {
return null;
}
}
function padRight(text, totalLength) {
return text + (text.length < totalLength ? new Array(totalLength - text.length).join(' ') : '');
}
function writeWordJs(data, src) {
var text = '// DO NOT EDIT THIS FILE!!! IT WILL BE AUTOMATICALLY GENERATED FROM src/i18n\n';
text += '/*global systemDictionary:true */\n';
text += '\'use strict\';\n\n';
text += 'systemDictionary = {\n';
for (var word in data) {
if (data.hasOwnProperty(word)) {
text += ' ' + padRight('"' + word.replace(/"/g, '\\"') + '": {', 50);
var line = '';
for (var lang in data[word]) {
if (data[word].hasOwnProperty(lang)) {
line += '"' + lang + '": "' + padRight(data[word][lang].replace(/"/g, '\\"') + '",', 50) + ' ';
}
}
if (line) {
line = line.trim();
line = line.substring(0, line.length - 1);
}
text += line + '},\n';
}
}
text = text.replace(/},\n$/, '}\n');
text += '};';
if (fs.existsSync(src + 'js/' + fileName)) {
fs.writeFileSync(src + 'js/' + fileName, text);
} else {
fs.writeFileSync(src + '' + fileName, text);
}
}
const EMPTY = '';
function words2languages(src) {
var langs = Object.assign({}, languages);
var data = readWordJs(src);
if (data) {
for (var word in data) {
if (data.hasOwnProperty(word)) {
for (var lang in data[word]) {
if (data[word].hasOwnProperty(lang)) {
langs[lang][word] = data[word][lang];
// pre-fill all other languages
for (var j in langs) {
if (langs.hasOwnProperty(j)) {
langs[j][word] = langs[j][word] || EMPTY;
}
}
}
}
}
}
if (!fs.existsSync(src + 'i18n/')) {
fs.mkdirSync(src + 'i18n/');
}
for (var l in langs) {
if (!langs.hasOwnProperty(l)) continue;
var keys = Object.keys(langs[l]);
//keys.sort();
var obj = {};
for (var k = 0; k < keys.length; k++) {
obj[keys[k]] = langs[l][keys[k]];
}
if (!fs.existsSync(src + 'i18n/' + l)) {
fs.mkdirSync(src + 'i18n/' + l);
}
fs.writeFileSync(src + 'i18n/' + l + '/translations.json', lang2data(obj));
}
} else {
console.error('Cannot read or parse ' + fileName);
}
}
function words2languagesFlat(src) {
var langs = Object.assign({}, languages);
var data = readWordJs(src);
if (data) {
for (var word in data) {
if (data.hasOwnProperty(word)) {
for (var lang in data[word]) {
if (data[word].hasOwnProperty(lang)) {
langs[lang][word] = data[word][lang];
// pre-fill all other languages
for (var j in langs) {
if (langs.hasOwnProperty(j)) {
langs[j][word] = langs[j][word] || EMPTY;
}
}
}
}
}
}
var keys = Object.keys(langs.en);
//keys.sort();
for (var l in langs) {
if (!langs.hasOwnProperty(l)) continue;
var obj = {};
for (var k = 0; k < keys.length; k++) {
obj[keys[k]] = langs[l][keys[k]];
}
langs[l] = obj;
}
if (!fs.existsSync(src + 'i18n/')) {
fs.mkdirSync(src + 'i18n/');
}
for (var ll in langs) {
if (!langs.hasOwnProperty(ll)) continue;
if (!fs.existsSync(src + 'i18n/' + ll)) {
fs.mkdirSync(src + 'i18n/' + ll);
}
fs.writeFileSync(src + 'i18n/' + ll + '/flat.txt', lang2data(langs[ll], langs.en));
}
fs.writeFileSync(src + 'i18n/flat.txt', keys.join('\n'));
} else {
console.error('Cannot read or parse ' + fileName);
}
}
function languagesFlat2words(src) {
var dirs = fs.readdirSync(src + 'i18n/');
var langs = {};
var bigOne = {};
var order = Object.keys(languages);
dirs.sort(function (a, b) {
var posA = order.indexOf(a);
var posB = order.indexOf(b);
if (posA === -1 && posB === -1) {
if (a > b) return 1;
if (a < b) return -1;
return 0;
} else if (posA === -1) {
return -1;
} else if (posB === -1) {
return 1;
} else {
if (posA > posB) return 1;
if (posA < posB) return -1;
return 0;
}
});
var keys = fs.readFileSync(src + 'i18n/flat.txt').toString().split('\n');
for (var l = 0; l < dirs.length; l++) {
if (dirs[l] === 'flat.txt') continue;
var lang = dirs[l];
var values = fs.readFileSync(src + 'i18n/' + lang + '/flat.txt').toString().split('\n');
langs[lang] = {};
keys.forEach(function (word, i) {
langs[lang][word] = values[i].replace(/<\/ i>/g, '</i>').replace(/<\/ b>/g, '</b>').replace(/<\/ span>/g, '</span>').replace(/% s/g, ' %s');
});
var words = langs[lang];
for (var word in words) {
if (words.hasOwnProperty(word)) {
bigOne[word] = bigOne[word] || {};
if (words[word] !== EMPTY) {
bigOne[word][lang] = words[word];
}
}
}
}
// read actual words.js
var aWords = readWordJs();
var temporaryIgnore = ['pt', 'fr', 'nl', 'flat.txt'];
if (aWords) {
// Merge words together
for (var w in aWords) {
if (aWords.hasOwnProperty(w)) {
if (!bigOne[w]) {
console.warn('Take from actual words.js: ' + w);
bigOne[w] = aWords[w]
}
dirs.forEach(function (lang) {
if (temporaryIgnore.indexOf(lang) !== -1) return;
if (!bigOne[w][lang]) {
console.warn('Missing "' + lang + '": ' + w);
}
});
}
}
}
writeWordJs(bigOne, src);
}
function languages2words(src) {
var dirs = fs.readdirSync(src + 'i18n/');
var langs = {};
var bigOne = {};
var order = Object.keys(languages);
dirs.sort(function (a, b) {
var posA = order.indexOf(a);
var posB = order.indexOf(b);
if (posA === -1 && posB === -1) {
if (a > b) return 1;
if (a < b) return -1;
return 0;
} else if (posA === -1) {
return -1;
} else if (posB === -1) {
return 1;
} else {
if (posA > posB) return 1;
if (posA < posB) return -1;
return 0;
}
});
for (var l = 0; l < dirs.length; l++) {
if (dirs[l] === 'flat.txt') continue;
var lang = dirs[l];
langs[lang] = fs.readFileSync(src + 'i18n/' + lang + '/translations.json').toString();
langs[lang] = JSON.parse(langs[lang]);
var words = langs[lang];
for (var word in words) {
if (words.hasOwnProperty(word)) {
bigOne[word] = bigOne[word] || {};
if (words[word] !== EMPTY) {
bigOne[word][lang] = words[word];
}
}
}
}
// read actual words.js
var aWords = readWordJs();
var temporaryIgnore = ['pt', 'fr', 'nl', 'it'];
if (aWords) {
// Merge words together
for (var w in aWords) {
if (aWords.hasOwnProperty(w)) {
if (!bigOne[w]) {
console.warn('Take from actual words.js: ' + w);
bigOne[w] = aWords[w]
}
dirs.forEach(function (lang) {
if (temporaryIgnore.indexOf(lang) !== -1) return;
if (!bigOne[w][lang]) {
console.warn('Missing "' + lang + '": ' + w);
}
});
}
}
}
writeWordJs(bigOne, src);
}
gulp.task('adminWords2languages', function (done) {
words2languages('./admin/');
done();
});
gulp.task('adminWords2languagesFlat', function (done) {
words2languagesFlat('./admin/');
done();
});
gulp.task('adminLanguagesFlat2words', function (done) {
languagesFlat2words('./admin/');
done();
});
gulp.task('adminLanguages2words', function (done) {
languages2words('./admin/');
done();
});
gulp.task('updatePackages', function (done) {
iopackage.common.version = pkg.version;
iopackage.common.news = iopackage.common.news || {};
if (!iopackage.common.news[pkg.version]) {
var news = iopackage.common.news;
var newNews = {};
newNews[pkg.version] = {
en: 'news',
de: 'neues',
ru: 'новое'
};
iopackage.common.news = Object.assign(newNews, news);
}
fs.writeFileSync('io-package.json', JSON.stringify(iopackage, null, 4));
done();
});
gulp.task('updateReadme', function (done) {
var readme = fs.readFileSync('README.md').toString();
var pos = readme.indexOf('## Changelog\n');
if (pos !== -1) {
var readmeStart = readme.substring(0, pos + '## Changelog\n'.length);
var readmeEnd = readme.substring(pos + '## Changelog\n'.length);
if (readme.indexOf(version) === -1) {
var timestamp = new Date();
var date = timestamp.getFullYear() + '-' +
('0' + (timestamp.getMonth() + 1).toString(10)).slice(-2) + '-' +
('0' + (timestamp.getDate()).toString(10)).slice(-2);
var news = '';
if (iopackage.common.news && iopackage.common.news[pkg.version]) {
news += '* ' + iopackage.common.news[pkg.version].en;
}
fs.writeFileSync('README.md', readmeStart + '### ' + version + ' (' + date + ')\n' + (news ? news + '\n\n' : '\n') + readmeEnd);
}
}
done();
});
gulp.task('default', ['updatePackages', 'updateReadme']);

BIN
img/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

106
io-package.json Normal file
View File

@ -0,0 +1,106 @@
{
"common": {
"name": "simple-api",
"version": "2.0.0",
"news": {
"2.0.0": {
"en": "BREAKING CHANGE: getBulk is returning data in a different structure",
"de": "BREAKING CHANGE: getBulk gibt Daten in einer anderen Struktur zurück",
"ru": "BREAKING CHANGE: getBulk возвращает данные в другой структуре",
"pt": "BREAKING CHANGE: o getBulk está retornando dados em uma estrutura diferente",
"nl": "BREAKING CHANGE: getBulk retourneert gegevens in een andere structuur",
"fr": "BREAKING CHANGE: getBulk renvoie des données dans une structure différente",
"it": "BREAKING CHANGE: getBulk restituisce i dati in una struttura diversa",
"es": "BREAKING CHANGE: getBulk está devolviendo datos en una estructura diferente",
"pl": "ZMIANA ZMIAN: getBulk zwraca dane w innej strukturze"
},
"1.6.3": {
"en": "Return used character encoding (UTF-8)",
"de": "Zeichencodierung zurückgegeben (UTF-8)",
"ru": "Возвращаемое кодирование символов (UTF-8)",
"pt": "Retornar a codificação de caracteres usados (UTF-8)",
"nl": "Gebruikte tekencodering retourneren (UTF-8)",
"fr": "Renvoie le codage de caractères utilisé (UTF-8)",
"it": "Restituisci codifica caratteri usati (UTF-8)",
"es": "Devuelve la codificación de caracteres usados (UTF-8)",
"pl": "Zwróć używane kodowanie znaków (UTF-8)"
},
"1.6.2": {
"en": "Fix decoding problems",
"de": "Dekodierungsprobleme behoben",
"ru": "Fix decoding problems"
},
"1.6.1": {
"en": "Fix statuscode for setBulk and optimize permission errors",
"de": "Statuscode für setBulk korrigiert und Rechte-Fehler optimiert",
"ru": "Fix statuscode for setBulk and optimize permission errors"
},
"1.6.0": {
"en": "Optimize Permission handling and add possibility to only allow access to states where user is also owner (needs js-contrioller 1.1.1!), fix handling of url-encoded values",
"de": "Berechtigungsbehandlung optimiert und Möglichkeit eingebaut nur den Zugriff auf Zustände zu erlauben wo der Benutzer auch Besitzer ist (benötigt js-controller 1.1.1), Behandlung von URL-kodierten Werten korrigiert",
"ru": "Optimize Permission handling and add possibility to only allow access to states where user is also owner (needs js-contrioller 1.1.1!), fix handling of url-encoded values"
},
"1.5.1": {
"en": "Add new POST method setValueFromBody",
"de": "Neue POST-Methode setValueFromBody hinzugefügt",
"ru": "Add new POST method setValueFromBody"
},
"1.4.0": {
"en": "can be used as web-extension",
"de": "Kann als Web-Extension benutzt werden",
"ru": "Может использоватся, как Web-плагин"
},
"1.3.0": {
"en": "сompatible only with new admin",
"de": "Nur mit neuem Admin kompatibel",
"ru": "Совместимо только с новым админ-драйвером"
},
"1.2.0": {
"en": "support of letsencrypt certificates",
"de": "Unterstützung von letsencrypt Zertifikaten",
"ru": "Поддержка letsencrypt сертификатов"
},
"1.1.1": {
"en": "support of chained certificates",
"de": "support of chained certificates",
"ru": "support of chained certificates"
}
},
"title": "simpleAPI Adapter",
"desc": "This adapter allows to read and write yunkong2 objects and state with web RESTful API",
"authors": [
"bluefox <dogafox@gmail.com>",
"Apollon77 <ingo@fischer-ka.de>"
],
"license": "MIT",
"platform": "Javascript/Node.js",
"mode": "daemon",
"loglevel": "info",
"icon": "simple-api.png",
"webExtension": "lib/simpleapi.js",
"readme": "https://git.spacen.net/yunkong2/yunkong2.simple-api/blob/master/README.md",
"keywords": ["web", "simpleAPI", "RESTful", "communication"],
"enabled": true,
"extIcon": "https://git.spacen.net/yunkong2/yunkong2.simple-api/master/admin/simple-api.png",
"type": "communication",
"stopBeforeUpdate": true,
"localLink": "%protocol%://%ip%:%port%/get/system.adapter.simple-api.%instance%.uptime?prettyPrint",
"dependencies": [{"js-controller": ">=0.15.0"}]
},
"native": {
"port": 8087,
"auth": false,
"secure": false,
"bind": "0.0.0.0",
"certPublic": "",
"certPrivate": "",
"certChained": "",
"defaultUser": "admin",
"onlyAllowWhenUserIsOwner": false,
"webInstance": "",
"leEnabled": false,
"leUpdate": false,
"leCheckPort": 80
}
}

905
lib/simpleapi.js Normal file
View File

@ -0,0 +1,905 @@
/* jshint -W097 */
/* jshint strict:false */
/*jslint node: true */
/*jshint -W061 */
'use strict';
/**
* SimpleAPI class
*
* From settings used only secure, auth and crossDomain
*
* @class
* @param {object} server http or https node.js object
* @param {object} webSettings settings of the web server, like <pre><code>{secure: settings.secure, port: settings.port}</code></pre>
* @param {object} adapter web adapter object
* @param {object} instanceSettings instance object with common and native
* @param {object} app express application
* @return {object} object instance
*/
function SimpleAPI(server, webSettings, adapter, instanceSettings, app) {
if (!(this instanceof SimpleAPI)) return new SimpleAPI(server, webSettings, adapter, instanceSettings, app);
this.server = server;
this.app = app;
this.adapter = adapter;
this.settings = webSettings;
this.config = instanceSettings ? instanceSettings.native : {};
this.namespace = instanceSettings ? instanceSettings._id.substring('system.adapter.'.length) : 'simple-api';
this.restApiDelayed = {
timer: null,
responseType: '',
response: null,
waitId: 0
};
var that = this;
// Cache
this.users = {};
var __construct = (function () {
that.adapter.log.info((that.settings.secure ? 'Secure ' : '') + 'simpleAPI server listening on port ' + that.settings.port);
that.adapter.config.defaultUser = that.adapter.config.defaultUser || 'system.user.admin';
if (!that.adapter.config.defaultUser.match(/^system\.user\./)) {
that.adapter.config.defaultUser = 'system.user.' + that.adapter.config.defaultUser;
}
if (that.adapter.config.onlyAllowWhenUserIsOwner === undefined) that.adapter.config.onlyAllowWhenUserIsOwner = false;
adapter.log.info('Allow states only when user is owner: ' + that.adapter.config.onlyAllowWhenUserIsOwner);
if (that.app) {
adapter.log.info('Install extension on /' + that.namespace + '/');
that.app.use('/' + that.namespace + '/', function (req, res, next) {
that.restApi.call(that, req, res);
});
// let it be accessible under old address too
for (var c in commandsPermissions) {
(function (command) {
adapter.log.info('Install extension on /' + command + '/');
that.app.use('/' + command + '/', function (req, res, next) {
req.url = '/' + command + req.url;
that.restApi.call(that, req, res);
});
})(c);
}
}
// Subscribe on user changes to manage the permissions cache
that.adapter.subscribeForeignObjects('system.group.*');
that.adapter.subscribeForeignObjects('system.user.*');
}.bind(this))();
this.isAuthenticated = function (values, callback) {
if (!values.user || !values.pass) {
that.adapter.log.warn('No password or username!');
callback(false);
} else {
that.adapter.checkPassword(values.user, values.pass, function (res) {
if (res) {
that.adapter.log.debug('Logged in: ' + values.user);
callback(true);
} else {
that.adapter.log.warn('Invalid password or user name: ' + values.user);
callback(false);
}
});
}
};
this.stateChange = function (id, state) {
if (that.restApiDelayed.id === id && state && state.ack) {
adapter.unsubscribeForeignStates(id);
that.restApiDelayed.response = state;
setTimeout(restApiDelayedAnswer, 0);
}
};
this.userReg = new RegExp('^system\.user\.');
this.groupReg = new RegExp('^system\.group\.');
// if user politics changes, clear cache
this.objectChange = function (id, state) {
if (this.userReg.test(id) || this.groupReg.test(id)) {
this.users = {};
}
};
function restApiPost(req, res, command, oId, values) {
var responseType = 'json';
var status = 500;
var headers = {'Access-Control-Allow-Origin': '*'};
var body = '';
req.on('data', function (data) {
body += data;
});
req.on('end', function () {
switch (command) {
case 'setBulk':
that.adapter.log.debug('POST-' + command + ': body = ' + body);
var arr = [];
if (body) {
arr = body.split('&');
}
for (var i = 0; i < arr.length; i++) {
arr[i] = arr[i].split('=');
try {
values[arr[i][0].trim()] = (arr[i][1] === undefined) ? null : decodeURIComponent((arr[i][1]+'').replace(/\+/g, '%20'));
}
catch (e) {
values[arr[i][0].trim()] = arr[i][1];
}
}
if (values.prettyPrint !== undefined) {
if (values.prettyPrint === 'false') values.prettyPrint = false;
if (values.prettyPrint === null) values.prettyPrint = true;
}
var cnt = 0;
var response = [];
that.adapter.log.debug('POST-' + command + ': values = ' + JSON.stringify(values));
for (var _id in values) {
if (!values.hasOwnProperty(_id) || _id === 'prettyPrint' || _id === 'user' || _id === 'pass') continue;
cnt++;
that.adapter.log.debug('"' + _id + '"');
findState(_id, values.user, function (err, id, originId) {
if (err) {
status = 500;
if (err.indexOf('permissionError') !== -1) {
status = 401;
}
doResponse(res, 'plain', status, headers, 'error: ' + err, values.prettyPrint);
cnt = 0;
} else if (!id) {
response.push({error: 'error: datapoint "' + originId + '" not found'});
if (!--cnt) doResponse(res, responseType, status, headers, response, values.prettyPrint);
} else {
var usedId = (values[originId]?originId:id);
that.adapter.log.debug('POST-' + command + ' for id=' + id + ', oid=' + originId + ', used=' + usedId + ', value='+values[usedId]);
if (values[usedId] === 'true') {
values[usedId] = true;
} else if (values[usedId] === 'false') {
values[usedId] = false;
} else if (!isNaN(values[usedId]) && values[usedId] == parseFloat(values[usedId])) {
values[usedId] = parseFloat(values[usedId]);
}
adapter.setForeignState(id, values[usedId], false, {user: values.user, limitToOwnerRights: that.adapter.config.onlyAllowWhenUserIsOwner}, function (err, id) {
if (err) {
status = 500;
if (err.indexOf('permissionError') !== -1) {
status = 401;
}
doResponse(res, 'plain', status, headers, 'error: ' + err, values.prettyPrint);
cnt = 0;
} else {
adapter.log.debug('Add to Response: ' + JSON.stringify({id: id, val: values[usedId]}));
response.push({id: id, val: values[usedId]});
if (!--cnt) {
status = 200;
doResponse(res, responseType, status, headers, response, values.prettyPrint);
}
}
});
}
});
}
if (!cnt) doResponse(res, responseType, status, headers, response, values.prettyPrint);
break;
case 'setValueFromBody':
//that.adapter.log.debug('POST-' + command + ': body = ' + JSON.stringify(body)); // "{0123456xx}"
//that.adapter.log.debug('POST-' + command + ': req.url = ' + JSON.stringify(req.url)); // "/setValueFromBody?javascript.0.Nuki.Devices.NukiSL1.NukiBridgeResponse&prettyPrint"
//that.adapter.log.debug('POST-' + command + ': valuesAA = ' + JSON.stringify(values)); // {"javascript.0.Nuki.Devices.NukiSL1.NukiBridgeResponse":null,"prettyPrint":true,"user":"system.user.admin"}
for (var _id2 in oId) {
if (oId.hasOwnProperty(_id2)) {
values[oId[_id2]] = body;
}
}
if (values.prettyPrint !== undefined) {
if (values.prettyPrint === 'false') values.prettyPrint = false;
if (values.prettyPrint === null) values.prettyPrint = true;
}
if (!oId.length || !oId[0]) {
doResponse(res, responseType, status, headers, {error: 'no object/datapoint given'}, values.prettyPrint);
break;
}
var response = [];
that.adapter.log.debug('POST-' + command + ': values = ' + JSON.stringify(values));
var cnt = oId.length;
for (var k = 0; k < oId.length; k++) {
that.adapter.log.debug('"' + oId[k] + '"');
findState(oId[k], values.user, function (err, id, originId) {
if (err) {
status = 500;
if (err.indexOf('permissionError') !== -1) {
status = 401;
}
doResponse(res, 'plain', status, headers, 'error: ' + err, values.prettyPrint);
cnt = 0;
} else if (!id) {
response.push({error: 'error: datapoint "' + originId + '" not found'});
if (!--cnt) doResponse(res, responseType, status, headers, response, values.prettyPrint);
} else {
var usedId = (values[originId]?originId:id);
that.adapter.log.debug('POST-' + command + ' for id=' + id + ', oid=' + originId + ', used=' + usedId + ', value='+values[usedId]);
if (values[usedId] === 'true') {
values[usedId] = true;
} else if (values[usedId] === 'false') {
values[usedId] = false;
} else if (!isNaN(values[usedId]) && values[usedId] == parseFloat(values[usedId])) {
values[usedId] = parseFloat(values[usedId]);
}
adapter.setForeignState(id, values[usedId], false, {user: values.user, limitToOwnerRights: that.adapter.config.onlyAllowWhenUserIsOwner}, function (err, id) {
if (err) {
status = 500;
if (err.indexOf('permissionError') !== -1) {
status = 401;
}
doResponse(res, 'plain', status, headers, 'error: ' + err, values.prettyPrint);
cnt = 0;
} else {
status = 200;
adapter.log.debug('Add to Response: ' + JSON.stringify({id: id, val: values[usedId]}));
response.push({id: id, val: values[usedId]});
if (!--cnt) doResponse(res, responseType, status, headers, response, values.prettyPrint);
}
});
}
});
}
if (!cnt) doResponse(res, responseType, status, headers, response, values.prettyPrint);
break;
default:
doResponse(res, responseType, status, headers, {error: 'command ' + command + ' unknown'}, values.prettyPrint);
break;
}
});
}
function restApiDelayedAnswer() {
if (that.restApiDelayed.timer) {
clearTimeout(that.restApiDelayed.timer);
that.restApiDelayed.timer = null;
doResponse(that.restApiDelayed.res, that.restApiDelayed.responseType, 200, {'Access-Control-Allow-Origin': '*'}, that.restApiDelayed.response, that.restApiDelayed.prettyPrint);
that.restApiDelayed.id = null;
that.restApiDelayed.res = null;
that.restApiDelayed.response = null;
that.restApiDelayed.prettyPrint = false;
}
}
function findState(idOrName, user, type, callback) {
if (typeof type === 'function') {
callback = type;
type = null;
}
adapter.findForeignObject(idOrName, type, {user: user, checked: true}, callback);
}
function getState(idOrName, user, type, callback) {
if (typeof type === 'function') {
callback = type;
type = null;
}
findState(idOrName, user, type, function (err, id, originId) {
if (err) {
if (callback) callback(err, undefined, null, originId);
} else
if (id) {
that.adapter.getForeignState(id, {user: user, limitToOwnerRights: that.adapter.config.onlyAllowWhenUserIsOwner}, function (err, obj) {
if (err || !obj) {
obj = undefined;
}
if (callback) callback (err, obj, id, originId);
});
} else {
if (callback) callback (null, undefined, null, originId);
}
});
}
function doResponse(res, type, status, headers, content, pretty) {
if (!headers) headers = {};
status = parseInt(status, 10) || 200;
if (pretty && typeof content === 'object') {
type = 'plain';
content = JSON.stringify(content, null, 2);
}
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
switch (type) {
case 'json':
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.statusCode = status;
res.end(JSON.stringify(content), 'utf8');
break;
case 'plain':
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.statusCode = status;
if (typeof content === 'object') {
content = JSON.stringify(content);
}
res.end(content, 'utf8');
break;
}
}
// static information
var commandsPermissions = {
getPlainValue: {type: 'state', operation: 'read'},
get: {type: 'state', operation: 'read'},
getBulk: {type: 'state', operation: 'read'},
set: {type: 'state', operation: 'write'},
toggle: {type: 'state', operation: 'write'},
setBulk: {type: 'state', operation: 'write'},
setValueFromBody: {type: 'state', operation: 'write'},
getObjects: {type: 'object', operation: 'list'},
objects: {type: 'object', operation: 'list'},
states: {type: 'state', operation: 'list'},
getStates: {type: 'state', operation: 'list'},
help: {type: '', operation: ''}
};
this.commands = [];
for (var c in commandsPermissions) {
this.commands.push(c);
}
// Register api by express
this.checkRequest = function (url) {
var parts = url.split('/', 2);
return (parts[1] && this.commands.indexOf(parts[1]) !== -1);
};
this.checkPermissions = function (user, command, callback) {
adapter.calculatePermissions(user, commandsPermissions, function (acl) {
if (user !== 'system.user.admin') {
// type: file, object, state, other
// operation: create, read, write, list, delete, sendto, execute, sendto
if (commandsPermissions[command]) {
// If permission required
if (commandsPermissions[command].type) {
if (acl[commandsPermissions[command].type] &&
acl[commandsPermissions[command].type][commandsPermissions[command].operation]) {
return callback(null);
}
} else {
return callback(null);
}
}
that.adapter.log.warn('No permission for "' + user + '" to call ' + command);
if (callback) callback('permissionError');
} else {
return callback(null);
}
});
};
this.restApi = function (req, res, isAuth, isChecked) {
var values = {};
var oId = [];
var wait = 0;
var responseType = 'json';
var status = 500;
var headers = {'Access-Control-Allow-Origin': '*'};
var response;
var url;
try {
url = decodeURI(req.url);
}
catch (e) {
url = req.url;
that.adapter.log.warn('Malformed URL encoding: ' + e);
}
var pos = url.indexOf('?');
if (pos !== -1) {
var arr = url.substring(pos + 1).split('&');
url = url.substring(0, pos);
for (var i = 0; i < arr.length; i++) {
arr[i] = arr[i].split('=');
//that.adapter.log.debug('Try Decode ' + i + ': ' + arr[i][1]);
try {
values[arr[i][0].trim()] = (arr[i][1] === undefined) ? null : decodeURIComponent((arr[i][1]+'').replace(/\+/g, '%20'));
}
catch(e) {
values[arr[i][0].trim()] = arr[i][1];
}
//that.adapter.log.debug(' Decode Result ' + i + ': ' + values[arr[i][0].trim()]);
}
if (values.prettyPrint !== undefined) {
if (values.prettyPrint === 'false') values.prettyPrint = false;
if (values.prettyPrint === null) values.prettyPrint = true;
}
// Default value for wait
if (values.wait === null) values.wait = 2000;
}
var parts = url.split('/');
var command = parts[1];
// Analyse system.adapter.socketio.0.uptime,system.adapter.history.0.memRss?value=78&wait=300
if (parts[2]) {
oId = parts[2].split(',');
for (var j = oId.length - 1; j >= 0; j--) {
oId[j] = oId[j].trim();
if (!oId[j]) oId.splice(j, 1);
}
}
// If authentication check is required
if (that.settings.auth) {
if (!isAuth) {
this.isAuthenticated(values, function (isAuth) {
if (isAuth) {
that.restApi(req, res, true);
} else {
doResponse(res, 'plain', 401, headers, 'error: authentication failed. Please write "http' + (that.settings.secure ? 's' : '') + '://' + req.headers.host + '?user=UserName&pass=Password"');
}
});
return;
} else
if (!isChecked) {
if (!values.user.match(/^system\.user\./)) values.user = 'system.user.' + values.user;
that.checkPermissions(values.user, command, function (err) {
if (!err) {
that.restApi(req, res, true, true);
} else {
doResponse(res, 'plain', 401, headers, 'error: ' + err, values.prettyPrint);
}
});
return;
}
} else {
req.user = req.user || that.adapter.config.defaultUser;
values.user = req.user;
if (!values.user.match(/^system\.user\./)) values.user = 'system.user.' + values.user;
if (!isChecked && command) {
that.checkPermissions(req.user || that.adapter.config.defaultUser, command, function (err) {
if (!err) {
that.restApi(req, res, true, true);
} else {
doResponse(res, 'plain', 401, headers, 'error: ' + err, values.prettyPrint);
}
});
return;
}
}
if (!values.user.match(/^system\.user\./)) values.user = 'system.user.' + values.user;
if (req.method === 'POST') {
restApiPost(req, res, command, oId, values);
return;
}
switch (command) {
case 'getPlainValue':
responseType = 'plain';
if (!oId.length || !oId[0]) {
doResponse(res, 'plain', status, headers, 'error: no datapoint given', values.prettyPrint);
break;
}
var pcnt = oId.length;
response = '';
for (var g = 0; g < oId.length; g++) {
getState(oId[g], values.user, function (err, obj, id, originId) {
if (err) {
status = 500;
response = 'error: ' + err;
if (err.indexOf('permissionError') !== -1) {
status = 401;
}
pcnt = 1;
} else if ((!id && originId) || obj === undefined) {
response += (response ? '\n' : '') + 'error: datapoint "' + originId + '" not found';
} else {
response += (response ? '\n' : '') + JSON.stringify(obj.val);
status = 200;
}
if (!--pcnt) doResponse(res, ((status === 500)?'plain':responseType), status, headers, response, values.prettyPrint);
});
}
break;
case 'get':
if (!oId.length || !oId[0]) {
doResponse(res, responseType, status, headers, {error: 'no object/datapoint given'}, values.prettyPrint);
break;
}
var gcnt = oId.length;
for (var k = 0; k < oId.length; k++) {
that.adapter.log.debug('work for ID ' + oId[k]);
getState(oId[k], values.user, function (err, state, id, originId) {
that.adapter.log.debug('return err ' + err);
that.adapter.log.debug('return state ' + state);
that.adapter.log.debug('return id ' + JSON.stringify(id));
that.adapter.log.debug('return originId' + originId);
if (err) {
gcnt = 0;
status = 500;
if (err.indexOf('permissionError') !== -1) {
status = 401;
}
doResponse(res, 'plain', status, headers, 'error: ' + err, values.prettyPrint);
} else
if (!id && originId) {
if (!response || obj === undefined) {
response = 'error: datapoint "' + originId + '" not found';
} else {
if (typeof response !== 'object' || response.constructor !== Array) {
response = [response];
}
response.push('error: datapoint "' + originId + '" not found');
}
if (!--gcnt) doResponse(res, responseType, status, headers, response, values.prettyPrint);
} else {
var vObj = state || {};
status = 200;
that.adapter.getForeignObject(id, function (err, obj) {
if (obj) {
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) {
vObj[attr] = obj[attr];
}
}
}
if (!response) {
response = vObj;
} else {
if (typeof response !== 'object' || response.constructor !== Array) response = [response];
response.push(vObj);
}
if (!--gcnt) doResponse(res, responseType, status, headers, response, values.prettyPrint);
});
}
});
}
break;
case 'getBulk':
if (!oId.length || !oId[0]) {
doResponse(res, responseType, status, headers, {error: 'no datapoints given'}, values.prettyPrint);
break;
}
var bcnt = oId.length;
response = [];
for (var b = 0; b < oId.length; b++) {
getState(oId[b], values.user, function (err, state, id, originId) {
if (err) {
bcnt = 0;
status = 500;
if (err.indexOf('permissionError') !== -1) {
status = 401;
}
doResponse(res, responseType, status, headers, 'error: ' + err, values.prettyPrint);
} else {
if (id) status = 200;
state = state || {};
response.push({val: state.val, ts: state.ts});
if (!--bcnt) doResponse(res, responseType, status, headers, response, values.prettyPrint);
}
});
}
if (!bcnt) doResponse(res, responseType, status, headers, response, values.prettyPrint);
break;
case 'set':
if (!oId.length || !oId[0]) {
doResponse(res, responseType, status, headers, {error: "object/datapoint not given"}, values.prettyPrint);
break;
}
if (values.value === undefined && values.val === undefined) {
doResponse(res, responseType, status, headers, 'error: no value found for "' + oId[0] + '". Use /set/id?value=1 or /set/id?value=1&wait=1000', values.prettyPrint);
} else {
findState(oId[0], values.user, function (err, id, originId) {
if (err) {
wait = 0;
status = 500;
if (err.indexOf('permissionError') !== -1) {
status = 401;
}
doResponse(res, 'plain', status, headers, 'error: ' + err);
} else
if (id) {
wait = values.wait || 0;
if (wait) wait = parseInt(wait, 10);
if (values.val === undefined) values.val = values.value;
if (values.val === 'true') {
values.val = true;
} else if (values.val === 'false') {
values.val = false;
} else if (!isNaN(values.val)) {
values.val = parseFloat(values.val);
}
if (wait) adapter.subscribeForeignStates(id);
adapter.setForeignState(id, values.val, false, {user: values.user, limitToOwnerRights: that.adapter.config.onlyAllowWhenUserIsOwner}, function (err) {
if (err) {
status = 500;
if (err.indexOf('permissionError') !== -1) {
status = 401;
}
doResponse(res, 'plain', status, headers, 'error: ' + err, values.prettyPrint);
wait = 0;
} else
if (!wait) {
status = 200;
response = {id: id, value: values.val, val: values.val};
doResponse(res, responseType, status, headers, response, values.prettyPrint);
}
});
if (wait) {
that.restApiDelayed.responseType = responseType;
that.restApiDelayed.response = null;
that.restApiDelayed.id = id;
that.restApiDelayed.res = res;
that.restApiDelayed.prettyPrint = values.prettyPrint;
that.restApiDelayed.timer = setTimeout(restApiDelayedAnswer, wait);
}
} else {
doResponse(res, responseType, status, headers, 'error: datapoint "' + originId + '" not found', values.prettyPrint);
}
});
}
break;
case 'toggle':
if (!oId.length || !oId[0]) {
doResponse(res, responseType, status, headers, {error: "state not given"}, values.prettyPrint);
break;
}
findState(oId[0], values.user, function (err, id, originId) {
if (err) {
doResponse(res, 'plain', 500, headers, 'error: ' + err, values.prettyPrint);
wait = 0;
} else if (id) {
wait = values.wait || 0;
if (wait) wait = parseInt(wait, 10);
// Read type of object
adapter.getForeignObject(id, {user: values.user, checked: true}, function (err, obj) {
if (err) {
status = 500;
if (err.indexOf('permissionError') !== -1) {
status = 401;
}
doResponse(res, 'plain', status, headers, 'error: ' + err, values.prettyPrint);
wait = 0;
} else {
// Read actual value
adapter.getForeignState(id, {user: values.user, checked: true}, function (err, state) {
if (err) {
status = 500;
if (err.indexOf('permissionError') !== -1) {
status = 401;
}
doResponse(res, 'plain', status, headers, 'error: ' + err, values.prettyPrint);
wait = 0;
} else
if (state) {
if (obj && obj.common && obj.common.type) {
if (obj.common.type === 'bool' || obj.common.type === 'boolean') {
if (state.val === 'true') {
state.val = true;
} else if (state.val === 'false') {
state.val = false;
}
state.val = !state.val;
} else
if (obj.common.type === 'number') {
state.val = parseFloat(state.val);
if (obj.common.max !== undefined) {
if (obj.common.min === undefined) obj.common.min = 0;
if (state.val > obj.common.max) state.val = obj.common.max;
if (state.val < obj.common.min) state.val = obj.common.min;
// Invert
state.val = obj.common.max + obj.common.min - state.val;
} else {
// default number is from 0 to 100
if (state.val > 100) state.val = 100;
if (state.val < 0) state.val = 0;
state.val = 100 - state.val;
}
} else {
if (state.val === 'true' || state.val === true) {
state.val = false;
} else if (state.val === 'false' || state.val === false) {
state.val = true;
} else if (parseInt(state.val, 10) == state.val) {
state.val = parseInt(state.val, 10) ? 0 : 1;
} else {
doResponse(res, responseType, status, headers, {error: 'state is neither number nor boolean'}, values.prettyPrint);
return;
}
}
} else {
if (state.val === 'true') {
state.val = true;
} else if (state.val === 'false') {
state.val = false;
} else if (!isNaN(state.val)) {
state.val = parseFloat(state.val);
}
if (state.val === true) state.val = 1;
if (state.val === false) state.val = 0;
state.val = 1 - parseInt(state.val, 10);
}
if (wait) adapter.subscribeForeignStates(id);
adapter.setForeignState(id, state.val, false, {user: values.user, limitToOwnerRights: that.adapter.config.onlyAllowWhenUserIsOwner}, function (err) {
if (err) {
status = 500;
if (err.indexOf('permissionError') !== -1) {
status = 401;
}
doResponse(res, 'plain', status, headers, 'error: ' + err, values.prettyPrint);
wait = 0;
} else
if (!wait) {
status = 200;
doResponse(res, responseType, status, headers, {id: id, value: state.val, val: state.val}, values.prettyPrint);
}
});
if (wait) {
that.restApiDelayed.responseType = responseType;
that.restApiDelayed.response = null;
that.restApiDelayed.id = id;
that.restApiDelayed.res = res;
that.restApiDelayed.prettyPrint = values.prettyPrint;
that.restApiDelayed.timer = setTimeout(restApiDelayedAnswer, wait);
}
} else {
doResponse(res, responseType, status, headers, {error: 'object has no state'}, values.prettyPrint);
}
});
}
});
} else {
doResponse(res, responseType, status, headers, {error: 'error: datapoint "' + originId + '" not found'}, values.prettyPrint);
}
});
break;
// /setBulk?BidCos-RF.FEQ1234567:1.LEVEL=0.7&Licht-Küche/LEVEL=0.7&Anwesenheit=0&950=1
case 'setBulk':
var cnt = 0;
response = [];
adapter.log.debug('Values: ' + JSON.stringify(values));
for (var _id in values) {
if (_id === 'prettyPrint' || _id === 'user' || _id === 'pass') continue;
cnt++;
findState(_id, values.user, function (err, id, originId) {
// id is "name", originId is the yunkong2-ID of the datapoint
if (err) {
adapter.log.debug('Error on ID lookup: ' + err);
status = 500;
if (err.indexOf('permissionError') !== -1) {
status = 401;
}
doResponse(res, 'plain', status, headers, 'error: ' + err, values.prettyPrint);
cnt = 0;
} else if (!id) {
response.push({error: 'error: datapoint "' + originId + '" not found'});
if (!--cnt) doResponse(res, responseType, status, headers, response, values.prettyPrint);
} else {
var usedId = (values[originId]?originId:id);
that.adapter.log.debug('GET-' + command + ' for id=' + id + ', oid=' + originId + 'used=' + usedId + ', value=' + values[usedId]);
if (values[usedId] === 'true') {
values[usedId] = true;
} else if (values[usedId] === 'false') {
values[usedId] = false;
} else if (!isNaN(values[usedId]) && values[usedId] == parseFloat(values[usedId])) {
values[usedId] = parseFloat(values[usedId]);
}
adapter.setForeignState(id, values[usedId], false, {user: values.user, limitToOwnerRights: that.adapter.config.onlyAllowWhenUserIsOwner}, function (err, id) {
if (err) {
status = 500;
if (err.indexOf('permissionError') !== -1) {
status = 401;
}
doResponse(res, 'plain', status, headers, 'error: ' + err, values.prettyPrint);
cnt = 0;
} else {
adapter.log.debug('Add to Response-Get: ' + JSON.stringify({id: id, val: values[usedId], value: values[usedId]}));
response.push({id: id, val: values[usedId], value: values[usedId]});
if (!--cnt) {
status = 200;
doResponse(res, responseType, status, headers, response, values.prettyPrint);
}
}
});
}
});
}
if (!cnt) doResponse(res, responseType, status, headers, response, values.prettyPrint);
break;
case 'getObjects':
case 'objects':
adapter.getForeignObjects(values.pattern || parts[2] || '*', values.type, {user: values.user, limitToOwnerRights: that.adapter.config.onlyAllowWhenUserIsOwner}, function (err, list) {
if (err) {
status = 500;
if (err.indexOf('permissionError') !== -1) {
status = 401;
}
doResponse(res, responseType, status, headers, {error: JSON.stringify(err)}, values.prettyPrint);
} else {
status = 200;
doResponse(res, responseType, status, headers, list, values.prettyPrint);
}
});
break;
case 'getStates':
case 'states':
adapter.getForeignStates(values.pattern || parts[2] || '*', {user: values.user, limitToOwnerRights: that.adapter.config.onlyAllowWhenUserIsOwner}, function (err, list) {
if (err) {
status = 500;
if (err.indexOf('permissionError') !== -1) {
status = 401;
}
doResponse(res, responseType, status, headers, {error: JSON.stringify(err)}, values.prettyPrint);
} else {
status = 200;
doResponse(res, responseType, status, headers, list, values.prettyPrint);
}
});
break;
case 'help':
// is default behaviour too
default:
var obj = (command === 'help') ? {} : {error: 'command ' + command + ' unknown'};
var request = 'http' + (that.settings.secure ? 's' : '') + '://' + req.headers.host;
if (this.app) request += '/' + this.namespace + '/';
var auth = '';
if (that.settings.auth) auth = 'user=UserName&pass=Password';
obj.getPlainValue = request + '/getPlainValue/stateID' + (auth ? '?' + auth : '');
obj.get = request + '/get/stateID/?prettyPrint' + (auth ? '&' + auth : '');
obj.getBulk = request + '/getBulk/stateID1,stateID2/?prettyPrint' + (auth ? '&' + auth : '');
obj.set = request + '/set/stateID?value=1&prettyPrint' + (auth ? '&' + auth : '');
obj.toggle = request + '/toggle/stateID&prettyPrint' + (auth ? '&' + auth : '');
obj.setBulk = request + '/setBulk?stateID1=0.7&stateID2=0&prettyPrint' + (auth ? '&' + auth : '');
obj.setValueFromBody = request + '/setValueFromBody?stateID1' + (auth ? '&' + auth : '');
obj.objects = request + '/objects?pattern=system.adapter.admin.0*&prettyPrint' + (auth ? '&' + auth : '');
obj.states = request + '/states?pattern=system.adapter.admin.0*&prettyPrint' + (auth ? '&' + auth : '');
doResponse(res, responseType, status, headers, obj, true);
break;
}
};
}
module.exports = SimpleAPI;

83
lib/utils.js Normal file
View File

@ -0,0 +1,83 @@
'use strict';
const fs = require('fs');
const path = require('path');
let controllerDir;
let appName;
/**
* returns application name
*
* The name of the application can be different and this function finds it out.
*
* @returns {string}
*/
function getAppName() {
const parts = __dirname.replace(/\\/g, '/').split('/');
return parts[parts.length - 2].split('.')[0];
}
/**
* looks for js-controller home folder
*
* @param {boolean} isInstall
* @returns {string}
*/
function getControllerDir(isInstall) {
// Find the js-controller location
const possibilities = [
'yunkong2.js-controller',
'yunkong2.js-controller',
];
/** @type {string} */
let controllerPath;
for (const pkg of possibilities) {
try {
const possiblePath = require.resolve(pkg);
if (fs.existsSync(possiblePath)) {
controllerPath = possiblePath;
break;
}
} catch (e) { /* not found */ }
}
if (controllerPath == null) {
if (!isInstall) {
console.log('Cannot find js-controller');
process.exit(10);
} else {
process.exit();
}
}
// we found the controller
return path.dirname(controllerPath);
}
/**
* reads controller base settings
*
* @alias getConfig
* @returns {object}
*/
function getConfig() {
let configPath;
if (fs.existsSync(
configPath = path.join(controllerDir, 'conf', appName + '.json')
)) {
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
} else if (fs.existsSync(
configPath = path.join(controllerDir, 'conf', + appName.toLowerCase() + '.json')
)) {
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
} else {
throw new Error('Cannot find ' + controllerDir + '/conf/' + appName + '.json');
}
}
appName = getAppName();
controllerDir = getControllerDir(typeof process !== 'undefined' && process.argv && process.argv.indexOf('--install') !== -1);
const adapter = require(path.join(controllerDir, 'lib/adapter.js'));
exports.controllerDir = controllerDir;
exports.getConfig = getConfig;
exports.Adapter = adapter;
exports.appName = appName;

133
main.js Normal file
View File

@ -0,0 +1,133 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
'use strict';
var utils = require(__dirname + '/lib/utils'); // Get common adapter utils
var SimpleAPI = require(__dirname + '/lib/simpleapi.js');
var LE = require(utils.controllerDir + '/lib/letsencrypt.js');
var webServer = null;
var fs = null;
var adapter = new utils.Adapter({
name: 'simple-api',
stateChange: function (id, state) {
if (webServer && webServer.api) {
webServer.api.stateChange(id, state);
}
},
objectChange: function (id, obj) {
if (webServer && webServer.api) {
webServer.api.objectChange(id, obj);
}
},
unload: function (callback) {
try {
adapter.log.info('terminating http' + (webServer.settings.secure ? 's' : '') + ' server on port ' + webServer.settings.port);
//if (webServer.api) webServer.api.close();
callback();
} catch (e) {
callback();
}
},
ready: function () {
main();
}
});
function main() {
if (adapter.config.webInstance) {
console.log('Adapter runs as a part of web service');
adapter.log.warn('Adapter runs as a part of web service');
adapter.setForeignState('system.adapter.' + adapter.namespace + '.alive', false, true, function () {
setTimeout(function () {
process.exit();
}, 1000);
});
return;
}
if (adapter.config.secure) {
// subscribe on changes of permissions
adapter.subscribeForeignObjects('system.group.*');
adapter.subscribeForeignObjects('system.user.*');
// Load certificates
adapter.getCertificates(function (err, certificates, leConfig) {
adapter.config.certificates = certificates;
adapter.config.leConfig = leConfig;
webServer = initWebServer(adapter.config);
});
} else {
webServer = initWebServer(adapter.config);
}
}
function requestProcessor(req, res) {
if (req.url.indexOf('favicon.ico') !== -1) {
if (!fs) fs = require('fs');
var stat = fs.statSync(__dirname + '/img/favicon.ico');
res.writeHead(200, {
'Content-Type': 'image/x-icon',
'Content-Length': stat.size
});
var readStream = fs.createReadStream(__dirname + '/img/favicon.ico');
// We replaced all the event handlers with a simple call to readStream.pipe()
readStream.pipe(res);
} else {
webServer.api.restApi(req, res);
}
}
//settings: {
// "port": 8080,
// "auth": false,
// "secure": false,
// "bind": "0.0.0.0", // "::"
// "cache": false
//}
function initWebServer(settings) {
var server = {
app: null,
server: null,
api: null,
io: null,
settings: settings
};
settings.port = parseInt(settings.port, 10);
if (settings.port) {
if (settings.secure && !adapter.config.certificates) return null;
server.server = LE.createServer(requestProcessor, settings, adapter.config.certificates, adapter.config.leConfig, adapter.log);
server.server.__server = server;
} else {
adapter.log.error('port missing');
process.exit(1);
}
if (server.server) {
adapter.getPort(settings.port, function (port) {
if (port !== settings.port && !adapter.config.findNextPort) {
adapter.log.error('port ' + settings.port + ' already in use');
process.exit(1);
}
server.server.listen(port);
adapter.log.info('http' + (settings.secure ? 's' : '') + ' server listening on port ' + port);
});
}
server.api = new SimpleAPI(server.server, settings, adapter);
if (server.server) {
return server;
} else {
return null;
}
}

39
package.json Normal file
View File

@ -0,0 +1,39 @@
{
"name": "yunkong2.simple-api",
"version": "2.0.0",
"description": "RESTful interface for yunkong2.",
"author": {
"name": "bluefox",
"email": "dogafox@gmail.com"
},
"contributors": [
{
"name": "Apollon77",
"email": "ingo@fischer-ka.de"
}
],
"homepage": "https://git.spacen.net/yunkong2/yunkong2.simple-api",
"keywords": [
"yunkong2",
"web"
],
"repository": {
"type": "git",
"url": "https://git.spacen.net/yunkong2/yunkong2.simple-api"
},
"dependencies": {},
"devDependencies": {
"gulp": "^3.9.1",
"mocha": "^5.0.1",
"chai": "^4.1.2",
"request": "^2.83.0"
},
"bugs": {
"url": "https://git.spacen.net/yunkong2/yunkong2.simple-api/issues"
},
"main": "main.js",
"scripts": {
"test": "node node_modules/mocha/bin/mocha --exit"
},
"license": "MIT"
}

728
test/lib/setup.js Normal file
View File

@ -0,0 +1,728 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
// check if tmp directory exists
var fs = require('fs');
var path = require('path');
var child_process = require('child_process');
var rootDir = path.normalize(__dirname + '/../../');
var pkg = require(rootDir + 'package.json');
var debug = typeof v8debug === 'object';
pkg.main = pkg.main || 'main.js';
var adapterName = path.normalize(rootDir).replace(/\\/g, '/').split('/');
adapterName = adapterName[adapterName.length - 2];
var adapterStarted = false;
function getAppName() {
var parts = __dirname.replace(/\\/g, '/').split('/');
return parts[parts.length - 3].split('.')[0];
}
var appName = getAppName().toLowerCase();
var objects;
var states;
var pid = null;
function copyFileSync(source, target) {
var targetFile = target;
//if target is a directory a new file with the same name will be created
if (fs.existsSync(target)) {
if ( fs.lstatSync( target ).isDirectory() ) {
targetFile = path.join(target, path.basename(source));
}
}
try {
fs.writeFileSync(targetFile, fs.readFileSync(source));
}
catch (err) {
console.log("file copy error: " +source +" -> " + targetFile + " (error ignored)");
}
}
function copyFolderRecursiveSync(source, target, ignore) {
var files = [];
var base = path.basename(source);
if (base === adapterName) {
base = pkg.name;
}
//check if folder needs to be created or integrated
var targetFolder = path.join(target, base);
if (!fs.existsSync(targetFolder)) {
fs.mkdirSync(targetFolder);
}
//copy
if (fs.lstatSync(source).isDirectory()) {
files = fs.readdirSync(source);
files.forEach(function (file) {
if (ignore && ignore.indexOf(file) !== -1) {
return;
}
var curSource = path.join(source, file);
var curTarget = path.join(targetFolder, file);
if (fs.lstatSync(curSource).isDirectory()) {
// ignore grunt files
if (file.indexOf('grunt') !== -1) return;
if (file === 'chai') return;
if (file === 'mocha') return;
copyFolderRecursiveSync(curSource, targetFolder, ignore);
} else {
copyFileSync(curSource, curTarget);
}
});
}
}
if (!fs.existsSync(rootDir + 'tmp')) {
fs.mkdirSync(rootDir + 'tmp');
}
function storeOriginalFiles() {
console.log('Store original files...');
var dataDir = rootDir + 'tmp/' + appName + '-data/';
var f = fs.readFileSync(dataDir + 'objects.json');
var objects = JSON.parse(f.toString());
if (objects['system.adapter.admin.0'] && objects['system.adapter.admin.0'].common) {
objects['system.adapter.admin.0'].common.enabled = false;
}
if (objects['system.adapter.admin.1'] && objects['system.adapter.admin.1'].common) {
objects['system.adapter.admin.1'].common.enabled = false;
}
fs.writeFileSync(dataDir + 'objects.json.original', JSON.stringify(objects));
try {
f = fs.readFileSync(dataDir + 'states.json');
fs.writeFileSync(dataDir + 'states.json.original', f);
}
catch (err) {
console.log('no states.json found - ignore');
}
}
function restoreOriginalFiles() {
console.log('restoreOriginalFiles...');
var dataDir = rootDir + 'tmp/' + appName + '-data/';
var f = fs.readFileSync(dataDir + 'objects.json.original');
fs.writeFileSync(dataDir + 'objects.json', f);
try {
f = fs.readFileSync(dataDir + 'states.json.original');
fs.writeFileSync(dataDir + 'states.json', f);
}
catch (err) {
console.log('no states.json.original found - ignore');
}
}
function checkIsAdapterInstalled(cb, counter, customName) {
customName = customName || pkg.name.split('.').pop();
counter = counter || 0;
var dataDir = rootDir + 'tmp/' + appName + '-data/';
console.log('checkIsAdapterInstalled...');
try {
var f = fs.readFileSync(dataDir + 'objects.json');
var objects = JSON.parse(f.toString());
if (objects['system.adapter.' + customName + '.0']) {
console.log('checkIsAdapterInstalled: ready!');
setTimeout(function () {
if (cb) cb();
}, 100);
return;
} else {
console.warn('checkIsAdapterInstalled: still not ready');
}
} catch (err) {
}
if (counter > 20) {
console.error('checkIsAdapterInstalled: Cannot install!');
if (cb) cb('Cannot install');
} else {
console.log('checkIsAdapterInstalled: wait...');
setTimeout(function() {
checkIsAdapterInstalled(cb, counter + 1);
}, 1000);
}
}
function checkIsControllerInstalled(cb, counter) {
counter = counter || 0;
var dataDir = rootDir + 'tmp/' + appName + '-data/';
console.log('checkIsControllerInstalled...');
try {
var f = fs.readFileSync(dataDir + 'objects.json');
var objects = JSON.parse(f.toString());
if (objects['system.adapter.admin.0']) {
console.log('checkIsControllerInstalled: installed!');
setTimeout(function () {
if (cb) cb();
}, 100);
return;
}
} catch (err) {
}
if (counter > 20) {
console.log('checkIsControllerInstalled: Cannot install!');
if (cb) cb('Cannot install');
} else {
console.log('checkIsControllerInstalled: wait...');
setTimeout(function() {
checkIsControllerInstalled(cb, counter + 1);
}, 1000);
}
}
function installAdapter(customName, cb) {
if (typeof customName === 'function') {
cb = customName;
customName = null;
}
customName = customName || pkg.name.split('.').pop();
console.log('Install adapter...');
var startFile = 'node_modules/' + appName + '.js-controller/' + appName + '.js';
// make first install
if (debug) {
child_process.execSync('node ' + startFile + ' add ' + customName + ' --enabled false', {
cwd: rootDir + 'tmp',
stdio: [0, 1, 2]
});
checkIsAdapterInstalled(function (error) {
if (error) console.error(error);
console.log('Adapter installed.');
if (cb) cb();
});
} else {
// add controller
var _pid = child_process.fork(startFile, ['add', customName, '--enabled', 'false'], {
cwd: rootDir + 'tmp',
stdio: [0, 1, 2, 'ipc']
});
waitForEnd(_pid, function () {
checkIsAdapterInstalled(function (error) {
if (error) console.error(error);
console.log('Adapter installed.');
if (cb) cb();
});
});
}
}
function waitForEnd(_pid, cb) {
if (!_pid) {
cb(-1, -1);
return;
}
_pid.on('exit', function (code, signal) {
if (_pid) {
_pid = null;
cb(code, signal);
}
});
_pid.on('close', function (code, signal) {
if (_pid) {
_pid = null;
cb(code, signal);
}
});
}
function installJsController(cb) {
console.log('installJsController...');
if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller') ||
!fs.existsSync(rootDir + 'tmp/' + appName + '-data')) {
// try to detect appName.js-controller in node_modules/appName.js-controller
// travis CI installs js-controller into node_modules
if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller')) {
console.log('installJsController: no js-controller => copy it from "' + rootDir + 'node_modules/' + appName + '.js-controller"');
// copy all
// stop controller
console.log('Stop controller if running...');
var _pid;
if (debug) {
// start controller
_pid = child_process.exec('node ' + appName + '.js stop', {
cwd: rootDir + 'node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2]
});
} else {
_pid = child_process.fork(appName + '.js', ['stop'], {
cwd: rootDir + 'node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2, 'ipc']
});
}
waitForEnd(_pid, function () {
// copy all files into
if (!fs.existsSync(rootDir + 'tmp')) fs.mkdirSync(rootDir + 'tmp');
if (!fs.existsSync(rootDir + 'tmp/node_modules')) fs.mkdirSync(rootDir + 'tmp/node_modules');
if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')){
console.log('Copy js-controller...');
copyFolderRecursiveSync(rootDir + 'node_modules/' + appName + '.js-controller', rootDir + 'tmp/node_modules/');
}
console.log('Setup js-controller...');
var __pid;
if (debug) {
// start controller
_pid = child_process.exec('node ' + appName + '.js setup first --console', {
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2]
});
} else {
__pid = child_process.fork(appName + '.js', ['setup', 'first', '--console'], {
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2, 'ipc']
});
}
waitForEnd(__pid, function () {
checkIsControllerInstalled(function () {
// change ports for object and state DBs
var config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json');
config.objects.port = 19001;
config.states.port = 19000;
fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2));
console.log('Setup finished.');
copyAdapterToController();
installAdapter(function () {
storeOriginalFiles();
if (cb) cb(true);
});
});
});
});
} else {
// check if port 9000 is free, else admin adapter will be added to running instance
var client = new require('net').Socket();
client.connect(9000, '127.0.0.1', function() {
console.error('Cannot initiate fisrt run of test, because one instance of application is running on this PC. Stop it and repeat.');
process.exit(0);
});
setTimeout(function () {
client.destroy();
if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')) {
console.log('installJsController: no js-controller => install from git');
child_process.execSync('npm install https://git.spacen.net/' + appName + '/' + appName + '.js-controller/tarball/master --prefix ./ --production', {
cwd: rootDir + 'tmp/',
stdio: [0, 1, 2]
});
} else {
console.log('Setup js-controller...');
var __pid;
if (debug) {
// start controller
child_process.exec('node ' + appName + '.js setup first', {
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2]
});
} else {
child_process.fork(appName + '.js', ['setup', 'first'], {
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2, 'ipc']
});
}
}
// let npm install admin and run setup
checkIsControllerInstalled(function () {
var _pid;
if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller/' + appName + '.js')) {
_pid = child_process.fork(appName + '.js', ['stop'], {
cwd: rootDir + 'node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2, 'ipc']
});
}
waitForEnd(_pid, function () {
// change ports for object and state DBs
var config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json');
config.objects.port = 19001;
config.states.port = 19000;
fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2));
copyAdapterToController();
installAdapter(function () {
storeOriginalFiles();
if (cb) cb(true);
});
});
});
}, 1000);
}
} else {
setTimeout(function () {
console.log('installJsController: js-controller installed');
if (cb) cb(false);
}, 0);
}
}
function copyAdapterToController() {
console.log('Copy adapter...');
// Copy adapter to tmp/node_modules/appName.adapter
copyFolderRecursiveSync(rootDir, rootDir + 'tmp/node_modules/', ['.idea', 'test', 'tmp', '.git', appName + '.js-controller']);
console.log('Adapter copied.');
}
function clearControllerLog() {
var dirPath = rootDir + 'tmp/log';
var files;
try {
if (fs.existsSync(dirPath)) {
console.log('Clear controller log...');
files = fs.readdirSync(dirPath);
} else {
console.log('Create controller log directory...');
files = [];
fs.mkdirSync(dirPath);
}
} catch(e) {
console.error('Cannot read "' + dirPath + '"');
return;
}
if (files.length > 0) {
try {
for (var i = 0; i < files.length; i++) {
var filePath = dirPath + '/' + files[i];
fs.unlinkSync(filePath);
}
console.log('Controller log cleared');
} catch (err) {
console.error('cannot clear log: ' + err);
}
}
}
function clearDB() {
var dirPath = rootDir + 'tmp/yunkong2-data/sqlite';
var files;
try {
if (fs.existsSync(dirPath)) {
console.log('Clear sqlite DB...');
files = fs.readdirSync(dirPath);
} else {
console.log('Create controller log directory...');
files = [];
fs.mkdirSync(dirPath);
}
} catch(e) {
console.error('Cannot read "' + dirPath + '"');
return;
}
if (files.length > 0) {
try {
for (var i = 0; i < files.length; i++) {
var filePath = dirPath + '/' + files[i];
fs.unlinkSync(filePath);
}
console.log('Clear sqlite DB');
} catch (err) {
console.error('cannot clear DB: ' + err);
}
}
}
function setupController(cb) {
installJsController(function (isInited) {
clearControllerLog();
clearDB();
if (!isInited) {
restoreOriginalFiles();
copyAdapterToController();
}
// read system.config object
var dataDir = rootDir + 'tmp/' + appName + '-data/';
var objs;
try {
objs = fs.readFileSync(dataDir + 'objects.json');
objs = JSON.parse(objs);
}
catch (e) {
console.log('ERROR reading/parsing system configuration. Ignore');
objs = {'system.config': {}};
}
if (!objs || !objs['system.config']) {
objs = {'system.config': {}};
}
if (cb) cb(objs['system.config']);
});
}
function startAdapter(objects, states, callback) {
if (adapterStarted) {
console.log('Adapter already started ...');
if (callback) callback(objects, states);
return;
}
adapterStarted = true;
console.log('startAdapter...');
if (fs.existsSync(rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main)) {
try {
if (debug) {
// start controller
pid = child_process.exec('node node_modules/' + pkg.name + '/' + pkg.main + ' --console silly', {
cwd: rootDir + 'tmp',
stdio: [0, 1, 2]
});
} else {
// start controller
pid = child_process.fork('node_modules/' + pkg.name + '/' + pkg.main, ['--console', 'silly'], {
cwd: rootDir + 'tmp',
stdio: [0, 1, 2, 'ipc']
});
}
} catch (error) {
console.error(JSON.stringify(error));
}
} else {
console.error('Cannot find: ' + rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main);
}
if (callback) callback(objects, states);
}
function startController(isStartAdapter, onObjectChange, onStateChange, callback) {
if (typeof isStartAdapter === 'function') {
callback = onStateChange;
onStateChange = onObjectChange;
onObjectChange = isStartAdapter;
isStartAdapter = true;
}
if (onStateChange === undefined) {
callback = onObjectChange;
onObjectChange = undefined;
}
if (pid) {
console.error('Controller is already started!');
} else {
console.log('startController...');
adapterStarted = false;
var isObjectConnected;
var isStatesConnected;
var Objects = require(rootDir + 'tmp/node_modules/' + appName + '.js-controller/lib/objects/objectsInMemServer');
objects = new Objects({
connection: {
"type" : "file",
"host" : "127.0.0.1",
"port" : 19001,
"user" : "",
"pass" : "",
"noFileCache": false,
"connectTimeout": 2000
},
logger: {
silly: function (msg) {
console.log(msg);
},
debug: function (msg) {
console.log(msg);
},
info: function (msg) {
console.log(msg);
},
warn: function (msg) {
console.warn(msg);
},
error: function (msg) {
console.error(msg);
}
},
connected: function () {
isObjectConnected = true;
if (isStatesConnected) {
console.log('startController: started!');
if (isStartAdapter) {
startAdapter(objects, states, callback);
} else {
if (callback) {
callback(objects, states);
callback = null;
}
}
}
},
change: onObjectChange
});
// Just open in memory DB itself
var States = require(rootDir + 'tmp/node_modules/' + appName + '.js-controller/lib/states/statesInMemServer');
states = new States({
connection: {
type: 'file',
host: '127.0.0.1',
port: 19000,
options: {
auth_pass: null,
retry_max_delay: 15000
}
},
logger: {
silly: function (msg) {
console.log(msg);
},
debug: function (msg) {
console.log(msg);
},
info: function (msg) {
console.log(msg);
},
warn: function (msg) {
console.log(msg);
},
error: function (msg) {
console.log(msg);
}
},
connected: function () {
isStatesConnected = true;
if (isObjectConnected) {
console.log('startController: started!!');
if (isStartAdapter) {
startAdapter(objects, states, callback);
} else {
if (callback) {
callback(objects, states);
callback = null;
}
}
}
},
change: onStateChange
});
}
}
function stopAdapter(cb) {
if (!pid) {
console.error('Controller is not running!');
if (cb) {
setTimeout(function () {
cb(false);
}, 0);
}
} else {
adapterStarted = false;
pid.on('exit', function (code, signal) {
if (pid) {
console.log('child process terminated due to receipt of signal ' + signal);
if (cb) cb();
pid = null;
}
});
pid.on('close', function (code, signal) {
if (pid) {
if (cb) cb();
pid = null;
}
});
pid.kill('SIGTERM');
}
}
function _stopController() {
if (objects) {
objects.destroy();
objects = null;
}
if (states) {
states.destroy();
states = null;
}
}
function stopController(cb) {
var timeout;
if (objects) {
console.log('Set system.adapter.' + pkg.name + '.0');
objects.setObject('system.adapter.' + pkg.name + '.0', {
common:{
enabled: false
}
});
}
stopAdapter(function () {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
_stopController();
if (cb) {
cb(true);
cb = null;
}
});
timeout = setTimeout(function () {
timeout = null;
console.log('child process NOT terminated');
_stopController();
if (cb) {
cb(false);
cb = null;
}
pid = null;
}, 5000);
}
// Setup the adapter
function setAdapterConfig(common, native, instance) {
var objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString());
var id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0);
if (common) objects[id].common = common;
if (native) objects[id].native = native;
fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/objects.json', JSON.stringify(objects));
}
// Read config of the adapter
function getAdapterConfig(instance) {
var objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString());
var id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0);
return objects[id];
}
if (typeof module !== undefined && module.parent) {
module.exports.getAdapterConfig = getAdapterConfig;
module.exports.setAdapterConfig = setAdapterConfig;
module.exports.startController = startController;
module.exports.stopController = stopController;
module.exports.setupController = setupController;
module.exports.stopAdapter = stopAdapter;
module.exports.startAdapter = startAdapter;
module.exports.installAdapter = installAdapter;
module.exports.appName = appName;
module.exports.adapterName = adapterName;
module.exports.adapterStarted = adapterStarted;
}

460
test/testApi.js Normal file
View File

@ -0,0 +1,460 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
/*jshint expr: true*/
var expect = require('chai').expect;
var setup = require(__dirname + '/lib/setup');
var request = require('request');
var objects = null;
var states = null;
process.env.NO_PROXY = '127.0.0.1';
function checkConnectionOfAdapter(cb, counter) {
counter = counter || 0;
console.log('Try check #' + counter);
if (counter > 30) {
if (cb) cb('Cannot check connection');
return;
}
states.getState('system.adapter.simple-api.0.alive', function (err, state) {
if (err) console.error(err);
if (state && state.val) {
if (cb) cb();
} else {
setTimeout(function () {
checkConnectionOfAdapter(cb, counter + 1);
}, 1000);
}
});
}
describe('Test RESTful API', function() {
before('Test RESTful API: Start js-controller', function (_done) {
this.timeout(600000); // because of first install from npm
var brokerStarted = false;
setup.adapterStarted = false;
setup.setupController(function () {
var config = setup.getAdapterConfig();
// enable adapter
config.common.enabled = true;
config.common.loglevel = 'debug';
config.native.port = 18183;
setup.setAdapterConfig(config.common, config.native);
setup.startController(function (_objects, _states) {
objects = _objects;
states = _states;
// give some time to start server
setTimeout(function () {
_done();
}, 2000);
});
});
});
it('Test adapter: Check if adapter started and create upload datapoint', function (done) {
this.timeout(60000);
checkConnectionOfAdapter(function (res) {
if (res) console.log(res);
expect(res).not.to.be.equal('Cannot check connection');
objects.setObject('javascript.0.test-string', {
common: {
name: 'test',
type: 'string',
role: 'value',
def: ''
},
native: {
},
type: 'state'
}, function (err) {
expect(err).to.be.null;
states.setState('javascript.0.test-string','', function(err) {
expect(err).to.be.null;
done();
});
});
});
});
it('Test RESTful API: get - must return value', function (done) {
request('http://127.0.0.1:18183/get/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('get/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
//{
// "val" : true,
// "ack" : true,
// "ts" : 1455009717,
// "q" : 0,
// "from" : "system.adapter.simple-api.0",
// "lc" : 1455009717,
// "expire" : 30000,
// "_id" : "system.adapter.simple-api.0.alive",
// "type" : "state",
// "common" : {
// "name" : "simple-api.0.alive",
// "type" : "boolean",
// "role" : "indicator.state"
// },
// "native" : {}
//
//}
expect(obj).to.be.ok;
expect(obj.val).to.be.true;
expect(obj.ack).to.be.true;
expect(obj.ts).to.be.ok;
//expect(obj.from).to.equal("system.adapter.simple-api.0");
expect(obj.type).to.equal("state");
expect(obj._id).to.equal("system.adapter.simple-api.0.alive");
expect(obj.common).to.be.ok;
expect(obj.native).to.be.ok;
expect(obj.common.name).to.equal("simple-api.0.alive");
expect(obj.common.role).to.equal("indicator.state");
expect(response.statusCode).to.equal(200);
done();
});
});
it('Test RESTful API: getPlainValue - must return plain value', function (done) {
request('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('getPlainValue/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('true');
expect(response.statusCode).to.equal(200);
done();
});
});
it('Test RESTful API: set - must set value', function (done) {
request('http://127.0.0.1:18183/set/system.adapter.simple-api.0.alive?val=false', function (error, response, body) {
console.log('set/system.adapter.simple-api.0.alive?val=false => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj.val).to.be.false;
expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('getPlainValue/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('false');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/get/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('get/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
expect(JSON.parse(body).val).equal(false);
expect(response.statusCode).to.equal(200);
done();
});
});
});
});
it('Test RESTful API: set - must set easy string value', function (done) {
request('http://127.0.0.1:18183/set/javascript.0.test-string?val=bla', function (error, response, body) {
console.log('set/javascript.0.test-string?val=bla => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj.val).equal('bla');
expect(obj.id).to.equal('javascript.0.test-string');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getPlainValue/javascript.0.test-string', function (error, response, body) {
console.log('getPlainValue/javascript.0.test-string => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('"bla"');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/get/javascript.0.test-string', function (error, response, body) {
console.log('get/javascript.0.test-string => ' + body);
expect(error).to.be.not.ok;
expect(JSON.parse(body).val).equal('bla');
expect(response.statusCode).to.equal(200);
done();
});
});
});
});
it('Test RESTful API: set - must set encoded string value', function (done) {
request('http://127.0.0.1:18183/set/javascript.0.test-string?val=bla%26fasel%2efoo%3Dhummer+hey', function (error, response, body) {
console.log('set/javascript.0.test-string?val=bla%20fasel%2efoo => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj.val).equal('bla&fasel.foo=hummer hey');
expect(obj.id).to.equal('javascript.0.test-string');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getPlainValue/javascript.0.test-string', function (error, response, body) {
console.log('getPlainValue/javascript.0.test-string => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('"bla&fasel.foo=hummer hey"');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/get/javascript.0.test-string', function (error, response, body) {
console.log('get/javascript.0.test-string => ' + body);
expect(error).to.be.not.ok;
expect(JSON.parse(body).val).equal('bla&fasel.foo=hummer hey');
expect(response.statusCode).to.equal(200);
done();
});
});
});
});
it('Test RESTful API: set - must set val', function (done) {
request('http://127.0.0.1:18183/set/system.adapter.simple-api.0.alive?val=true', function (error, response, body) {
console.log('set/system.adapter.simple-api.0.alive?val=true => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj.val).to.be.true;
expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('getPlainValue/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('true');
expect(response.statusCode).to.equal(200);
done();
});
});
});
it('Test RESTful API: toggle - must toggle boolean value to false', function (done) {
request('http://127.0.0.1:18183/toggle/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('toggle/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj.val).to.be.false;
expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('getPlainValue/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('false');
expect(response.statusCode).to.equal(200);
done();
});
});
});
it('Test RESTful API: toggle - must toggle boolean value to true', function (done) {
request('http://127.0.0.1:18183/toggle/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('toggle/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj.val).to.be.true;
expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('getPlainValue/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('true');
expect(response.statusCode).to.equal(200);
done();
});
});
});
it('Test RESTful API: toggle - must toggle number value to 100', function (done) {
request('http://127.0.0.1:18183/toggle/system.adapter.simple-api.upload', function (error, response, body) {
console.log('toggle/system.adapter.simple-api.upload => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj.val).to.be.equal(100);
expect(obj.id).to.equal('system.adapter.simple-api.upload');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.upload', function (error, response, body) {
console.log('getPlainValue/system.adapter.simple-api.upload => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('100');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/set/system.adapter.simple-api.upload?val=49', function (error, response, body) {
console.log('set/system.adapter.simple-api.upload?val=49 => ' + body);
request('http://127.0.0.1:18183/toggle/system.adapter.simple-api.upload', function (error, response, body) {
console.log('toggle/system.adapter.simple-api.upload => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj.val).to.be.equal(51);
expect(obj.id).to.equal('system.adapter.simple-api.upload');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.upload', function (error, response, body) {
console.log('getPlainValue/system.adapter.simple-api.upload => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('51');
expect(response.statusCode).to.equal(200);
done();
});
});
});
});
});
});
it('Test RESTful API: setBulk - must set values', function (done) {
request('http://127.0.0.1:18183/setBulk?system.adapter.simple-api.upload=50&system.adapter.simple-api.0.alive=false', function (error, response, body) {
console.log('setBulk/?system.adapter.simple-api.upload=50&system.adapter.simple-api.0.alive=false => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj[0].val).to.be.equal(50);
expect(obj[0].id).to.equal('system.adapter.simple-api.upload');
expect(obj[1].val).to.be.equal(false);
expect(obj[1].id).to.equal('system.adapter.simple-api.0.alive');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getBulk/system.adapter.simple-api.upload,system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('getBulk/system.adapter.simple-api.upload,system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj[0].val).equal(50);
expect(obj[1].val).equal(false);
expect(response.statusCode).to.equal(200);
done();
});
});
});
it('Test RESTful API: objects - must return objects', function (done) {
request('http://127.0.0.1:18183/objects?pattern=system.adapter.*', function (error, response, body) {
console.log('objects?pattern=system.adapter.* => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj['system.adapter.simple-api.0.alive']._id).to.be.ok;
expect(response.statusCode).to.equal(200);
done();
});
});
it('Test RESTful API: objects - must return objects', function (done) {
request('http://127.0.0.1:18183/objects?pattern=system.adapter.*&type=instance', function (error, response, body) {
console.log('objects?pattern=system.adapter.* => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj['system.adapter.simple-api.0']._id).to.be.ok;
expect(response.statusCode).to.equal(200);
done();
});
});
it('Test RESTful API: states - must return states', function (done) {
request('http://127.0.0.1:18183/states?pattern=system.adapter.*', function (error, response, body) {
console.log('states?pattern=system.adapter.* => ' + body);
expect(error).to.be.not.ok;
var states = JSON.parse(body);
expect(states['system.adapter.simple-api.0.uptime'].val).to.be.least(0);
expect(response.statusCode).to.equal(200);
done();
});
});
it('Test RESTful API: setBulk(POST) - must set values', function (done) {
request({
uri: 'http://127.0.0.1:18183/setBulk',
method: 'POST',
body: 'system.adapter.simple-api.upload=50&system.adapter.simple-api.0.alive=false&javascript.0.test-string=bla%26fasel%2efoo%3Dhummer+hey'
}, function(error, response, body) {
console.log('setBulk/?system.adapter.simple-api.upload=50&system.adapter.simple-api.0.alive=false&javascript.0.test-string=bla%26fasel%2efoo%3Dhummer+hey => ' + JSON.stringify(body));
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj[0].val).to.be.equal(50);
expect(obj[0].id).to.equal('system.adapter.simple-api.upload');
expect(obj[1].val).to.be.equal(false);
expect(obj[1].id).to.equal('system.adapter.simple-api.0.alive');
expect(obj[2].val).to.be.equal('bla&fasel.foo=hummer hey');
expect(obj[2].id).to.equal('javascript.0.test-string');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getBulk/system.adapter.simple-api.upload,system.adapter.simple-api.0.alive,javascript.0.test-string', function (error, response, body) {
console.log('getBulk/system.adapter.simple-api.upload,system.adapter.simple-api.0.alive,javascript.0.test-string => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj[0].val).equal(50);
expect(obj[1].val).equal(false);
expect(obj[2].val).equal('bla&fasel.foo=hummer hey');
expect(response.statusCode).to.equal(200);
done();
});
});
});
it('Test RESTful API: setBulk(POST-GET-Mix) - must set values', function (done) {
request({
uri: 'http://127.0.0.1:18183/setBulk?system.adapter.simple-api.upload=51&system.adapter.simple-api.0.alive=false',
method: 'POST',
body: ''
}, function(error, response, body) {
console.log('setBulk/?system.adapter.simple-api.upload=51&system.adapter.simple-api.0.alive=false => ' + JSON.stringify(body));
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj[0].val).to.be.equal(51);
expect(obj[0].id).to.equal('system.adapter.simple-api.upload');
expect(obj[1].val).to.be.equal(false);
expect(obj[1].id).to.equal('system.adapter.simple-api.0.alive');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getBulk/system.adapter.simple-api.upload,system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('getBulk/system.adapter.simple-api.upload,system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj[0].val).equal(51);
expect(obj[1].val).equal(false);
expect(response.statusCode).to.equal(200);
done();
});
});
});
it('Test RESTful API: setValueFromBody(POST) - must set one value', function (done) {
request({
uri: 'http://127.0.0.1:18183/setValueFromBody/system.adapter.simple-api.upload',
method: 'POST',
body: '55'
}, function(error, response, body) {
console.log('setValueFromBody/?system.adapter.simple-api.upload => ' + JSON.stringify(body));
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj[0].val).to.be.equal(55);
expect(obj[0].id).to.equal('system.adapter.simple-api.upload');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getBulk/system.adapter.simple-api.upload', function (error, response, body) {
console.log('getBulk/system.adapter.simple-api.upload => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj[0].val).equal(55);
expect(response.statusCode).to.equal(200);
done();
});
});
});
after('Test RESTful API: Stop js-controller', function (done) {
this.timeout(6000);
setup.stopController(function (normalTerminated) {
console.log('Adapter normal terminated: ' + normalTerminated);
done();
});
});
});

View File

@ -0,0 +1,277 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
/*jshint expr: true*/
var expect = require('chai').expect;
var setup = require(__dirname + '/lib/setup');
var request = require('request');
var objects = null;
var states = null;
process.env.NO_PROXY = '127.0.0.1';
function checkConnectionOfAdapter(cb, counter) {
counter = counter || 0;
console.log('Try check #' + counter);
if (counter > 30) {
if (cb) cb('Cannot check connection');
return;
}
states.getState('system.adapter.simple-api.0.alive', function (err, state) {
if (err) console.error(err);
if (state && state.val) {
if (cb) cb();
} else {
setTimeout(function () {
checkConnectionOfAdapter(cb, counter + 1);
}, 1000);
}
});
}
describe('Test RESTful API as Owner-User', function() {
before('Test RESTful API as Owner-User: Start js-controller', function (_done) {
this.timeout(600000); // because of first install from npm
setup.adapterStarted = false;
var brokerStarted = false;
setup.setupController(function () {
var config = setup.getAdapterConfig();
// enable adapter
config.common.enabled = true;
config.common.loglevel = 'debug';
config.native.port = 18183;
config.native.defaultUser = 'myuser';
config.native.onlyAllowWhenUserIsOwner = true;
setup.setAdapterConfig(config.common, config.native);
setup.startController(function (_objects, _states) {
objects = _objects;
states = _states;
// give some time to start server
setTimeout(function () {
_done();
}, 2000);
});
});
});
it('Test adapter: Check if adapter started and create upload datapoint', function (done) {
this.timeout(60000);
checkConnectionOfAdapter(function (res) {
if (res) console.log(res);
expect(res).not.to.be.equal('Cannot check connection');
objects.setObject('system.group.writer', {
"common": {
"name": "Writer",
"desc": "",
"members": [
"system.user.myuser"
],
"acl": {
"object": {
"list": false,
"read": false,
"write": false,
"delete": false
},
"state": {
"list": false,
"read": true,
"write": true,
"create": false,
"delete": false
},
"users": {
"write": false,
"create": false,
"delete": false
},
"other": {
"execute": false,
"http": false,
"sendto": false
},
"file": {
"list": false,
"read": false,
"write": false,
"create": false,
"delete": false
}
}
},
"native": {},
"acl": {
"object": 1638,
"owner": "system.user.admin",
"ownerGroup": "system.group.administrator"
},
"_id": "system.group.writer",
"type": "group"
}, function (err) {
expect(err).to.be.null;
objects.setObject('system.user.myuser', {
"type": "user",
"common": {
"name": "myuser",
"enabled": true,
"groups": [],
"password": "pbkdf2$10000$ab4104d8bb68390ee7e6c9397588e768de6c025f0c732c18806f3d1270c83f83fa86a7bf62583770e5f8d0b405fbb3ad32214ef3584f5f9332478f2506414443a910bf15863b36ebfcaa7cbb19253ae32cd3ca390dab87b29cd31e11be7fa4ea3a01dad625d9de44e412680e1a694227698788d71f1e089e5831dc1bbacfa794b45e1c995214bf71ee4160d98b4305fa4c3e36ee5f8da19b3708f68e7d2e8197375c0f763d90e31143eb04760cc2148c8f54937b9385c95db1742595634ed004fa567655dfe1d9b9fa698074a9fb70c05a252b2d9cf7ca1c9b009f2cd70d6972ccf0ee281d777d66a0346c6c6525436dd7fe3578b28dca2c7adbfde0ecd45148$31c3248ba4dc9600a024b4e0e7c3e585"
},
"_id": "system.user.myuser",
"native": {},
"acl": {
"object": 1638
}
}, function (err) {
expect(err).to.be.null;
objects.setObject('javascript.0.test', {
common: {
name: 'test',
type: 'number',
role: 'level',
min: -100,
max: 100,
def: 1
},
native: {
},
type: 'state',
acl: {
object: 1638,
owner: "system.user.myuser",
ownerGroup:"system.group.administrator",
state: 1638
}
}, function (err) {
expect(err).to.be.null;
states.setState('javascript.0.test',1, function(err) {
console.log('END javascript.0.test ' + err);
expect(err).to.be.null;
done();
});
});
});
});
});
});
it('Test RESTful API as Owner-User: get - must return value', function (done) {
console.log('START get/system.adapter.simple-api.0.alive');
request('http://127.0.0.1:18183/get/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('get/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
expect(body).to.be.equal('error: permissionError');
expect(response.statusCode).to.equal(401);
done();
});
});
it('Test RESTful API as Owner-User: getPlainValue - must return plain value', function (done) {
request('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('getPlainValue/system.adapter.simple-api.0.alive => ' + body);
expect(body).to.be.equal('error: permissionError');
expect(response.statusCode).to.equal(401);
done();
});
});
it('Test RESTful API as Owner-User: getPlainValue 4 Test-Endpoint - must return plain value', function (done) {
request('http://127.0.0.1:18183/getPlainValue/javascript.0.test', function (error, response, body) {
console.log('getPlainValue/javascript.0.test => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('1');
expect(response.statusCode).to.equal(200);
done();
});
});
it('Test RESTful API as Owner-User: set 4 Test-Endpoint - must set value', function (done) {
request('http://127.0.0.1:18183/set/javascript.0.test?val=2', function (error, response, body) {
console.log('set/javascript.0.test?val=false => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj.val).to.be.equal(2);
expect(obj.id).to.equal('javascript.0.test');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getPlainValue/javascript.0.test', function (error, response, body) {
console.log('getPlainValue/javascript.0.test => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('2');
expect(response.statusCode).to.equal(200);
done();
});
});
});
it('Test RESTful API as Owner-User: set - must set value', function (done) {
request('http://127.0.0.1:18183/set/system.adapter.simple-api.0.alive?val=false', function (error, response, body) {
console.log('set/system.adapter.simple-api.0.alive?val=false => ' + body);
expect(body).to.be.equal('error: permissionError');
expect(response.statusCode).to.equal(401);
done();
});
});
it('Test RESTful API as Owner-User: set - must set val', function (done) {
request('http://127.0.0.1:18183/set/system.adapter.simple-api.0.alive?val=true', function (error, response, body) {
console.log('set/system.adapter.simple-api.0.alive?val=true => ' + body);
expect(body).to.be.equal('error: permissionError');
expect(response.statusCode).to.equal(401);
done();
});
});
it('Test RESTful API as Owner-User: objects - must return objects', function (done) {
request('http://127.0.0.1:18183/objects?pattern=system.adapter.*', function (error, response, body) {
console.log('objects?pattern=system.adapter.* => ' + body);
expect(body).to.be.equal('error: permissionError');
expect(response.statusCode).to.equal(401);
done();
});
});
it('Test RESTful API as Owner-User: objects - must return objects', function (done) {
request('http://127.0.0.1:18183/objects?pattern=system.adapter.*&type=instance', function (error, response, body) {
console.log('objects?pattern=system.adapter.* => ' + body);
expect(body).to.be.equal('error: permissionError');
expect(response.statusCode).to.equal(401);
done();
});
});
it('Test RESTful API as Owner-User: states - must return states', function (done) {
request('http://127.0.0.1:18183/states?pattern=system.adapter.*', function (error, response, body) {
console.log('states?pattern=system.adapter.* => ' + body);
expect(body).to.be.equal('error: permissionError');
expect(response.statusCode).to.equal(401);
done();
});
});
it('Test RESTful API as Owner-User: setValueFromBody(POST) - must set one value', function (done) {
request({
uri: 'http://127.0.0.1:18183/setValueFromBody/system.adapter.simple-api.upload',
method: 'POST',
body: '55'
}, function(error, response, body) {
console.log('setValueFromBody/?system.adapter.simple-api.upload => ' + JSON.stringify(body));
expect(body).to.be.equal('error: permissionError');
expect(response.statusCode).to.equal(401);
done();
});
});
after('Test RESTful API as Owner-User: Stop js-controller', function (done) {
this.timeout(6000);
setup.stopController(function (normalTerminated) {
console.log('Adapter normal terminated: ' + normalTerminated);
done();
});
});
});

508
test/testApiAsUser.js Normal file
View File

@ -0,0 +1,508 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
/*jshint expr: true*/
var expect = require('chai').expect;
var setup = require(__dirname + '/lib/setup');
var request = require('request');
var objects = null;
var states = null;
process.env.NO_PROXY = '127.0.0.1';
function checkConnectionOfAdapter(cb, counter) {
counter = counter || 0;
console.log('Try check #' + counter);
if (counter > 30) {
if (cb) cb('Cannot check connection');
return;
}
states.getState('system.adapter.simple-api.0.alive', function (err, state) {
if (err) console.error(err);
if (state && state.val) {
if (cb) cb();
} else {
setTimeout(function () {
checkConnectionOfAdapter(cb, counter + 1);
}, 1000);
}
});
}
describe('Test RESTful API as User', function() {
before('Test RESTful API as User: Start js-controller', function (_done) {
this.timeout(600000); // because of first install from npm
setup.adapterStarted = false;
var brokerStarted = false;
setup.setupController(function () {
var config = setup.getAdapterConfig();
// enable adapter
config.common.enabled = true;
config.common.loglevel = 'debug';
config.native.port = 18183;
config.native.defaultUser = 'myuser';
setup.setAdapterConfig(config.common, config.native);
setup.startController(function (_objects, _states) {
objects = _objects;
states = _states;
// give some time to start server
setTimeout(function () {
_done();
}, 2000);
});
});
});
it('Test adapter: Check if adapter started and create upload datapoint', function (done) {
this.timeout(60000);
checkConnectionOfAdapter(function (res) {
if (res) console.log(res);
expect(res).not.to.be.equal('Cannot check connection');
objects.setObject('system.group.writer', {
"common": {
"name": "Writer",
"desc": "",
"members": [
"system.user.myuser"
],
"acl": {
"object": {
"list": false,
"read": false,
"write": false,
"delete": false
},
"state": {
"list": false,
"read": true,
"write": true,
"create": false,
"delete": false
},
"users": {
"write": false,
"create": false,
"delete": false
},
"other": {
"execute": false,
"http": false,
"sendto": false
},
"file": {
"list": false,
"read": false,
"write": false,
"create": false,
"delete": false
}
}
},
"native": {},
"acl": {
"object": 1638,
"owner": "system.user.admin",
"ownerGroup": "system.group.administrator"
},
"_id": "system.group.writer",
"type": "group"
}, function (err) {
expect(err).to.be.null;
objects.setObject('system.user.myuser', {
"type": "user",
"common": {
"name": "myuser",
"enabled": true,
"groups": [],
"password": "pbkdf2$10000$ab4104d8bb68390ee7e6c9397588e768de6c025f0c732c18806f3d1270c83f83fa86a7bf62583770e5f8d0b405fbb3ad32214ef3584f5f9332478f2506414443a910bf15863b36ebfcaa7cbb19253ae32cd3ca390dab87b29cd31e11be7fa4ea3a01dad625d9de44e412680e1a694227698788d71f1e089e5831dc1bbacfa794b45e1c995214bf71ee4160d98b4305fa4c3e36ee5f8da19b3708f68e7d2e8197375c0f763d90e31143eb04760cc2148c8f54937b9385c95db1742595634ed004fa567655dfe1d9b9fa698074a9fb70c05a252b2d9cf7ca1c9b009f2cd70d6972ccf0ee281d777d66a0346c6c6525436dd7fe3578b28dca2c7adbfde0ecd45148$31c3248ba4dc9600a024b4e0e7c3e585"
},
"_id": "system.user.myuser",
"native": {},
"acl": {
"object": 1638
}
}, function (err) {
expect(err).to.be.null;
objects.setObject('javascript.0.test', {
common: {
name: 'test',
type: 'number',
role: 'level',
min: -100,
max: 100,
def: 1
},
native: {
},
type: 'state',
acl: {
object: 1638,
owner: "system.user.myuser",
ownerGroup:"system.group.administrator",
state: 1638
}
}, function (err) {
expect(err).to.be.null;
states.setState('javascript.0.test',1, function(err) {
expect(err).to.be.null;
done();
});
});
});
});
});
});
it('Test RESTful API as User: get - must return value', function (done) {
request('http://127.0.0.1:18183/get/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('get/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
//{
// "val" : true,
// "ack" : true,
// "ts" : 1455009717,
// "q" : 0,
// "from" : "system.adapter.simple-api.0",
// "lc" : 1455009717,
// "expire" : 30000,
// "_id" : "system.adapter.simple-api.0.alive",
// "type" : "state",
// "common" : {
// "name" : "simple-api.0.alive",
// "type" : "boolean",
// "role" : "indicator.state"
// },
// "native" : {}
//
//}
expect(obj).to.be.ok;
expect(obj.val).to.be.true;
expect(obj.ack).to.be.true;
expect(obj.ts).to.be.ok;
//expect(obj.from).to.equal("system.adapter.simple-api.0");
expect(obj.type).to.equal("state");
expect(obj._id).to.equal("system.adapter.simple-api.0.alive");
expect(obj.common).to.be.ok;
expect(obj.native).to.be.ok;
expect(obj.common.name).to.equal("simple-api.0.alive");
expect(obj.common.role).to.equal("indicator.state");
expect(response.statusCode).to.equal(200);
done();
});
});
it('Test RESTful API as User: getPlainValue - must return plain value', function (done) {
request('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('getPlainValue/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('true');
expect(response.statusCode).to.equal(200);
done();
});
});
it('Test RESTful API as User: getPlainValue 4 Test-Endpoint - must return plain value', function (done) {
request('http://127.0.0.1:18183/getPlainValue/javascript.0.test', function (error, response, body) {
console.log('getPlainValue/javascript.0.test => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('1');
expect(response.statusCode).to.equal(200);
done();
});
});
it('Test RESTful API as User: set 4 Test-Endpoint - must set value', function (done) {
request('http://127.0.0.1:18183/set/javascript.0.test?val=2', function (error, response, body) {
console.log('set/javascript.0.test?val=false => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj.val).to.be.equal(2);
expect(obj.id).to.equal('javascript.0.test');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getPlainValue/javascript.0.test', function (error, response, body) {
console.log('getPlainValue/javascript.0.test => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('2');
expect(response.statusCode).to.equal(200);
done();
});
});
});
it('Test RESTful API as User: set - must set value', function (done) {
request('http://127.0.0.1:18183/set/system.adapter.simple-api.0.alive?val=false', function (error, response, body) {
console.log('set/system.adapter.simple-api.0.alive?val=false => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj.val).to.be.false;
expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('getPlainValue/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('false');
expect(response.statusCode).to.equal(200);
done();
});
});
});
it('Test RESTful API as User: set - must set val', function (done) {
request('http://127.0.0.1:18183/set/system.adapter.simple-api.0.alive?val=true', function (error, response, body) {
console.log('set/system.adapter.simple-api.0.alive?val=true => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj.val).to.be.true;
expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('getPlainValue/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('true');
expect(response.statusCode).to.equal(200);
done();
});
});
});
it('Test RESTful API as User: toggle - must toggle boolean value to false', function (done) {
request('http://127.0.0.1:18183/toggle/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('toggle/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj.val).to.be.false;
expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('getPlainValue/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('false');
expect(response.statusCode).to.equal(200);
done();
});
});
});
it('Test RESTful API as User: toggle - must toggle boolean value to true', function (done) {
request('http://127.0.0.1:18183/toggle/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('toggle/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj.val).to.be.true;
expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('getPlainValue/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('true');
expect(response.statusCode).to.equal(200);
done();
});
});
});
it('Test RESTful API as User: toggle - must toggle number value to 100', function (done) {
request('http://127.0.0.1:18183/toggle/system.adapter.simple-api.upload', function (error, response, body) {
console.log('toggle/system.adapter.simple-api.upload => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj.val).to.be.equal(100);
expect(obj.id).to.equal('system.adapter.simple-api.upload');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.upload', function (error, response, body) {
console.log('getPlainValue/system.adapter.simple-api.upload => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('100');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/set/system.adapter.simple-api.upload?val=49', function (error, response, body) {
console.log('set/system.adapter.simple-api.upload?val=49 => ' + body);
request('http://127.0.0.1:18183/toggle/system.adapter.simple-api.upload', function (error, response, body) {
console.log('toggle/system.adapter.simple-api.upload => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj.val).to.be.equal(51);
expect(obj.id).to.equal('system.adapter.simple-api.upload');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.upload', function (error, response, body) {
console.log('getPlainValue/system.adapter.simple-api.upload => ' + body);
expect(error).to.be.not.ok;
expect(body).equal('51');
expect(response.statusCode).to.equal(200);
done();
});
});
});
});
});
});
it('Test RESTful API as User: setBulk - must set values', function (done) {
request('http://127.0.0.1:18183/setBulk/?system.adapter.simple-api.upload=50&system.adapter.simple-api.0.alive=false&javascript.0.test=3', function (error, response, body) {
console.log('setBulk/?system.adapter.simple-api.upload=50&system.adapter.simple-api.0.alive=false&javascript.0.test=3 => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj[0].val).to.be.equal(50);
expect(obj[0].id).to.equal('system.adapter.simple-api.upload');
expect(obj[1].val).to.be.equal(false);
expect(obj[1].id).to.equal('system.adapter.simple-api.0.alive');
expect(obj[2].val).to.be.equal(3);
expect(obj[2].id).to.equal('javascript.0.test');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getBulk/system.adapter.simple-api.upload,system.adapter.simple-api.0.alive,javascript.0.test', function (error, response, body) {
console.log('getBulk/system.adapter.simple-api.upload,system.adapter.simple-api.0.alive&javascript.0.test => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj[0].val).equal(50);
expect(obj[1].val).equal(false);
expect(obj[2].val).equal(3);
expect(response.statusCode).to.equal(200);
done();
});
});
});
it('Test RESTful API as User: objects - must return objects', function (done) {
request('http://127.0.0.1:18183/objects?pattern=system.adapter.*', function (error, response, body) {
console.log('objects?pattern=system.adapter.* => ' + body);
expect(body).to.be.equal('error: permissionError');
expect(response.statusCode).to.equal(401);
done();
});
});
it('Test RESTful API as User: objects - must return objects', function (done) {
request('http://127.0.0.1:18183/objects?pattern=system.adapter.*&type=instance', function (error, response, body) {
console.log('objects?pattern=system.adapter.* => ' + body);
expect(body).to.be.equal('error: permissionError');
expect(response.statusCode).to.equal(401);
done();
});
});
it('Test RESTful API as User: states - must return states', function (done) {
request('http://127.0.0.1:18183/states?pattern=system.adapter.*', function (error, response, body) {
console.log('states?pattern=system.adapter.* => ' + body);
expect(body).to.be.equal('error: permissionError');
expect(response.statusCode).to.equal(401);
done();
});
});
it('Test RESTful API as User: setBulk(POST) - must set values', function (done) {
request({
uri: 'http://127.0.0.1:18183/setBulk',
method: 'POST',
body: 'system.adapter.simple-api.upload=50&system.adapter.simple-api.0.alive=false&javascript.0.test=4'
}, function(error, response, body) {
console.log('setBulk/?system.adapter.simple-api.upload=50&system.adapter.simple-api.0.alive=false&javascript.0.test=4 => ' + JSON.stringify(body));
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj[0].val).to.be.equal(50);
expect(obj[0].id).to.equal('system.adapter.simple-api.upload');
expect(obj[1].val).to.be.equal(false);
expect(obj[1].id).to.equal('system.adapter.simple-api.0.alive');
expect(obj[2].val).to.be.equal(4);
expect(obj[2].id).to.equal('javascript.0.test');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getBulk/system.adapter.simple-api.upload,system.adapter.simple-api.0.alive,javascript.0.test', function (error, response, body) {
console.log('getBulk/system.adapter.simple-api.upload,system.adapter.simple-api.0.alive,javascript.0.test => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj[0].val).equal(50);
expect(obj[1].val).equal(false);
expect(obj[2].val).equal(4);
expect(response.statusCode).to.equal(200);
done();
});
});
});
it('Test RESTful API as User: setBulk(POST-GET-Mix) - must set values', function (done) {
request({
uri: 'http://127.0.0.1:18183/setBulk?system.adapter.simple-api.upload=51&system.adapter.simple-api.0.alive=false',
method: 'POST',
body: ''
}, function(error, response, body) {
console.log('setBulk/?system.adapter.simple-api.upload=51&system.adapter.simple-api.0.alive=false => ' + JSON.stringify(body));
expect(error).to.be.not.ok;
expect(response.statusCode).to.equal(200);
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj[0].val).to.be.equal(51);
expect(obj[0].id).to.equal('system.adapter.simple-api.upload');
expect(obj[1].val).to.be.equal(false);
expect(obj[1].id).to.equal('system.adapter.simple-api.0.alive');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getBulk/system.adapter.simple-api.upload,system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('getBulk/system.adapter.simple-api.upload,system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj[0].val).equal(51);
expect(obj[1].val).equal(false);
expect(response.statusCode).to.equal(200);
done();
});
});
});
it('Test RESTful API as User: setValueFromBody(POST) - must set one value', function (done) {
request({
uri: 'http://127.0.0.1:18183/setValueFromBody/system.adapter.simple-api.upload',
method: 'POST',
body: '55'
}, function(error, response, body) {
console.log('setValueFromBody/?system.adapter.simple-api.upload => ' + JSON.stringify(body));
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj[0].val).to.be.equal(55);
expect(obj[0].id).to.equal('system.adapter.simple-api.upload');
expect(response.statusCode).to.equal(200);
request('http://127.0.0.1:18183/getBulk/system.adapter.simple-api.upload', function (error, response, body) {
console.log('getBulk/system.adapter.simple-api.upload => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj[0].val).equal(55);
expect(response.statusCode).to.equal(200);
done();
});
});
});
after('Test RESTful API as User: Stop js-controller', function (done) {
this.timeout(6000);
setup.stopController(function (normalTerminated) {
console.log('Adapter normal terminated: ' + normalTerminated);
done();
});
});
});

91
test/testPackageFiles.js Normal file
View File

@ -0,0 +1,91 @@
/* jshint -W097 */
/* jshint strict:false */
/* jslint node: true */
/* jshint expr: true */
var expect = require('chai').expect;
var fs = require('fs');
describe('Test package.json and io-package.json', function() {
it('Test package files', function (done) {
console.log();
var fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json', 'utf8');
var ioPackage = JSON.parse(fileContentIOPackage);
var fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json', 'utf8');
var npmPackage = JSON.parse(fileContentNPMPackage);
expect(ioPackage).to.be.an('object');
expect(npmPackage).to.be.an('object');
expect(ioPackage.common.version, 'ERROR: Version number in io-package.json needs to exist').to.exist;
expect(npmPackage.version, 'ERROR: Version number in package.json needs to exist').to.exist;
expect(ioPackage.common.version, 'ERROR: Version numbers in package.json and io-package.json needs to match').to.be.equal(npmPackage.version);
if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {
console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!');
console.log();
}
expect(npmPackage.author, 'ERROR: Author in package.json needs to exist').to.exist;
expect(ioPackage.common.authors, 'ERROR: Authors in io-package.json needs to exist').to.exist;
if (ioPackage.common.name.indexOf('template') !== 0) {
if (Array.isArray(ioPackage.common.authors)) {
expect(ioPackage.common.authors.length, 'ERROR: Author in io-package.json needs to be set').to.not.be.equal(0);
if (ioPackage.common.authors.length === 1) {
expect(ioPackage.common.authors[0], 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
}
}
else {
expect(ioPackage.common.authors, 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
}
}
else {
console.log('WARNING: Testing for set authors field in io-package skipped because template adapter');
console.log();
}
expect(fs.existsSync(__dirname + '/../README.md'), 'ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.').to.be.true;
if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== 'object') {
console.log('WARNING: titleLang is not existing in io-package.json. Please add');
console.log();
}
if (
ioPackage.common.title.indexOf('yunkong2') !== -1 ||
ioPackage.common.title.indexOf('yunkong2') !== -1 ||
ioPackage.common.title.indexOf('adapter') !== -1 ||
ioPackage.common.title.indexOf('Adapter') !== -1
) {
console.log('WARNING: title contains Adapter or yunkong2. It is clear anyway, that it is adapter for yunkong2.');
console.log();
}
if (ioPackage.common.name.indexOf('vis-') !== 0) {
if (!ioPackage.common.materialize || !fs.existsSync(__dirname + '/../admin/index_m.html') || !fs.existsSync(__dirname + '/../gulpfile.js')) {
console.log('WARNING: Admin3 support is missing! Please add it');
console.log();
}
if (ioPackage.common.materialize) {
expect(fs.existsSync(__dirname + '/../admin/index_m.html'), 'Admin3 support is enabled in io-package.json, but index_m.html is missing!').to.be.true;
}
}
var licenseFileExists = fs.existsSync(__dirname + '/../LICENSE');
var fileContentReadme = fs.readFileSync(__dirname + '/../README.md', 'utf8');
if (fileContentReadme.indexOf('## Changelog') === -1) {
console.log('Warning: The README.md should have a section ## Changelog');
console.log();
}
expect((licenseFileExists || fileContentReadme.indexOf('## License') !== -1), 'A LICENSE must exist as LICENSE file or as part of the README.md').to.be.true;
if (!licenseFileExists) {
console.log('Warning: The License should also exist as LICENSE file');
console.log();
}
if (fileContentReadme.indexOf('## License') === -1) {
console.log('Warning: The README.md should also have a section ## License to be shown in Admin3');
console.log();
}
done();
});
});

194
test/testSsl.js Normal file
View File

@ -0,0 +1,194 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
/*jshint expr: true*/
var expect = require('chai').expect;
var setup = require(__dirname + '/lib/setup');
var request = require('request');
var objects = null;
var states = null;
process.env.NO_PROXY = '127.0.0.1';
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
function checkConnectionOfAdapter(cb, counter) {
counter = counter || 0;
console.log('Try check #' + counter);
if (counter > 30) {
if (cb) cb('Cannot check connection');
return;
}
states.getState('system.adapter.simple-api.0.alive', function (err, state) {
if (err) console.error(err);
if (state && state.val) {
if (cb) cb();
} else {
setTimeout(function () {
checkConnectionOfAdapter(cb, counter + 1);
}, 1000);
}
});
}
describe('Test RESTful API SSL', function() {
before('Test RESTful API SSL: Start js-controller', function (_done) {
this.timeout(600000); // because of first install from npm
setup.adapterStarted = false;
var brokerStarted = false;
setup.setupController(function () {
var config = setup.getAdapterConfig();
// enable adapter
config.common.enabled = true;
config.common.loglevel = 'debug';
config.native.port = 18183;
config.native.auth = true;
config.native.secure = true;
config.native.certPublic = 'defaultPublic';
config.native.certPrivate = 'defaultPrivate';
setup.setAdapterConfig(config.common, config.native);
setup.startController(function (_objects, _states) {
objects = _objects;
states = _states;
// give some time to start server
setTimeout(function () {
_done();
}, 2000);
});
});
});
it('Test adapter: Check if adapter started and create upload datapoint', function (done) {
this.timeout(60000);
checkConnectionOfAdapter(function (res) {
if (res) console.log(res);
expect(res).not.to.be.equal('Cannot check connection');
done();
});
});
it('Test RESTful API SSL: get - must return value', function (done) {
request('https://127.0.0.1:18183/get/system.adapter.simple-api.0.alive?user=admin&pass=yunkong2', function (error, response, body) {
console.log('get/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
//{
// "val" : true,
// "ack" : true,
// "ts" : 1455009717,
// "q" : 0,
// "from" : "system.adapter.simple-api.0",
// "lc" : 1455009717,
// "expire" : 30000,
// "_id" : "system.adapter.simple-api.0.alive",
// "type" : "state",
// "common" : {
// "name" : "simple-api.0.alive",
// "type" : "boolean",
// "role" : "indicator.state"
// },
// "native" : {}
//
//}
expect(obj).to.be.ok;
expect(obj.val).to.be.true;
expect(obj.ack).to.be.true;
expect(obj.ts).to.be.ok;
//expect(obj.from).to.equal("system.adapter.simple-api.0");
expect(obj.type).to.equal("state");
expect(obj._id).to.equal("system.adapter.simple-api.0.alive");
expect(obj.common).to.be.ok;
expect(obj.native).to.be.ok;
expect(obj.common.name).to.equal("simple-api.0.alive");
expect(obj.common.role).to.equal("indicator.state");
expect(response.statusCode).to.equal(200);
done();
});
});
it('Test RESTful API SSL: get with no credentials', function (done) {
request('https://127.0.0.1:18183/get/system.adapter.simple-api.0.alive?user=admin&pass=io', function (error, response, body) {
console.log('get/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
expect(response.statusCode).to.be.equal(401);
done();
});
});
it('Test RESTful API SSL: get with wrong credentials', function (done) {
request('https://127.0.0.1:18183/get/system.adapter.simple-api.0.alive', function (error, response, body) {
console.log('get/system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
expect(response.statusCode).to.be.equal(401);
done();
});
});
it('Test RESTful API SSL: setBulk(POST) - must set values', function (done) {
request({
uri: 'https://127.0.0.1:18183/setBulk?user=admin&pass=yunkong2',
method: 'POST',
body: 'system.adapter.simple-api.upload=50&system.adapter.simple-api.0.alive=false'
}, function(error, response, body) {
console.log('setBulk/?system.adapter.simple-api.upload=50&system.adapter.simple-api.0.alive=false => ' + JSON.stringify(body));
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj[0].val).to.be.equal(50);
expect(obj[0].id).to.equal('system.adapter.simple-api.upload');
expect(obj[1].val).to.be.equal(false);
expect(obj[1].id).to.equal('system.adapter.simple-api.0.alive');
expect(response.statusCode).to.equal(200);
request('https://127.0.0.1:18183/getBulk/system.adapter.simple-api.upload,system.adapter.simple-api.0.alive?user=admin&pass=yunkong2', function (error, response, body) {
console.log('getBulk/system.adapter.simple-api.upload,system.adapter.simple-api.0.alive => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj[0].val).equal(50);
expect(obj[1].val).equal(false);
expect(response.statusCode).to.equal(200);
done();
});
});
});
it('Test RESTful API SSL: setValueFromBody(POST) - must set values', function (done) {
request({
uri: 'https://127.0.0.1:18183/setValueFromBody/system.adapter.simple-api.upload?user=admin&pass=yunkong2&',
method: 'POST',
body: '55'
}, function(error, response, body) {
console.log('setValueFromBody/?system.adapter.simple-api.upload => ' + JSON.stringify(body));
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj).to.be.ok;
expect(obj[0].val).to.be.equal(55);
expect(obj[0].id).to.equal('system.adapter.simple-api.upload');
expect(response.statusCode).to.equal(200);
body = "";
request('https://127.0.0.1:18183/getBulk/system.adapter.simple-api.upload?user=admin&pass=yunkong2', function (error, response, body) {
console.log('getBulk/system.adapter.simple-api.upload => ' + body);
expect(error).to.be.not.ok;
var obj = JSON.parse(body);
expect(obj[0].val).equal(55);
expect(response.statusCode).to.equal(200);
done();
});
});
});
after('Test RESTful API SSL: Stop js-controller', function (done) {
this.timeout(6000);
setup.stopController(function (normalTerminated) {
console.log('Adapter normal terminated: ' + normalTerminated);
done();
});
});
});