Skip to content

Commit

Permalink
Merge pull request #379 from mStirner/dev
Browse files Browse the repository at this point in the history
Refactored labels array & moved item schema definition
  • Loading branch information
mStirner committed Dec 22, 2023
2 parents 70a747d + 1251bd5 commit 37fb4c2
Show file tree
Hide file tree
Showing 12 changed files with 377 additions and 22 deletions.
48 changes: 34 additions & 14 deletions postman.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"_id\": \"65818753e275da05046aaa78\",\n \"name\": \"Livingroom\" \n}",
"raw": "{\n \"_id\": \"65818753e275da05046aaa78\",\n \"name\": \"Livingroom\",\n \"labels\": [\n \"foo=bar\",\n \"baz=true\"\n ]\n}",
"options": {
"raw": {
"language": "json"
Expand Down Expand Up @@ -854,7 +854,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"_id\": \"65818856464bebcf19ebec4c\",\n \"topic\": \"air-sensor/sensor/particulate_matter_25m_concentration\"\n}",
"raw": "{\n \"_id\": \"65818856464bebcf19ebec4c\",\n \"topic\": \"air-sensor/sensor/particulate_matter_25m_concentration\",\n \"labels\": [\n \"manufacturer=custom\",\n \"esp8266=true\"\n ]\n}",
"options": {
"raw": {
"language": "json"
Expand Down Expand Up @@ -982,7 +982,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"description\": \"Ikea VINDRIKTNING MQTT modd\"\n}",
"raw": "{\n \"description\": \"Ikea VINDRIKTNING MQTT mod\",\n \"labels\": [\n \"manufacturer=Ikea\",\n \"model=VINDRIKTNING\",\n \"esp8266=true\"\n ]\n}",
"options": {
"raw": {
"language": "json"
Expand Down Expand Up @@ -1342,7 +1342,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"_id\": \"658188a7aadfcc026a0e0131\",\n \"name\": \"Hans Hubert #3\",\n \"email\": \"[email protected]\",\n \"password\": \"Pa$$w0rd\"\n}",
"raw": "{\n \"_id\": \"658188a7aadfcc026a0e0131\",\n \"name\": \"Hans Hubert #3\",\n \"email\": \"[email protected]\",\n \"password\": \"Pa$$w0rd\",\n \"labels\": [\n \"expires=29991231\"\n ]\n}",
"options": {
"raw": {
"language": "json"
Expand Down Expand Up @@ -1571,7 +1571,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"_id\": \"658188e93cde9987c3228806\",\n \"name\": \"Plugin Boilerplate Demo\",\n \"enabled\": true,\n \"version\": 1,\n \"intents\": [\n \"devices\",\n \"endpoints\",\n \"plugins\",\n \"rooms\",\n \"ssdp\",\n \"store\",\n \"users\",\n \"vault\"\n ]\n}",
"raw": "{\n \"_id\": \"658188e93cde9987c3228806\",\n \"name\": \"Plugin Boilerplate Demo\",\n \"uuid\": \"6951dee2-8541-4a69-bd3e-629fdadf093a\",\n \"enabled\": true,\n \"version\": 1,\n \"intents\": [\n \"devices\",\n \"endpoints\",\n \"plugins\",\n \"rooms\",\n \"ssdp\",\n \"store\",\n \"users\",\n \"vault\"\n ]\n}",
"options": {
"raw": {
"language": "json"
Expand Down Expand Up @@ -1642,7 +1642,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"name\": \"New updated\",\n \"enabled\": true,\n \"autostart\": false\n}",
"raw": "{\n \"name\": \"New updated\",\n \"enabled\": true,\n \"autostart\": false,\n \"labels\": [\n \"worker_thread=false\",\n \"my_custom_label={\\\"json\\\":true, \\\"number\\\":0815420}\"\n ]\n}",
"options": {
"raw": {
"language": "json"
Expand All @@ -1667,6 +1667,26 @@
},
{
"name": "Upload plugin *.tgz content",
"event": [
{
"listen": "prerequest",
"script": {
"exec": [
""
],
"type": "text/javascript"
}
},
{
"listen": "test",
"script": {
"exec": [
""
],
"type": "text/javascript"
}
}
],
"request": {
"method": "PUT",
"header": [],
Expand Down Expand Up @@ -1776,7 +1796,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"_id\": \"65818918d32dad8dab53e433\",\n \"name\": \"SmartMeter\",\n \"interfaces\": [{\n \"_id\": \"6581c55abc21a0a3122b9998\",\n \"type\": \"ETHERNET\",\n \"description\": \"WebSocket API\",\n \"settings\": {\n \"host\": \"192.168.2.155\",\n \"port\": 8080\n },\n \"adapter\": [\"raw\"]\n }],\n \"room\": \"62a4bc8bd9256b5e8d6988a0\",\n \"icon\": \"fa-solid fa-gauge-high\"\n}",
"raw": "{\n \"_id\": \"65818918d32dad8dab53e433\",\n \"name\": \"SmartMeter\",\n \"interfaces\": [{\n \"_id\": \"6581c55abc21a0a3122b9998\",\n \"type\": \"ETHERNET\",\n \"description\": \"WebSocket API\",\n \"settings\": {\n \"host\": \"192.168.2.155\",\n \"port\": 8080\n },\n \"adapter\": [\"raw\"]\n }],\n \"room\": \"62a4bc8bd9256b5e8d6988a0\",\n \"icon\": \"fa-solid fa-gauge-high\",\n \"labels\": [\n \"test=true\",\n \"protected=false\", \n \"foo=bar\"\n ]\n}",
"options": {
"raw": {
"language": "json"
Expand Down Expand Up @@ -1846,7 +1866,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"enabled\": true,\n \"name\": \"SaMsUnG FrIdGe\"\n}",
"raw": "{\n \"enabled\": true,\n \"name\": \"SaMsUnG FrIdGe\",\n \"labels\": [\n \"test=true\",\n \"protected=true\", \n \"foo=bar\"\n ]\n}",
"options": {
"raw": {
"language": "json"
Expand Down Expand Up @@ -2505,7 +2525,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"item\": \"6375343d0b555ccd42460a2e\"\n}",
"raw": "{\n \"item\": \"6375343d0b555ccd42460a2e\",\n \"labels\": [\n \"device=65818918d32dad8dab53e433\",\n \"endpoint=658189336fa19198939caa21\"\n ]\n}",
"options": {
"raw": {
"language": "json"
Expand Down Expand Up @@ -2599,11 +2619,6 @@
"}",
"",
"",
"pm.test(\"content-type = application/json\", () => {",
" pm.expect(pm.response.headers.get('Content-Type')).to.include('application/json');",
"});",
"",
"",
"pm.test(\"Status code 200 || 202\", () => {",
" pm.expect(pm.response.code).to.be.oneOf([",
" 200,",
Expand All @@ -2612,6 +2627,11 @@
"});",
"",
"",
"pm.test(\"content-type = application/json\", () => {",
" pm.expect(pm.response.headers.get('Content-Type')).to.include('application/json');",
"});",
"",
"",
"pm.test(\"Response has no error field\", () => {",
"",
" let length = pm.response.headers.get(\"content-length\");",
Expand Down
24 changes: 22 additions & 2 deletions system/component/class.component.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
const mongodb = require("mongodb");
const Joi = require("joi");
//const Joi = require("joi");

const _extend = require("../../helper/extend");
const _merge = require("../../helper/merge");

const COMMON = require("./class.common.js");
const Item = require("./class.item.js");

const PENDING_CHANGE_EVENTS = new Set();

Expand Down Expand Up @@ -94,11 +95,29 @@ module.exports = class COMPONENT extends COMMON {
});

this.collection = mongodb.client.collection(name);
this.schema = Item.schema().concat(schema);

/*
let baseSchema = Joi.object({
//labels: Joi.array().items(Joi.string().regex(/^[a-zA-Z0-9]+=[a-zA-Z0-9]+$/)).default([])
//labels: Joi.array().items(Joi.string().regex(/^[a-z0-9\.]+=[a-z0-9]+$/)).default([]),
labels: Joi.array().items(Joi.string().regex(/^.+?=.+|.+=.+$/i)).default([]),
//labels: Joi.array().items(Joi.string().regex(/^.+?=.+|.+=.+$/i)).default([]),
labels: Joi.array().items(Joi.alternatives().try(
Joi.string().regex(/^.+?=.+|.+=.+$/i),
Joi.object().custom((value, helpers) => {
if (value instanceof Label) {
// convert to string
// otherwise the serialized object is saved into the database
return value.toString();
} else {
return helpers.error("any.custom");
}
}, "Instance of class.label.js")
)).default([]),
timestamps: Joi.object({
...schema?.timestamps,
created: Joi.number().allow(null).default(null),
Expand All @@ -108,6 +127,7 @@ module.exports = class COMPONENT extends COMMON {
// concat base schema with component specific
this.schema = baseSchema.concat(schema);
*/

if (process.env.DATABASE_WATCH_CHANGES === "true") {
try {
Expand Down
69 changes: 69 additions & 0 deletions system/component/class.item.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
const Joi = require("joi");

const Labels = require("./class.labels.js");
const Label = require("./class.label.js");

module.exports = class Item {

constructor(obj) {
Expand All @@ -11,6 +16,70 @@ module.exports = class Item {
// see issue https://github.com/OpenHausIO/backend/issues/352
// this.labels = obj.labels.map(...);

obj.labels?.forEach((txt, i, arr) => {
if (!(txt instanceof Label)) {
arr[i] = new Label(txt);
}
});

let labels = new Labels(...obj.labels);

Object.defineProperty(this, "labels", {
get() {

//console.log("get called");
return labels;

},
set(value) {

// clear array
// needed when new array has fewer items than old one
// otherwise it contains old items & has wrong size
labels.splice(0, labels.length);

value.forEach((txt, i) => {
if (!(txt instanceof Label)) {
labels[i] = new Label(txt);
} else {
labels[i] = txt;
}
});

},
enumerable: true,
configurable: false
});

}

static schema() {
return Joi.object({
labels: Joi.array().items(Joi.alternatives().try(
Joi.string().regex(/^.+?=.+|.+=.+$/i),
Joi.object().custom((value, helpers) => {
if (value instanceof Label) {

// convert to string
// otherwise the serialized object is saved into the database
return value.toString();

} else {

return helpers.error("any.custom");

}
}, "Instance of class.label.js")
)).default([]),
timestamps: Joi.object({
created: Joi.number().allow(null).default(null),
updated: Joi.number().allow(null).default(null)
})
});
}

static validate(data) {
return Item.schema().validate(data);
}

};
52 changes: 52 additions & 0 deletions system/component/class.label.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
module.exports = class Label {

constructor(label) {

let [key, value] = label.split("=");

Object.defineProperty(this, "key", {
set(val) {
key = val;
label = `${key}=${value}`;
},
get() {
return key;
},
enumerable: true
});

Object.defineProperty(this, "value", {
set(val) {
value = val;
label = `${key}=${value}`;
},
get() {
return value;
},
enumerable: true
});

Object.defineProperty(this, "label", {
set(val) {
let { k, v } = label.split("=");
label = val;
key = k;
value = v;
},
get() {
return label;
},
enumerable: true
});

}

toJSON() {
return `${this.key}=${this.value}`;
}

toString() {
return `${this.key}=${this.value}`;
}

};
51 changes: 51 additions & 0 deletions system/component/class.labels.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
module.exports = class Labels extends Array {

constructor(...args) {
super(...args);
}

value(key) {
return this.find((label) => {
return label.key === key;
})?.value;
}

key(value) {
return this.find((label) => {
return label.value === value;
})?.key;
}

has(key) {
return !!this.find((label) => {
return label.key === key;
});
}

filter(query) {

let [k, v] = query.split("=");

return Array.prototype.filter.call(this, (label) => {

if (k !== "*") {
return label.key === k;
}

if (v !== "*") {
return label.value === v;
}

return label.key === k && label.value === v;

});

}

toJSON() {
return this.map(({ key, value }) => {
return `${key}=${value}`;
});
}

};
12 changes: 10 additions & 2 deletions tests/components/endpoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ try {
C_COMPONENT.add({
_id,
name: "Endpoint #1",
device: _device
device: _device,
labels: [
"key1=value1",
"key2=value2"
]
}, (err, item) => {
try {

Expand Down Expand Up @@ -59,7 +63,11 @@ try {

workflow(C_COMPONENT, "update", (done) => {
C_COMPONENT.update(_id, {
name: "Endpoint #2 - updated"
name: "Endpoint #2 - updated",
labels: [
"overriden=true",
"key3=value3"
]
}, (err, item) => {
try {

Expand Down
Loading

0 comments on commit 37fb4c2

Please sign in to comment.