From b99fe8adc2d687175001396e2719de88556e9312 Mon Sep 17 00:00:00 2001 From: BluJ Date: Mon, 16 Jan 2023 16:30:34 -0700 Subject: [PATCH] chore: including options --- backups.ts | 93 ++++++++++++++------- types.ts | 238 +++++++++++++++++++++++++++++++++-------------------- 2 files changed, 212 insertions(+), 119 deletions(-) diff --git a/backups.ts b/backups.ts index 6fd4289..d795352 100644 --- a/backups.ts +++ b/backups.ts @@ -1,21 +1,48 @@ import { ok } from "./util.ts"; import * as T from "./types.ts"; -/** +export const DEFAULT_OPTIONS: T.BackupOptions = { + delete: true, + force: true, + ignoreExisting: false, + exclude: [], +}; +type BackupSet = { + srcPath: string; + srcVolume: string; + dstPath: string; + dstVolume: string; + options?: Partial; +}; +/** * This utility simplifies the volume backup process. * ```ts * export const { createBackup, restoreBackup } = Backups.volumes("main").build(); * ``` -*/ + * + * Changing the options of the rsync, (ie exludes) use either + * ```ts + * Backups.volumes("main").set_options({exclude: ['bigdata/']}).volumes('excludedVolume').build() + * // or + * Backups.with_options({exclude: ['bigdata/']}).volumes('excludedVolume').build() + * ``` + * + * Using the more fine control, using the addSets for more control + * ```ts + * Backups.addSets({ + * srcVolume: 'main', srcPath:'smallData/', dstPath: 'main/smallData/', dstVolume: : Backups.BACKUP + * }, { + * srcVolume: 'main', srcPath:'bigData/', dstPath: 'main/bigData/', dstVolume: : Backups.BACKUP, options: {exclude:['bigData/excludeThis']}} + * ).build() + * ``` + */ export class Backups { static BACKUP = "BACKUP" as const; - public backupSet = [] as { - srcPath: string; - srcVolume: string; - dstPath: string; - dstVolume: string; - }[]; - constructor() { + + constructor( + private options = DEFAULT_OPTIONS, + private backupSet = [] as BackupSet[], + ) { } static volumes(...volumeNames: string[]) { return new Backups().addSets(...volumeNames.map((srcVolume) => ({ @@ -26,24 +53,34 @@ export class Backups { }))); } static addSets( - ...options: { - srcPath: string; - srcVolume: string; - dstPath: string; - dstVolume: string; - }[] + ...options: BackupSet[] ) { return new Backups().addSets(...options); } + static with_options(options?: Partial) { + return new Backups({ ...DEFAULT_OPTIONS, ...options }); + } + set_options(options?: Partial) { + this.options = { + ...this.options, + ...options, + }; + return this; + } + volumes(...volumeNames: string[]) { + return this.addSets(...volumeNames.map((srcVolume) => ({ + srcVolume, + srcPath: "./", + dstPath: `./${srcVolume}/`, + dstVolume: Backups.BACKUP, + }))); + } addSets( - ...options: { - srcPath: string; - srcVolume: string; - dstPath: string; - dstVolume: string; - }[] + ...options: BackupSet[] ) { - options.forEach((x) => this.backupSet.push(x)); + options.forEach((x) => + this.backupSet.push({ ...x, options: { ...this.options, ...x.options } }) + ); return this; } build() { @@ -58,10 +95,8 @@ export class Backups { await effects.runRsync({ ...item, options: { - delete: true, - force: true, - ignoreExisting: false, - exclude: [], + ...this.options, + ...item.options, }, }).wait(); } @@ -77,10 +112,8 @@ export class Backups { } await effects.runRsync({ options: { - delete: true, - force: true, - ignoreExisting: false, - exclude: [], + ...this.options, + ...item.options, }, srcVolume: item.dstVolume, dstVolume: item.srcVolume, diff --git a/types.ts b/types.ts index 7a4ccee..d3702a5 100644 --- a/types.ts +++ b/types.ts @@ -1,7 +1,10 @@ // deno-lint-ignore no-namespace export namespace ExpectedExports { /** Set configuration is called after we have modified and saved the configuration in the embassy ui. Use this to make a file for the docker to read from for configuration. */ - export type setConfig = (effects: Effects, input: Config) => Promise>; + export type setConfig = ( + effects: Effects, + input: Config, + ) => Promise>; /** Get configuration returns a shape that describes the format that the embassy ui will generate, and later send to the set config */ export type getConfig = (effects: Effects) => Promise>; /** These are how we make sure the our dependency configurations are valid and if not how to fix them. */ @@ -9,17 +12,31 @@ export namespace ExpectedExports { /** For backing up service data though the embassyOS UI */ export type createBackup = (effects: Effects) => Promise>; /** For restoring service data that was previously backed up using the embassyOS UI create backup flow. Backup restores are also triggered via the embassyOS UI, or doing a system restore flow during setup. */ - export type restoreBackup = (effects: Effects) => Promise>; + export type restoreBackup = ( + effects: Effects, + ) => Promise>; /** Properties are used to get values from the docker, like a username + password, what ports we are hosting from */ - export type properties = (effects: Effects) => Promise>; + export type properties = ( + effects: Effects, + ) => Promise>; export type health = { /** Should be the health check id */ - [id: string]: (effects: Effects, dateMs: number) => Promise>; + [id: string]: ( + effects: Effects, + dateMs: number, + ) => Promise>; }; - export type migration = (effects: Effects, version: string, ...args: unknown[]) => Promise>; + export type migration = ( + effects: Effects, + version: string, + ...args: unknown[] + ) => Promise>; export type action = { - [id: string]: (effects: Effects, config?: Config) => Promise>; + [id: string]: ( + effects: Effects, + config?: Config, + ) => Promise>; }; /** @@ -32,7 +49,9 @@ export namespace ExpectedExports { /** Used to reach out from the pure js runtime */ export type Effects = { /** Usable when not sandboxed */ - writeFile(input: { path: string; volumeId: string; toWrite: string }): Promise; + writeFile( + input: { path: string; volumeId: string; toWrite: string }, + ): Promise; readFile(input: { volumeId: string; path: string }): Promise; metadata(input: { volumeId: string; path: string }): Promise; /** Create a directory. Usable when not sandboxed */ @@ -42,12 +61,18 @@ export type Effects = { removeFile(input: { volumeId: string; path: string }): Promise; /** Write a json file into an object. Usable when not sandboxed */ - writeJsonFile(input: { volumeId: string; path: string; toWrite: Record }): Promise; + writeJsonFile( + input: { volumeId: string; path: string; toWrite: Record }, + ): Promise; /** Read a json file into an object */ - readJsonFile(input: { volumeId: string; path: string }): Promise>; + readJsonFile( + input: { volumeId: string; path: string }, + ): Promise>; - runCommand(input: { command: string; args?: string[]; timeoutMillis?: number }): Promise>; + runCommand( + input: { command: string; args?: string[]; timeoutMillis?: number }, + ): Promise>; runDaemon(input: { command: string; args?: string[] }): { wait(): Promise>; term(): Promise; @@ -77,7 +102,7 @@ export type Effects = { method?: "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "PATCH"; headers?: Record; body?: string; - } + }, ): Promise<{ method: string; ok: boolean; @@ -91,18 +116,25 @@ export type Effects = { }>; runRsync(options: { - srcVolume: string, - dstVolume: string, - srcPath: string, - dstPath: string, + srcVolume: string; + dstVolume: string; + srcPath: string; + dstPath: string; // rsync options: https://linux.die.net/man/1/rsync - options: { - delete: boolean, - force: boolean, - ignoreExisting: boolean, - exclude: string[] - } - }): {id: () => Promise, wait: () => Promise, progress: () => Promise} + options: BackupOptions; + }): { + id: () => Promise; + wait: () => Promise; + progress: () => Promise; + }; +}; + +// rsync options: https://linux.die.net/man/1/rsync +export type BackupOptions = { + delete: boolean; + force: boolean; + ignoreExisting: boolean; + exclude: string[]; }; export type Metadata = { fileType: string; @@ -177,8 +209,8 @@ export type Target = V & { export type UniqueBy = | { - any: UniqueBy[]; - } + any: UniqueBy[]; + } | string | null; @@ -188,19 +220,19 @@ export type WithNullable = T & { export type DefaultString = | string | { - /** The chars available for the randome generation */ - charset?: string; - /** Length that we generate to */ - len: number; - }; + /** The chars available for the randome generation */ + charset?: string; + /** Length that we generate to */ + len: number; + }; export type ValueSpecString = // deno-lint-ignore ban-types ( | {} | { - pattern: string; - "pattern-description": string; - } + pattern: string; + "pattern-description": string; + } ) & { copyable?: boolean; masked?: boolean; @@ -217,63 +249,71 @@ export type ValueSpecNumber = { export type ValueSpecBoolean = Record; export type ValueSpecAny = | Tag<"boolean", WithDescription>> - | Tag<"string", WithDescription, DefaultString>>> - | Tag<"number", WithDescription, number>>> | Tag< - "enum", - WithDescription< - WithDefault< - { - values: readonly string[] | string[]; - "value-names": { - [key: string]: string; - }; - }, - string - > + "string", + WithDescription< + WithNullableDefault, DefaultString> + > + > + | Tag< + "number", + WithDescription, number>> + > + | Tag< + "enum", + WithDescription< + WithDefault< + { + values: readonly string[] | string[]; + "value-names": { + [key: string]: string; + }; + }, + string > > + > | Tag<"list", ValueSpecList> | Tag<"object", WithDescription>> | Tag<"union", WithDescription>> | Tag< - "pointer", - WithDescription< - | Subtype< - "package", - | Target< - "tor-key", - { - "package-id": string; - interface: string; - } - > - | Target< - "tor-address", - { - "package-id": string; - interface: string; - } - > - | Target< - "lan-address", - { - "package-id": string; - interface: string; - } - > - | Target< - "config", - { - "package-id": string; - selector: string; - multi: boolean; - } - > - > - | Subtype<"system", Record> + "pointer", + WithDescription< + | Subtype< + "package", + | Target< + "tor-key", + { + "package-id": string; + interface: string; + } + > + | Target< + "tor-address", + { + "package-id": string; + interface: string; + } + > + | Target< + "lan-address", + { + "package-id": string; + interface: string; + } + > + | Target< + "config", + { + "package-id": string; + selector: string; + multi: boolean; + } + > > - >; + | Subtype<"system", Record> + > + >; export type ValueSpecUnion = { /** What tag for the specification, for tag unions */ tag: { @@ -297,12 +337,32 @@ export type ValueSpecObject = { "unique-by"?: UniqueBy; }; export type ValueSpecList = - | Subtype<"boolean", WithDescription, boolean[]>>> - | Subtype<"string", WithDescription, string[]>>> - | Subtype<"number", WithDescription, number[]>>> - | Subtype<"enum", WithDescription, string[]>>> - | Subtype<"object", WithDescription, Record[]>>> - | Subtype<"union", WithDescription, string[]>>>; + | Subtype< + "boolean", + WithDescription, boolean[]>> + > + | Subtype< + "string", + WithDescription, string[]>> + > + | Subtype< + "number", + WithDescription, number[]>> + > + | Subtype< + "enum", + WithDescription, string[]>> + > + | Subtype< + "object", + WithDescription< + WithNullableDefault, Record[]> + > + > + | Subtype< + "union", + WithDescription, string[]>> + >; export type ValueSpecEnum = { values: string[]; "value-names": { [key: string]: string }; @@ -354,8 +414,8 @@ export type DependsOn = { export type KnownError = | { error: string } | { - "error-code": [number, string] | readonly [number, string]; - }; + "error-code": [number, string] | readonly [number, string]; + }; export type ResultType = KnownError | { result: T }; export type PackagePropertiesV2 = {