Skip to content

Commit

Permalink
Merge pull request #465 from mStirner/dev
Browse files Browse the repository at this point in the history
Working on issues (forward v3)
  • Loading branch information
mStirner authored May 30, 2024
2 parents b8f5200 + 4472b5c commit 500f0b8
Show file tree
Hide file tree
Showing 19 changed files with 1,237 additions and 215 deletions.
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# https://medium.com/@kahana.hagai/docker-compose-with-node-js-and-mongodb-dbdadab5ce0a

# The instructions for the first stage
FROM node:16-alpine as builder
FROM node:20-alpine as builder

ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}
Expand All @@ -22,7 +22,7 @@ RUN npm install


# The instructions for second stage
FROM node:16-alpine
FROM node:20-alpine

WORKDIR /opt/OpenHaus/backend
COPY --from=builder node_modules node_modules
Expand Down
25 changes: 24 additions & 1 deletion components/devices/class.device.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ const Interface = require("./class.interface.js");
const Item = require("../../system/component/class.item.js");

const mixins = require("../../helper/mixins.js");
const injectMethod = require("../../helper/injectMethod.js");

//const { parse, calculateChecksum } = require("./net-helper.js");

/**
* @description
Expand All @@ -25,7 +28,7 @@ const mixins = require("../../helper/mixins.js");
* @see interfaceStream components/devices/class.interfaceStream.js
*/
module.exports = class Device extends Item {
constructor(props) {
constructor(props, scope) {

super(props);

Expand All @@ -38,6 +41,11 @@ module.exports = class Device extends Item {
// for each interface class, create a interface stream
this.interfaces = props.interfaces.map((obj) => {


// NOTE: refactor interfaceStream in v4
// move .bridge method there and pass device instance?
// > Would this also create a ciruclar reference in Interface class
// > since its stored via `Object.defineProperty(this, "stream",...);`
let stream = new InterfaceStream({
// duplex stream options
emitClose: false
Expand All @@ -50,6 +58,21 @@ module.exports = class Device extends Item {
let iface = new Interface(obj, stream);


// inject bridge method into interface instance
// passing deivce instance into Interface class, creates a ciruclar reference
// TODO: Move this into "interfaceStream" (needs to be refactored)
// NOTE: remove "device" for bridging requests (only needed in connector)?
// > See: https://github.com/OpenHausIO/connector/issues/54
// > When done, "device" property can be removed, and the `.bridge()` method can be moved into Interface class
injectMethod(iface, "bridge", (cb) => {
return Interface._bridge({
events: scope.events,
interface: iface,
device: this._id
}, cb);
});


// "hide" stream behind iface object
// so we can use the interface object
// as duplex stream
Expand Down
79 changes: 79 additions & 0 deletions components/devices/class.interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ const Joi = require("joi");
const { Agent } = require("http");
const mongodb = require("mongodb");
const { Transform, Duplex } = require("stream");
const { randomUUID } = require("crypto");
//const path = require("path");

//const Adapter = require("./class.adapter.js");


const timeout = require("../../helper/timeout.js");
const promisfy = require("../../helper/promisify.js");


/**
* @description
Expand Down Expand Up @@ -372,5 +381,75 @@ module.exports = class Interface {
}


// bridge methods connects adapter with the underlaying network socket
// create a `.socket()` method that returns the palin websocket stream
static _bridge({ device, interface: iface, events }, cb) {
return promisfy((done) => {

console.log("Bridge request, iface", iface, device);

// create a random uuid
// used as identifier for responses
let uuid = randomUUID();
//let uuid = "4c6de542-f89f-42ac-a2b5-1c26f9e68d73";


// timeout after certain time
// no connector available, not mocks or whatever reaseon
let caller = timeout(5000, (timedout, duration, args) => {
if (timedout) {
done(new Error("TIMEDOUT"));
} else {
done(null, args[0]);
}
});


// socket response handler
// listen for uuid and compare it with generated
let handler = ({ stream, type, uuid: id, socket }) => {
if (uuid === id && type === "response" && socket) {

console.log("adapter", iface.adapter);

/*
// create adapter stack here
// pass adapter stack as caller argument
//caller(stack);
let stack = iface.adapter.map((name) => {
try {
return require(path.join(process.cwd(), "adapter", `${name}.js`))();
} catch (err) {
console.error(`Error in adapter "${name}" `, err);
}
});
console.log("stack", stack);
stream = new Adapter(stack, stream, {
emitClose: false,
end: false
});
console.log("stream", stream)
*/

caller(stream);

}
};

events.on("socket", handler);

events.emit("socket", {
uuid,
device,
interface: iface._id,
type: "request"
});

}, cb);
}


};
9 changes: 9 additions & 0 deletions components/plugins/class.plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,15 @@ module.exports = class Plugin extends Item {
let init = (dependencies, cb) => {
try {

// NOTE: Monkey patch ready/abort method to init?
// A plugin could siganlize if its ready or needs to be restarted
/*
let init = new Promise((resolve, reject) => {
init.ready = resolve;
init.abort = reject;
});
*/

const granted = dependencies.every((c) => {
if (this.intents.includes(c)) {

Expand Down
35 changes: 35 additions & 0 deletions helper/injectMethod.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* @function injectMethod
* Add a method to a given object into the prototype
* Default values are set to the exact same values when the method would be defined in the object/class body
*
* @param {Object} obj Object to add property to
* @param {String} prop The property name
* @param {*} value Value of the property
* @param {Object} [options={}] Property descriptor options
* @param {Boolean} [options.writable=true]
* @param {Boolean} [options.enumerable=false]
* @param {Boolean} [options.configurable=true]
*
* @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#description
*/

function injectMethod(obj, prop, value, options = {}) {

if (!(value instanceof Function)) {
throw new TypeError(`Value must be a function, received ${typeof value}`);
}

// NOTE: Setting on prototype of given object, breaks iface.bridge()...
// Object.defineProperty(Object.getPrototypeOf(obj), prop, {
Object.defineProperty(obj, prop, {
value,
writable: true,
enumerable: false,
configurable: true,
...options
});

}

module.exports = injectMethod;
46 changes: 26 additions & 20 deletions helper/request.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const url = require("url");

const promisify = require("./promisify.js");

/**
* Does a http request
* @param {*} uri
Expand All @@ -12,6 +14,11 @@ const url = require("url");
*/
function perform(uri, options, cb) {

if(!options && !cb){
options = {};
cb = () => {};
}

let { protocol } = new url.URL(uri);

if (!["http:", "https:"].includes(protocol)) {
Expand Down Expand Up @@ -51,7 +58,9 @@ function perform(uri, options, cb) {
cb(null, {
headers: res.headers,
status: res.statusCode,
body
body,
res,
req: request
});

});
Expand Down Expand Up @@ -84,17 +93,13 @@ function perform(uri, options, cb) {
* @returns {http.ClientRequest} https://nodejs.org/dist/latest-v16.x/docs/api/http.html#class-httpclientrequest
*/
module.exports = function request(uri, options, cb) {
function request(uri, options, cb) {

if (!cb && options instanceof Function) {
cb = options;
options = {};
}

if (!cb) {
cb = () => { };
}

options = Object.assign({
method: "GET",
body: "",
Expand All @@ -103,25 +108,26 @@ module.exports = function request(uri, options, cb) {
setKeepAliveHeader: true
}, options);

return promisify((done) => {
perform(uri, options, (err, result) => {
if (err) {

return perform(uri, options, (err, result) => {
if (err) {

cb(err);

} else {

if (options.followRedirects && result.status >= 300 && result.status < 400) {

perform(result.headers.location, options, cb);
done(err);

} else {

cb(null, result);
if (options.followRedirects && result.status >= 300 && result.status < 400 && result.headers?.location) {
perform(result.headers.location, options, done);
} else {
done(null, result);
}

}
});
}, cb);

}
});
}

};
module.exports = Object.assign(request, {
perform
});
Loading

0 comments on commit 500f0b8

Please sign in to comment.