Create basic scaffolding of working CLI interface
This commit is contained in:
@@ -12,10 +12,9 @@ export abstract class Adapter<C = unknown> {
|
||||
}
|
||||
|
||||
#validateRequiredFields(config: Partial<C>): C {
|
||||
for(const field of this.requiredFields) {
|
||||
if (!config[field]) {
|
||||
throw new Error(`Required config field '${field.toString()}' is missing in ${this.name} profile`);
|
||||
}
|
||||
const missingField = this.requiredFields.find(field => !config.hasOwnProperty(field));
|
||||
if (missingField) {
|
||||
throw new Error(`The required '${missingField.toString()}' is missing in '${this.name}' source config`);
|
||||
}
|
||||
|
||||
return config as C;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { program } from "commander";
|
||||
|
||||
import { CLIOptions } from "@types";
|
||||
import { CLIOptions, ImportOptions } from "@types";
|
||||
import { parseConfig } from "../config";
|
||||
import { importThings } from "../importer";
|
||||
|
||||
@@ -10,6 +10,9 @@ const getOptions = () => program
|
||||
.requiredOption("-c, --config <file>", "sets the path to the YAML file with configuration")
|
||||
.option("-x, --set <arg>", "overrides the config option for this specific run (arg: <key>=<name>, i.e. mqtt.brokerURL=mqtt://localhost:1883", (v: string, prev: string[]) => prev.concat([v]), [])
|
||||
.option("-s, --sources [source...]", "forces the given sources to be queried for new things")
|
||||
.option("-f, --force", "overrides the existing things with new schemas", false)
|
||||
.option("-n, --dry-run", "disables all mutations against OpenHAB (data creating, updating or deletion), only data retrieval will be allowed", false)
|
||||
.option("-j, --json", "prints things to be imported as a JSON", false)
|
||||
.parse()
|
||||
.opts<CLIOptions>();
|
||||
|
||||
@@ -36,6 +39,9 @@ export const run = async () => {
|
||||
});
|
||||
}
|
||||
|
||||
const things = await importThings(config, options.sources ?? []);
|
||||
console.log(things);
|
||||
const things = await importThings(config, options);
|
||||
|
||||
if(options.json) {
|
||||
console.log(JSON.stringify(things, undefined, 3));
|
||||
}
|
||||
}
|
||||
@@ -2,4 +2,13 @@ import { Config } from '@types';
|
||||
import { readFileSync } from 'fs';
|
||||
import { parse } from 'yaml';
|
||||
|
||||
export const parseConfig = (filePath: string) => parse(readFileSync(filePath, 'utf8')) as Config;
|
||||
export const parseConfig = (filePath: string) => {
|
||||
const config = parse(readFileSync(filePath, 'utf8')) as Partial<Config>;
|
||||
const missingField = ['openHAB', 'sources'].find(field => !config.hasOwnProperty(field));
|
||||
|
||||
if (missingField) {
|
||||
throw new Error(`The required '${missingField}' is missing in global config`);
|
||||
}
|
||||
|
||||
return config as Config;
|
||||
};
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import axios from "axios";
|
||||
import { Thing, Config } from "@types";
|
||||
import { Config, ImportOptions } from "@types";
|
||||
import { adapters } from "../adapters";
|
||||
import { DummyOpenHAB, OpenHAB } from "../openhab";
|
||||
|
||||
|
||||
export const importThings = async (config: Config, sources: string[]) => {
|
||||
const openhab = axios.create({
|
||||
baseURL: config.baseURL,
|
||||
headers: {
|
||||
Authorization: `Bearer ${config.token}`
|
||||
}
|
||||
});
|
||||
export const importThings = async (config: Config, options: ImportOptions) => {
|
||||
const OH = options.dryRun ? DummyOpenHAB : OpenHAB;
|
||||
const openhab = new OH(config.openHAB);
|
||||
|
||||
const sources = options.sources ?? Object.keys(config.sources);
|
||||
|
||||
const things = (await Promise.all(sources.map(source => {
|
||||
const { type, config: cfg } = config.sources[source];
|
||||
@@ -23,17 +21,13 @@ export const importThings = async (config: Config, sources: string[]) => {
|
||||
return new constructor(source, cfg).loadThings();
|
||||
}))).flat();
|
||||
|
||||
if (config.override) {
|
||||
// things.forEach(t => openhab.delete(`/things/${t.UID}`));
|
||||
if (options.force) {
|
||||
await openhab.deleteThings(...things.map(t => t.UID));
|
||||
}
|
||||
|
||||
// const getThingsResponse = await openhab.get<Thing[]>('/things');
|
||||
// const existingThingsUIDs = getThingsResponse.data.map(t => t.UID);
|
||||
|
||||
// const thingsToImport = things.filter(t => !existingThingsUIDs.includes(t.UID));
|
||||
const existingThings = await openhab.getThings();
|
||||
const existingThingsUIDs = existingThings.map(t => t.UID);
|
||||
const thingsToImport = things.filter(t => !existingThingsUIDs.includes(t.UID));
|
||||
|
||||
// openhab.post('/things', thingsToImport);
|
||||
|
||||
// return thingsToImport;
|
||||
return things;
|
||||
return thingsToImport;
|
||||
}
|
||||
42
src/openhab/index.ts
Normal file
42
src/openhab/index.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { OpenHABConfig, Thing } from "@types";
|
||||
import axios, { AxiosInstance } from "axios";
|
||||
|
||||
export class OpenHAB {
|
||||
#api: AxiosInstance;
|
||||
|
||||
constructor({ baseURL, token }: Partial<OpenHABConfig>) {
|
||||
if (!baseURL || !token) {
|
||||
throw new Error(`Both 'baseURL' and 'token' properties are required on OpenHAB configuration.`);
|
||||
}
|
||||
|
||||
this.#api = axios.create({
|
||||
baseURL: `${baseURL}/rest`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async getThings(): Promise<Thing[]> {
|
||||
const response = await this.#api.get<Thing[]>('/things');
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async deleteThings(...UIDs: string[]): Promise<void> {
|
||||
await Promise.all(UIDs.map(uid => this.#api.delete(`/things/${uid}`)));
|
||||
}
|
||||
|
||||
async createThings(...things: Thing[]): Promise<Thing[]> {
|
||||
const response = await this.#api.post<Thing[]>('/things');
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export class DummyOpenHAB extends OpenHAB {
|
||||
override async createThings(...things: Thing[]): Promise<Thing[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
override async deleteThings(...UIDs: string[]): Promise<void> {
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
export type CLIOptions = {
|
||||
import { ImportOptions } from "./importer";
|
||||
|
||||
export type CLIOptions = ImportOptions & {
|
||||
config: string;
|
||||
set: string[];
|
||||
sources?: string[];
|
||||
json: boolean;
|
||||
};
|
||||
@@ -1,8 +1,10 @@
|
||||
export type Config = {
|
||||
baseURL: string;
|
||||
token: string;
|
||||
import { OpenHABConfig } from "./openhab";
|
||||
|
||||
export type Config = {
|
||||
override?: boolean;
|
||||
|
||||
openHAB: OpenHABConfig;
|
||||
|
||||
sources: Record<string, SourceConfig>;
|
||||
};
|
||||
|
||||
|
||||
5
src/types/importer.ts
Normal file
5
src/types/importer.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export type ImportOptions = {
|
||||
force: boolean;
|
||||
dryRun: boolean;
|
||||
sources?: string[];
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from "./openhab";
|
||||
export * from "./z2m";
|
||||
export * from "./config";
|
||||
export * from "./cli";
|
||||
export * from "./cli";
|
||||
export * from "./importer";
|
||||
@@ -1,3 +1,8 @@
|
||||
export type OpenHABConfig = {
|
||||
baseURL: string;
|
||||
token: string;
|
||||
};
|
||||
|
||||
export type Thing = {
|
||||
label: string;
|
||||
bridgeUID: string;
|
||||
|
||||
Reference in New Issue
Block a user