& {
24 | ts?: string | number;
25 | lc?: string | number;
26 | }) | null;
27 | type CommandName = keyof typeof commandsPermissions;
28 | /**
29 | * SimpleAPI class
30 | *
31 | * From settings used only secure, auth and crossDomain
32 | *
33 | * @param webSettings settings of the web server, like {secure: settings.secure, port: settings.port}
34 | * @param adapter web adapter object
35 | * @param instanceSettings instance object with common and native
36 | * @param app express application
37 | * @returns object instance
38 | */
39 | export declare class SimpleAPI {
40 | private readonly adapter;
41 | private readonly settings;
42 | private readonly config;
43 | private readonly namespace;
44 | private readonly app?;
45 | private readonly cachedNames;
46 | private readonly cachedIds;
47 | private readonly restApiDelayed;
48 | constructor(_server: Server, webSettings: {
49 | secure: boolean;
50 | port: number | string;
51 | defaultUser?: string;
52 | auth?: boolean;
53 | language?: ioBroker.Languages;
54 | }, adapter: ioBroker.Adapter, instanceSettings: ioBroker.InstanceObject, app?: Express);
55 | static convertRelativeTime(relativeTime: string | undefined): number | null;
56 | isAuthenticated(req: Request & {
57 | user?: string;
58 | }, query: SimpleApiQuery): Promise;
59 | stateChange(id: string, state: ioBroker.State | null | undefined): void;
60 | objectChange(id: string, _obj: ioBroker.Object | null | undefined): void;
61 | static parseQuery(input: string | undefined, query: SimpleApiQuery, values: Record): void;
62 | setStates(values: Record, query: SimpleApiQuery): Promise<{
63 | id?: string;
64 | val?: boolean | string | number;
65 | error?: string;
66 | }[]>;
67 | restApiPost(req: Request, res: Response, command: CommandName, oId: string[], values: Record, query: SimpleApiQuery): Promise;
68 | findState(idOrName: string, user: `system.user.${string}`): Promise<{
69 | id: string;
70 | name: string;
71 | }>;
72 | getState(idOrName: string, user: `system.user.${string}`, query: SimpleApiQuery): Promise<{
73 | state: IoBrokerStateWithIsoTime;
74 | id: string;
75 | }>;
76 | doResponse(res: Response, responseType: 'json' | 'plain', content?: any, query?: SimpleApiQuery): void;
77 | doErrorResponse(res: Response, responseType: 'json' | 'plain', status: 401 | 403 | 404 | 422 | 500, error?: string): void;
78 | checkPermissions(user: `system.user.${string}`, command: CommandName): Promise;
79 | setValue(id: string, value: ioBroker.StateValue, res: Response, wait: number, query: SimpleApiQuery, responseType: 'json' | 'plain'): Promise;
80 | restApi(req: Request, res: Response, overwriteUrl?: string): Promise;
81 | }
82 | export {};
83 |
--------------------------------------------------------------------------------
/admin/jsonConfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "tabs",
3 | "i18n": true,
4 | "items": {
5 | "mainTab": {
6 | "type": "panel",
7 | "label": "Main settings",
8 | "items": {
9 | "webInstance": {
10 | "type": "instance",
11 | "label": "Extend WEB adapter",
12 | "all": true,
13 | "xs": 12,
14 | "sm": 12,
15 | "md": 6,
16 | "lg": 3,
17 | "adapter": "web"
18 | },
19 | "bind": {
20 | "hidden": "!!data.webInstance",
21 | "newLine": true,
22 | "type": "ip",
23 | "listenOnAllPorts": true,
24 | "label": "IP",
25 | "xs": 12,
26 | "sm": 12,
27 | "md": 8,
28 | "lg": 5
29 | },
30 | "port": {
31 | "hidden": "!!data.webInstance",
32 | "type": "number",
33 | "min": 1,
34 | "max": 65565,
35 | "label": "Port",
36 | "xs": 12,
37 | "sm": 12,
38 | "md": 4,
39 | "lg": 3
40 | },
41 | "secure": {
42 | "hidden": "!!data.webInstance",
43 | "newLine": true,
44 | "type": "checkbox",
45 | "label": "Secure(HTTPS)",
46 | "xs": 12,
47 | "sm": 12,
48 | "md": 6,
49 | "lg": 2
50 | },
51 | "certPublic": {
52 | "type": "certificate",
53 | "hidden": "!data.secure || !!data.webInstance",
54 | "certType": "public",
55 | "validator": "!data.secure || data.certPublic",
56 | "label": "Public certificate",
57 | "xs": 12,
58 | "sm": 12,
59 | "md": 6,
60 | "lg": 2
61 | },
62 | "certPrivate": {
63 | "hidden": "!data.secure || !!data.webInstance",
64 | "type": "certificate",
65 | "certType": "private",
66 | "validator": "!data.secure || data.certPrivate",
67 | "label": "Private certificate",
68 | "xs": 12,
69 | "sm": 12,
70 | "md": 6,
71 | "lg": 2
72 | },
73 | "certChained": {
74 | "hidden": "!data.secure || !!data.webInstance",
75 | "type": "certificate",
76 | "certType": "chained",
77 | "label": "Chained certificate",
78 | "xs": 12,
79 | "sm": 12,
80 | "md": 6,
81 | "lg": 2
82 | },
83 | "auth": {
84 | "newLine": true,
85 | "hidden": "!!data.webInstance",
86 | "type": "checkbox",
87 | "confirm": {
88 | "condition": "!data.secure && data.auth",
89 | "title": "Warning!",
90 | "text": "Unsecure_Auth",
91 | "ok": "Ignore warning",
92 | "cancel": "Disable authentication",
93 | "type": "warning",
94 | "alsoDependsOn": ["secure"]
95 | },
96 | "label": "Authentication",
97 | "xs": 12,
98 | "sm": 12,
99 | "md": 6,
100 | "lg": 2
101 | },
102 | "defaultUser": {
103 | "hidden": "!!data.auth || !!data.webInstance",
104 | "type": "user",
105 | "label": "Run as",
106 | "xs": 12,
107 | "sm": 12,
108 | "md": 6,
109 | "lg": 2
110 | },
111 | "onlyAllowWhenUserIsOwner": {
112 | "newLine": true,
113 | "type": "checkbox",
114 | "label": "Allow only when User is Owner",
115 | "xs": 12,
116 | "sm": 12
117 | },
118 | "dataSource": {
119 | "newLine": true,
120 | "type": "instance",
121 | "label": "Select data source",
122 | "xs": 12,
123 | "sm": 12,
124 | "md": 6,
125 | "lg": 3,
126 | "adapter": "_dataSources"
127 | },
128 | "allDatapoints": {
129 | "type": "checkbox",
130 | "hidden": "!data.dataSource",
131 | "label": "List all datapoints",
132 | "xs": 12,
133 | "sm": 12,
134 | "md": 6,
135 | "lg": 3
136 | },
137 | "accessControlAllowOrigin": {
138 | "type": "text",
139 | "label": "Allow origin (CORS)",
140 | "help": "enable_authentication_with_cors",
141 | "xs": 12,
142 | "sm": 12,
143 | "md": 6,
144 | "lg": 3
145 | }
146 | }
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/.github/workflows/test-and-release.yml:
--------------------------------------------------------------------------------
1 | # This is a composition of lint and test scripts
2 | # Make sure to update this file along with the others
3 |
4 | name: Test and Release
5 |
6 | # Run this job on all pushes and pull requests
7 | # as well as tags with a semantic version
8 | on:
9 | push:
10 | branches:
11 | - '*'
12 | tags:
13 | # normal versions
14 | - 'v?[0-9]+.[0-9]+.[0-9]+'
15 | # pre-releases
16 | - 'v?[0-9]+.[0-9]+.[0-9]+-**'
17 | pull_request: {}
18 |
19 | # Cancel previous PR/branch runs when a new commit is pushed
20 | concurrency:
21 | group: ${{ github.ref }}
22 | cancel-in-progress: true
23 |
24 | jobs:
25 | # Performs quick checks before the expensive test runs
26 | check-and-lint:
27 | if: contains(github.event.head_commit.message, '[skip ci]') == false
28 |
29 | runs-on: ubuntu-latest
30 |
31 | steps:
32 | - uses: actions/checkout@v5
33 | - name: Use Node.js 20.x
34 | uses: actions/setup-node@v6
35 | with:
36 | node-version: 20.x
37 |
38 | - name: Install Dependencies
39 | run: npm i
40 |
41 | # - name: Perform a type check
42 | # run: npm run check:ts
43 | # env:
44 | # CI: true
45 | # - name: Lint TypeScript code
46 | # run: npm run lint
47 | # - name: Test package files
48 | # run: npm run test:package
49 |
50 | # Runs adapter tests on all supported node versions and OSes
51 | adapter-tests:
52 | if: contains(github.event.head_commit.message, '[skip ci]') == false
53 |
54 | needs: [check-and-lint]
55 |
56 | runs-on: ${{ matrix.os }}
57 | strategy:
58 | matrix:
59 | node-version: [18.x, 20.x, 22.x]
60 | os: [ubuntu-latest, windows-latest, macos-latest]
61 |
62 | steps:
63 | - uses: actions/checkout@v5
64 | - name: Use Node.js ${{ matrix.node-version }}
65 | uses: actions/setup-node@v6
66 | with:
67 | node-version: ${{ matrix.node-version }}
68 |
69 | - name: Install Dependencies
70 | run: npm i
71 |
72 | - name: Build
73 | run: npm run build
74 |
75 | - name: Run local tests
76 | run: npm test
77 | # - name: Run unit tests
78 | # run: npm run test:unit
79 | # - name: Run integration tests # (linux/osx)
80 | # if: startsWith(runner.OS, 'windows') == false
81 | # run: DEBUG=testing:* npm run test:integration
82 | # - name: Run integration tests # (windows)
83 | # if: startsWith(runner.OS, 'windows')
84 | # run: set DEBUG=testing:* & npm run test:integration
85 |
86 | # Deploys the final package to NPM
87 | deploy:
88 | needs: [adapter-tests]
89 |
90 | # Trigger this step only when a commit on master is tagged with a version number
91 | if: |
92 | contains(github.event.head_commit.message, '[skip ci]') == false &&
93 | github.event_name == 'push' &&
94 | startsWith(github.ref, 'refs/tags/')
95 | runs-on: ubuntu-latest
96 | steps:
97 | - name: Checkout code
98 | uses: actions/checkout@v5
99 |
100 | - name: Use Node.js 20.x
101 | uses: actions/setup-node@v6
102 | with:
103 | node-version: 20.x
104 |
105 | - name: Extract the version and commit body from the tag
106 | id: extract_release
107 | # The body may be multiline, therefore we need to escape some characters
108 | run: |
109 | VERSION="${{ github.ref }}"
110 | VERSION=${VERSION##*/}
111 | VERSION=${VERSION##*v}
112 | echo "::set-output name=VERSION::$VERSION"
113 | BODY=$(git show -s --format=%b)
114 | BODY="${BODY//'%'/'%25'}"
115 | BODY="${BODY//$'\n'/'%0A'}"
116 | BODY="${BODY//$'\r'/'%0D'}"
117 | echo "::set-output name=BODY::$BODY"
118 |
119 | - name: Install Dependencies
120 | run: npm i
121 |
122 | - name: Build
123 | run: npm run build
124 |
125 | # - name: Create a clean build
126 | # run: npm run build
127 | - name: Publish package to npm
128 | run: |
129 | npm config set //registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}
130 | npm whoami
131 | npm publish
132 |
133 | - name: Create Github Release
134 | uses: actions/create-release@v1
135 | env:
136 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
137 | with:
138 | tag_name: ${{ github.ref }}
139 | release_name: Release v${{ steps.extract_release.outputs.VERSION }}
140 | draft: false
141 | # Prerelease versions create pre-releases on GitHub
142 | prerelease: ${{ contains(steps.extract_release.outputs.VERSION, '-') }}
143 | body: ${{ steps.extract_release.outputs.BODY }}
144 |
145 | - name: Notify Sentry.io about the release
146 | run: |
147 | npm i -g @sentry/cli
148 | export SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
149 | export SENTRY_URL=https://sentry.iobroker.net
150 | export SENTRY_ORG=iobroker
151 | export SENTRY_PROJECT=iobroker-simple-api
152 | export SENTRY_VERSION=iobroker.simple-api@${{ steps.extract_release.outputs.VERSION }}
153 | sentry-cli releases new $SENTRY_VERSION
154 | sentry-cli releases set-commits $SENTRY_VERSION --auto
155 | sentry-cli releases finalize $SENTRY_VERSION
156 |
157 | # Add the following line BEFORE finalize if sourcemap uploads are needed
158 | # sentry-cli releases files $SENTRY_VERSION upload-sourcemaps build/
159 |
--------------------------------------------------------------------------------
/dist/main.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
--------------------------------------------------------------------------------
/dist/main.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __importDefault = (this && this.__importDefault) || function (mod) {
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
4 | };
5 | Object.defineProperty(exports, "__esModule", { value: true });
6 | exports.SimpleApiAdapter = void 0;
7 | const express_1 = __importDefault(require("express"));
8 | const node_fs_1 = require("node:fs");
9 | const cookie_parser_1 = __importDefault(require("cookie-parser"));
10 | const body_parser_1 = __importDefault(require("body-parser"));
11 | const adapter_core_1 = require("@iobroker/adapter-core");
12 | const webserver_1 = require("@iobroker/webserver");
13 | const SimpleAPI_1 = require("./lib/SimpleAPI");
14 | class SimpleApiAdapter extends adapter_core_1.Adapter {
15 | webServer = {
16 | app: null,
17 | server: null,
18 | api: null,
19 | };
20 | certificates;
21 | constructor(options = {}) {
22 | super({
23 | ...options,
24 | name: 'simple-api',
25 | unload: callback => this.onUnload(callback),
26 | stateChange: (id, state) => {
27 | this.webServer?.api?.stateChange(id, state);
28 | },
29 | ready: () => this.main(),
30 | objectChange: (id, obj) => {
31 | this.webServer?.api?.objectChange(id, obj);
32 | },
33 | });
34 | }
35 | onUnload(callback) {
36 | try {
37 | if (this.webServer.server) {
38 | this.log.info(`terminating http${this.config.secure ? 's' : ''} server on port ${this.config.port}`);
39 | this.webServer.server.close();
40 | this.webServer.server = null;
41 | }
42 | }
43 | catch {
44 | // ignore
45 | }
46 | if (callback) {
47 | callback();
48 | }
49 | }
50 | async main() {
51 | if (this.config.webInstance) {
52 | console.log('Adapter runs as a part of web service');
53 | this.log.warn('Adapter runs as a part of web service');
54 | await this.setState('info.extension', true, true);
55 | return this.setForeignState(`system.adapter.${this.namespace}.alive`, false, true, () => this.setTimeout(() => (this.terminate ? this.terminate() : process.exit()), 1000));
56 | }
57 | await this.setState('info.extension', false, true);
58 | if (this.config.secure) {
59 | // Load certificates
60 | await new Promise(resolve => this.getCertificates(undefined, undefined, undefined, (_err, certificates) => {
61 | this.certificates = certificates;
62 | resolve();
63 | }));
64 | }
65 | await this.initWebServer();
66 | }
67 | serveStatic = (req, res, next) => {
68 | if ((req.url || '').includes('favicon.ico')) {
69 | let stat;
70 | try {
71 | if ((0, node_fs_1.existsSync)(`${__dirname}/../img/favicon.ico`)) {
72 | stat = (0, node_fs_1.statSync)(`${__dirname}/../img/favicon.ico`);
73 | }
74 | }
75 | catch {
76 | // no special handling
77 | }
78 | if (stat) {
79 | res.writeHead(200, {
80 | 'Content-Type': 'image/x-icon',
81 | 'Content-Length': stat.size,
82 | });
83 | const readStream = (0, node_fs_1.createReadStream)(`${__dirname}/../img/favicon.ico`);
84 | // We replaced all the event handlers with a simple call to readStream.pipe()
85 | readStream.pipe(res);
86 | }
87 | else {
88 | res.writeHead(404, { 'Content-Type': 'text/plain' });
89 | res.write('404 Not Found\n');
90 | res.end();
91 | }
92 | }
93 | else {
94 | next();
95 | }
96 | };
97 | async initWebServer() {
98 | this.config.port = parseInt(this.config.port, 10);
99 | this.webServer.app = (0, express_1.default)();
100 | this.webServer.app.use(this.serveStatic);
101 | if (this.config.port) {
102 | if (this.config.secure && !this.certificates) {
103 | return;
104 | }
105 | try {
106 | const webserver = new webserver_1.WebServer({
107 | app: this.webServer.app,
108 | adapter: this,
109 | secure: this.config.secure,
110 | });
111 | this.webServer.server = (await webserver.init());
112 | if (this.config.auth) {
113 | // Install OAuth2 handler
114 | this.webServer.app.use((0, cookie_parser_1.default)());
115 | this.webServer.app.use(body_parser_1.default.urlencoded({ extended: true }));
116 | this.webServer.app.use(body_parser_1.default.json());
117 | (0, webserver_1.createOAuth2Server)(this, {
118 | app: this.webServer.app,
119 | secure: this.config.secure,
120 | accessLifetime: parseInt(this.config.ttl, 10) || 3600,
121 | });
122 | }
123 | }
124 | catch (err) {
125 | this.log.error(`Cannot create webserver: ${err}`);
126 | this.terminate
127 | ? this.terminate(adapter_core_1.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION)
128 | : process.exit(adapter_core_1.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
129 | return;
130 | }
131 | if (!this.webServer.server) {
132 | this.log.error(`Cannot create webserver`);
133 | this.terminate
134 | ? this.terminate(adapter_core_1.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION)
135 | : process.exit(adapter_core_1.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
136 | return;
137 | }
138 | this.webServer.app.use((req, res) => this.webServer.api?.restApi(req, res));
139 | this.webServer.server.__server = this.webServer;
140 | }
141 | else {
142 | this.log.error('port missing');
143 | if (this.terminate) {
144 | this.terminate(adapter_core_1.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
145 | }
146 | else {
147 | process.exit(adapter_core_1.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
148 | }
149 | return;
150 | }
151 | if (this.webServer.server) {
152 | let serverListening = false;
153 | let serverPort = this.config.port;
154 | this.webServer.server.on('error', e => {
155 | if (e.toString().includes('EACCES') && serverPort <= 1024) {
156 | this.log.error(`node.js process has no rights to start server on the port ${serverPort}.\n` +
157 | `Do you know that on linux you need special permissions for ports under 1024?\n` +
158 | `You can call in shell following scrip to allow it for node.js: "iobroker fix"`);
159 | }
160 | else {
161 | this.log.error(`Cannot start server on ${this.config.bind || '0.0.0.0'}:${serverPort}: ${e}`);
162 | }
163 | if (!serverListening) {
164 | this.terminate
165 | ? this.terminate(adapter_core_1.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION)
166 | : process.exit(adapter_core_1.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
167 | }
168 | });
169 | this.getPort(this.config.port, !this.config.bind || this.config.bind === '0.0.0.0' ? undefined : this.config.bind || undefined, port => {
170 | if (port !== this.config.port) {
171 | this.log.error(`port ${this.config.port} already in use`);
172 | if (this.terminate) {
173 | this.terminate(1);
174 | }
175 | else {
176 | process.exit(1);
177 | }
178 | return;
179 | }
180 | serverPort = port;
181 | if (this.webServer.server) {
182 | // create web server
183 | this.webServer.server.listen(port, !this.config.bind || this.config.bind === '0.0.0.0'
184 | ? undefined
185 | : this.config.bind || undefined, () => (serverListening = true));
186 | webserver_1.createOAuth2Server;
187 | this.log.info(`http${this.config.secure ? 's' : ''} server listening on port ${port}`);
188 | }
189 | else {
190 | this.log.error('server initialization failed');
191 | if (this.terminate) {
192 | this.terminate(1);
193 | }
194 | else {
195 | process.exit(1);
196 | }
197 | }
198 | });
199 | }
200 | this.webServer.api = new SimpleAPI_1.SimpleAPI(this.webServer.server, this.config, this, {
201 | native: this.config,
202 | _id: `system.adapter.${this.namespace}`,
203 | common: this.common,
204 | type: 'instance',
205 | objects: [],
206 | instanceObjects: [],
207 | });
208 | }
209 | }
210 | exports.SimpleApiAdapter = SimpleApiAdapter;
211 | if (require.main !== module) {
212 | // Export the constructor in compact mode
213 | module.exports = (options) => new SimpleApiAdapter(options);
214 | }
215 | else {
216 | // otherwise start the instance directly
217 | (() => new SimpleApiAdapter())();
218 | }
219 | //# sourceMappingURL=main.js.map
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import express, { type Express, type NextFunction, type Request, type Response } from 'express';
2 | import { createReadStream, existsSync, type Stats, statSync } from 'node:fs';
3 | import cookieParser from 'cookie-parser';
4 | import bodyParser from 'body-parser';
5 |
6 | import { Adapter, type AdapterOptions, EXIT_CODES } from '@iobroker/adapter-core';
7 | import { createOAuth2Server, WebServer } from '@iobroker/webserver';
8 | import { SimpleAPI, type Server } from './lib/SimpleAPI';
9 | import type { SimpleApiAdapterConfig } from './types';
10 |
11 | interface WebStructure {
12 | server: null | (Server & { __server: WebStructure });
13 | api: SimpleAPI | null;
14 | app: Express | null;
15 | }
16 |
17 | export class SimpleApiAdapter extends Adapter {
18 | declare public config: SimpleApiAdapterConfig;
19 | private webServer: WebStructure = {
20 | app: null,
21 | server: null,
22 | api: null,
23 | };
24 | private certificates: ioBroker.Certificates | undefined;
25 |
26 | public constructor(options: Partial = {}) {
27 | super({
28 | ...options,
29 | name: 'simple-api',
30 | unload: callback => this.onUnload(callback),
31 | stateChange: (id, state) => {
32 | this.webServer?.api?.stateChange(id, state);
33 | },
34 | ready: () => this.main(),
35 | objectChange: (id: string, obj: ioBroker.Object | null | undefined): void => {
36 | this.webServer?.api?.objectChange(id, obj);
37 | },
38 | });
39 | }
40 |
41 | onUnload(callback: () => void): void {
42 | try {
43 | if (this.webServer.server) {
44 | this.log.info(`terminating http${this.config.secure ? 's' : ''} server on port ${this.config.port}`);
45 | this.webServer.server.close();
46 | this.webServer.server = null;
47 | }
48 | } catch {
49 | // ignore
50 | }
51 | if (callback) {
52 | callback();
53 | }
54 | }
55 |
56 | async main(): Promise {
57 | if (this.config.webInstance) {
58 | console.log('Adapter runs as a part of web service');
59 | this.log.warn('Adapter runs as a part of web service');
60 | await this.setState('info.extension', true, true);
61 | return this.setForeignState(`system.adapter.${this.namespace}.alive`, false, true, () =>
62 | this.setTimeout(() => (this.terminate ? this.terminate() : process.exit()), 1000),
63 | );
64 | }
65 |
66 | await this.setState('info.extension', false, true);
67 |
68 | if (this.config.secure) {
69 | // Load certificates
70 | await new Promise(resolve =>
71 | this.getCertificates(undefined, undefined, undefined, (_err, certificates): void => {
72 | this.certificates = certificates;
73 | resolve();
74 | }),
75 | );
76 | }
77 |
78 | await this.initWebServer();
79 | }
80 |
81 | serveStatic = (req: Request, res: Response, next: NextFunction): void => {
82 | if ((req.url || '').includes('favicon.ico')) {
83 | let stat: Stats | undefined;
84 | try {
85 | if (existsSync(`${__dirname}/../img/favicon.ico`)) {
86 | stat = statSync(`${__dirname}/../img/favicon.ico`);
87 | }
88 | } catch {
89 | // no special handling
90 | }
91 |
92 | if (stat) {
93 | res.writeHead(200, {
94 | 'Content-Type': 'image/x-icon',
95 | 'Content-Length': stat.size,
96 | });
97 |
98 | const readStream = createReadStream(`${__dirname}/../img/favicon.ico`);
99 | // We replaced all the event handlers with a simple call to readStream.pipe()
100 | readStream.pipe(res);
101 | } else {
102 | res.writeHead(404, { 'Content-Type': 'text/plain' });
103 | res.write('404 Not Found\n');
104 | res.end();
105 | }
106 | } else {
107 | next();
108 | }
109 | };
110 |
111 | async initWebServer(): Promise {
112 | this.config.port = parseInt(this.config.port as string, 10);
113 |
114 | this.webServer.app = express();
115 | this.webServer.app.use(this.serveStatic);
116 |
117 | if (this.config.port) {
118 | if (this.config.secure && !this.certificates) {
119 | return;
120 | }
121 |
122 | try {
123 | const webserver = new WebServer({
124 | app: this.webServer.app,
125 | adapter: this,
126 | secure: this.config.secure,
127 | });
128 |
129 | this.webServer.server = (await webserver.init()) as Server & { __server: WebStructure };
130 |
131 | if (this.config.auth) {
132 | // Install OAuth2 handler
133 | this.webServer.app.use(cookieParser());
134 | this.webServer.app.use(bodyParser.urlencoded({ extended: true }));
135 | this.webServer.app.use(bodyParser.json());
136 |
137 | createOAuth2Server(this, {
138 | app: this.webServer.app,
139 | secure: this.config.secure,
140 | accessLifetime: parseInt(this.config.ttl as string, 10) || 3600,
141 | });
142 | }
143 | } catch (err) {
144 | this.log.error(`Cannot create webserver: ${err}`);
145 | this.terminate
146 | ? this.terminate(EXIT_CODES.ADAPTER_REQUESTED_TERMINATION)
147 | : process.exit(EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
148 | return;
149 | }
150 | if (!this.webServer.server) {
151 | this.log.error(`Cannot create webserver`);
152 | this.terminate
153 | ? this.terminate(EXIT_CODES.ADAPTER_REQUESTED_TERMINATION)
154 | : process.exit(EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
155 | return;
156 | }
157 |
158 | this.webServer.app.use((req, res) => this.webServer.api?.restApi(req, res));
159 |
160 | this.webServer.server.__server = this.webServer;
161 | } else {
162 | this.log.error('port missing');
163 | if (this.terminate) {
164 | this.terminate(EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
165 | } else {
166 | process.exit(EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
167 | }
168 | return;
169 | }
170 |
171 | if (this.webServer.server) {
172 | let serverListening = false;
173 | let serverPort = this.config.port;
174 |
175 | this.webServer.server.on('error', e => {
176 | if (e.toString().includes('EACCES') && serverPort <= 1024) {
177 | this.log.error(
178 | `node.js process has no rights to start server on the port ${serverPort}.\n` +
179 | `Do you know that on linux you need special permissions for ports under 1024?\n` +
180 | `You can call in shell following scrip to allow it for node.js: "iobroker fix"`,
181 | );
182 | } else {
183 | this.log.error(`Cannot start server on ${this.config.bind || '0.0.0.0'}:${serverPort}: ${e}`);
184 | }
185 | if (!serverListening) {
186 | this.terminate
187 | ? this.terminate(EXIT_CODES.ADAPTER_REQUESTED_TERMINATION)
188 | : process.exit(EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
189 | }
190 | });
191 |
192 | this.getPort(
193 | this.config.port,
194 | !this.config.bind || this.config.bind === '0.0.0.0' ? undefined : this.config.bind || undefined,
195 | port => {
196 | if (port !== this.config.port) {
197 | this.log.error(`port ${this.config.port} already in use`);
198 | if (this.terminate) {
199 | this.terminate(1);
200 | } else {
201 | process.exit(1);
202 | }
203 | return;
204 | }
205 | serverPort = port;
206 |
207 | if (this.webServer.server) {
208 | // create web server
209 | this.webServer.server.listen(
210 | port,
211 | !this.config.bind || this.config.bind === '0.0.0.0'
212 | ? undefined
213 | : this.config.bind || undefined,
214 | () => (serverListening = true),
215 | );
216 |
217 | createOAuth2Server;
218 |
219 | this.log.info(`http${this.config.secure ? 's' : ''} server listening on port ${port}`);
220 | } else {
221 | this.log.error('server initialization failed');
222 | if (this.terminate) {
223 | this.terminate(1);
224 | } else {
225 | process.exit(1);
226 | }
227 | }
228 | },
229 | );
230 | }
231 |
232 | this.webServer.api = new SimpleAPI(this.webServer.server, this.config, this, {
233 | native: this.config,
234 | _id: `system.adapter.${this.namespace}`,
235 | common: this.common as ioBroker.InstanceCommon,
236 | type: 'instance',
237 | objects: [],
238 | instanceObjects: [],
239 | });
240 | }
241 | }
242 |
243 | if (require.main !== module) {
244 | // Export the constructor in compact mode
245 | module.exports = (options: Partial | undefined) => new SimpleApiAdapter(options);
246 | } else {
247 | // otherwise start the instance directly
248 | (() => new SimpleApiAdapter())();
249 | }
250 |
--------------------------------------------------------------------------------
/test/testSsl.js:
--------------------------------------------------------------------------------
1 | const expect = require('chai').expect;
2 | const setup = require('@iobroker/legacy-testing');
3 | const axios = require('axios');
4 | const { Agent } = require('node:https');
5 |
6 | let objects = null;
7 | let states = null;
8 | const httpsAgent = new Agent({ rejectUnauthorized: false });
9 |
10 | process.env.NO_PROXY = 'localhost';
11 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
12 | const TEST_STATE_ID = 'simple-api.0.testNumber';
13 |
14 | function checkConnectionOfAdapter(cb, counter) {
15 | counter = counter || 0;
16 | console.log(`Try check #${counter}`);
17 | if (counter > 30) {
18 | if (cb) cb('Cannot check connection');
19 | return;
20 | }
21 |
22 | states.getState('system.adapter.simple-api.0.alive', (err, state) => {
23 | if (err) console.error(err);
24 | if (state && state.val) {
25 | cb && cb();
26 | } else {
27 | setTimeout(() => checkConnectionOfAdapter(cb, counter + 1), 1000);
28 | }
29 | });
30 | }
31 |
32 | function createTestState(cb) {
33 | objects.setObject(
34 | TEST_STATE_ID,
35 | {
36 | _id: TEST_STATE_ID,
37 | type: 'state',
38 | common: {
39 | name: 'Test state',
40 | type: 'number',
41 | read: true,
42 | write: false,
43 | role: 'indicator.state',
44 | unit: '%',
45 | def: 0,
46 | desc: 'test state',
47 | },
48 | native: {},
49 | },
50 | () => {
51 | states.setState(TEST_STATE_ID, { val: 0, ack: true }, () => {
52 | objects.setObject(
53 | 'javascript.0.test-boolean',
54 | {
55 | common: {
56 | name: 'test',
57 | type: 'boolean',
58 | role: 'value',
59 | def: 0,
60 | },
61 | native: {},
62 | type: 'state',
63 | },
64 | err => {
65 | states.setState('javascript.0.test-boolean', { val: false, ack: false }, err => {
66 | cb();
67 | });
68 | },
69 | );
70 | });
71 | },
72 | );
73 | }
74 |
75 | describe('Test RESTful API SSL', function () {
76 | before('Test RESTful API SSL: Start js-controller', function (_done) {
77 | this.timeout(600000); // because of the first installation from npm
78 | setup.adapterStarted = false;
79 |
80 | setup.setupController(async () => {
81 | const config = await setup.getAdapterConfig();
82 | // enable adapter
83 | config.common.enabled = true;
84 | config.common.loglevel = 'debug';
85 | config.native.port = 18183;
86 | config.native.auth = true;
87 | config.native.secure = true;
88 | config.native.certPublic = 'defaultPublic';
89 | config.native.certPrivate = 'defaultPrivate';
90 |
91 | await setup.setAdapterConfig(config.common, config.native);
92 |
93 | setup.startController((_objects, _states) => {
94 | objects = _objects;
95 | states = _states;
96 | // give some time to start server
97 | setTimeout(() => createTestState(() => _done()), 2000);
98 | });
99 | });
100 | });
101 |
102 | it('Test adapter: Check if adapter started and create upload datapoint', done => {
103 | checkConnectionOfAdapter(res => {
104 | if (res) {
105 | console.log(res);
106 | }
107 |
108 | expect(res).not.to.be.equal('Cannot check connection');
109 |
110 | objects.setObject(
111 | 'javascript.0.test-boolean',
112 | {
113 | common: {
114 | name: 'boolean',
115 | type: 'boolean',
116 | role: 'value',
117 | def: 0,
118 | },
119 | native: {},
120 | type: 'state',
121 | },
122 | err => {
123 | expect(err).to.be.null;
124 | states.setState('javascript.0.test-boolean', true, err => {
125 | expect(err).to.be.null;
126 | done();
127 | });
128 | },
129 | );
130 | });
131 | }).timeout(60000);
132 |
133 | it('Test RESTful API SSL: get - must return value', done => {
134 | axios
135 | .get('https://localhost:18183/get/system.adapter.simple-api.0.alive?user=admin&pass=iobroker', {
136 | httpsAgent,
137 | })
138 | .then(response => {
139 | console.log(`get/system.adapter.simple-api.0.alive => ${JSON.stringify(response.data)}`);
140 | const obj = response.data;
141 | expect(obj).to.be.ok;
142 | expect(obj.val).to.be.true;
143 | expect(obj.ack).to.be.true;
144 | expect(obj.ts).to.be.ok;
145 | expect(obj.type).to.equal('state');
146 | expect(obj._id).to.equal('system.adapter.simple-api.0.alive');
147 | expect(obj.common).to.be.ok;
148 | expect(obj.native).to.be.ok;
149 | expect(obj.common.name).to.equal('simple-api.0 alive');
150 | expect(obj.common.role).to.equal('indicator.state');
151 | expect(response.status).to.equal(200);
152 | done();
153 | })
154 | .catch(error => {
155 | console.error(error);
156 | done(error);
157 | });
158 | });
159 |
160 | it('Test RESTful API SSL: get - must return value with basic authentication', done => {
161 | axios
162 | .get('https://localhost:18183/get/system.adapter.simple-api.0.alive', {
163 | auth: {
164 | username: 'admin',
165 | password: 'iobroker',
166 | },
167 | httpsAgent,
168 | })
169 | .then(response => {
170 | console.log(`get/system.adapter.simple-api.0.alive => ${JSON.stringify(response.data)}`);
171 | const obj = response.data;
172 | expect(obj).to.be.ok;
173 | expect(obj.val).to.be.true;
174 | expect(obj.ack).to.be.true;
175 | expect(obj.ts).to.be.ok;
176 | expect(obj.type).to.equal('state');
177 | expect(obj._id).to.equal('system.adapter.simple-api.0.alive');
178 | expect(obj.common).to.be.ok;
179 | expect(obj.native).to.be.ok;
180 | expect(obj.common.name).to.equal('simple-api.0 alive');
181 | expect(obj.common.role).to.equal('indicator.state');
182 | expect(response.status).to.equal(200);
183 | done();
184 | })
185 | .catch(error => {
186 | console.error(error);
187 | done(error);
188 | });
189 | });
190 |
191 | it('Test RESTful API SSL: get with no credentials', done => {
192 | axios
193 | .get('https://localhost:18183/get/system.adapter.simple-api.0.alive?user=admin&pass=io', {
194 | validateStatus: false,
195 | httpsAgent,
196 | })
197 | .then(response => {
198 | console.log(`get/system.adapter.simple-api.0.alive => ${JSON.stringify(response.data)}`);
199 | expect(response.status).to.be.equal(401);
200 | done();
201 | })
202 | .catch(error => {
203 | console.error(error);
204 | done(error);
205 | });
206 | });
207 |
208 | it('Test RESTful API SSL: get with wrong credentials', done => {
209 | axios
210 | .get('https://localhost:18183/get/system.adapter.simple-api.0.alive', { validateStatus: false, httpsAgent })
211 | .then(response => {
212 | console.log(`get/system.adapter.simple-api.0.alive => ${JSON.stringify(response.data)}`);
213 | expect(response.status).to.be.equal(401);
214 | done();
215 | })
216 | .catch(error => {
217 | console.error(error);
218 | done(error);
219 | });
220 | });
221 |
222 | it('Test RESTful API SSL: setBulk(POST) - must set values', done => {
223 | axios
224 | .post(
225 | 'https://127.0.0.1:18183/setBulk?user=admin&pass=iobroker',
226 | `${TEST_STATE_ID}=50&javascript.0.test-boolean=true`,
227 | { validateStatus: false, responseType: 'text', httpsAgent, headers: { 'Content-Type': 'text/plain' } },
228 | )
229 | .then(response => {
230 | console.log(`setBulk/?${TEST_STATE_ID}=50&javascript.0.test-boolean=true => ${response.data}`);
231 | const obj = JSON.parse(response.data);
232 | expect(obj).to.be.ok;
233 | expect(obj[0].val).to.be.equal(50);
234 | expect(obj[0].id).to.equal(TEST_STATE_ID);
235 | expect(obj[1].val).to.be.equal(true);
236 | expect(obj[1].id).to.equal('javascript.0.test-boolean');
237 | expect(response.status).to.equal(200);
238 |
239 | return axios.get(
240 | `https://localhost:18183/getBulk/${TEST_STATE_ID},javascript.0.test-boolean?user=admin&pass=iobroker`,
241 | {
242 | validateStatus: false,
243 | responseType: 'text',
244 | httpsAgent,
245 | },
246 | );
247 | })
248 | .then(response => {
249 | console.log(`getBulk/${TEST_STATE_ID},javascript.0.test-boolean => ${response.data}`);
250 | const obj = JSON.parse(response.data);
251 | expect(obj[0].val).equal(50);
252 | expect(obj[1].val).equal(true);
253 | expect(response.status).to.equal(200);
254 | done();
255 | })
256 | .catch(error => {
257 | console.log(`CAnnot get response: ${error}`);
258 | });
259 | });
260 |
261 | it('Test RESTful API SSL: setValueFromBody(POST) - must set values', done => {
262 | axios
263 | .post(`https://localhost:18183/setValueFromBody/${TEST_STATE_ID}?user=admin&pass=iobroker`, '55', {
264 | validateStatus: false,
265 | responseType: 'text',
266 | httpsAgent,
267 | headers: { 'Content-Type': 'text/plain' },
268 | })
269 | .then(response => {
270 | console.log(`setValueFromBody/?${TEST_STATE_ID} => ${response.data}`);
271 | const obj = JSON.parse(response.data);
272 | expect(obj).to.be.ok;
273 | expect(obj[0].val).to.be.equal(55);
274 | expect(obj[0].id).to.equal(TEST_STATE_ID);
275 | expect(response.status).to.equal(200);
276 |
277 | return axios.get(`https://localhost:18183/getBulk/${TEST_STATE_ID}?user=admin&pass=iobroker`, {
278 | validateStatus: false,
279 | responseType: 'text',
280 | httpsAgent,
281 | });
282 | })
283 | .then(response => {
284 | console.log(`getBulk/${TEST_STATE_ID} => ${response.data}`);
285 | const obj = JSON.parse(response.data);
286 | expect(obj[0].val).equal(55);
287 | expect(response.status).to.equal(200);
288 | done();
289 | })
290 | .catch(error => {
291 | console.error(error);
292 | done(error);
293 | });
294 | });
295 |
296 | after('Test RESTful API SSL: Stop js-controller', function (done) {
297 | this.timeout(9000);
298 | setup.stopController(normalTerminated => {
299 | console.log(`Adapter normal terminated: ${normalTerminated}`);
300 | setTimeout(done, 3000);
301 | });
302 | });
303 | });
304 |
--------------------------------------------------------------------------------
/io-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "common": {
3 | "name": "simple-api",
4 | "version": "3.0.7",
5 | "news": {
6 | "3.0.7": {
7 | "en": "corrected reading of history data",
8 | "de": "korrigiertes lesen von geschichte-daten",
9 | "ru": "исправленное чтение исторических данных",
10 | "pt": "leitura corrigida dos dados do histórico",
11 | "nl": "gecorrigeerd lezen van historische gegevens",
12 | "fr": "lecture corrigée des données d'historique",
13 | "it": "lettura corretta dei dati storici",
14 | "es": "lectura corregida de datos de historia",
15 | "pl": "poprawiony odczyt danych historycznych",
16 | "uk": "виправлено читання історичних даних",
17 | "zh-cn": "历史数据的校正读取"
18 | },
19 | "3.0.6": {
20 | "en": "Added support for 'Access-Control-Allow-Origin'\nRemoved letsencrypt information\nAdded basic and OAuth2 authentication\nImplemented JSONP response\nImplemented relative times for query",
21 | "de": "Unterstützung für 'Access-Control-Allow-Origin '\nEntfernte letsencrypt Informationen\nHinzugefügt grundlegende und OAuth2 Authentifizierung\nUmsetzung der JSONP-Antwort\nErgänzte Relativzeiten für Abfrage",
22 | "ru": "Добавлена поддержка «Access-Control-Allow-Origin» \"\nУдалённая информация Letsencrypt\nДобавлена базовая и OAuth2 аутентификация\nРеакция JSONP\nРеализовано относительное время для запроса",
23 | "pt": "Adicionado suporte para 'Access-Control-Allow-Origin '\nRemover informação letsencrypt\nAdicionada autenticação básica e OAuth2\nResposta JSONP implementada\nTempos relativos implementados para consulta",
24 | "nl": "Toegevoegde ondersteuning voor 'Access-Control-Allow-Origin '\nVerwijderde letsencrypt-informatie\nBasis- en OAuth2-authenticatie toegevoegd\nGeïmplementeerd JSONP-antwoord\nGeïmplementeerde relatieve tijden voor query",
25 | "fr": "Ajout du support pour 'Access-Control-Autorisation-Origine '\nSupprimé les informations letsencrypt\nAjout de l'authentification basique et OAuth2\nMise en œuvre de la réponse du JSONP\nTemps relatifs mis en œuvre pour la requête",
26 | "it": "Aggiunto il supporto per 'Access-Control-Allow-Origin '\nRimuovete le informazioni letsencrypt\nAggiunta l'autenticazione di base e OAuth2\nRisposta JSONP implementata\nTempo relativo implementato per query",
27 | "es": "Apoyo añadido para 'Access-Control-Allow-Origin '\nQuitar información de letsencriptada\nAutenticación básica y OAuth2\nRespuesta aplicada a JSONP\nTiempos relativos aplicados para la consulta",
28 | "pl": "Dodano wsparcie dla 'Akcesoria - Control- Allow- Pochodzenie'\nUsunięto informacje o szyfrowaniu listów\nDodano podstawowe i uwierzytelnianie OAuth2\nWdrożona odpowiedź JSONP\nWdrożony względny czas zapytania",
29 | "uk": "Додано підтримку \"Access-Control-Allow-Origin\" Р\nВилучена інформація\nДодано базову та автентифікацію OAuth2\nРеалізовано відповідь JSONP\nРеалізовано відносні часи для запиту",
30 | "zh-cn": "添加对“ 访问控制- Allow- Origin” 的支持 '\n删除后允许加密信息\n添加基本认证和 OAuth2 认证\n已执行的JSONP反应\n已执行查询的相对时间"
31 | },
32 | "3.0.5": {
33 | "en": "Corrected the indication of running mode in admin\nCorrected the writing of numeric values\nClear cache after 10 minutes",
34 | "de": "Korrektur der Anzeige des Betriebsmodus in admin\nKorrektur des Schreibens von numerischen Werten\nCache nach 10 Minuten",
35 | "ru": "Исправлено указание режима работы в админ\nИсправлено написание числовых значений\nЧистый кэш через 10 минут",
36 | "pt": "Corrigido a indicação do modo de execução no admin\nCorrigido a escrita de valores numéricos\nLimpar o cache após 10 minutos",
37 | "nl": "De indicatie van de lopende modus in admin gecorrigeerd\nGecorrigeerd schrijven van numerieke waarden\nCache wissen na 10 minuten",
38 | "fr": "Correction de l'indication du mode d'exécution en admin\nCorrection de l'écriture des valeurs numériques\nEffacer le cache après 10 minutes",
39 | "it": "Corretto l'indicazione della modalità di esecuzione in admin\nCorretto la scrittura dei valori numerici\nCancella la cache dopo 10 minuti",
40 | "es": "Corregido la indicación del modo de funcionamiento en admin\nCorregido la escritura de valores numéricos\nCaché transparente después de 10 minutos",
41 | "pl": "Poprawiono wskazanie trybu pracy w admin\nKorekta zapisu wartości liczbowych\nWyczyść pamięć podręczną po 10 minutach",
42 | "uk": "Виправлено показання режиму роботи в адміні\nВиправлено написання нумеричних значень\nОчистити кеш через 10 хвилин",
43 | "zh-cn": "纠正管理器中运行模式的提示\n更正数值的写入\n10分钟后清除缓存"
44 | },
45 | "3.0.0": {
46 | "en": "Updated packages\nMigrated to TypeScript\nIf State/Object not found, the response will be 404 (and not 500)\nIf a user has no permission, the response will be 403 (and not 401)",
47 | "de": "Aktualisierte Pakete\nZu TypeScript migriert\nWenn Staat/Ziel nicht gefunden wird, wird die Antwort 404 (und nicht 500)\nWenn ein Benutzer keine Erlaubnis hat, wird die Antwort 403 (und nicht 401)",
48 | "ru": "Обновленные пакеты\nИммиграция в TypeScript\nЕсли состояние/объект не найден, ответ будет 404 (а не 500)\nЕсли у пользователя нет разрешения, ответ будет 403 (а не 401)",
49 | "pt": "Pacotes atualizados\nMigrado para TypeScript\nSe o Estado/Objeto não for encontrado, a resposta será 404 (e não 500)\nSe um usuário não tiver permissão, a resposta será 403 (e não 401)",
50 | "nl": "Bijgewerkte pakketten\nNaar typeScript migreren\nAls staat/object niet wordt gevonden, zal het antwoord 404 (en niet 500) zijn\nAls een gebruiker geen toestemming heeft, zal het antwoord 403 zijn (en niet 401)",
51 | "fr": "Paquets mis à jour\nMigré vers TypeScript\nSi l'État/l'objet n'est pas trouvé, la réponse sera 404 (et non 500)\nSi un utilisateur n'a pas la permission, la réponse sera 403 (et non 401)",
52 | "it": "Pacchetti aggiornati\nMigrato a TypeScript\nSe State/Obiettivo non trovato, la risposta sarà 404 (e non 500)\nSe un utente non ha il permesso, la risposta sarà 403 (e non 401)",
53 | "es": "Paquetes actualizados\nMigrado a TipoScript\nSi el Estado/Objeto no se encuentra, la respuesta será 404 (y no 500)\nSi un usuario no tiene permiso, la respuesta será 403 (y no 401)",
54 | "pl": "Aktualizacja pakietów\nMigrated to TypeScript\nJeżeli stan / obiekt nie zostanie znaleziony, odpowiedź wyniesie 404 (a nie 500)\nJeśli użytkownik nie ma pozwolenia, odpowiedź będzie 403 (a nie 401)",
55 | "uk": "Оновлені пакети\nMigrated до TypeScript\nЯкщо не знайдено державного/об’єкта, відповідь буде 404 (і не 500)\nЯкщо користувач не має дозволу, відповідь буде 403 (і не 401)",
56 | "zh-cn": "更新软件包\n移动到类型脚本\n如果国家/目标未找到,答复将是404(而不是500)\n如果用户未获许可,则回复为403(而不是401)"
57 | },
58 | "2.8.0": {
59 | "en": "ported to `@iobroker/webserver`",
60 | "de": "an `@iobroker/webserver `",
61 | "ru": "в порту `@iobroker/webserver \"",
62 | "pt": "portado para `@iobroker/webserver \"",
63 | "nl": "geporteerd naar .@iobroker/webserver wat",
64 | "fr": "porté à `@iobroker/webserver \"",
65 | "it": "inviato a `@iobroker/webserver #",
66 | "es": "portada a `@iobroker/webserver `",
67 | "pl": "wysłany do '@ iobroker / webserver'",
68 | "uk": "сайт: www.iobroker.com й",
69 | "zh-cn": "移植到 xqio 经纪人/网络服务器 `"
70 | },
71 | "2.7.2": {
72 | "en": "Prepare for future js-controller versions",
73 | "de": "Bereiten Sie sich auf zukünftige js-Controller-Versionen",
74 | "ru": "Подготовьтесь к будущим версиям js-controller",
75 | "pt": "Prepare-se para futuras versões js-controller",
76 | "nl": "Bereid je voor op toekomstige Js-controller versie",
77 | "fr": "Préparez-vous pour les versions futures de js-controller",
78 | "it": "Prepararsi per le future versioni js-controller",
79 | "es": "Prepararse para futuras versiones js-controller",
80 | "pl": "Prepare for future js-controller version",
81 | "zh-cn": "今后防污版本的编制"
82 | },
83 | "2.7.1": {
84 | "en": "Check if the port is occupied only on defined interface\nAdded JSON config",
85 | "de": "Überprüfen Sie, ob der Port nur auf definierter Schnittstelle besetzt ist\nJSON config",
86 | "ru": "Проверьте, занят ли порт только на определенном интерфейсе\nДобавлено JSON config",
87 | "pt": "Verifique se a porta está ocupada apenas na interface definida\nAdicionado JSON config",
88 | "nl": "Controleer of de haven alleen bezet is op definitieve interface\n_",
89 | "fr": "Vérifiez si le port n'est occupé que sur l'interface définie\nAjouté JSON config",
90 | "it": "Controllare se la porta è occupata solo su interfaccia definita\nAggiunto JSON config",
91 | "es": "Compruebe si el puerto está ocupado sólo en la interfaz definida\nConfiguración JSON",
92 | "pl": "Jeśli port jest zajęty tylko na określonym interfejsie\nStrona internetowa JSON",
93 | "zh-cn": "如果港口只在界定的界线上被占有,则该港口将被扣押。\n增 编"
94 | }
95 | },
96 | "titleLang": {
97 | "en": "Simple RESTful API",
98 | "de": "Einfache RESTful API",
99 | "ru": "Простой RESTful API",
100 | "pt": "API RESTful simples",
101 | "nl": "Eenvoudige RESTful API",
102 | "fr": "API RESTful simple",
103 | "it": "API RESTful semplice",
104 | "es": "API RESTful simple",
105 | "pl": "Prosty interfejs API RESTful",
106 | "uk": "Простий RESTful API",
107 | "zh-cn": "简单的RESTful API"
108 | },
109 | "desc": {
110 | "en": "This adapter allows to read and write ioBroker objects and state with web RESTful API",
111 | "de": "Dieser Adapter ermöglicht das Lesen und Schreiben von ioBroker-Objekten und den Status mit der Web-RESTful-API",
112 | "ru": "Этот адаптер позволяет читать и записывать объекты и состояния ioBroker с помощью веб-RESTful API",
113 | "pt": "Esse adaptador permite ler e gravar objetos ioBroker e declarar com a API RESTful da web",
114 | "nl": "Deze adapter maakt het mogelijk om ioBroker-objecten te lezen en te schrijven en aan te geven met de web RESTful API",
115 | "fr": "Cet adaptateur permet de lire et d'écrire des objets et des états ioBroker avec l'API Web RESTful",
116 | "it": "Questo adattatore consente di leggere e scrivere oggetti e stato di ioBroker con l'API RESTful web",
117 | "es": "Este adaptador permite leer y escribir objetos y estados ioBroker con API RESTful web",
118 | "pl": "Ten adapter umożliwia odczyt i zapis obiektów ioBroker oraz ich stan za pomocą web RESTful API",
119 | "uk": "Цей адаптер дозволяє читати та записувати об'єкти та стан ioBroker за допомогою веб-RESTful API",
120 | "zh-cn": "该适配器允许使用Web RESTful API读写ioBroker对象和状态"
121 | },
122 | "authors": [
123 | "bluefox ",
124 | "Apollon77 ",
125 | "Marco.K "
126 | ],
127 | "platform": "Javascript/Node.js",
128 | "mode": "daemon",
129 | "loglevel": "info",
130 | "icon": "simple-api.svg",
131 | "webExtension": "dist/lib/SimpleAPI.js",
132 | "readme": "https://github.com/ioBroker/ioBroker.simple-api/blob/master/README.md",
133 | "keywords": [
134 | "web",
135 | "simpleAPI",
136 | "RESTful",
137 | "communication"
138 | ],
139 | "enabled": true,
140 | "compact": true,
141 | "extIcon": "https://raw.githubusercontent.com/ioBroker/ioBroker.simple-api/master/admin/simple-api.svg",
142 | "type": "communication",
143 | "stopBeforeUpdate": true,
144 | "dependencies": [
145 | {
146 | "js-controller": ">=6.0.17"
147 | }
148 | ],
149 | "globalDependencies": [
150 | {
151 | "admin": ">=6.17.14"
152 | }
153 | ],
154 | "adminUI": {
155 | "config": "json"
156 | },
157 | "plugins": {
158 | "sentry": {
159 | "dsn": "https://1ad1b116fa644ec29e6f5d724b39f999@sentry.iobroker.net/12"
160 | }
161 | },
162 | "connectionType": "none",
163 | "dataSource": "none",
164 | "tier": 3,
165 | "licenseInformation": {
166 | "type": "free",
167 | "license": "MIT"
168 | },
169 | "messages": [
170 | {
171 | "condition": {
172 | "operand": "and",
173 | "rules": [
174 | "oldVersion<3.0.0",
175 | "newVersion>=3.0.0"
176 | ]
177 | },
178 | "title": {
179 | "en": "Important information about working as web extension",
180 | "de": "Wichtige Informationen zur Arbeit als Weberweiterung",
181 | "ru": "Важная информация о работе в качестве веб-расширения",
182 | "pt": "Informações importantes sobre o trabalho como extensão da web",
183 | "nl": "Belangrijke informatie over het werken als webextensie",
184 | "fr": "Informations importantes sur le travail en tant qu'extension Web",
185 | "it": "Informazioni importanti sul lavoro come estensione web",
186 | "es": "Información importante sobre el trabajo como extensión web",
187 | "pl": "Ważne informacje o pracy jako rozszerzenie sieciowe",
188 | "zh-cn": "关于作为Web扩展工作的相关重要信息",
189 | "uk": "Важлива інформація про роботу як веб-розширення"
190 | },
191 | "text": {
192 | "en": "When the adapter is configured to work as a web extension, no own local port is opened anymore",
193 | "de": "Wenn der Adapter so konfiguriert ist, dass er als Weberweiterung arbeitet, wird kein eigener lokaler Port mehr geöffnet",
194 | "ru": "Когда адаптер настроен на работу в качестве веб-расширения, больше не открывается собственный локальный порт",
195 | "pt": "Quando o adaptador é configurado para funcionar como uma extensão da web, nenhuma porta local própria é mais aberta",
196 | "nl": "Wanneer de adapter is geconfigureerd om als webextensie te werken, wordt er geen eigen lokale poort meer geopend",
197 | "fr": "Lorsque l'adaptateur est configuré pour fonctionner comme une extension Web, aucun port local propre n'est plus ouvert",
198 | "it": "Quando l'adattatore è configurato per funzionare come estensione web, non viene più aperta alcuna porta locale propria",
199 | "es": "Cuando el adaptador está configurado para funcionar como una extensión web, ya no se abre ningún puerto local propio",
200 | "pl": "Kiedy adapter jest skonfigurowany do pracy jako rozszerzenie sieciowe, nie jest już otwierany żaden własny lokalny port",
201 | "zh-cn": "当适配器配置为作为Web扩展工作时,不再打开自己的本地端口",
202 | "uk": "Коли адаптер налаштований на роботу як веб-розширення, більше не відкривається власний локальний порт"
203 | },
204 | "level": "warn",
205 | "buttons": [
206 | "ok",
207 | "cancel"
208 | ]
209 | }
210 | ]
211 | },
212 | "native": {
213 | "port": 8087,
214 | "auth": false,
215 | "secure": false,
216 | "bind": "0.0.0.0",
217 | "certPublic": "",
218 | "certPrivate": "",
219 | "certChained": "",
220 | "defaultUser": "admin",
221 | "onlyAllowWhenUserIsOwner": false,
222 | "webInstance": "",
223 | "leEnabled": false,
224 | "leUpdate": false,
225 | "leCheckPort": 80,
226 | "dataSource": "",
227 | "allDatapoints": false,
228 | "accessControlAllowOrigin": "*"
229 | },
230 | "instanceObjects": [
231 | {
232 | "_id": "info",
233 | "type": "channel",
234 | "common": {
235 | "name": {
236 | "en": "Information",
237 | "de": "Information",
238 | "ru": "Информация",
239 | "pt": "Em formação",
240 | "nl": "Informatie",
241 | "fr": "Information",
242 | "it": "Informazione",
243 | "es": "Información",
244 | "pl": "Informacja",
245 | "uk": "Інформація",
246 | "zh-cn": "信息"
247 | }
248 | },
249 | "native": {}
250 | },
251 | {
252 | "_id": "info.extension",
253 | "type": "state",
254 | "common": {
255 | "role": "indicator",
256 | "name": "If instance is in only extension mode",
257 | "type": "boolean",
258 | "read": true,
259 | "expert": true,
260 | "write": false,
261 | "def": false
262 | },
263 | "native": {}
264 | }
265 | ]
266 | }
267 |
--------------------------------------------------------------------------------
/test/testApiAsLimitedUser.js:
--------------------------------------------------------------------------------
1 | /* jshint -W097 */
2 | /* jshint strict: false */
3 | /* jslint node: true */
4 | /* jshint expr: true*/
5 |
6 | const expect = require('chai').expect;
7 | const setup = require('@iobroker/legacy-testing');
8 | const axios = require('axios');
9 |
10 | let objects = null;
11 | let states = null;
12 |
13 | const PORT = 18183;
14 | const TEST_STATE_ID = 'simple-api.0.testNumber';
15 |
16 | process.env.NO_PROXY = '127.0.0.1';
17 |
18 | function checkConnectionOfAdapter(cb, counter) {
19 | counter = counter || 0;
20 | console.log(`Try check #${counter}`);
21 | if (counter > 30) {
22 | if (cb) cb('Cannot check connection');
23 | return;
24 | }
25 |
26 | states.getState('system.adapter.simple-api.0.alive', (err, state) => {
27 | if (err) console.error(err);
28 | if (state && state.val) {
29 | cb && cb();
30 | } else {
31 | setTimeout(() => checkConnectionOfAdapter(cb, counter + 1), 1000);
32 | }
33 | });
34 | }
35 |
36 | function createTestState(cb) {
37 | objects.setObject(
38 | TEST_STATE_ID,
39 | {
40 | _id: TEST_STATE_ID,
41 | type: 'state',
42 | common: {
43 | name: 'Test state',
44 | type: 'number',
45 | read: true,
46 | write: false,
47 | role: 'indicator.state',
48 | unit: '%',
49 | def: 0,
50 | desc: 'test state',
51 | },
52 | native: {},
53 | },
54 | () => {
55 | states.setState(TEST_STATE_ID, { val: 0, ack: true }, cb);
56 | },
57 | );
58 | }
59 |
60 | describe('Test RESTful API as Owner-User', function () {
61 | before('Test RESTful API as Owner-User: Start js-controller', function (_done) {
62 | this.timeout(600000); // because of the first installation from npm
63 | setup.adapterStarted = false;
64 |
65 | setup.setupController(async () => {
66 | const config = await setup.getAdapterConfig();
67 | // enable adapter
68 | config.common.enabled = true;
69 | config.common.loglevel = 'debug';
70 | config.native.port = PORT;
71 | config.native.defaultUser = 'myuser';
72 | config.native.onlyAllowWhenUserIsOwner = true;
73 | await setup.setAdapterConfig(config.common, config.native);
74 |
75 | setup.startController((_objects, _states) => {
76 | objects = _objects;
77 | states = _states;
78 | // give some time to start server
79 | setTimeout(() => createTestState(() => _done()), 2000);
80 | });
81 | });
82 | });
83 |
84 | it('Test adapter: Check if adapter started and create upload datapoint', done => {
85 | checkConnectionOfAdapter(res => {
86 | res && console.log(res);
87 |
88 | expect(res).not.to.be.equal('Cannot check connection');
89 |
90 | objects.setObject(
91 | 'system.group.writer',
92 | {
93 | common: {
94 | name: 'Writer',
95 | desc: '',
96 | members: ['system.user.myuser'],
97 | acl: {
98 | object: {
99 | list: true,
100 | read: true,
101 | write: false,
102 | delete: false,
103 | },
104 | state: {
105 | list: false,
106 | read: true,
107 | write: true,
108 | create: false,
109 | delete: false,
110 | },
111 | users: {
112 | write: false,
113 | create: false,
114 | delete: false,
115 | },
116 | other: {
117 | execute: false,
118 | http: false,
119 | sendto: false,
120 | },
121 | file: {
122 | list: false,
123 | read: false,
124 | write: false,
125 | create: false,
126 | delete: false,
127 | },
128 | },
129 | },
130 | native: {},
131 | acl: {
132 | object: 1638,
133 | owner: 'system.user.admin',
134 | ownerGroup: 'system.group.administrator',
135 | },
136 | _id: 'system.group.writer',
137 | type: 'group',
138 | },
139 | err => {
140 | expect(err).to.be.null;
141 |
142 | objects.setObject(
143 | 'system.user.myuser',
144 | {
145 | type: 'user',
146 | common: {
147 | name: 'myuser',
148 | enabled: true,
149 | groups: [],
150 | password:
151 | 'pbkdf2$10000$ab4104d8bb68390ee7e6c9397588e768de6c025f0c732c18806f3d1270c83f83fa86a7bf62583770e5f8d0b405fbb3ad32214ef3584f5f9332478f2506414443a910bf15863b36ebfcaa7cbb19253ae32cd3ca390dab87b29cd31e11be7fa4ea3a01dad625d9de44e412680e1a694227698788d71f1e089e5831dc1bbacfa794b45e1c995214bf71ee4160d98b4305fa4c3e36ee5f8da19b3708f68e7d2e8197375c0f763d90e31143eb04760cc2148c8f54937b9385c95db1742595634ed004fa567655dfe1d9b9fa698074a9fb70c05a252b2d9cf7ca1c9b009f2cd70d6972ccf0ee281d777d66a0346c6c6525436dd7fe3578b28dca2c7adbfde0ecd45148$31c3248ba4dc9600a024b4e0e7c3e585',
152 | },
153 | _id: 'system.user.myuser',
154 | native: {},
155 | acl: {
156 | object: 1638,
157 | },
158 | },
159 | err => {
160 | expect(err).to.be.null;
161 | objects.setObject(
162 | 'javascript.0.test',
163 | {
164 | common: {
165 | name: 'test',
166 | type: 'number',
167 | role: 'level',
168 | min: -100,
169 | max: 100,
170 | def: 1,
171 | },
172 | native: {},
173 | type: 'state',
174 | acl: {
175 | object: 1638,
176 | owner: 'system.user.myuser',
177 | ownerGroup: 'system.group.administrator',
178 | state: 1638,
179 | },
180 | },
181 | err => {
182 | expect(err).to.be.null;
183 | states.setState('javascript.0.test', 1, err => {
184 | console.log(`END javascript.0.test ${err}`);
185 | expect(err).to.be.null;
186 | objects.setObject(
187 | 'javascript.0.test-number',
188 | {
189 | common: {
190 | name: 'test',
191 | type: 'number',
192 | role: 'value',
193 | def: 0,
194 | },
195 | native: {},
196 | type: 'state',
197 | },
198 | err => {
199 | expect(err).to.be.null;
200 | states.setState('javascript.0.test-number', 0, err => {
201 | expect(err).to.be.null;
202 | done();
203 | });
204 | },
205 | );
206 | });
207 | },
208 | );
209 | },
210 | );
211 | },
212 | );
213 | });
214 | }).timeout(60000);
215 |
216 | it('Test RESTful API as Owner-User: get - must return value', done => {
217 | console.log('START get/system.adapter.simple-api.0.alive');
218 | axios
219 | .get(`http://127.0.0.1:${PORT}/get/system.adapter.simple-api.0.alive`, { validateStatus: false, responseType: 'text' })
220 | .then(response => {
221 | console.log(`get/system.adapter.simple-api.0.alive => ${response.data}`);
222 | expect(response.data).to.be.equal('{"error":"permissionError"}');
223 | expect(response.status).to.equal(403);
224 | done();
225 | })
226 | .catch(error => {
227 | console.error(error);
228 | done(error);
229 | });
230 | });
231 |
232 | it('Test RESTful API as Owner-User: getPlainValue - must return plain value', done => {
233 | axios
234 | .get(`http://127.0.0.1:${PORT}/getPlainValue/system.adapter.simple-api.0.alive`, {
235 | validateStatus: false,
236 | responseType: 'text',
237 | })
238 | .then(response => {
239 | console.log(`getPlainValue/system.adapter.simple-api.0.alive => ${response.data}`);
240 | expect(response.data).to.be.equal('error: permissionError');
241 | expect(response.status).to.equal(403);
242 | done();
243 | })
244 | .catch(error => {
245 | console.error(error);
246 | done(error);
247 | });
248 | });
249 |
250 | it('Test RESTful API as Owner-User: getPlainValue 4 Test-Endpoint - must return plain value', done => {
251 | axios
252 | .get(`http://127.0.0.1:${PORT}/getPlainValue/javascript.0.test`, {
253 | responseType: 'text',
254 | })
255 | .then(response => {
256 | console.log(`getPlainValue/javascript.0.test => ${response.data}`);
257 | expect(response.data).equal('1');
258 | expect(response.status).to.equal(200);
259 | done();
260 | })
261 | .catch(error => {
262 | console.error(error);
263 | done(error);
264 | });
265 | });
266 |
267 | it('Test RESTful API as Owner-User: set 4 Test-Endpoint - must set value', done => {
268 | axios
269 | .get(`http://127.0.0.1:${PORT}/set/javascript.0.test?val=2`)
270 | .then(response => {
271 | console.log(`set/javascript.0.test?val=false => ${response.data}`);
272 | const obj = response.data;
273 | expect(obj).to.be.ok;
274 | expect(obj.val).to.be.equal(2);
275 | expect(obj.id).to.equal('javascript.0.test');
276 | expect(response.status).to.equal(200);
277 | return axios.get(`http://127.0.0.1:${PORT}/getPlainValue/javascript.0.test`, {
278 | responseType: 'text',
279 | });
280 | })
281 | .then(response => {
282 | console.log(`getPlainValue/javascript.0.test => ${response.data}`);
283 | expect(response.data).equal('2');
284 | expect(response.status).to.equal(200);
285 | done();
286 | })
287 | .catch(error => {
288 | console.error(error);
289 | done(error);
290 | });
291 | });
292 |
293 | it('Test RESTful API as Owner-User: set - must set value', done => {
294 | axios
295 | .get(`http://127.0.0.1:${PORT}/set/system.adapter.simple-api.0.alive?val=false`, {
296 | validateStatus: false,
297 | responseType: 'text',
298 | })
299 | .then(response => {
300 | console.log(`set/system.adapter.simple-api.0.alive?val=false => ${response.data}`);
301 | expect(response.data).to.be.equal('{"error":"permissionError"}');
302 | expect(response.status).to.equal(403);
303 | done();
304 | })
305 | .catch(error => {
306 | console.error(error);
307 | done(error);
308 | });
309 | });
310 |
311 | it('Test RESTful API as Owner-User: set - must set val', done => {
312 | axios
313 | .get(`http://127.0.0.1:${PORT}/set/system.adapter.simple-api.0.alive?val=true`, {
314 | validateStatus: false,
315 | responseType: 'text',
316 | })
317 | .then(response => {
318 | console.log(`set/system.adapter.simple-api.0.alive?val=true => ${response.data}`);
319 | expect(response.data).to.be.equal('{"error":"permissionError"}');
320 | expect(response.status).to.equal(403);
321 | done();
322 | })
323 | .catch(error => {
324 | console.error(error);
325 | done(error);
326 | });
327 | });
328 |
329 | it('Test RESTful API as Owner-User: objects - must return objects', done => {
330 | axios
331 | .get(`http://127.0.0.1:${PORT}/objects?pattern=system.adapter.*`)
332 | .then(response => {
333 | //console.log('objects?pattern=system.adapter.* => ' + response.data);
334 | expect(!!response.data).to.be.true;
335 | expect(response.status).to.equal(200);
336 | done();
337 | })
338 | .catch(error => {
339 | console.error(error);
340 | done(error);
341 | });
342 | });
343 |
344 | it('Test RESTful API as Owner-User: objects - must return objects', done => {
345 | axios
346 | .get(`http://127.0.0.1:${PORT}/objects?pattern=system.adapter.*&type=instance`)
347 | .then(response => {
348 | //console.log('objects?pattern=system.adapter.* => ' + response.data);
349 | //expect(response.data).to.be.equal('error: permissionError');
350 | expect(!!response.data).to.be.true;
351 | expect(response.status).to.equal(200);
352 | done();
353 | })
354 | .catch(error => {
355 | console.error(error);
356 | done(error);
357 | });
358 | });
359 |
360 | it('Test RESTful API as Owner-User: states - must return states', done => {
361 | axios
362 | .get(`http://127.0.0.1:${PORT}/states?pattern=system.adapter.*`, {
363 | validateStatus: false,
364 | responseType: 'text',
365 | })
366 | .then(response => {
367 | console.log(`states?pattern=system.adapter.* => ${response.data}`);
368 | expect(response.data).to.be.equal('{"error":"permissionError"}');
369 | expect(response.status).to.equal(403);
370 | done();
371 | })
372 | .catch(error => {
373 | console.error(error);
374 | done(error);
375 | });
376 | });
377 |
378 | it('Test RESTful API as Owner-User: setValueFromBody(POST) - must set one value', done => {
379 | axios
380 | .post(`http://127.0.0.1:${PORT}/setValueFromBody/${TEST_STATE_ID}`, '55', {
381 | validateStatus: false,
382 | responseType: 'text',
383 | })
384 | .then(response => {
385 | console.log(`setValueFromBody/?${TEST_STATE_ID} => ${JSON.stringify(response.data)}`);
386 | expect(response.data).to.be.equal('{"error":"permissionError"}');
387 | expect(response.status).to.equal(403);
388 | done();
389 | })
390 | .catch(error => {
391 | console.error(error);
392 | done(error);
393 | });
394 | });
395 |
396 | after('Test RESTful API as Owner-User: Stop js-controller', function (done) {
397 | this.timeout(9000);
398 | setup.stopController(normalTerminated => {
399 | console.log(`Adapter normal terminated: ${normalTerminated}`);
400 | setTimeout(done, 3000);
401 | });
402 | });
403 | });
404 |
--------------------------------------------------------------------------------
/admin/words.js:
--------------------------------------------------------------------------------
1 | /*global systemDictionary:true */
2 | /*
3 | +===================== DO NOT MODIFY ======================+
4 | | This file was generated by translate-adapter, please use |
5 | | `translate-adapter adminLanguages2words` to update it. |
6 | +===================== DO NOT MODIFY ======================+
7 | */
8 | 'use strict';
9 |
10 | systemDictionary = {
11 | "Allow only when User is Owner": { "en": "Allow only when User is Owner", "de": "Nur zulassen, wenn der Benutzer Eigentümer ist", "ru": "Разрешить только когда пользователь является владельцем", "pt": "Permitir somente quando o usuário for proprietário", "nl": "Alleen toestaan wanneer de gebruiker eigenaar is", "fr": "Autoriser uniquement lorsque l'utilisateur est propriétaire", "it": "Consenti solo quando l'utente è il proprietario", "es": "Permitir sólo cuando el usuario sea el propietario", "pl": "Zezwól tylko, gdy użytkownik jest właścicielem", "uk": "Дозволити лише тоді, коли користувач є власником", "zh-cn": "仅当用户是所有者时才允许"},
12 | "Allow origin (CORS)": { "en": "Allow origin (CORS)", "de": "Ursprung zulassen (CORS)", "ru": "Разрешить происхождение (CORS)", "pt": "Permitir origem (CORS)", "nl": "Oorsprong toestaan (CORS)", "fr": "Autoriser l'origine (CORS)", "it": "Consenti origine (CORS)", "es": "Permitir origen (CORS)", "pl": "Zezwalaj na pochodzenie (CORS)", "uk": "Дозволити походження (CORS)", "zh-cn": "允许来源 (CORS)"},
13 | "Authentication": { "en": "Authentication", "de": "Authentifizierung", "ru": "Аутентификация", "pt": "Autenticação", "nl": "Authenticatie", "fr": "Authentification", "it": "Autenticazione", "es": "Autenticación", "pl": "Uwierzytelnianie", "uk": "Аутентифікація", "zh-cn": "验证"},
14 | "Authentication was deactivated": { "en": "Authentication was deactivated", "de": "Authentifizierung wurde deaktiviert", "ru": "Аутентификация была деактивирована", "pt": "A autenticação foi desativada", "nl": "Authenticatie is gedeactiveerd", "fr": "L'authentification a été désactivée", "it": "L'autenticazione è stata disattivata", "es": "La autenticación fue desactivada", "pl": "Uwierzytelnianie zostało wyłączone", "uk": "Автентифікацію було вимкнено", "zh-cn": "身份验证已停用"},
15 | "Chained certificate": { "en": "Chained certificate", "de": "Verkettetes Zertifikat", "ru": "Связанный сертификат", "pt": "Certificado encadeado", "nl": "Gekoppeld certificaat", "fr": "Certificat chaîné", "it": "Certificato concatenato", "es": "Certificado encadenado", "pl": "Certyfikat łańcuchowy", "uk": "Прикутий сертифікат", "zh-cn": "链式证书"},
16 | "Disable authentication": { "en": "Disable authentication", "de": "Authentifizierung deaktivieren", "ru": "Отключить аутентификацию", "pt": "Desativar autenticação", "nl": "Authenticatie uitschakelen", "fr": "Désactiver l'authentification", "it": "Disabilitare l'autenticazione", "es": "Deshabilitar la autenticación", "pl": "Wyłącz uwierzytelnianie", "uk": "Вимкнути автентифікацію", "zh-cn": "禁用身份验证"},
17 | "Extend WEB adapter": { "en": "Use as web adapter extension", "de": "Verwendung als Webadapter-Erweiterung", "ru": "Использовать как расширение веб-адаптера", "pt": "Usar como extensão do adaptador web", "nl": "Gebruik als webadapter-extensie", "fr": "Utiliser comme extension d'adaptateur Web", "it": "Utilizzare come estensione dell'adattatore Web", "es": "Usar como extensión del adaptador web", "pl": "Użyj jako rozszerzenia adaptera internetowego", "uk": "Використовувати як розширення веб-адаптера", "zh-cn": "用作 Web 适配器扩展"},
18 | "IP": { "en": "IP", "de": "IP", "ru": "ИС", "pt": "Propriedade Intelectual", "nl": "IE", "fr": "propriété intellectuelle", "it": "Proprietà intellettuale", "es": "Propiedad intelectual", "pl": "IP", "uk": "IP", "zh-cn": "知识产权"},
19 | "Ignore warning": { "en": "Ignore warning", "de": "Warnung ignorieren", "ru": "Игнорировать предупреждение", "pt": "Ignorar aviso", "nl": "Negeer waarschuwing", "fr": "Ignorer l'avertissement", "it": "Ignora avviso", "es": "Ignorar la advertencia", "pl": "Zignoruj ostrzeżenie", "uk": "Ігнорувати попередження", "zh-cn": "忽略警告"},
20 | "Let's Encrypt settings": { "en": "Let's Encrypt settings", "de": "Let’s Encrypt-Einstellungen", "ru": "Настройки Let's Encrypt", "pt": "Configurações do Let's Encrypt", "nl": "Let's Encrypt-instellingen", "fr": "Paramètres de Let's Encrypt", "it": "Impostazioni di Let's Encrypt", "es": "Configuración de Let's Encrypt", "pl": "Ustawienia Let's Encrypt", "uk": "Давайте зашифруємо налаштування", "zh-cn": "Let's Encrypt 设置"},
21 | "List all datapoints": { "en": "List all states", "de": "Alle Bundesstaaten auflisten", "ru": "Список всех штатов", "pt": "Listar todos os estados", "nl": "Lijst alle staten", "fr": "Lister tous les états", "it": "Elenca tutti gli stati", "es": "Listar todos los estados", "pl": "Wypisz wszystkie stany", "uk": "Перелічіть усі штати", "zh-cn": "列出所有州"},
22 | "Listen on all IPs": { "en": "Listen on all IPs", "de": "Hören Sie auf allen IPs", "ru": "Прослушивание всех IP-адресов", "pt": "Ouça em todos os IPs", "nl": "Luister op alle IP's", "fr": "Écouter sur toutes les IP", "it": "Ascolta su tutti gli IP", "es": "Escuchar en todas las IP", "pl": "Słuchaj na wszystkich IP", "uk": "Прослуховування на всіх IP", "zh-cn": "监听所有 IP"},
23 | "Port": { "en": "Port", "de": "Hafen", "ru": "Порт", "pt": "Porta", "nl": "Haven", "fr": "Port", "it": "Porta", "es": "Puerto", "pl": "Port", "uk": "Порт", "zh-cn": "港口"},
24 | "Port to check the domain": { "en": "Port to check the domain", "de": "Port zur Überprüfung der Domäne", "ru": "Порт для проверки домена", "pt": "Porta para verificar o domínio", "nl": "Poort om het domein te controleren", "fr": "Port pour vérifier le domaine", "it": "Porta per controllare il dominio", "es": "Puerto para comprobar el dominio", "pl": "Port do sprawdzenia domeny", "uk": "Порт для перевірки домену", "zh-cn": "检查域名的端口"},
25 | "Private certificate": { "en": "Private certificate", "de": "Privates Zertifikat", "ru": "Частный сертификат", "pt": "Certificado privado", "nl": "Privécertificaat", "fr": "Certificat privé", "it": "Certificato privato", "es": "Certificado privado", "pl": "Certyfikat prywatny", "uk": "Приватний сертифікат", "zh-cn": "私人证书"},
26 | "Public certificate": { "en": "Public certificate", "de": "Öffentliches Zertifikat", "ru": "Публичный сертификат", "pt": "Certificado público", "nl": "Openbaar certificaat", "fr": "Certificat public", "it": "Certificato pubblico", "es": "Certificado público", "pl": "Certyfikat publiczny", "uk": "Публічний сертифікат", "zh-cn": "公共证书"},
27 | "Run as": { "en": "Execute as user", "de": "Als Benutzer ausführen", "ru": "Выполнить как пользователь", "pt": "Executar como usuário", "nl": "Uitvoeren als gebruiker", "fr": "Exécuter en tant qu'utilisateur", "it": "Esegui come utente", "es": "Ejecutar como usuario", "pl": "Wykonaj jako użytkownik", "uk": "Виконати як користувач", "zh-cn": "以用户身份执行"},
28 | "Secure(HTTPS)": { "en": "Secure (HTTPS)", "de": "Sicher (HTTPS)", "ru": "Безопасный (HTTPS)", "pt": "Seguro (HTTPS)", "nl": "Veilig (HTTPS)", "fr": "Sécurisé (HTTPS)", "it": "Sicuro (HTTPS)", "es": "Seguro (HTTPS)", "pl": "Bezpieczny (HTTPS)", "uk": "Безпечний (HTTPS)", "zh-cn": "安全(HTTPS)"},
29 | "Select data source": { "en": "Select data source", "de": "Datenquelle auswählen", "ru": "Выберите источник данных", "pt": "Selecione a fonte de dados", "nl": "Selecteer gegevensbron", "fr": "Sélectionner la source de données", "it": "Seleziona la fonte dei dati", "es": "Seleccionar fuente de datos", "pl": "Wybierz źródło danych", "uk": "Виберіть джерело даних", "zh-cn": "选择数据源"},
30 | "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 Zertifikate oder lade sie zuerst unter System/Einstellungen (oben rechts).", "ru": "Установите сертификаты или сначала загрузите их в настройках системы (справа вверху).", "pt": "Defina os certificados ou carregue-os primeiro nas configurações do sistema (canto superior direito).", "nl": "Stel certificaten in of laad ze eerst in de systeeminstellingen (rechtsboven).", "fr": "Définissez les certificats ou chargez-les d'abord dans les paramètres système (en haut à droite).", "it": "Impostare i certificati o caricarli prima nelle impostazioni di sistema (in alto a destra).", "es": "Configure los certificados o cárguelos primero en la configuración del sistema (arriba a la derecha).", "pl": "Ustaw certyfikaty lub najpierw załaduj je w ustawieniach systemu (prawy górny róg).", "uk": "Спершу встановіть сертифікати або завантажте їх у налаштуваннях системи (справа вгорі).", "zh-cn": "在系统设置(右上角)中设置证书或先加载。"},
31 | "Unsecure_Auth": { "en": "The password will be sent unencrypted via an unsecure connection. To protect your passwords enable the secure connection (HTTPS)!", "de": "Das Passwort wird unverschlüsselt über eine unsichere Verbindung übermittelt. Um Ihre Passwörter zu schützen, aktivieren Sie die sichere Verbindung (HTTPS)!", "ru": "Пароль будет отправлен в незашифрованном виде через незащищенное соединение. Для защиты паролей включите защищенное соединение (HTTPS)!", "pt": "A senha será enviada sem criptografia por meio de uma conexão não segura. Para proteger suas senhas, habilite a conexão segura (HTTPS)!", "nl": "Het wachtwoord wordt onversleuteld verzonden via een onveilige verbinding. Om uw wachtwoorden te beschermen, schakelt u de beveiligde verbinding (HTTPS) in!", "fr": "Le mot de passe sera envoyé en clair via une connexion non sécurisée. Pour protéger vos mots de passe, activez la connexion sécurisée (HTTPS) !", "it": "La password verrà inviata non crittografata tramite una connessione non sicura. Per proteggere le tue password abilita la connessione sicura (HTTPS)!", "es": "La contraseña se enviará sin cifrar mediante una conexión no segura. Para proteger sus contraseñas, active la conexión segura (HTTPS).", "pl": "Hasło zostanie wysłane bez szyfrowania za pośrednictwem niezabezpieczonego połączenia. Aby chronić swoje hasła, włącz bezpieczne połączenie (HTTPS)!", "uk": "Пароль буде надіслано незашифрованим через незахищене з’єднання. Щоб захистити свої паролі, увімкніть безпечне з'єднання (HTTPS)!", "zh-cn": "密码将通过不安全的连接以未加密形式发送。要保护您的密码,请启用安全连接 (HTTPS)!"},
32 | "Use Lets Encrypt certificates": { "en": "Use Let's Encrypt certificates", "de": "Verwenden Sie Let’s Encrypt-Zertifikate", "ru": "Используйте сертификаты Let's Encrypt", "pt": "Use certificados Let's Encrypt", "nl": "Gebruik Let's Encrypt-certificaten", "fr": "Utiliser les certificats Let's Encrypt", "it": "Utilizzare i certificati Let's Encrypt", "es": "Utilice certificados Let's Encrypt", "pl": "Użyj certyfikatów Let's Encrypt", "uk": "Використовуйте сертифікати Let's Encrypt", "zh-cn": "使用 Let's Encrypt 证书"},
33 | "Use this instance for automatic update": { "en": "Use this instance for automatic update", "de": "Diese Instanz für automatische Updates verwenden", "ru": "Используйте этот экземпляр для автоматического обновления", "pt": "Use esta instância para atualização automática", "nl": "Gebruik dit exemplaar voor automatische updates", "fr": "Utilisez cette instance pour la mise à jour automatique", "it": "Utilizzare questa istanza per l'aggiornamento automatico", "es": "Utilice esta instancia para la actualización automática", "pl": "Użyj tej instancji do automatycznej aktualizacji", "uk": "Використовуйте цей екземпляр для автоматичного оновлення", "zh-cn": "使用此实例进行自动更新"},
34 | "Warning!": { "en": "Warning!", "de": "Warnung!", "ru": "Предупреждение!", "pt": "Aviso!", "nl": "Waarschuwing!", "fr": "Avertissement!", "it": "Avvertimento!", "es": "¡Advertencia!", "pl": "Ostrzeżenie!", "uk": "УВАГА!", "zh-cn": "警告!"},
35 | "all": { "en": "all", "de": "alle", "ru": "все", "pt": "todos", "nl": "alle", "fr": "tous", "it": "Tutto", "es": "todo", "pl": "Wszystko", "uk": "все", "zh-cn": "全部"},
36 | "enable_authentication_with_cors": { "en": "You can define here the \"Access-Control-Allow-Origin\" header. It is suggested to enable authentication if you plan to access it from outside.", "de": "Hier können Sie den Header \"Access-Control-Allow-Origin\" definieren. Es wird empfohlen, die Authentifizierung zu aktivieren, wenn Sie von außen darauf zugreifen möchten.", "ru": "Здесь можно определить заголовок \"Access-Control-Allow-Origin\". Рекомендуется включить аутентификацию, если вы планируете получить к нему доступ извне.", "pt": "Você pode definir aqui o cabeçalho \"Access-Control-Allow-Origin\". É sugerido habilitar a autenticação se você planeja acessá-lo de fora.", "nl": "U kunt hier de header \"Access-Control-Allow-Origin\" definiëren. Het wordt aangeraden om authenticatie in te schakelen als u van plan bent om er van buitenaf toegang toe te krijgen.", "fr": "Vous pouvez définir ici l'en-tête « Access-Control-Allow-Origin ». Il est conseillé d'activer l'authentification si vous prévoyez d'y accéder depuis l'extérieur.", "it": "Qui puoi definire l'intestazione \"Access-Control-Allow-Origin\". Si consiglia di abilitare l'autenticazione se si prevede di accedervi dall'esterno.", "es": "Aquí puede definir el encabezado \"Access-Control-Allow-Origin\". Se recomienda habilitar la autenticación si planea acceder desde el exterior.", "pl": "Tutaj możesz zdefiniować nagłówek „Access-Control-Allow-Origin”. Zaleca się włączenie uwierzytelniania, jeśli planujesz uzyskać do niego dostęp z zewnątrz.", "uk": "Тут можна визначити заголовок \"Access-Control-Allow-Origin\". Рекомендується ввімкнути автентифікацію, якщо ви плануєте отримати до нього доступ ззовні.", "zh-cn": "您可以在此处定义“Access-Control-Allow-Origin”标头。如果您计划从外部访问,建议启用身份验证。"},
37 | "none": { "en": "none", "de": "keiner", "ru": "никто", "pt": "nenhum", "nl": "geen", "fr": "aucun", "it": "nessuno", "es": "ninguno", "pl": "nic", "uk": "немає", "zh-cn": "没有任何"},
38 | "place here": { "en": "Drop the files here", "de": "Legen Sie die Dateien hier ab", "ru": "Перетащите файлы сюда", "pt": "Solte os arquivos aqui", "nl": "Zet de bestanden hier neer", "fr": "Déposez les fichiers ici", "it": "Trascina i file qui", "es": "Suelta los archivos aquí", "pl": "Upuść pliki tutaj", "uk": "Перетягніть файли сюди", "zh-cn": "将文件拖放到此处"},
39 | };
--------------------------------------------------------------------------------
/test/testApiAsUserNoRights.js:
--------------------------------------------------------------------------------
1 | /* jshint -W097 */ // jshint strict:false
2 | /*jslint node: true */
3 | /*jshint expr: true*/
4 | const expect = require('chai').expect;
5 | const setup = require('@iobroker/legacy-testing');
6 | const axios = require('axios');
7 |
8 | let objects = null;
9 | let states = null;
10 | const TEST_STATE_ID = 'simple-api.0.testNumber';
11 |
12 | process.env.NO_PROXY = '127.0.0.1';
13 |
14 | function checkConnectionOfAdapter(cb, counter) {
15 | counter = counter || 0;
16 | console.log(`Try check #${counter}`);
17 | if (counter > 30) {
18 | if (cb) cb('Cannot check connection');
19 | return;
20 | }
21 |
22 | states.getState('system.adapter.simple-api.0.alive', function (err, state) {
23 | if (err) console.error(err);
24 | if (state && state.val) {
25 | if (cb) cb();
26 | } else {
27 | setTimeout(function () {
28 | checkConnectionOfAdapter(cb, counter + 1);
29 | }, 1000);
30 | }
31 | });
32 | }
33 |
34 | function createTestState(cb) {
35 | objects.setObject(
36 | TEST_STATE_ID,
37 | {
38 | _id: TEST_STATE_ID,
39 | type: 'state',
40 | common: {
41 | name: 'Test state',
42 | type: 'number',
43 | read: true,
44 | write: false,
45 | role: 'indicator.state',
46 | unit: '%',
47 | def: 0,
48 | desc: 'test state',
49 | },
50 | native: {},
51 | },
52 | () => {
53 | states.setState(TEST_STATE_ID, { val: 0, ack: true }, cb);
54 | },
55 | );
56 | }
57 |
58 | describe('Test RESTful API as User(No rights)', function () {
59 | before('Test RESTful API as User:(No rights) Start js-controller', function (_done) {
60 | this.timeout(600000); // because of the first installation from npm
61 | setup.adapterStarted = false;
62 |
63 | setup.setupController(async () => {
64 | const config = await setup.getAdapterConfig();
65 | // enable adapter
66 | config.common.enabled = true;
67 | config.common.loglevel = 'debug';
68 | config.native.port = 18183;
69 | config.native.defaultUser = 'norights';
70 | await setup.setAdapterConfig(config.common, config.native);
71 |
72 | setup.startController((_objects, _states) => {
73 | objects = _objects;
74 | states = _states;
75 | // give some time to start server
76 | setTimeout(() => createTestState(() => _done()), 2000);
77 | });
78 | });
79 | });
80 |
81 | it('Test adapter: Check if adapter started and create upload datapoint', done => {
82 | this.timeout(60000);
83 | checkConnectionOfAdapter(res => {
84 | if (res) {
85 | console.log(res);
86 | }
87 | expect(res).not.to.be.equal('Cannot check connection');
88 | objects.setObject(
89 | 'system.group.norights',
90 | {
91 | common: {
92 | name: 'norights',
93 | desc: '',
94 | members: ['system.user.norights'],
95 | acl: {
96 | object: {
97 | list: false,
98 | read: false,
99 | write: false,
100 | delete: false,
101 | },
102 | state: {
103 | list: true,
104 | read: true,
105 | write: true,
106 | create: false,
107 | delete: false,
108 | },
109 | users: {
110 | write: false,
111 | create: false,
112 | delete: false,
113 | },
114 | other: {
115 | execute: false,
116 | http: false,
117 | sendto: false,
118 | },
119 | file: {
120 | list: false,
121 | read: false,
122 | write: false,
123 | create: false,
124 | delete: false,
125 | },
126 | },
127 | },
128 | native: {},
129 | acl: {
130 | object: 1638,
131 | owner: 'system.user.admin',
132 | ownerGroup: 'system.group.administrator',
133 | },
134 | _id: 'system.group.norights',
135 | type: 'group',
136 | },
137 | function (err) {
138 | expect(err).to.be.null;
139 |
140 | objects.setObject(
141 | 'system.user.norights',
142 | {
143 | type: 'user',
144 | common: {
145 | name: 'norights',
146 | enabled: true,
147 | groups: [],
148 | password:
149 | 'pbkdf2$10000$ab4104d8bb68390ee7e6c9397588e768de6c025f0c732c18806f3d1270c83f83fa86a7bf62583770e5f8d0b405fbb3ad32214ef3584f5f9332478f2506414443a910bf15863b36ebfcaa7cbb19253ae32cd3ca390dab87b29cd31e11be7fa4ea3a01dad625d9de44e412680e1a694227698788d71f1e089e5831dc1bbacfa794b45e1c995214bf71ee4160d98b4305fa4c3e36ee5f8da19b3708f68e7d2e8197375c0f763d90e31143eb04760cc2148c8f54937b9385c95db1742595634ed004fa567655dfe1d9b9fa698074a9fb70c05a252b2d9cf7ca1c9b009f2cd70d6972ccf0ee281d777d66a0346c6c6525436dd7fe3578b28dca2c7adbfde0ecd45148$31c3248ba4dc9600a024b4e0e7c3e585',
150 | },
151 | _id: 'system.user.norights',
152 | native: {},
153 | acl: {
154 | object: 1638,
155 | },
156 | },
157 | function (err) {
158 | expect(err).to.be.null;
159 | objects.setObject(
160 | 'javascript.0.test',
161 | {
162 | common: {
163 | name: 'test',
164 | type: 'number',
165 | role: 'level',
166 | min: -100,
167 | max: 100,
168 | def: 1,
169 | },
170 | native: {},
171 | type: 'state',
172 | acl: {
173 | object: 1638,
174 | owner: 'system.user.norights',
175 | ownerGroup: 'system.group.administrator',
176 | state: 1638,
177 | },
178 | },
179 | function (err) {
180 | expect(err).to.be.null;
181 | states.setState('javascript.0.test', 1, function (err) {
182 | expect(err).to.be.null;
183 | done();
184 | });
185 | },
186 | );
187 | },
188 | );
189 | },
190 | );
191 | });
192 | });
193 |
194 | it('Test RESTful API as User:(No rights) get - must return value', done => {
195 | axios
196 | .get('http://127.0.0.1:18183/get/system.adapter.simple-api.0.alive', {
197 | validateStatus: false,
198 | responseType: 'text',
199 | })
200 | .then(response => {
201 | console.log(`get/system.adapter.simple-api.0.alive => ${response.data}`);
202 | expect(response.data).to.be.equal('{"error":"permissionError"}');
203 | expect(response.status).to.equal(403);
204 | done();
205 | })
206 | .catch(error => {
207 | console.error(error);
208 | done(error);
209 | });
210 | });
211 |
212 | it('Test RESTful API as User:(No rights) getPlainValue - must return plain value', done => {
213 | axios
214 | .get('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', {
215 | validateStatus: false,
216 | responseType: 'text',
217 | })
218 | .then(response => {
219 | console.log(`getPlainValue/system.adapter.simple-api.0.alive => ${response.data}`);
220 | expect(response.data).to.be.equal('error: permissionError');
221 | expect(response.status).to.equal(403);
222 | done();
223 | })
224 | .catch(error => {
225 | console.error(error);
226 | done(error);
227 | });
228 | });
229 |
230 | it('Test RESTful API as User:(No rights) getPlainValue 4 Test-Endpoint - must return plain value', done => {
231 | axios
232 | .get('http://127.0.0.1:18183/getPlainValue/javascript.0.test', {
233 | validateStatus: false,
234 | responseType: 'text',
235 | })
236 | .then(response => {
237 | console.log(`getPlainValue/javascript.0.test => ${response.data}`);
238 | expect(response.data).to.be.equal('error: permissionError');
239 | expect(response.status).to.equal(403);
240 | done();
241 | })
242 | .catch(error => {
243 | console.error(error);
244 | done(error);
245 | });
246 | });
247 |
248 | it('Test RESTful API as User:(No rights) set 4 Test-Endpoint - must set value', done => {
249 | axios
250 | .get('http://127.0.0.1:18183/set/javascript.0.test?val=2', { validateStatus: false, responseType: 'text' })
251 | .then(response => {
252 | console.log(`set/javascript.0.test?val=false => ${response.data}`);
253 | expect(response.data).to.be.equal('{"error":"permissionError"}');
254 | expect(response.status).to.equal(403);
255 | return axios.get('http://127.0.0.1:18183/getPlainValue/javascript.0.test', {
256 | validateStatus: false,
257 | responseType: 'text',
258 | });
259 | })
260 | .then(response => {
261 | console.log(`getPlainValue/javascript.0.test => ${response.data}`);
262 | expect(response.data).to.be.equal('error: permissionError');
263 | expect(response.status).to.equal(403);
264 | done();
265 | })
266 | .catch(error => {
267 | console.error(error);
268 | done(error);
269 | });
270 | });
271 |
272 | it('Test RESTful API as User:(No rights) set - must set value', done => {
273 | axios
274 | .get('http://127.0.0.1:18183/set/system.adapter.simple-api.0.alive?val=false', {
275 | validateStatus: false,
276 | responseType: 'text',
277 | })
278 | .then(response => {
279 | console.log(`set/system.adapter.simple-api.0.alive?val=false => ${response.data}`);
280 | expect(response.data).to.be.equal('{"error":"permissionError"}');
281 | expect(response.status).to.equal(403);
282 | return axios.get('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', {
283 | validateStatus: false,
284 | responseType: 'text',
285 | });
286 | })
287 | .then(response => {
288 | console.log(`getPlainValue/system.adapter.simple-api.0.alive => ${response.data}`);
289 | expect(response.data).to.be.equal('error: permissionError');
290 | expect(response.status).to.equal(403);
291 | done();
292 | })
293 | .catch(error => {
294 | console.error(error);
295 | done(error);
296 | });
297 | });
298 |
299 | it('Test RESTful API as User:(No rights) set - must set val', done => {
300 | axios
301 | .get('http://127.0.0.1:18183/set/system.adapter.simple-api.0.alive?val=true', {
302 | validateStatus: false,
303 | responseType: 'text',
304 | })
305 | .then(response => {
306 | console.log(`set/system.adapter.simple-api.0.alive?val=true => ${response.data}`);
307 | expect(response.data).to.be.equal('{"error":"permissionError"}');
308 | expect(response.status).to.equal(403);
309 | return axios.get('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', {
310 | validateStatus: false,
311 | responseType: 'text',
312 | });
313 | })
314 | .then(response => {
315 | console.log(`getPlainValue/system.adapter.simple-api.0.alive => ${response.data}`);
316 | expect(response.data).to.be.equal('error: permissionError');
317 | expect(response.status).to.equal(403);
318 | done();
319 | })
320 | .catch(error => {
321 | console.error(error);
322 | done(error);
323 | });
324 | });
325 |
326 | it('Test RESTful API as User:(No rights) objects - must return objects', done => {
327 | axios
328 | .get('http://127.0.0.1:18183/objects?pattern=system.adapter.*', {
329 | validateStatus: false,
330 | responseType: 'text',
331 | })
332 | .then(response => {
333 | console.log(`objects?pattern=system.adapter.* => ${response.data}`);
334 | expect(response.data).to.be.equal('{"error":"permissionError"}');
335 | expect(response.status).to.equal(403);
336 | done();
337 | })
338 | .catch(error => {
339 | console.error(error);
340 | done(error);
341 | });
342 | });
343 |
344 | it('Test RESTful API as User:(No rights) objects - must return objects', done => {
345 | axios
346 | .get('http://127.0.0.1:18183/objects?pattern=system.adapter.*&type=instance', {
347 | validateStatus: false,
348 | responseType: 'text',
349 | })
350 | .then(response => {
351 | console.log(`objects?pattern=system.adapter.* => ${response.data}`);
352 | expect(response.data).to.be.equal('{"error":"permissionError"}');
353 | expect(response.status).to.equal(403);
354 | done();
355 | })
356 | .catch(error => {
357 | console.error(error);
358 | done(error);
359 | });
360 | });
361 |
362 | it('Test RESTful API as User:(No rights) states - must return states', done => {
363 | axios
364 | .get('http://127.0.0.1:18183/states?pattern=system.adapter.*', {
365 | validateStatus: false,
366 | responseType: 'text',
367 | })
368 | .then(response => {
369 | console.log(`states?pattern=system.adapter.* => ${response.data}`);
370 | expect(response.data).to.be.equal('{"error":"permissionError"}');
371 | expect(response.status).to.equal(403);
372 | done();
373 | })
374 | .catch(error => {
375 | console.error(error);
376 | done(error);
377 | });
378 | });
379 |
380 | it('Test RESTful API as User:(No rights) setBulk(POST) - must set values', done => {
381 | axios
382 | .post(
383 | 'http://127.0.0.1:18183/setBulk',
384 | `${TEST_STATE_ID}=50&system.adapter.simple-api.0.alive=false&javascript.0.test=4`,
385 | { validateStatus: false, responseType: 'text' },
386 | )
387 | .then(response => {
388 | console.log(
389 | `setBulk/?${TEST_STATE_ID}=50&system.adapter.simple-api.0.alive=false&javascript.0.test=4 => ${JSON.stringify(response.data)}`,
390 | );
391 | expect(response.data).to.be.equal('{"error":"permissionError"}');
392 | expect(response.status).to.equal(403);
393 | return axios.get(
394 | `http://127.0.0.1:18183/getBulk/${TEST_STATE_ID},system.adapter.simple-api.0.alive,javascript.0.test`,
395 | { validateStatus: false, responseType: 'text' },
396 | );
397 | })
398 | .then(response => {
399 | console.log(
400 | `getBulk/${TEST_STATE_ID},system.adapter.simple-api.0.alive,javascript.0.test => ${response.data}`,
401 | );
402 | expect(response.data).to.be.equal('{"error":"permissionError"}');
403 | expect(response.status).to.equal(403);
404 | done();
405 | })
406 | .catch(error => {
407 | console.error(error);
408 | done(error);
409 | });
410 | });
411 |
412 | it('Test RESTful API as User:(No rights) setBulk(POST-GET-Mix) - must set values', done => {
413 | axios
414 | .post(`http://127.0.0.1:18183/setBulk?${TEST_STATE_ID}=51&system.adapter.simple-api.0.alive=false`, '', {
415 | validateStatus: false,
416 | responseType: 'text',
417 | })
418 | .then(response => {
419 | console.log(
420 | `setBulk/?${TEST_STATE_ID}=51&system.adapter.simple-api.0.alive=false => ${JSON.stringify(response.data)}`,
421 | );
422 | expect(response.data).to.be.equal('{"error":"permissionError"}');
423 | expect(response.status).to.equal(403);
424 | return axios.get(`http://127.0.0.1:18183/getBulk/${TEST_STATE_ID},system.adapter.simple-api.0.alive`, {
425 | validateStatus: false,
426 | responseType: 'text',
427 | });
428 | })
429 | .then(response => {
430 | console.log(`getBulk/${TEST_STATE_ID},system.adapter.simple-api.0.alive => ${response.data}`);
431 | expect(response.data).to.be.equal('{"error":"permissionError"}');
432 | expect(response.status).to.equal(403);
433 | done();
434 | })
435 | .catch(error => {
436 | console.error(error);
437 | done(error);
438 | });
439 | });
440 |
441 | it('Test RESTful API as User:(No rights) setValueFromBody(POST) - must set one value', done => {
442 | axios
443 | .post(`http://127.0.0.1:18183/setValueFromBody/${TEST_STATE_ID}`, '55', {
444 | validateStatus: false,
445 | responseType: 'text',
446 | })
447 | .then(response => {
448 | console.log(`setValueFromBody/?${TEST_STATE_ID} => ${JSON.stringify(response.data)}`);
449 | expect(response.data).to.be.equal('{"error":"permissionError"}');
450 | expect(response.status).to.equal(403);
451 | return axios.get(`http://127.0.0.1:18183/getBulk/${TEST_STATE_ID}`, {
452 | validateStatus: false,
453 | responseType: 'text',
454 | });
455 | })
456 | .then(response => {
457 | console.log(`getBulk/${TEST_STATE_ID} => ${response.data}`);
458 | expect(response.data).to.be.equal('{"error":"permissionError"}');
459 | expect(response.status).to.equal(403);
460 | done();
461 | })
462 | .catch(error => {
463 | console.error(error);
464 | done(error);
465 | });
466 | });
467 |
468 | after('Test RESTful API as User:(No rights) Stop js-controller', function (done) {
469 | this.timeout(9000);
470 | setup.stopController(normalTerminated => {
471 | console.log('Adapter normal terminated: ' + normalTerminated);
472 | setTimeout(done, 3000);
473 | });
474 | });
475 | });
476 |
--------------------------------------------------------------------------------
/test/testApiAsUser.js:
--------------------------------------------------------------------------------
1 | /* jshint -W097 */
2 | /* jshint strict: false */
3 | /* jslint node: true */
4 | /* jshint expr: true*/
5 |
6 | const expect = require('chai').expect;
7 | const setup = require('@iobroker/legacy-testing');
8 | const axios = require('axios');
9 |
10 | let objects = null;
11 | let states = null;
12 |
13 | process.env.NO_PROXY = '127.0.0.1';
14 | const TEST_STATE_ID = 'simple-api.0.testNumber';
15 |
16 | function checkConnectionOfAdapter(cb, counter) {
17 | counter = counter || 0;
18 | console.log(`Try check #${counter}`);
19 | if (counter > 30) {
20 | if (cb) cb('Cannot check connection');
21 | return;
22 | }
23 |
24 | states.getState('system.adapter.simple-api.0.alive', function (err, state) {
25 | if (err) console.error(err);
26 | if (state && state.val) {
27 | if (cb) cb();
28 | } else {
29 | setTimeout(function () {
30 | checkConnectionOfAdapter(cb, counter + 1);
31 | }, 1000);
32 | }
33 | });
34 | }
35 |
36 | function createTestState(cb) {
37 | objects.setObject(
38 | TEST_STATE_ID,
39 | {
40 | _id: TEST_STATE_ID,
41 | type: 'state',
42 | common: {
43 | name: 'Test state',
44 | type: 'number',
45 | read: true,
46 | write: false,
47 | role: 'indicator.state',
48 | unit: '%',
49 | def: 0,
50 | desc: 'test state',
51 | },
52 | native: {},
53 | },
54 | () => {
55 | states.setState(TEST_STATE_ID, { val: 0, ack: true }, cb);
56 | },
57 | );
58 | }
59 |
60 | describe('Test RESTful API as User', function () {
61 | before('Test RESTful API as User: Start js-controller', function (_done) {
62 | this.timeout(600000); // because of the first installation from npm
63 | setup.adapterStarted = false;
64 |
65 | setup.setupController(async () => {
66 | const config = await setup.getAdapterConfig();
67 | // enable adapter
68 | config.common.enabled = true;
69 | config.common.loglevel = 'debug';
70 | config.native.port = 18183;
71 | config.native.defaultUser = 'myuser';
72 | await setup.setAdapterConfig(config.common, config.native);
73 |
74 | setup.startController(function (_objects, _states) {
75 | objects = _objects;
76 | states = _states;
77 | // give some time to start server
78 | setTimeout(() => createTestState(() => _done()), 2000);
79 | });
80 | });
81 | });
82 |
83 | it('Test adapter: Check if adapter started and create upload datapoint', done => {
84 | checkConnectionOfAdapter(function (res) {
85 | if (res) console.log(res);
86 | expect(res).not.to.be.equal('Cannot check connection');
87 | objects.setObject(
88 | 'system.group.writer',
89 | {
90 | common: {
91 | name: 'Writer',
92 | desc: '',
93 | members: ['system.user.myuser'],
94 | acl: {
95 | object: {
96 | list: true,
97 | read: true,
98 | write: false,
99 | delete: false,
100 | },
101 | state: {
102 | list: true,
103 | read: true,
104 | write: true,
105 | create: false,
106 | delete: false,
107 | },
108 | users: {
109 | write: false,
110 | create: false,
111 | delete: false,
112 | },
113 | other: {
114 | execute: false,
115 | http: false,
116 | sendto: false,
117 | },
118 | file: {
119 | list: false,
120 | read: false,
121 | write: false,
122 | create: false,
123 | delete: false,
124 | },
125 | },
126 | },
127 | native: {},
128 | acl: {
129 | object: 1638,
130 | owner: 'system.user.admin',
131 | ownerGroup: 'system.group.administrator',
132 | },
133 | _id: 'system.group.writer',
134 | type: 'group',
135 | },
136 | function (err) {
137 | expect(err).to.be.null;
138 |
139 | objects.setObject(
140 | 'system.user.myuser',
141 | {
142 | type: 'user',
143 | common: {
144 | name: 'myuser',
145 | enabled: true,
146 | groups: [],
147 | password:
148 | 'pbkdf2$10000$ab4104d8bb68390ee7e6c9397588e768de6c025f0c732c18806f3d1270c83f83fa86a7bf62583770e5f8d0b405fbb3ad32214ef3584f5f9332478f2506414443a910bf15863b36ebfcaa7cbb19253ae32cd3ca390dab87b29cd31e11be7fa4ea3a01dad625d9de44e412680e1a694227698788d71f1e089e5831dc1bbacfa794b45e1c995214bf71ee4160d98b4305fa4c3e36ee5f8da19b3708f68e7d2e8197375c0f763d90e31143eb04760cc2148c8f54937b9385c95db1742595634ed004fa567655dfe1d9b9fa698074a9fb70c05a252b2d9cf7ca1c9b009f2cd70d6972ccf0ee281d777d66a0346c6c6525436dd7fe3578b28dca2c7adbfde0ecd45148$31c3248ba4dc9600a024b4e0e7c3e585',
149 | },
150 | _id: 'system.user.myuser',
151 | native: {},
152 | acl: {
153 | object: 1638,
154 | },
155 | },
156 | function (err) {
157 | expect(err).to.be.null;
158 | objects.setObject(
159 | 'javascript.0.test',
160 | {
161 | common: {
162 | name: 'test',
163 | type: 'number',
164 | role: 'level',
165 | min: -100,
166 | max: 100,
167 | def: 1,
168 | },
169 | native: {},
170 | type: 'state',
171 | acl: {
172 | object: 1638,
173 | owner: 'system.user.myuser',
174 | ownerGroup: 'system.group.administrator',
175 | state: 1638,
176 | },
177 | },
178 | function (err) {
179 | expect(err).to.be.null;
180 | states.setState('javascript.0.test', 1, function (err) {
181 | expect(err).to.be.null;
182 | objects.setObject(
183 | 'javascript.0.test-number',
184 | {
185 | common: {
186 | name: 'test',
187 | type: 'number',
188 | role: 'value',
189 | def: 0,
190 | },
191 | native: {},
192 | type: 'state',
193 | },
194 | err => {
195 | expect(err).to.be.null;
196 | states.setState('javascript.0.test-number', 0, err => {
197 | expect(err).to.be.null;
198 | done();
199 | });
200 | },
201 | );
202 | });
203 | },
204 | );
205 | },
206 | );
207 | },
208 | );
209 | });
210 | }).timeout(60000);
211 |
212 | it('Test RESTful API as User: get - must return value', done => {
213 | axios.get('http://127.0.0.1:18183/get/system.adapter.simple-api.0.alive').then(response => {
214 | console.log(`get/system.adapter.simple-api.0.alive => ${JSON.stringify(response.data)}`);
215 | const obj = response.data;
216 | //{
217 | // "val" : true,
218 | // "ack" : true,
219 | // "ts" : 1455009717,
220 | // "q" : 0,
221 | // "from" : "system.adapter.simple-api.0",
222 | // "lc" : 1455009717,
223 | // "expire" : 30000,
224 | // "_id" : "system.adapter.simple-api.0.alive",
225 | // "type" : "state",
226 | // "common" : {
227 | // "name" : "simple-api.0.alive",
228 | // "type" : "boolean",
229 | // "role" : "indicator.state"
230 | // },
231 | // "native" : {}
232 | //
233 | //}
234 |
235 | expect(obj).to.be.ok;
236 | expect(obj.val).to.be.true;
237 | expect(obj.ack).to.be.true;
238 | expect(obj.ts).to.be.ok;
239 | //expect(obj.from).to.equal("system.adapter.simple-api.0");
240 | expect(obj.type).to.equal('state');
241 | expect(obj._id).to.equal('system.adapter.simple-api.0.alive');
242 | expect(obj.common).to.be.ok;
243 | expect(obj.native).to.be.ok;
244 | expect(obj.common.name).to.equal('simple-api.0 alive');
245 | expect(obj.common.role).to.equal('indicator.state');
246 | expect(response.status).to.equal(200);
247 | done();
248 | });
249 | });
250 |
251 | it('Test RESTful API as User: getPlainValue - must return plain value', done => {
252 | axios
253 | .get('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', { responseType: 'text' })
254 | .then(response => {
255 | console.log(`getPlainValue/system.adapter.simple-api.0.alive => ${response.data}`);
256 | expect(response.data).equal('true');
257 | expect(response.status).to.equal(200);
258 | done();
259 | });
260 | });
261 |
262 | it('Test RESTful API as User: getPlainValue 4 Test-Endpoint - must return plain value', done => {
263 | axios.get('http://127.0.0.1:18183/getPlainValue/javascript.0.test', { responseType: 'text' }).then(response => {
264 | console.log(`getPlainValue/javascript.0.test => ${response.data}`);
265 | expect(response.data).equal('1');
266 | expect(response.status).to.equal(200);
267 | done();
268 | });
269 | });
270 |
271 | it('Test RESTful API as User: set 4 Test-Endpoint - must set value', done => {
272 | axios.get('http://127.0.0.1:18183/set/javascript.0.test?val=2').then(response => {
273 | console.log(`set/javascript.0.test?val=false => ${response.data}`);
274 | const obj = response.data;
275 | expect(obj).to.be.ok;
276 | expect(obj.val).to.be.equal(2);
277 | expect(obj.id).to.equal('javascript.0.test');
278 | expect(response.status).to.equal(200);
279 | axios
280 | .get('http://127.0.0.1:18183/getPlainValue/javascript.0.test', { responseType: 'text' })
281 | .then(response => {
282 | console.log(`getPlainValue/javascript.0.test => ${response.data}`);
283 | expect(response.data).equal('2');
284 | expect(response.status).to.equal(200);
285 | done();
286 | });
287 | });
288 | });
289 |
290 | it('Test RESTful API as User: set - must set value', done => {
291 | axios.get('http://127.0.0.1:18183/set/system.adapter.simple-api.0.alive?val=false').then(response => {
292 | console.log(`set/system.adapter.simple-api.0.alive?val=false => ${response.data}`);
293 | const obj = response.data;
294 | expect(obj).to.be.ok;
295 | expect(obj.val).to.be.false;
296 | expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
297 | expect(response.status).to.equal(200);
298 | axios
299 | .get('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', { responseType: 'text' })
300 | .then(response => {
301 | console.log(`getPlainValue/system.adapter.simple-api.0.alive => ${response.data}`);
302 | expect(response.data).equal('false');
303 | expect(response.status).to.equal(200);
304 | done();
305 | });
306 | });
307 | });
308 |
309 | it('Test RESTful API as User: set - must set val', done => {
310 | axios.get('http://127.0.0.1:18183/set/system.adapter.simple-api.0.alive?val=true').then(response => {
311 | console.log(`set/system.adapter.simple-api.0.alive?val=true => ${response.data}`);
312 | const obj = response.data;
313 | expect(obj).to.be.ok;
314 | expect(obj.val).to.be.true;
315 | expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
316 | expect(response.status).to.equal(200);
317 | axios
318 | .get('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', { responseType: 'text' })
319 | .then(response => {
320 | console.log(`getPlainValue/system.adapter.simple-api.0.alive => ${response.data}`);
321 | expect(response.data).equal('true');
322 | expect(response.status).to.equal(200);
323 | done();
324 | });
325 | });
326 | });
327 |
328 | it('Test RESTful API as User: toggle - must toggle boolean value to false', done => {
329 | axios.get('http://127.0.0.1:18183/toggle/system.adapter.simple-api.0.alive').then(response => {
330 | console.log(`toggle/system.adapter.simple-api.0.alive => ${response.data}`);
331 | const obj = response.data;
332 | expect(obj).to.be.ok;
333 | expect(obj.val).to.be.false;
334 | expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
335 | expect(response.status).to.equal(200);
336 |
337 | axios
338 | .get('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', { responseType: 'text' })
339 | .then(response => {
340 | console.log(`getPlainValue/system.adapter.simple-api.0.alive => ${response.data}`);
341 | expect(response.data).equal('false');
342 | expect(response.status).to.equal(200);
343 | done();
344 | });
345 | });
346 | });
347 |
348 | it('Test RESTful API as User: toggle - must toggle boolean value to true', done => {
349 | axios.get('http://127.0.0.1:18183/toggle/system.adapter.simple-api.0.alive').then(response => {
350 | console.log(`toggle/system.adapter.simple-api.0.alive => ${response.data}`);
351 | const obj = response.data;
352 | expect(obj).to.be.ok;
353 | expect(obj.val).to.be.true;
354 | expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
355 | expect(response.status).to.equal(200);
356 |
357 | axios
358 | .get('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', { responseType: 'text' })
359 | .then(response => {
360 | console.log(`getPlainValue/system.adapter.simple-api.0.alive => ${response.data}`);
361 | expect(response.data).equal('true');
362 | expect(response.status).to.equal(200);
363 | done();
364 | });
365 | });
366 | });
367 |
368 | it('Test RESTful API as User: toggle - must toggle number value to 100', done => {
369 | axios.get(`http://127.0.0.1:18183/toggle/${TEST_STATE_ID}`).then(response => {
370 | console.log(`toggle/${TEST_STATE_ID} => ${response.data}`);
371 | const obj = response.data;
372 | expect(obj).to.be.ok;
373 | expect(obj.val).to.be.equal(100);
374 | expect(obj.id).to.equal(TEST_STATE_ID);
375 | expect(response.status).to.equal(200);
376 |
377 | axios
378 | .get(`http://127.0.0.1:18183/getPlainValue/${TEST_STATE_ID}`, { responseType: 'text' })
379 | .then(response => {
380 | console.log(`getPlainValue/${TEST_STATE_ID} => ${response.data}`);
381 | expect(response.data).equal('100');
382 | expect(response.status).to.equal(200);
383 | axios.get(`http://127.0.0.1:18183/set/${TEST_STATE_ID}?val=49`).then(response => {
384 | console.log(`set/${TEST_STATE_ID}?val=49 => ` + response.data);
385 | axios.get(`http://127.0.0.1:18183/toggle/${TEST_STATE_ID}`).then(response => {
386 | console.log(`toggle/${TEST_STATE_ID} => ${response.data}`);
387 | const obj = response.data;
388 | expect(obj).to.be.ok;
389 | expect(obj.val).to.be.equal(51);
390 | expect(obj.id).to.equal(TEST_STATE_ID);
391 | expect(response.status).to.equal(200);
392 |
393 | axios
394 | .get(`http://127.0.0.1:18183/getPlainValue/${TEST_STATE_ID}`, { responseType: 'text' })
395 | .then(response => {
396 | console.log(`getPlainValue/${TEST_STATE_ID} => ${response.data}`);
397 | expect(response.data).equal('51');
398 | expect(response.status).to.equal(200);
399 | done();
400 | });
401 | });
402 | });
403 | });
404 | });
405 | });
406 |
407 | it('Test RESTful API as User: setBulk - must set values', done => {
408 | axios
409 | .get(
410 | `http://127.0.0.1:18183/setBulk/?${TEST_STATE_ID}=50&system.adapter.simple-api.0.alive=false&javascript.0.test=3`,
411 | )
412 | .then(response => {
413 | console.log(
414 | `setBulk/?${TEST_STATE_ID}=50&system.adapter.simple-api.0.alive=false&javascript.0.test=3 => ${response.data}`,
415 | );
416 | const obj = response.data;
417 | expect(obj).to.be.ok;
418 |
419 | expect(obj[0].val).to.be.equal(50);
420 | expect(obj[0].id).to.equal(TEST_STATE_ID);
421 | expect(obj[1].val).to.be.equal(false);
422 | expect(obj[1].id).to.equal('system.adapter.simple-api.0.alive');
423 | expect(obj[2].val).to.be.equal(3);
424 | expect(obj[2].id).to.equal('javascript.0.test');
425 | expect(response.status).to.equal(200);
426 |
427 | axios
428 | .get(
429 | `http://127.0.0.1:18183/getBulk/${TEST_STATE_ID},system.adapter.simple-api.0.alive,javascript.0.test`,
430 | )
431 | .then(response => {
432 | console.log(
433 | `getBulk/${TEST_STATE_ID},system.adapter.simple-api.0.alive&javascript.0.test => ${response.data}`,
434 | );
435 | const obj = response.data;
436 | expect(obj[0].val).equal(50);
437 | expect(obj[1].val).equal(false);
438 | expect(obj[2].val).equal(3);
439 | expect(response.status).to.equal(200);
440 | done();
441 | });
442 | });
443 | });
444 |
445 | it('Test RESTful API as User: objects - must return objects', done => {
446 | axios.get('http://127.0.0.1:18183/objects?pattern=system.adapter.*').then(response => {
447 | console.log(`objects?pattern=system.adapter.* => ${response.data}`);
448 | expect(response.data).to.be.not.equal('error: permissionError');
449 | expect(response.status).to.equal(200);
450 | done();
451 | });
452 | });
453 |
454 | it('Test RESTful API as User: objects - must return objects', done => {
455 | axios.get('http://127.0.0.1:18183/objects?pattern=system.adapter.*&type=instance').then(response => {
456 | console.log(`objects?pattern=system.adapter.* => ${response.data}`);
457 | expect(response.data).to.be.not.equal('error: permissionError');
458 | expect(response.status).to.equal(200);
459 | done();
460 | });
461 | });
462 |
463 | it('Test RESTful API as User: states - must return states', done => {
464 | axios.get('http://127.0.0.1:18183/states?pattern=system.adapter.*').then(response => {
465 | console.log(`states?pattern=system.adapter.* => ${response.data}`);
466 | expect(response.data).to.be.not.equal('error: permissionError');
467 | expect(response.status).to.equal(200);
468 | done();
469 | });
470 | });
471 |
472 | it('Test RESTful API as User: setBulk(POST) - must set values', done => {
473 | axios
474 | .post(
475 | 'http://127.0.0.1:18183/setBulk',
476 | `${TEST_STATE_ID}=50&system.adapter.simple-api.0.alive=false&javascript.0.test=4`,
477 | )
478 | .then(response => {
479 | console.log(
480 | `setBulk/?${TEST_STATE_ID}=50&system.adapter.simple-api.0.alive=false&javascript.0.test=4 => ${JSON.stringify(response.data)}`,
481 | );
482 | const obj = response.data;
483 | expect(obj).to.be.ok;
484 | expect(obj[0].val).to.be.equal(50);
485 | expect(obj[0].id).to.equal(TEST_STATE_ID);
486 | expect(obj[1].val).to.be.equal(false);
487 | expect(obj[1].id).to.equal('system.adapter.simple-api.0.alive');
488 | expect(obj[2].val).to.be.equal(4);
489 | expect(obj[2].id).to.equal('javascript.0.test');
490 | expect(response.status).to.equal(200);
491 |
492 | axios
493 | .get(
494 | `http://127.0.0.1:18183/getBulk/${TEST_STATE_ID},system.adapter.simple-api.0.alive,javascript.0.test`,
495 | )
496 | .then(response => {
497 | console.log(
498 | `getBulk/${TEST_STATE_ID},system.adapter.simple-api.0.alive,javascript.0.test => ${JSON.stringify(response.data)}`,
499 | );
500 | const obj = response.data;
501 | expect(obj[0].val).equal(50);
502 | expect(obj[1].val).equal(false);
503 | expect(obj[2].val).equal(4);
504 | expect(response.status).to.equal(200);
505 | done();
506 | });
507 | });
508 | });
509 |
510 | it('Test RESTful API as User: setBulk(POST-GET-Mix) - must set values', done => {
511 | axios
512 | .post(`http://127.0.0.1:18183/setBulk?${TEST_STATE_ID}=51&system.adapter.simple-api.0.alive=false`, '')
513 | .then(response => {
514 | console.log(
515 | `setBulk/?${TEST_STATE_ID}=51&system.adapter.simple-api.0.alive=false => ${JSON.stringify(response.data)}`,
516 | );
517 | expect(response.status).to.equal(200);
518 |
519 | const obj = response.data;
520 | expect(obj).to.be.ok;
521 | expect(obj[0].val).to.be.equal(51);
522 | expect(obj[0].id).to.equal(TEST_STATE_ID);
523 | expect(obj[1].val).to.be.equal(false);
524 | expect(obj[1].id).to.equal('system.adapter.simple-api.0.alive');
525 |
526 | return axios.get(`http://127.0.0.1:18183/getBulk/${TEST_STATE_ID},system.adapter.simple-api.0.alive`);
527 | })
528 | .then(response => {
529 | console.log(`getBulk/${TEST_STATE_ID},system.adapter.simple-api.0.alive => ${response.data}`);
530 | expect(response.status).to.equal(200);
531 |
532 | const obj = response.data;
533 | expect(obj[0].val).equal(51);
534 | expect(obj[1].val).equal(false);
535 | done();
536 | })
537 | .catch(error => {
538 | console.error(error);
539 | done(error);
540 | });
541 | });
542 |
543 | it('Test RESTful API as User: setValueFromBody(POST) - must set one value', done => {
544 | axios
545 | .post(`http://127.0.0.1:18183/setValueFromBody/${TEST_STATE_ID}`, '55')
546 | .then(response => {
547 | console.log(`setValueFromBody/?${TEST_STATE_ID} => ${JSON.stringify(response.data)}`);
548 | expect(response.status).to.equal(200);
549 |
550 | const obj = response.data;
551 | expect(obj).to.be.ok;
552 | expect(obj[0].val).to.be.equal(55);
553 | expect(obj[0].id).to.equal(TEST_STATE_ID);
554 |
555 | return axios.get(`http://127.0.0.1:18183/getBulk/${TEST_STATE_ID}`);
556 | })
557 | .then(response => {
558 | console.log(`getBulk/${TEST_STATE_ID} => ${response.data}`);
559 | expect(response.status).to.equal(200);
560 |
561 | const obj = response.data;
562 | expect(obj[0].val).equal(55);
563 | done();
564 | })
565 | .catch(error => {
566 | console.error(error);
567 | done(error);
568 | });
569 | });
570 |
571 | after('Test RESTful API as User: Stop js-controller', function (done) {
572 | this.timeout(9000);
573 | setup.stopController(normalTerminated => {
574 | console.log(`Adapter normal terminated: ${normalTerminated}`);
575 | setTimeout(done, 3000);
576 | });
577 | });
578 | });
579 |
--------------------------------------------------------------------------------
/test/testApi.js:
--------------------------------------------------------------------------------
1 | /* jshint -W097 */
2 | /* jshint strict: false */
3 | /* jslint node: true */
4 | /* jshint expr: true*/
5 | const expect = require('chai').expect;
6 | const setup = require('@iobroker/legacy-testing');
7 | const axios = require('axios');
8 |
9 | let objects = null;
10 | let states = null;
11 | const TEST_STATE_ID = 'simple-api.0.testNumber';
12 |
13 | process.env.NO_PROXY = '127.0.0.1';
14 |
15 | function checkConnectionOfAdapter(cb, counter) {
16 | counter = counter || 0;
17 | console.log(`Try check #${counter}`);
18 | if (counter > 30) {
19 | if (cb) cb('Cannot check connection');
20 | return;
21 | }
22 |
23 | states.getState('system.adapter.simple-api.0.alive', (err, state) => {
24 | if (err) console.error(err);
25 | if (state && state.val) {
26 | if (cb) cb();
27 | } else {
28 | setTimeout(() => checkConnectionOfAdapter(cb, counter + 1), 1000);
29 | }
30 | });
31 | }
32 |
33 | function createTestState(cb) {
34 | objects.setObject(
35 | TEST_STATE_ID,
36 | {
37 | _id: TEST_STATE_ID,
38 | type: 'state',
39 | common: {
40 | name: 'Test state',
41 | type: 'number',
42 | read: true,
43 | write: false,
44 | role: 'indicator.state',
45 | unit: '%',
46 | def: 0,
47 | desc: 'test state',
48 | },
49 | native: {},
50 | },
51 | () => {
52 | states.setState(TEST_STATE_ID, { val: 0, ack: true }, cb && cb);
53 | },
54 | );
55 | }
56 |
57 | describe('Test RESTful API', function () {
58 | before('Test RESTful API: Start js-controller', function (_done) {
59 | this.timeout(600000); // because of the first installation from npm
60 | setup.adapterStarted = false;
61 |
62 | setup.setupController(async () => {
63 | const config = await setup.getAdapterConfig();
64 | // enable adapter
65 | config.common.enabled = true;
66 | config.common.loglevel = 'debug';
67 | config.native.port = 18183;
68 | await setup.setAdapterConfig(config.common, config.native);
69 |
70 | setup.startController((_objects, _states) => {
71 | objects = _objects;
72 | states = _states;
73 |
74 | // give some time to start server
75 | setTimeout(() => createTestState(() => _done()), 2000);
76 | });
77 | });
78 | });
79 |
80 | it('Test adapter: Check if adapter started and create datapoint', done => {
81 | checkConnectionOfAdapter(res => {
82 | res && console.log(res);
83 | expect(res).not.to.be.equal('Cannot check connection');
84 | objects.setObject(
85 | 'javascript.0.test-string',
86 | {
87 | common: {
88 | name: 'test',
89 | type: 'string',
90 | role: 'value',
91 | def: '',
92 | },
93 | native: {},
94 | type: 'state',
95 | },
96 | err => {
97 | expect(err).to.be.null;
98 | states.setState('javascript.0.test-string', '', err => {
99 | expect(err).to.be.null;
100 | objects.setObject(
101 | 'javascript.0.test-number',
102 | {
103 | common: {
104 | name: 'test',
105 | type: 'number',
106 | role: 'value',
107 | def: 0,
108 | },
109 | native: {},
110 | type: 'state',
111 | },
112 | err => {
113 | expect(err).to.be.null;
114 | states.setState('javascript.0.test-number', 0, err => {
115 | expect(err).to.be.null;
116 | done();
117 | });
118 | },
119 | );
120 | });
121 | },
122 | );
123 | });
124 | }).timeout(60000);
125 |
126 | it('Test RESTful API: get - must return value', done => {
127 | axios
128 | .get('http://127.0.0.1:18183/get/system.adapter.simple-api.0.alive')
129 | .then(response => {
130 | console.log(`get/system.adapter.simple-api.0.alive => ${response.data}`);
131 | const obj = response.data;
132 | expect(obj).to.be.ok;
133 | expect(obj.val).to.be.true;
134 | expect(obj.ack).to.be.true;
135 | expect(obj.ts).to.be.ok;
136 | expect(obj.type).to.equal('state');
137 | expect(obj._id).to.equal('system.adapter.simple-api.0.alive');
138 | expect(obj.common).to.be.ok;
139 | expect(obj.native).to.be.ok;
140 | expect(obj.common.name).to.equal('simple-api.0 alive');
141 | expect(obj.common.role).to.equal('indicator.state');
142 | expect(response.status).to.equal(200);
143 | done();
144 | })
145 | .catch(error => {
146 | console.error(error);
147 | done(error);
148 | });
149 | });
150 |
151 | it('Test RESTful API: get - must return error', done => {
152 | axios
153 | .get('http://127.0.0.1:18183/get/system.adapter.simple-api.0.alive%23test', { validateStatus: false, responseType: 'text' })
154 | .then(response => {
155 | console.log(`get/system.adapter.simple-api.0.alive%23test => ${response.data}`);
156 | expect(response.data).to.be.equal(
157 | '{"error":"datapoint \\"system.adapter.simple-api.0.alive#test\\" not found"}',
158 | );
159 | expect(response.status).to.equal(404);
160 | done();
161 | })
162 | .catch(error => {
163 | console.error(error);
164 | done(error);
165 | });
166 | });
167 |
168 | it('Test RESTful API: get - must return error 2', done => {
169 | axios
170 | .get('http://127.0.0.1:18183/get/system.adapter.simple-api.0.%23alive%23test', { validateStatus: false, responseType: 'text' })
171 | .then(response => {
172 | console.log(`get/system.adapter.simple-api.0.alive#%23test => ${response.data}`);
173 | expect(response.data).to.be.equal(
174 | '{"error":"datapoint \\"system.adapter.simple-api.0.#alive#test\\" not found"}',
175 | );
176 | expect(response.status).to.equal(404);
177 | done();
178 | })
179 | .catch(error => {
180 | console.error(error);
181 | done(error);
182 | });
183 | });
184 |
185 | it('Test RESTful API: getPlainValue - must return plain value', done => {
186 | axios
187 | .get('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', { responseType: 'text' })
188 | .then(response => {
189 | console.log(`getPlainValue/system.adapter.simple-api.0.alive => ${response.data}`);
190 | expect(response.data).equal('true');
191 | expect(response.status).to.equal(200);
192 | done();
193 | })
194 | .catch(error => {
195 | console.error(error);
196 | done(error);
197 | });
198 | });
199 |
200 | it('Test RESTful API: set - must set value', done => {
201 | axios
202 | .get('http://127.0.0.1:18183/set/system.adapter.simple-api.0.alive?val=false')
203 | .then(response => {
204 | console.log(`set/system.adapter.simple-api.0.alive?val=false => ${response.data}`);
205 | const obj = response.data;
206 | expect(obj).to.be.ok;
207 | expect(obj.val).to.be.false;
208 | expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
209 | expect(response.status).to.equal(200);
210 | return axios.get('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', { responseType: 'text' });
211 | })
212 | .then(response => {
213 | console.log(`getPlainValue/system.adapter.simple-api.0.alive => ${response.data}`);
214 | expect(response.data).equal('false');
215 | expect(response.status).to.equal(200);
216 | return axios.get('http://127.0.0.1:18183/get/system.adapter.simple-api.0.alive');
217 | })
218 | .then(response => {
219 | console.log(`get/system.adapter.simple-api.0.alive => ${response.data}`);
220 | expect(response.data.val).equal(false);
221 | expect(response.status).to.equal(200);
222 | done();
223 | })
224 | .catch(error => {
225 | console.error(error);
226 | done(error);
227 | });
228 | });
229 |
230 | it('Test RESTful API: set - must set easy string value', done => {
231 | axios
232 | .get('http://127.0.0.1:18183/set/javascript.0.test-string?val=19,1-bla')
233 | .then(response => {
234 | console.log(`set/javascript.0.test-string?val=19,1-bla => ${response.data}`);
235 | const obj = response.data;
236 | expect(obj).to.be.ok;
237 | expect(obj.val).equal('19,1-bla');
238 | expect(obj.id).to.equal('javascript.0.test-string');
239 | expect(response.status).to.equal(200);
240 | return axios.get('http://127.0.0.1:18183/getPlainValue/javascript.0.test-string', { responseType: 'text' });
241 | })
242 | .then(response => {
243 | console.log(`getPlainValue/javascript.0.test-string => ${response.data}`);
244 | expect(response.data).equal('"19,1-bla"');
245 | expect(response.status).to.equal(200);
246 | return axios.get('http://127.0.0.1:18183/get/javascript.0.test-string');
247 | })
248 | .then(response => {
249 | console.log(`get/javascript.0.test-string => ${response.data}`);
250 | expect(response.data.val).equal('19,1-bla');
251 | expect(response.status).to.equal(200);
252 | done();
253 | })
254 | .catch(error => {
255 | console.error(error);
256 | done(error);
257 | });
258 | });
259 |
260 | it('Test RESTful API: set - must set encoded string value', done => {
261 | axios
262 | .get('http://127.0.0.1:18183/set/javascript.0.test-string?val=bla%26fasel%2efoo%3Dhummer+hey')
263 | .then(response => {
264 | console.log(`set/javascript.0.test-string?val=bla%20fasel%2efoo => ${response.data}`);
265 | const obj = response.data;
266 | expect(obj).to.be.ok;
267 | expect(obj.val).equal('bla&fasel.foo=hummer hey');
268 | expect(obj.id).to.equal('javascript.0.test-string');
269 | expect(response.status).to.equal(200);
270 | return axios.get('http://127.0.0.1:18183/getPlainValue/javascript.0.test-string', { responseType: 'text' });
271 | })
272 | .then(response => {
273 | console.log(`getPlainValue/javascript.0.test-string => ${response.data}`);
274 | expect(response.data).equal('"bla&fasel.foo=hummer hey"');
275 | expect(response.status).to.equal(200);
276 | return axios.get('http://127.0.0.1:18183/get/javascript.0.test-string');
277 | })
278 | .then(response => {
279 | console.log(`get/javascript.0.test-string => ${response.data}`);
280 | expect(response.data.val).equal('bla&fasel.foo=hummer hey');
281 | expect(response.status).to.equal(200);
282 | done();
283 | })
284 | .catch(error => {
285 | console.error(error);
286 | done(error);
287 | });
288 | });
289 |
290 | it('Test RESTful API: set - must set val', done => {
291 | axios
292 | .get('http://127.0.0.1:18183/set/system.adapter.simple-api.0.alive?val=true')
293 | .then(response => {
294 | console.log(`set/system.adapter.simple-api.0.alive?val=true => ${response.data}`);
295 | const obj = response.data;
296 | expect(obj).to.be.ok;
297 | expect(obj.val).to.be.true;
298 | expect(typeof obj.val).to.be.equal('boolean');
299 | expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
300 | expect(response.status).to.equal(200);
301 | return axios.get('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', { responseType: 'text' });
302 | })
303 | .then(response => {
304 | console.log(`getPlainValue/system.adapter.simple-api.0.alive => ${response.data}`);
305 | expect(response.data).equal('true');
306 | expect(response.status).to.equal(200);
307 | done();
308 | })
309 | .catch(error => {
310 | console.error(error);
311 | done(error);
312 | });
313 | });
314 |
315 | it('Test RESTful API: set - must have ack true', done => {
316 | axios
317 | .get('http://127.0.0.1:18183/set/system.adapter.simple-api.0.alive?val=true&ack=true')
318 | .then(response => {
319 | console.log(`set/system.adapter.simple-api.0.alive?val=true => ${response.data}`);
320 | const obj = response.data;
321 | expect(obj).to.be.ok;
322 | expect(obj.val).to.be.true;
323 | expect(typeof obj.val).to.be.equal('boolean');
324 | expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
325 | expect(response.status).to.equal(200);
326 | return axios.get('http://127.0.0.1:18183/get/system.adapter.simple-api.0.alive');
327 | })
328 | .then(response => {
329 | console.log(`get/system.adapter.simple-api.0.alive => ${response.data}`);
330 | const body = response.data;
331 | expect(body.val).equal(true);
332 | expect(body.ack).equal(true);
333 | expect(response.status).to.equal(200);
334 | done();
335 | })
336 | .catch(error => {
337 | console.error(error);
338 | done(error);
339 | });
340 | });
341 |
342 | it('Test RESTful API: toggle - must toggle boolean value to false', done => {
343 | axios
344 | .get('http://127.0.0.1:18183/toggle/system.adapter.simple-api.0.alive')
345 | .then(response => {
346 | console.log(`toggle/system.adapter.simple-api.0.alive => ${response.data}`);
347 | const obj = response.data;
348 | expect(obj).to.be.ok;
349 | expect(obj.val).to.be.false;
350 | expect(typeof obj.val).to.be.equal('boolean');
351 | expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
352 | expect(response.status).to.equal(200);
353 | return axios.get('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', { responseType: 'text' });
354 | })
355 | .then(response => {
356 | console.log(`getPlainValue/system.adapter.simple-api.0.alive => ${response.data}`);
357 | expect(response.data).equal('false');
358 | expect(response.status).to.equal(200);
359 | done();
360 | })
361 | .catch(error => {
362 | console.error(error);
363 | done(error);
364 | });
365 | });
366 |
367 | it('Test RESTful API: toggle - must toggle boolean value to true', done => {
368 | axios
369 | .get('http://127.0.0.1:18183/toggle/system.adapter.simple-api.0.alive')
370 | .then(response => {
371 | console.log(`toggle/system.adapter.simple-api.0.alive => ${response.data}`);
372 | const obj = response.data;
373 | expect(obj).to.be.ok;
374 | expect(obj.val).to.be.true;
375 | expect(obj.id).to.equal('system.adapter.simple-api.0.alive');
376 | expect(response.status).to.equal(200);
377 | return axios.get('http://127.0.0.1:18183/getPlainValue/system.adapter.simple-api.0.alive', { responseType: 'text' });
378 | })
379 | .then(response => {
380 | console.log(`getPlainValue/system.adapter.simple-api.0.alive => ${response.data}`);
381 | expect(response.data).equal('true');
382 | expect(response.status).to.equal(200);
383 | done();
384 | })
385 | .catch(error => {
386 | console.error(error);
387 | done(error);
388 | });
389 | });
390 |
391 | it('Test RESTful API: toggle - must toggle number value to 100', done => {
392 | axios
393 | .get(`http://127.0.0.1:18183/toggle/${TEST_STATE_ID}`)
394 | .then(response => {
395 | console.log(`toggle/${TEST_STATE_ID} => ${response.data}`);
396 | const obj = response.data;
397 | expect(obj).to.be.ok;
398 | expect(obj.val).to.be.equal(100);
399 | expect(typeof obj.val).to.be.equal('number');
400 | expect(obj.id).to.equal(TEST_STATE_ID);
401 | expect(response.status).to.equal(200);
402 | return axios.get(`http://127.0.0.1:18183/getPlainValue/${TEST_STATE_ID}`, { responseType: 'text' });
403 | })
404 | .then(response => {
405 | console.log(`getPlainValue/${TEST_STATE_ID} => ${response.data}`);
406 | expect(response.data).equal('100');
407 | expect(response.status).to.equal(200);
408 | return axios.get(`http://127.0.0.1:18183/set/${TEST_STATE_ID}?val=49`);
409 | })
410 | .then(response => {
411 | console.log(`set/${TEST_STATE_ID}?val=49 => ${response.data}`);
412 | return axios.get(`http://127.0.0.1:18183/toggle/${TEST_STATE_ID}`);
413 | })
414 | .then(response => {
415 | console.log(`toggle/${TEST_STATE_ID} => ${response.data}`);
416 | const obj = response.data;
417 | expect(obj).to.be.ok;
418 | expect(obj.val).to.be.equal(51);
419 | expect(obj.id).to.equal(TEST_STATE_ID);
420 | expect(response.status).to.equal(200);
421 | return axios.get(`http://127.0.0.1:18183/getPlainValue/${TEST_STATE_ID}`, { responseType: 'text' });
422 | })
423 | .then(response => {
424 | console.log(`getPlainValue/${TEST_STATE_ID} => ${response.data}`);
425 | expect(response.data).equal('51');
426 | expect(response.status).to.equal(200);
427 | done();
428 | })
429 | .catch(error => {
430 | console.error(error);
431 | done(error);
432 | });
433 | });
434 |
435 | it('Test RESTful API: setBulk - must set values', done => {
436 | axios
437 | .get(`http://127.0.0.1:18183/setBulk?${TEST_STATE_ID}=50&system.adapter.simple-api.0.alive=false`)
438 | .then(response => {
439 | console.log(`setBulk/?${TEST_STATE_ID}=50&system.adapter.simple-api.0.alive=false => ${response.data}`);
440 | const obj = response.data;
441 | expect(obj).to.be.ok;
442 | expect(obj[0].val).to.be.equal(50);
443 | expect(obj[0].id).to.equal(TEST_STATE_ID);
444 | expect(obj[1].val).to.be.equal(false);
445 | expect(obj[1].id).to.equal('system.adapter.simple-api.0.alive');
446 | expect(response.status).to.equal(200);
447 | return axios.get(`http://127.0.0.1:18183/getBulk/${TEST_STATE_ID},system.adapter.simple-api.0.alive`);
448 | })
449 | .then(response => {
450 | console.log(`getBulk/${TEST_STATE_ID},system.adapter.simple-api.0.alive => ${response.data}`);
451 | const obj = response.data;
452 | expect(obj[0].val).equal(50);
453 | expect(obj[1].val).equal(false);
454 | expect(response.status).to.equal(200);
455 | done();
456 | })
457 | .catch(error => {
458 | console.error(error);
459 | done(error);
460 | });
461 | });
462 |
463 | it('Test RESTful API: objects - must return objects', done => {
464 | axios.get('http://127.0.0.1:18183/objects?pattern=system.adapter.*').then(response => {
465 | console.log(`objects?pattern=system.adapter.* => ${response.data}`);
466 | const obj = response.data;
467 | expect(obj['system.adapter.simple-api.0.alive']._id).to.be.ok;
468 | expect(response.status).to.equal(200);
469 | done();
470 | });
471 | });
472 |
473 | it('Test RESTful API: objects - must return objects', done => {
474 | axios
475 | .get('http://127.0.0.1:18183/objects?pattern=system.adapter.*&type=instance')
476 | .then(response => {
477 | console.log(`objects?pattern=system.adapter.* => ${response.data}`);
478 | const obj = response.data;
479 | expect(obj['system.adapter.simple-api.0']._id).to.be.ok;
480 | expect(response.status).to.equal(200);
481 | done();
482 | })
483 | .catch(error => {
484 | console.error(error);
485 | done(error);
486 | });
487 | });
488 |
489 | it('Test RESTful API: states - must return states', done => {
490 | axios
491 | .get('http://127.0.0.1:18183/states?pattern=system.adapter.*')
492 | .then(response => {
493 | console.log(`states?pattern=system.adapter.* => ${response.data}`);
494 | const states = response.data;
495 | expect(states['system.adapter.simple-api.0.uptime'].val).to.be.least(0);
496 | expect(response.status).to.equal(200);
497 | done();
498 | })
499 | .catch(error => {
500 | console.error(error);
501 | done(error);
502 | });
503 | });
504 |
505 | it('Test RESTful API: setBulk(POST) - must set values', done => {
506 | axios
507 | .post(
508 | 'http://127.0.0.1:18183/setBulk',
509 | `${TEST_STATE_ID}=50&system.adapter.simple-api.0.alive=false&javascript.0.test-string=bla%26fasel%2efoo%3Dhummer+hey&ack=true`,
510 | )
511 | .then(response => {
512 | console.log(
513 | `setBulk/?${TEST_STATE_ID}=50&system.adapter.simple-api.0.alive=false&javascript.0.test-string=bla%26fasel%2efoo%3Dhummer+hey => ${JSON.stringify(response.data)}`,
514 | );
515 | const obj = response.data;
516 | expect(obj).to.be.ok;
517 | expect(obj[0].val).to.be.equal(50);
518 | expect(obj[0].id).to.equal(TEST_STATE_ID);
519 | expect(obj[1].val).to.be.equal(false);
520 | expect(obj[1].id).to.equal('system.adapter.simple-api.0.alive');
521 | expect(obj[2].val).to.be.equal('bla&fasel.foo=hummer hey');
522 | expect(obj[2].id).to.equal('javascript.0.test-string');
523 | expect(response.status).to.equal(200);
524 |
525 | return axios.get(
526 | `http://127.0.0.1:18183/getBulk/${TEST_STATE_ID},system.adapter.simple-api.0.alive,javascript.0.test-string`,
527 | );
528 | })
529 | .then(response => {
530 | console.log(
531 | `getBulk/${TEST_STATE_ID},system.adapter.simple-api.0.alive,javascript.0.test-string => ${response.data}`,
532 | );
533 | const obj = response.data;
534 | expect(obj[0].val).equal(50);
535 | expect(obj[0].ack).equal(true);
536 | expect(obj[1].val).equal(false);
537 | expect(obj[1].ack).equal(true);
538 | expect(obj[2].val).equal('bla&fasel.foo=hummer hey');
539 | expect(obj[2].ack).equal(true);
540 | expect(response.status).to.equal(200);
541 | done();
542 | })
543 | .catch(error => {
544 | console.error(error);
545 | done(error);
546 | });
547 | });
548 |
549 | it('Test RESTful API: setBulk(POST-GET-Mix) - must set values', done => {
550 | axios
551 | .post(`http://127.0.0.1:18183/setBulk?${TEST_STATE_ID}=51&system.adapter.simple-api.0.alive=false`, '')
552 | .then(response => {
553 | console.log(
554 | `setBulk/?${TEST_STATE_ID}=51&system.adapter.simple-api.0.alive=false => ${JSON.stringify(response.data)}`,
555 | );
556 | const obj = response.data;
557 | expect(obj).to.be.ok;
558 | expect(obj[0].val).to.be.equal(51);
559 | expect(obj[0].id).to.equal(TEST_STATE_ID);
560 | expect(obj[1].val).to.be.equal(false);
561 | expect(obj[1].id).to.equal('system.adapter.simple-api.0.alive');
562 | expect(response.status).to.equal(200);
563 |
564 | return axios.get(`http://127.0.0.1:18183/getBulk/${TEST_STATE_ID},system.adapter.simple-api.0.alive`);
565 | })
566 | .then(response => {
567 | console.log(`getBulk/${TEST_STATE_ID},system.adapter.simple-api.0.alive => ${response.data}`);
568 | const obj = response.data;
569 | expect(obj[0].val).equal(51);
570 | expect(obj[1].val).equal(false);
571 | expect(response.status).to.equal(200);
572 | done();
573 | })
574 | .catch(error => {
575 | console.error(error);
576 | done(error);
577 | });
578 | });
579 |
580 | it('Test RESTful API: setValueFromBody(POST) - must set one value', done => {
581 | axios
582 | .post(`http://127.0.0.1:18183/setValueFromBody/${TEST_STATE_ID}`, '55')
583 | .then(response => {
584 | console.log(`setValueFromBody/?${TEST_STATE_ID} => ${JSON.stringify(response.data)}`);
585 | const obj = response.data;
586 | expect(obj).to.be.ok;
587 | expect(obj[0].val).to.be.equal(55);
588 | expect(obj[0].id).to.equal(TEST_STATE_ID);
589 | expect(response.status).to.equal(200);
590 |
591 | return axios.get(`http://127.0.0.1:18183/getBulk/${TEST_STATE_ID}`);
592 | })
593 | .then(response => {
594 | console.log(`getBulk/${TEST_STATE_ID} => ${response.data}`);
595 | const obj = response.data;
596 | expect(obj[0].val).equal(55);
597 | expect(response.status).to.equal(200);
598 | done();
599 | })
600 | .catch(error => {
601 | console.error(error);
602 | done(error);
603 | });
604 | });
605 |
606 | after('Test RESTful API: Stop js-controller', function (done) {
607 | this.timeout(9000);
608 | setup.stopController(normalTerminated => {
609 | console.log(`Adapter normal terminated: ${normalTerminated}`);
610 | setTimeout(done, 3000);
611 | });
612 | });
613 | });
614 |
--------------------------------------------------------------------------------