OpenHausIO / backend

HTTP API for the OpenHaus SmartHome/IoT solution
https://docs.open-haus.io
6 stars 2 forks source link

Add a labels array "serializer/deserializer" #483

Open mStirner opened 2 months ago

mStirner commented 2 months ago

Convert label(s) dot notoation to a object. e.g.

let labels = [
    "oh.notifications.smtp.enabled=true",
    "oh.notifications.smtp.topic=Hello World",
    "oh.notifications.smtp.sender=foo@example.com", // wrong - gerts converted to object instead of string
    "oh.notifications.enabled=true",
    "oh.notifications.states[]=foo",
    "oh.notifications.states[]=bar",
    "oh.history.states[]=*",
    "oh.history.duration=3600",
    "private=true",
    "my-super-label=value",
    `oh.json=${JSON.stringify(json)}`
];

will be:

{
  oh: {
    notifications: {
      smtp: {
        enabled: 'true',
        topic: 'Hello World',
        sender: 'foo@example.com'
      },
      enabled: 'true',
      states: [ 'foo', 'bar' ]
    },
    history: { states: [ '*' ], duration: '3600' },
    json: '{"timemstamp":1720719319460,"bool":true,"zero":null,"obj":{"str":"Hello from json"}}'
  },
  private: 'true',
  'my-super-label': 'value'
}
marc@workstation:~/projects/OpenHaus/plugins/oh-plg-smtp-sender$ nodemon test.js 
[nodemon] 3.1.0
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node test.js`
labels [
  'oh.notifications.smtp.enabled=true',
  'oh.notifications.smtp.topic=Hello World',
  'oh.notifications.smtp.sender=foo@example.com',
  'oh.notifications.enabled=true',
  'oh.notifications.states[]=foo',
  'oh.notifications.states[]=bar',
  'oh.history.states[]=*',
  'oh.history.duration=3600',
  'private=true',
  'my-super-label=value',
  'oh.json={"timemstamp":1720719321281,"bool":true,"zero":null,"obj":{"str":"Hello from json"}}'
]
parsedObj {
  oh: {
    notifications: {
      smtp: {
        enabled: 'true',
        topic: 'Hello World',
        sender: 'foo@example.com'
      },
      enabled: 'true',
      states: [ 'foo', 'bar' ]
    },
    history: { states: [ '*' ], duration: '3600' },
    json: '{"timemstamp":1720719321281,"bool":true,"zero":null,"obj":{"str":"Hello from json"}}'
  },
  private: 'true',
  'my-super-label': 'value'
}
function arrayToObject(labels) {

    let result = {};

    labels.forEach((label, i) => {

        let [path, value] = label.split("=");
        let parts = path.split('.');
        let current = result;

        for (let i = 0; i < parts.length; i++) {

            let key = parts[i];
            let isLast = (i === parts.length - 1);
            let isArray = key.endsWith('[]');

            if (isArray) {
                key = key.slice(0, -2);
            }

            if (isLast) {

                //value = (value === "true") ? true : (value === "false") ? false : value;

                if (isArray) {

                    if (!current[key]) {
                        current[key] = [];
                    }

                    current[key].push(value);

                } else {

                    current[key] = value;

                }

            } else {

                if (!current[key]) {
                    current[key] = {};
                }

                current = current[key];

            }

        }

    });

    return result;

}

function objectToArray(obj, prefix = "") {

    let result = [];

    for (let key in obj) {
        let value = obj[key];
        let newKey = prefix ? `${prefix}.${key}` : key;

        if (typeof value === 'object' && !Array.isArray(value)) {
            result = result.concat(objectToArray(value, newKey));
        } else if (Array.isArray(value)) {
            value.forEach(val => {
                result.push(`${newKey}[]=${val}`);
            });
        } else {
            result.push(`${newKey}=${value}`);
        }
    }

    return result;

}

Could be added as static methods to the Labels class (class.labels.js)

mStirner commented 1 month ago

Improved version:

const util = require("util");

function arrayToObject(labels) {

    let result = {};

    labels.forEach((label, i) => {

        let [path, value] = label.split("=");
        let parts = path.split('.');
        let current = result;

        for (let i = 0; i < parts.length; i++) {

            let key = parts[i];
            let isLast = (i === parts.length - 1);
            let isArray = key.endsWith('[]');

            if (isArray) {
                key = key.slice(0, -2);
            }

            if (isLast) {

                //value = (value === "true") ? true : (value === "false") ? false : value;

                if (isArray) {

                    if (!current[key]) {
                        current[key] = [];
                    }

                    current[key].push(value);

                } else {

                    current[key] = value;

                }

            } else {

                if (!current[key]) {
                    current[key] = {};
                }

                current = current[key];

            }

        }

    });

    return result;

}

function objectToArray(obj, prefix = "") {

    let result = [];

    for (let key in obj) {
        let value = obj[key];
        let newKey = prefix ? `${prefix}.${key}` : key;

        if (typeof value === 'object' && !Array.isArray(value)) {
            result = result.concat(objectToArray(value, newKey));
        } else if (Array.isArray(value)) {
            value.forEach(val => {
                result.push(`${newKey}[]=${val}`);
            });
        } else {
            result.push(`${newKey}=${value}`);
        }
    }

    return result;

}

const json = {
    timemstamp: Date.now(),
    bool: true,
    zero: null,
    obj: {
        str: "Hello from json"
    }
}

// Beispielaufruf
let labels = [
    "oh.notifications.smtp.enabled=true",
    "oh.notifications.smtp.topic=Hello World",
    "oh.notifications.smtp.sender=foo@example.com", // wrong - gerts converted to object instead of string
    "oh.notifications.enabled=true",
    "oh.notifications.states[]=foo",
    "oh.notifications.states[]=bar",
    "oh.history.states[]=*",
    "oh.history.duration=3600",
    "private=true",
    "my-super-label=value",
    `oh.json=${JSON.stringify(json)}`
];

console.log("labels", labels)

/*
const ohLabels = labels.filter((label) => {
    return !label.startsWith("oh.");
});
*/

const parsedObj = arrayToObject(labels);
const arrayFromObj = objectToArray(parsedObj);

console.log("parsedObj", util.inspect(parsedObj, false, 100, true));
console.log("arrayFromObj", arrayFromObj);

const same = (() => {

    let valid = labels.every((label) => {
        return arrayFromObj.includes(label);
    });

    valid &= arrayFromObj.length === labels.length;

    return Boolean(valid);

})();

console.log("Array same", same);

Result:

labels [
  'oh.notifications.smtp.enabled=true',
  'oh.notifications.smtp.topic=Hello World',
  'oh.notifications.smtp.sender=foo@example.com',
  'oh.notifications.enabled=true',
  'oh.notifications.states[]=foo',
  'oh.notifications.states[]=bar',
  'oh.history.states[]=*',
  'oh.history.duration=3600',
  'private=true',
  'my-super-label=value',
  'oh.json={"timemstamp":1721501606716,"bool":true,"zero":null,"obj":{"str":"Hello from json"}}'
]
parsedObj {
  oh: {
    notifications: {
      smtp: {
        enabled: 'true',
        topic: 'Hello World',
        sender: 'foo@example.com'
      },
      enabled: 'true',
      states: [ 'foo', 'bar' ]
    },
    history: { states: [ '*' ], duration: '3600' },
    json: '{"timemstamp":1721501606716,"bool":true,"zero":null,"obj":{"str":"Hello from json"}}'
  },
  private: 'true',
  'my-super-label': 'value'
}
arrayFromObj [
  'oh.notifications.smtp.enabled=true',
  'oh.notifications.smtp.topic=Hello World',
  'oh.notifications.smtp.sender=foo@example.com',
  'oh.notifications.enabled=true',
  'oh.notifications.states[]=foo',
  'oh.notifications.states[]=bar',
  'oh.history.states[]=*',
  'oh.history.duration=3600',
  'oh.json={"timemstamp":1721501606716,"bool":true,"zero":null,"obj":{"str":"Hello from json"}}',
  'private=true',
  'my-super-label=value'
]
Array same true