Skip to content

Commit 861bcad

Browse files
committed
refactor(cli): improve configuration building and keep it sync with json schema
1 parent 6f5953c commit 861bcad

14 files changed

Lines changed: 186 additions & 166 deletions

package-lock.json

Lines changed: 19 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
}
1515
},
1616
"scripts": {
17-
"build": "tsc -b && npm run build -w packages/browser-bundle",
17+
"build": "npm run build:json -w packages/cli && tsc -b && npm run build -w packages/browser-bundle",
1818
"pretest": "npm run build",
1919
"start": "cd packages/cli && npm run start",
2020
"debug": "cd packages/cli && npm run debug",

packages/cli/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
src/generated

packages/cli/eslint.config.mjs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { defineConfig, globalIgnores } from "eslint/config";
2+
import baseConfig from "../../eslint.config.mjs";
3+
4+
export default defineConfig([
5+
baseConfig,
6+
globalIgnores(["src/generated/**.ts", "./import-json.js"])
7+
])

packages/cli/import-json.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const { readFileSync, writeFileSync } = require("fs");
2+
3+
const schema = readFileSync("./src/wot-servient-schema.conf.json", "utf8");
4+
const package = readFileSync("./package.json", "utf8");
5+
const { version } = JSON.parse(package);
6+
7+
writeFileSync(
8+
"./src/generated/wot-servient-schema.conf.ts",
9+
`const schema = ${schema.trimEnd()} as const \nexport default schema;`
10+
);
11+
writeFileSync("./src/generated/version.ts", `const version = "${version}" as const \nexport default version;`);

packages/cli/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
"lodash": "^4.17.21"
3434
},
3535
"scripts": {
36-
"build": "tsc -b",
36+
"build:json": "node import-json.js",
37+
"build": "npm run build:json && tsc -b",
3738
"start": "ts-node src/cli.ts",
3839
"debug": "node -r ts-node/register --inspect-brk=9229 src/cli.ts",
3940
"lint": "eslint .",
@@ -47,6 +48,7 @@
4748
"homepage": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/cli#readme",
4849
"keywords": [],
4950
"devDependencies": {
50-
"@types/lodash": "^4.14.199"
51+
"@types/lodash": "^4.14.199",
52+
"json-schema-to-ts": "^3.1.1"
5153
}
5254
}

packages/cli/src/cli-default-servient.ts

Lines changed: 6 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable @typescript-eslint/no-explicit-any */
21
/********************************************************************************
32
* Copyright (c) 2018 Contributors to the Eclipse Foundation
43
*
@@ -29,89 +28,34 @@ import { ThingModelHelpers } from "@thingweb/thing-model";
2928
import { createContext, Script } from "vm";
3029
import { CompilerFunction } from "./compiler-function";
3130
import { LogLevel, setLogLevel } from "./utils/set-log-level";
31+
import { ConfigurationAfterDefaults } from "./configuration";
3232

3333
const { debug, error, info } = createLoggers("cli", "cli-default-servient");
3434

35-
// Helper function needed for `mergeConfigs` function
36-
function isObject(item: unknown) {
37-
return item != null && typeof item === "object" && !Array.isArray(item);
38-
}
39-
40-
/**
41-
* Helper function merging default parameters into a custom config file.
42-
*
43-
* @param {object} target - an object containing default config parameters
44-
* @param {object} source - an object containing custom config parameters
45-
*
46-
* @return {object} The new config file containing both custom and default parameters
47-
*/
48-
function mergeConfigs(target: any, source: any): any {
49-
const output = Object.assign({}, target);
50-
Object.keys(source).forEach((key) => {
51-
if (!(key in target)) {
52-
Object.assign(output, { [key]: source[key] });
53-
} else {
54-
if (isObject(target[key]) && isObject(source[key])) {
55-
output[key] = mergeConfigs(target[key], source[key]);
56-
} else {
57-
Object.assign(output, { [key]: source[key] });
58-
}
59-
}
60-
});
61-
return output;
62-
}
6335
export interface ScriptOptions {
6436
argv?: Array<string>;
6537
compiler?: CompilerFunction;
6638
env?: Record<string, string>;
6739
}
6840
export default class DefaultServient extends Servient {
69-
private static readonly defaultConfig = {
70-
servient: {
71-
clientOnly: false,
72-
scriptAction: false,
73-
},
74-
http: {
75-
port: 8080,
76-
allowSelfSigned: false,
77-
},
78-
coap: {
79-
port: 5683,
80-
},
81-
};
82-
8341
private uncaughtListeners: Array<NodeJS.UncaughtExceptionListener> = [];
8442
private runtime: typeof WoT | undefined;
85-
public readonly config: any;
43+
public readonly config: ConfigurationAfterDefaults;
8644
// current log level
8745
public logLevel = "info";
8846

89-
public constructor(clientOnly: boolean, config?: any) {
47+
public constructor(config: ConfigurationAfterDefaults) {
9048
super();
9149

92-
// init config
93-
this.config =
94-
typeof config === "object"
95-
? mergeConfigs(DefaultServient.defaultConfig, config)
96-
: DefaultServient.defaultConfig;
97-
98-
// apply flags
99-
if (clientOnly) {
100-
this.config.servient ??= {};
101-
this.config.servient.clientOnly = true;
102-
}
50+
this.config = config;
10351

10452
// load credentials from config
10553
this.addCredentials(this.config.credentials);
10654

107-
// remove secrets from original for displaying config (already added)
108-
if (this.config.credentials != null) {
109-
delete this.config.credentials;
110-
}
111-
11255
// display
11356
debug("DefaultServient configured with");
114-
debug(`${this.config}`);
57+
// remove secrets from original for displaying config
58+
debug(`%O`, { ...this.config, credentials: null });
11559

11660
// apply config
11761
if (typeof this.config.servient.staticAddress === "string") {

packages/cli/src/cli.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,22 @@ import DefaultServient, { ScriptOptions } from "./cli-default-servient";
2020
import * as path from "path";
2121
import { Command, Argument, Option } from "commander";
2222
import Ajv, { ValidateFunction } from "ajv";
23-
import ConfigSchema from "./wot-servient-schema.conf.json";
24-
import { version } from "@node-wot/core/package.json";
23+
import ConfigSchema from "./generated/wot-servient-schema.conf";
24+
import version from "./generated/version";
2525
import { createLoggers } from "@node-wot/core";
26-
import { buildConfig } from "./config-builder";
2726
import { loadCompiler, loadEnvVariables } from "./utils";
2827
import { runScripts } from "./script-runner";
2928
import { readdir } from "fs/promises";
3029
import { parseConfigFile, parseConfigParams, parseIp } from "./parsers";
3130
import { setLogLevel } from "./utils/set-log-level";
31+
import { buildConfig, buildConfigFromFile, Configuration, defaultConfiguration } from "./configuration";
32+
import { cloneDeep } from "lodash";
3233

3334
const { error, info, warn, debug } = createLoggers("cli", "cli");
3435

3536
const program = new Command();
36-
const ajv = new Ajv({ strict: true });
37-
const schemaValidator = ajv.compile(ConfigSchema) as ValidateFunction;
37+
const ajv = new Ajv({ strict: true, allErrors: true });
38+
const schemaValidator = ajv.compile(ConfigSchema) as ValidateFunction<Configuration>;
3839
const defaultFile = "wot-servient.conf.json";
3940
const baseDir = ".";
4041

@@ -127,7 +128,7 @@ program
127128
).choices(["debug", "info", "warn", "error"])
128129
)
129130
.option("-f, --config-file <file>", "load configuration from specified file", (value, previous) =>
130-
parseConfigFile(value, previous, schemaValidator)
131+
parseConfigFile(value, previous)
131132
)
132133
.option(
133134
"-p, --config-params <param...>",
@@ -148,6 +149,7 @@ program.action(async function (_, options, cmd) {
148149
if (process.env.DEBUG == null) {
149150
// by default enable error logs and warnings
150151
// user can override using command line option
152+
// or later by config file.
151153
setLogLevel(options.logLevel ?? "warn");
152154
}
153155

@@ -161,18 +163,21 @@ program.action(async function (_, options, cmd) {
161163
debug("command line environment variables", args);
162164

163165
try {
164-
const config = await buildConfig(options, defaultFilePath, env);
165-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
166-
setLogLevel((config.log as any).level ?? options.logLevel ?? "warn");
167-
servient = new DefaultServient(options.clientOnly, config);
166+
const config = await buildConfigFromFile(options, defaultFilePath, env, schemaValidator);
167+
setLogLevel(options.logLevel ?? config.logLevel);
168+
config.servient.clientOnly = options.clientOnly ?? config.servient.clientOnly;
169+
servient = new DefaultServient(config);
168170
} catch (err) {
169171
if ((err as NodeJS.ErrnoException)?.code !== "ENOENT" || options.configFile != null) {
170-
error("WoT-Servient config file error. %O", err);
172+
error("WoT-Servient configuration file error:\n%O\nClose.", err);
171173
process.exit((err as NodeJS.ErrnoException).errno ?? 1);
172174
}
173175

174176
warn(`WoT-Servient using defaults as %s does not exist`, defaultFile);
175-
servient = new DefaultServient(options.clientOnly);
177+
178+
const config = await buildConfig(options, cloneDeep(defaultConfiguration), env, schemaValidator);
179+
config.servient.clientOnly = options.clientOnly ?? config.servient.clientOnly;
180+
servient = new DefaultServient(config);
176181
}
177182

178183
await servient.start();

packages/cli/src/config-builder.ts

Lines changed: 0 additions & 42 deletions
This file was deleted.

0 commit comments

Comments
 (0)