Skip to content

Commit

Permalink
Import ServiceClient from js-service-client (#21)
Browse files Browse the repository at this point in the history
Remove the ServiceClient implementation from this package and import
from `@amrc-factoryplus/service-client` instead.

This is a backwards-incompatible change! See the README.
  • Loading branch information
amrc-benmorrow authored Apr 4, 2024
2 parents d09daed + 1476529 commit 48b58d7
Show file tree
Hide file tree
Showing 22 changed files with 58 additions and 1,754 deletions.
136 changes: 38 additions & 98 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,99 +1,36 @@
> **Note**
> The AMRC Connectivity Stack is an open-source implementation of the AMRC's [Factory+ Framework](https://factoryplus.app.amrc.co.uk/).
This is a NodeJS library for writing clients for Factory+. It was used extensively when building the AMRC Connectivity Stack.
This is a NodeJS library for writing clients for Factory+. It was used
extensively when building the AMRC Connectivity Stack. This library is
now being cut down into a compatibility shim for existing code; new code
trying to consume Factory+ services should use
`@amrc-factoryplus/service-client` instead.

## Getting Started

Because this library has native code dependencies, the easiest way to use it from your code is to base your container image on the Docker images that we provide.

Start a new project by running

```bash
npm init -y
```

then follow the instructions, creating a new `Dockerfile` instead of updating an existing one.

### Updating your Dockerfile

If your code currently has a basic Node.js Dockerfile which looks like this:

```dockerfile
FROM node:lts-alpine

RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
WORKDIR /home/node/app

COPY package*.json ./
USER node
RUN npm install --save=false

COPY --chown=node . .

CMD npm start
```

then you need to replace it with one which looks like this, setting `utility_ver` to the desired version:

```dockerfile
ARG utility_prefix=ghcr.io/amrc-factoryplus/utilities
ARG utility_ver=v1.0.6
## Compatibility

FROM ${utility_prefix}-build:${utility_ver} AS build
Version 2.0.0 of this package has broken backwards compatibility as
follows:

# Install the node application on the build container where we can
# compile the native modules.
RUN install -d -o node -g node /home/node/app
WORKDIR /home/node/app
USER node
COPY package*.json ./
RUN npm install --save=false
COPY . .
* The method `basic_sparkplug_node` on the MQTT interface now returns a
Promise and must be awaited.
* The Debug class no longer reads `VERBOSE` from the environment, the
option must be supplied explicitly.
* The WebAPI class now requires a `verbose` option to enable logging.

FROM ${utility_prefix}-run:${utility_ver}
Further versions in the 2.x series are intended only to support the
existing ACS codebase and may break backwards compat further as needed
to migrate the code out into other packages. If you need functionality
from this package which is not provided by
`@amrc-factoryplus/service-client`, please speak to us first.

# Copy across from the build container.
WORKDIR /home/node/app
COPY --from=build --chown=root:root /home/node/app ./

USER node
CMD npm start
```

This makes the following changes from the original:

* The build is now multi-stage, because we need compilers and so on for the build stage which we don't need at runtime.
This means that we do most of the building in one container, and then just copy the results across into a fresh
container at the end.

* Both stages of the build are based on the Docker images provided for this library. These images include the tools
needed to build the library and the native libraries needed to run it. They also set up npm to reference the NPM
registry for `@amrc-factoryplus` packages.

* The build stage runs the build as user `node`, but the code is copied across to the run stage owned by `root`. This
improves security but may cause problems if your app assumes it can write to its working directory. This is, in
general, a bad Idea for a Docker container (you should be writing to a volume probably), but if necessary the commands
can be adjusted to change the permissions.

If you have a more complicated Dockerfile you will need to adjust this appropriately. Try to do as much work as possible
in the build container, and then just copy the results across into the runtime container. This will make the final
images smaller.

### Adding to `package.json`

You now need to add the following entry to your `package.json`:

```
{
"dependencies": {
"@amrc-factoryplus/utilities": "^1.0.0"
}
}
```
## Getting Started

The library will install on Windows; however we do not have access to the GSSAPI libraries on Windows so most of the
functionality will not work. However this allows `npm update` to work at least.
This library has native code dependencies: it requires GSSAPI libraries
and (for ACS use) a Postgres library built with GSSAPI support. The most
straightforward way to meet these is to build a Docker image using the
base images from the ACS repository. See that repository for example
Dockerfiles.

## Using the package

Expand Down Expand Up @@ -124,10 +61,12 @@ If you are using Typescript then the ESM import should work fine. There are curr
### Third-party libraries

```js
import { MQTT, GSS, Pg, SpB, fetch } from "@amrc-factoryplus/utilities";
import { MQTT, GSS, Pg, SpB } from "@amrc-factoryplus/utilities";
```

These are re-exports of third party modules. They are re-exported here partly to provide protection from future changes to the third-party modules, and partly to work around bugs or problems with importing.
These are re-exports of third party modules. They are re-exported here
partly to provide protection from future changes to the third-party
modules, and partly to work around bugs or problems with importing.

- [Full Third-Party Library Documentation](./docs/deps.md)

Expand Down Expand Up @@ -205,17 +144,18 @@ Classes useful in implementing an HTTP service confirming to the Factory+ spec.

- [Full Web API Documentation](./docs/webapi.md)

### Deprecated APIs

```js
import { debug, secrets, gss_mqtt } from "@amrc-factoryplus/utilities";
```
### Removed APIs

These are deprecated APIs.
As of version 2.0.0 these exports have been removed:

* `debug` has been replaced by the Debug object.
* `secrets` provides support for reading from Docker secrets; since moving to Kubernetes this has been redundant.
* `gss_mqtt` connects to an MQTT server with GSSAPI authentication. It is better to use a ServiceClient instead, as this will discover the MQTT server via the Directory.
* `secrets` provided support for reading from Docker secrets; since
moving to Kubernetes this has been redundant.
* `gss_mqtt` connected to an MQTT server with GSSAPI authentication. It
is better to use a ServiceClient instead, as this will discover the
MQTT server via the Directory. If it is really necessary the MQTT URL
can be overridden in the ServiceClient configuration.
* `fetch` has been removed as an export.

### Coding Style

Expand All @@ -225,4 +165,4 @@ These are deprecated APIs.

## Contributing

Development practice follows [GitHub flow](https://guides.github.com/introduction/flow/).
Development practice follows [GitHub flow](https://guides.github.com/introduction/flow/).
9 changes: 4 additions & 5 deletions lib/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
* Copyright 2021 AMRC
*/

import timers from "node:timers/promises";
import timers from "timers/promises";

import { Debug } from "@amrc-factoryplus/service-client";

import { Pg } from "./deps.js";
import { Debug } from "./debug.js";

const debug = new Debug();
const debug = new Debug({ verbose: process.env.VERBOSE });

export class DB {
constructor (opts) {
this.isolation = opts.isolation ?? "read committed";
this.readonly = opts.readonly;
this.deferrable = opts.deferrable;
this.verbose = opts.verbose;
this.version = opts.version;

this.pool = new Pg.Pool();
Expand Down Expand Up @@ -69,7 +69,6 @@ export class DB {
const isolation = opts.isolation ?? this.isolation;
const readonly = opts.readonly ?? this.readonly;
const deferrable = opts.deferrable ?? this.deferrable;
const verbose = opts.verbose ?? this.verbose;

const begin = [
"begin",
Expand Down
2 changes: 1 addition & 1 deletion lib/debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Copyright 2022 AMRC.
*/

import util from "node:util";
import util from "util";

export class Debug {
constructor (opts) {
Expand Down
27 changes: 0 additions & 27 deletions lib/deps.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,6 @@

import { createRequire } from "module";

/* 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";

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

/* The 'pg' module is not properly ESM-compatible. While pure-JS use can
* be accomplished with `import Pg from "pg"` this does not provide
* access to the native (libpq) bindings. They are only available via
Expand All @@ -30,19 +19,3 @@ const require = createRequire(import.meta.url);
const pg_cjs = require("pg");
export const Pg = pg_cjs.native ?? pg_cjs;

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

/* We have to go round the houses a bit here... */
import got from "got";
import { createFetch } from "got-fetch";

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

/* Bug fix */
export function fetch (url, opts) {
return got_fetch(`${url}`, opts);
}
10 changes: 2 additions & 8 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,13 @@
* Copyright 2022 AMRC.
*/

export * from "@amrc-factoryplus/service-client";

export * from "./db.js";
export * from "./debug.js";
export * from "./deps.js";
export * as secrets from "./secrets.js";
export * from "./service-client.js";
export * from "./sparkplug/util.js";
export * from "./util.js";
export * as UUIDs from "./uuids.js";
export * from "./webapi.js";
export * from "./service/service-interface.js"

/* Compat export; better is to go via ServiceClient. */
export { gss_mqtt } from "./service/mqtt.js";

import { pkgVersion } from "./util.js";

Expand Down
40 changes: 0 additions & 40 deletions lib/secrets.js

This file was deleted.

Loading

0 comments on commit 48b58d7

Please sign in to comment.