dbus-sdk
Version:
A Node.js SDK for interacting with DBus, enabling seamless service calling and exposure with TypeScript support
212 lines (211 loc) • 10.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DBusInterface = void 0;
const DBusPropertyAccess_1 = require("./lib/enums/DBusPropertyAccess");
const Errors_1 = require("./lib/Errors");
const DBusSignedValue_1 = require("./lib/DBusSignedValue");
class DBusInterface {
#signalEmitter;
/**
* Constructor for DBusInterface.
* Initializes the DBus interface with the provided options and sets up references to the DBus connection,
* service, object, and introspection data.
* @param opts - Configuration options for the DBus interface.
*/
constructor(opts) {
this.opts = opts;
this.name = this.opts.iface;
this.dbus = this.opts.dbus;
this.service = this.opts.dbusService;
this.object = this.opts.dbusObject;
this.introspectInterface = this.opts.introspectInterface;
}
/**
* Converts a parameter object to an array of arguments based on introspection data.
* Extracts input parameters from the provided parameter object, mapping them to the expected order of arguments
* for a given method as defined in the introspection metadata. If a required parameter is missing, an error is thrown.
* @param methodInfo - The introspection metadata of the method, containing argument definitions.
* @param inputParameterObject - The parameter object containing named arguments to be mapped.
* @returns An array of arguments extracted from the parameter object, ordered according to the method's signature.
* @throws {NotEnoughParamsError} If a required parameter is missing from the input object.
*/
objectifiedParameterToArguments(methodInfo, inputParameterObject) {
const inputParameters = methodInfo.arg
.filter((arg) => arg.direction === 'in')
.map((arg, index) => ({
...arg,
name: arg.name ? arg.name : `param${index}`
}));
const args = [];
for (const inputParameter of inputParameters) {
const name = inputParameter.name;
const arg = inputParameterObject[name];
if (arg === undefined)
throw new Errors_1.NotEnoughParamsError(`Parameter "${name}" is required`);
args.push(arg);
}
return args;
}
/**
* Getter for methods with reply mode.
* Dynamically creates an object containing methods that expect a reply from the DBus invocation.
* Each method handles input arguments with type signatures and returns the result of the invocation.
* @returns A record of method names mapped to their callable implementations with reply mode.
*/
get method() {
const methods = {};
this.listMethods().forEach((methodInfo) => {
methods[methodInfo.name] = async (...args) => {
const types = methodInfo.arg
.filter((arg) => arg.direction === 'in')
.map((arg) => arg.type);
const signature = types.length ? types.join('') : undefined;
const inputArguments = signature ? args.length ? DBusSignedValue_1.DBusSignedValue.parse(signature, args.length > 1 ? args : args[0]) : [] : [];
const result = await this.dbus.invoke({
service: this.service.name,
objectPath: this.object.name,
interface: this.name,
method: methodInfo.name,
signature: signature,
args: inputArguments
});
if (result.length > 1)
return result;
return result[0];
};
});
return methods;
}
/**
* Getter for methods with reply mode using parameter object pattern.
* Dynamically creates an object containing methods that expect a reply from the DBus invocation.
* Each method accepts a single parameter object containing named arguments, which are mapped to the expected input parameters
* based on the introspection data. This approach enhances readability and avoids errors due to parameter order.
* @returns A record of method names mapped to their callable implementations with reply mode, accepting a parameter object.
*/
get parameterObjectifyMethod() {
const methods = {};
this.listMethods().forEach((methodInfo) => {
methods[methodInfo.name] = async (parameterObject) => {
const args = this.objectifiedParameterToArguments(methodInfo, parameterObject ? parameterObject : {});
return await this.method[methodInfo.name](...args);
};
});
return methods;
}
/**
* Getter for methods with no-reply mode.
* Dynamically creates an object containing methods that do not expect a reply from the DBus invocation.
* These methods are used for fire-and-forget calls.
* @returns A record of method names mapped to their callable implementations with no-reply mode.
*/
get noReplyMethod() {
const noReplyMethods = {};
this.listMethods().forEach((methodInfo) => {
noReplyMethods[methodInfo.name] = (...args) => {
const types = methodInfo.arg
.filter((arg) => arg.direction === 'in')
.map((arg) => arg.type);
this.dbus.invoke({
service: this.service.name,
objectPath: this.object.name,
interface: this.name,
method: methodInfo.name,
signature: types.length ? types.join('') : undefined,
args: args
}, true);
};
});
return noReplyMethods;
}
/**
* Getter for methods with no-reply mode using parameter object pattern.
* Dynamically creates an object containing methods that do not expect a reply from the DBus invocation.
* Each method accepts a single parameter object containing named arguments, which are mapped to the expected input parameters
* based on the introspection data. This approach enhances readability and avoids errors due to parameter order.
* These methods are used for fire-and-forget calls.
* @returns A record of method names mapped to their callable implementations with no-reply mode, accepting a parameter object.
*/
get parameterObjectifyNoReplyMethod() {
const noReplyMethods = {};
this.listMethods().forEach((methodInfo) => {
noReplyMethods[methodInfo.name] = (parameterObject) => {
const args = this.objectifiedParameterToArguments(methodInfo, parameterObject ? parameterObject : {});
return this.noReplyMethod[methodInfo.name](...args);
};
});
return noReplyMethods;
}
/**
* Getter for properties of the DBus interface.
* Dynamically creates an object containing property operations (get/set) for each property defined in the introspection data.
* Access control is enforced based on property access mode (read/write/readwrite).
* @returns A record of property names mapped to their get/set operations.
*/
get property() {
const properties = {};
this.listProperties().forEach((propertyInfo) => {
properties[propertyInfo.name] = {
set: async (value) => {
if (![DBusPropertyAccess_1.DBusPropertyAccess.WRITE, DBusPropertyAccess_1.DBusPropertyAccess.READWRITE].includes(propertyInfo.access))
throw new Errors_1.AccessPropertyForbiddenError(`Access to attribute ${propertyInfo.name} is prohibited, and its access mode is ${propertyInfo.access}`);
return this.dbus.setProperty({
service: this.service.name,
objectPath: this.object.name,
interface: this.name,
property: propertyInfo.name,
value: value
});
},
get: async () => {
if (![DBusPropertyAccess_1.DBusPropertyAccess.READ, DBusPropertyAccess_1.DBusPropertyAccess.READWRITE].includes(propertyInfo.access))
throw new Errors_1.AccessPropertyForbiddenError(`Access to attribute ${propertyInfo.name} is prohibited, and its access mode is ${propertyInfo.access}`);
return this.dbus.getProperty({
service: this.service.name,
objectPath: this.object.name,
interface: this.name,
property: propertyInfo.name
});
}
};
});
return properties;
}
/**
* Getter for the DBus signal emitter.
* Lazily initializes and returns a signal emitter for emitting signals associated with this interface.
* @returns A DBusSignalEmitter instance for handling signal emissions.
*/
get signal() {
if (!this.#signalEmitter)
this.#signalEmitter = this.dbus.createSignalEmitter({
service: this.service.name,
uniqueName: this.service.uniqueName,
objectPath: this.object.name,
interface: this.name
});
return this.#signalEmitter;
}
/**
* Lists all methods defined in the introspection data for this interface.
* @returns An array of IntrospectMethod objects representing the methods of this interface.
*/
listMethods() {
return this.introspectInterface.method;
}
/**
* Lists all properties defined in the introspection data for this interface.
* @returns An array of IntrospectProperty objects representing the properties of this interface.
*/
listProperties() {
return this.introspectInterface.property;
}
/**
* Lists all signals defined in the introspection data for this interface.
* @returns An array of IntrospectSignal objects representing the signals of this interface.
*/
listSignals() {
return this.introspectInterface.signal;
}
}
exports.DBusInterface = DBusInterface;