Skip to content

Commit

Permalink
Separate out the ServiceClient (#1)
Browse files Browse the repository at this point in the history
Pull the ServiceClient code out from the rest of
@amrc-factoryplus/utilities.

This still includes more code than I wanted it to, because it's
difficult to separate out the dependencies. But this package is now
webpackable.
  • Loading branch information
amrc-benmorrow authored Mar 12, 2024
2 parents 44b3f3d + 2962c59 commit 4630c60
Show file tree
Hide file tree
Showing 18 changed files with 1,758 additions and 0 deletions.
43 changes: 43 additions & 0 deletions lib/debug.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Factory+ JS Service Client
* Debugging / logging support.
* Copyright 2022 AMRC.
*/

import util from "util";

export class Debug {
constructor (opts) {
opts ??= {};
const verb = opts.verbose
?? opts.default
?? "";

this.levels = new Set();
this.suppress = new Set();
this.verbose = false;

for (const lev of verb.split(",")) {
if (lev == "1" || lev == "ALL")
this.verbose = true;
else if (lev.startsWith("!"))
this.suppress.add(lev.slice(1))
else
this.levels.add(lev);
}
}

log (level, msg, ...args) {
const want = this.verbose || this.levels.has(level);
if (!want || this.suppress.has(level))
return;

const out = util.format(msg, ...args);
const spc = " ".repeat(Math.max(0, 8 - level.length));
console.log(`${level}${spc} : ${out}`);
}

bound (level) {
return this.log.bind(this, level);
}
}
34 changes: 34 additions & 0 deletions lib/deps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Factory+ NodeJS Utilities
* Re-exports of libraries we use.
* Copyright 2024 AMRC
*/

/* No GSS on Windows. */
export const GSS = await
import("gssapi.js")
.then(mod => mod.default)
.catch(e => undefined);

/* Annoying re-export syntax... If you find yourself having to document
* 'you can't do `export Foo from "foo"`' then maybe you should design
* the syntax so you can... ? */
export { default as MQTT } from "mqtt";

export async function build_node_fetch () {
/* We have to go round the houses a bit here... */
const { got } = await import("got");
const { createFetch } = await import("got-fetch");

const configured_got = got.extend({
cacheOptions: { shared: false },
});
const got_fetch = createFetch(configured_got);

/* Bug fix */
return (url, opts) => got_fetch(`${url}`, opts);
}

import sparkplug_payload from "sparkplug-payload";
export const SpB = sparkplug_payload.get("spBv1.0");

10 changes: 10 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export * from "./debug.js";
export * from "./service-client.js";
export * from "./service/service-interface.js"

export * as UUIDs from "./uuids.js";

export * from "./sparkplug/util.js";

/* XXX I don't want to export these; maybe I want a /deps library? */
export { GSS, MQTT, SpB } from "./deps.js";
86 changes: 86 additions & 0 deletions lib/service-client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Factory+ NodeJS Utilities
* Service client library.
* Copyright 2022 AMRC
*/

import { Debug } from "./debug.js";
import { Service } from "./uuids.js";

import Auth from "./service/auth.js";
import CmdEsc from "./service/cmdesc.js";
import ConfigDB from "./service/configdb.js";
import Directory from "./service/directory.js";
import Discovery from "./service/discovery.js";
import Fetch from "./service/fetch.js";
import Git from "./service/git.js";
import MQTTInterface from "./service/mqtt.js";

function opts_from_env (env) {
const opts = `
AUTHN_URL
CONFIGDB_URL
DIRECTORY_URL
MQTT_URL
ROOT_PRINCIPAL
SERVICE_USERNAME:username
SERVICE_PASSWORD:password
VERBOSE
` .split(/\s+/)
.map(v => v.includes(":") ? v.split(":") : [v, v.toLowerCase()])
.filter(v => v[0] in env)
.map(v => [v[1], env[v[0]]]);
return Object.fromEntries(opts);
}

export class ServiceClient {
constructor (opts) {
opts ??= {};
this.opts = "env" in opts
? { ...opts_from_env(opts.env), ...opts }
: opts;
delete this.opts.env;

this.debug = new Debug(opts);
}

async init () {
return this;
}

static define_interfaces (...interfaces) {
for (const [name, klass, methlist] of interfaces) {
Object.defineProperty(this.prototype, name, {
configurable: true,
get () { return this[`_${name}`] ??= new klass(this); },
});

const meths = methlist.split(/\s+/).filter(s => s.length);
for (const meth of meths) {
const [mine, theirs] = meth.split(":");
Object.defineProperty(this.prototype, mine, {
configurable: true,
writable: true,
value (...args) {
return this[name][theirs ?? mine](...args);
},
});
}
}
}
}

/* The methods delegeted here from the ServiceClient should be
* considered backwards-compatible shims. Future service methods will
* mostly be defined only on the service interface. */
ServiceClient.define_interfaces(
["Auth", Auth, `check_acl fetch_acl resolve_principal`],
["CmdEsc", CmdEsc, ``],
["ConfigDB", ConfigDB, `fetch_configdb:get_config`],
["Directory", Directory, ``],
["Discovery", Discovery,
`set_service_url set_service_discovery service_url service_urls`],
["Fetch", Fetch, `fetch`],
["Git", Git, ``],
["MQTT", MQTTInterface, `mqtt_client`],
);
Loading

0 comments on commit 4630c60

Please sign in to comment.