diff --git a/sdk/base/lib/util/GetSystemSmtp.ts b/sdk/base/lib/util/GetSystemSmtp.ts index cd87a9db6..8e7db00b6 100644 --- a/sdk/base/lib/util/GetSystemSmtp.ts +++ b/sdk/base/lib/util/GetSystemSmtp.ts @@ -1,10 +1,11 @@ import { Effects } from "../Effects" +import * as T from "../types" export class GetSystemSmtp { constructor(readonly effects: Effects) {} /** - * Returns the system SMTP credentials. Restarts the service if the credentials change + * Returns the system SMTP credentials. Reruns the context from which it has been called if the underlying value changes */ const() { return this.effects.getSystemSmtp({ @@ -17,8 +18,9 @@ export class GetSystemSmtp { once() { return this.effects.getSystemSmtp({}) } + /** - * Watches the system SMTP credentials. Takes a custom callback function to run whenever the credentials change + * Watches the system SMTP credentials. Returns an async iterator that yields whenever the value changes */ async *watch() { while (true) { @@ -32,4 +34,34 @@ export class GetSystemSmtp { await waitForNext } } + + /** + * Watches the system SMTP credentials. Takes a custom callback function to run whenever the credentials change + */ + onChange( + callback: ( + value: T.SmtpValue | null, + error?: Error, + ) => void | Promise, + ) { + ;(async () => { + for await (const value of this.watch()) { + try { + await callback(value) + } catch (e) { + console.error( + "callback function threw an error @ GetSystemSmtp.onChange", + e, + ) + } + } + })() + .catch((e) => callback(null, e)) + .catch((e) => + console.error( + "callback function threw an error @ GetSystemSmtp.onChange", + e, + ), + ) + } } diff --git a/sdk/base/lib/util/getServiceInterface.ts b/sdk/base/lib/util/getServiceInterface.ts index 2e81e5ee2..44c7c6fc8 100644 --- a/sdk/base/lib/util/getServiceInterface.ts +++ b/sdk/base/lib/util/getServiceInterface.ts @@ -205,7 +205,7 @@ export class GetServiceInterface { ) {} /** - * Returns the value of Store at the provided path. Restart the service if the value changes + * Returns the requested service interface. Reruns the context from which it has been called if the underlying value changes */ async const() { const { id, packageId } = this.opts @@ -220,7 +220,7 @@ export class GetServiceInterface { return interfaceFilled } /** - * Returns the value of ServiceInterfacesFilled at the provided path. Does nothing if the value changes + * Returns the requested service interface. Does nothing if the value changes */ async once() { const { id, packageId } = this.opts @@ -234,7 +234,7 @@ export class GetServiceInterface { } /** - * Watches the value of ServiceInterfacesFilled at the provided path. Takes a custom callback function to run whenever the value changes + * Watches the requested service interface. Returns an async iterator that yields whenever the value changes */ async *watch() { const { id, packageId } = this.opts @@ -252,6 +252,36 @@ export class GetServiceInterface { await waitForNext } } + + /** + * Watches the requested service interface. Takes a custom callback function to run whenever the value changes + */ + onChange( + callback: ( + value: ServiceInterfaceFilled | null, + error?: Error, + ) => void | Promise, + ) { + ;(async () => { + for await (const value of this.watch()) { + try { + await callback(value) + } catch (e) { + console.error( + "callback function threw an error @ GetServiceInterface.onChange", + e, + ) + } + } + })() + .catch((e) => callback(null, e)) + .catch((e) => + console.error( + "callback function threw an error @ GetServiceInterface.onChange", + e, + ), + ) + } } export function getServiceInterface( effects: Effects, diff --git a/sdk/base/lib/util/getServiceInterfaces.ts b/sdk/base/lib/util/getServiceInterfaces.ts index faeb508b4..de5a8d015 100644 --- a/sdk/base/lib/util/getServiceInterfaces.ts +++ b/sdk/base/lib/util/getServiceInterfaces.ts @@ -47,7 +47,7 @@ export class GetServiceInterfaces { ) {} /** - * Returns the value of Store at the provided path. Restart the service if the value changes + * Returns the service interfaces for the package. Reruns the context from which it has been called if the underlying value changes */ async const() { const { packageId } = this.opts @@ -62,7 +62,7 @@ export class GetServiceInterfaces { return interfaceFilled } /** - * Returns the value of ServiceInterfacesFilled at the provided path. Does nothing if the value changes + * Returns the service interfaces for the package. Does nothing if the value changes */ async once() { const { packageId } = this.opts @@ -76,7 +76,7 @@ export class GetServiceInterfaces { } /** - * Watches the value of ServiceInterfacesFilled at the provided path. Takes a custom callback function to run whenever the value changes + * Watches the service interfaces for the package. Returns an async iterator that yields whenever the value changes */ async *watch() { const { packageId } = this.opts @@ -93,6 +93,36 @@ export class GetServiceInterfaces { await waitForNext } } + + /** + * Watches the service interfaces for the package. Takes a custom callback function to run whenever the value changes + */ + onChange( + callback: ( + value: ServiceInterfaceFilled[] | null, + error?: Error, + ) => void | Promise, + ) { + ;(async () => { + for await (const value of this.watch()) { + try { + await callback(value) + } catch (e) { + console.error( + "callback function threw an error @ GetServiceInterfaces.onChange", + e, + ) + } + } + })() + .catch((e) => callback(null, e)) + .catch((e) => + console.error( + "callback function threw an error @ GetServiceInterfaces.onChange", + e, + ), + ) + } } export function getServiceInterfaces( effects: Effects, diff --git a/sdk/package/lib/health/HealthCheck.ts b/sdk/package/lib/health/HealthCheck.ts index 05c1a214a..50aab97f0 100644 --- a/sdk/package/lib/health/HealthCheck.ts +++ b/sdk/package/lib/health/HealthCheck.ts @@ -1,4 +1,4 @@ -import { Effects, HealthReceipt } from "../../../base/lib/types" +import { Effects, HealthCheckId, HealthReceipt } from "../../../base/lib/types" import { HealthCheckResult } from "./checkFns/HealthCheckResult" import { Trigger } from "../trigger" import { TriggerInput } from "../trigger/TriggerInput" @@ -8,6 +8,7 @@ import { object, unknown } from "ts-matches" export type HealthCheckParams = { effects: Effects + id: HealthCheckId name: string trigger?: Trigger fn(): Promise | HealthCheckResult @@ -35,7 +36,7 @@ export function healthCheck(o: HealthCheckParams) { const { result, message } = await o.fn() await o.effects.setHealth({ name: o.name, - id: o.name, + id: o.id, result, message: message || "", }) @@ -46,7 +47,7 @@ export function healthCheck(o: HealthCheckParams) { } catch (e) { await o.effects.setHealth({ name: o.name, - id: o.name, + id: o.id, result: "failure", message: asMessage(e) || "", }) diff --git a/sdk/package/lib/store/getStore.ts b/sdk/package/lib/store/getStore.ts index 3bde46a25..131b7f7f7 100644 --- a/sdk/package/lib/store/getStore.ts +++ b/sdk/package/lib/store/getStore.ts @@ -12,7 +12,7 @@ export class GetStore { ) {} /** - * Returns the value of Store at the provided path. Restart the service if the value changes + * Returns the value of Store at the provided path. Reruns the context from which it has been called if the underlying value changes */ const() { return this.effects.store.get({ @@ -32,7 +32,7 @@ export class GetStore { } /** - * Watches the value of Store at the provided path. Takes a custom callback function to run whenever the value changes + * Watches the value of Store at the provided path. Returns an async iterator that yields whenever the value changes */ async *watch() { while (true) { @@ -48,6 +48,33 @@ export class GetStore { await waitForNext } } + + /** + * Watches the value of Store at the provided path. Takes a custom callback function to run whenever the value changes + */ + onChange( + callback: (value: StoreValue | null, error?: Error) => void | Promise, + ) { + ;(async () => { + for await (const value of this.watch()) { + try { + await callback(value) + } catch (e) { + console.error( + "callback function threw an error @ GetStore.onChange", + e, + ) + } + } + })() + .catch((e) => callback(null, e)) + .catch((e) => + console.error( + "callback function threw an error @ GetStore.onChange", + e, + ), + ) + } } export function getStore( effects: Effects, diff --git a/sdk/package/lib/util/GetSslCertificate.ts b/sdk/package/lib/util/GetSslCertificate.ts index 72bf9e22d..8ea870ffb 100644 --- a/sdk/package/lib/util/GetSslCertificate.ts +++ b/sdk/package/lib/util/GetSslCertificate.ts @@ -9,7 +9,7 @@ export class GetSslCertificate { ) {} /** - * Returns the system SMTP credentials. Restarts the service if the credentials change + * Returns the an SSL Certificate for the given hostnames if permitted. Restarts the service if it changes */ const() { return this.effects.getSslCertificate({ @@ -19,7 +19,7 @@ export class GetSslCertificate { }) } /** - * Returns the system SMTP credentials. Does nothing if the credentials change + * Returns the an SSL Certificate for the given hostnames if permitted. Does nothing if it changes */ once() { return this.effects.getSslCertificate({ @@ -27,8 +27,9 @@ export class GetSslCertificate { algorithm: this.algorithm, }) } + /** - * Watches the system SMTP credentials. Takes a custom callback function to run whenever the credentials change + * Watches the SSL Certificate for the given hostnames if permitted. Returns an async iterator that yields whenever the value changes */ async *watch() { while (true) { @@ -44,4 +45,34 @@ export class GetSslCertificate { await waitForNext } } + + /** + * Watches the SSL Certificate for the given hostnames if permitted. Takes a custom callback function to run whenever it changes + */ + onChange( + callback: ( + value: [string, string, string] | null, + error?: Error, + ) => void | Promise, + ) { + ;(async () => { + for await (const value of this.watch()) { + try { + await callback(value) + } catch (e) { + console.error( + "callback function threw an error @ GetSslCertificate.onChange", + e, + ) + } + } + })() + .catch((e) => callback(null, e)) + .catch((e) => + console.error( + "callback function threw an error @ GetSslCertificate.onChange", + e, + ), + ) + } } diff --git a/sdk/package/lib/util/fileHelper.ts b/sdk/package/lib/util/fileHelper.ts index d47af510c..0190f838e 100644 --- a/sdk/package/lib/util/fileHelper.ts +++ b/sdk/package/lib/util/fileHelper.ts @@ -1,7 +1,6 @@ import * as matches from "ts-matches" import * as YAML from "yaml" import * as TOML from "@iarna/toml" -import merge from "lodash.merge" import * as T from "../../../base/lib/types" import * as fs from "node:fs/promises" import { asError } from "../../../base/lib/util" @@ -43,6 +42,24 @@ async function onCreated(path: string) { } } +function fileMerge(...args: any[]): any { + let res = args.shift() + for (const arg of args) { + if (res === arg) continue + else if ( + typeof res === "object" && + typeof arg === "object" && + !Array.isArray(res) && + !Array.isArray(arg) + ) { + for (const key of Object.keys(arg)) { + res[key] = fileMerge(res[key], arg[key]) + } + } else res = arg + } + return res +} + /** * @description Use this class to read/write an underlying configuration file belonging to the upstream service. * @@ -158,11 +175,38 @@ export class FileHelper { return null } + private readOnChange( + callback: (value: A | null, error?: Error) => void | Promise, + ) { + ;(async () => { + for await (const value of this.readWatch()) { + try { + await callback(value) + } catch (e) { + console.error( + "callback function threw an error @ FileHelper.read.onChange", + e, + ) + } + } + })() + .catch((e) => callback(null, e)) + .catch((e) => + console.error( + "callback function threw an error @ FileHelper.read.onChange", + e, + ), + ) + } + get read() { return { once: () => this.readOnce(), const: (effects: T.Effects) => this.readConst(effects), watch: () => this.readWatch(), + onChange: ( + callback: (value: A | null, error?: Error) => void | Promise, + ) => this.readOnChange(callback), } } @@ -171,7 +215,7 @@ export class FileHelper { */ async write(data: A) { const fileData = (await this.readFile()) || {} - const mergeData = merge({}, fileData, data) + const mergeData = fileMerge({}, fileData, data) return await this.writeFile(this.validate(mergeData)) } @@ -184,7 +228,7 @@ export class FileHelper { (() => { throw new Error(`${this.path}: does not exist`) })() - const mergeData = merge({}, fileData, data) + const mergeData = fileMerge({}, fileData, data) return await this.writeFile(this.validate(mergeData)) }