import { getConfig, setConfig } from "./mod.ts"; import * as T from "../types.ts"; import * as M from "../migrations.ts"; import * as util from "../util.ts"; import { EmVer } from "../emver-lite/mod.ts"; import { ConfigSpec } from "../types/config-types.ts"; export interface NoRepeat { version: version; type: type; } /** * @param fn function making desired modifications to the config * @param configured whether or not the service should be considered "configured" * @param noRepeat (optional) supply the version and type of the migration * @param noFail (optional, default:false) whether or not to fail the migration if fn throws an error * @returns a migraion function */ export function updateConfig( fn: (config: ConfigSpec, effects: T.Effects) => ConfigSpec | Promise, configured: boolean, noRepeat?: NoRepeat, noFail = false ): M.MigrationFn { return M.migrationFn(async (effects: T.Effects) => { await noRepeatGuard(effects, noRepeat, async () => { let config = util.unwrapResultType(await getConfig({})(effects)).config; if (config) { try { config = await fn(config, effects); } catch (e) { if (!noFail) { throw e; } else { configured = false; } } util.unwrapResultType(await setConfig(effects, config)); } }); return { configured }; }); } export async function noRepeatGuard( effects: T.Effects, noRepeat: NoRepeat | undefined, fn: () => Promise ): Promise { if (!noRepeat) { return fn(); } if (!(await util.exists(effects, { path: "start9/migrations", volumeId: "main" }))) { await effects.createDir({ path: "start9/migrations", volumeId: "main" }); } const migrationPath = { path: `start9/migrations/${noRepeat.version}.complete`, volumeId: "main", }; if (noRepeat.type === "up") { if (!(await util.exists(effects, migrationPath))) { await fn(); await effects.writeFile({ ...migrationPath, toWrite: "" }); } } else if (noRepeat.type === "down") { if (await util.exists(effects, migrationPath)) { await fn(); await effects.removeFile(migrationPath); } } } export async function initNoRepeat( effects: T.Effects, migrations: M.MigrationMapping, startingVersion: string ) { if (!(await util.exists(effects, { path: "start9/migrations", volumeId: "main" }))) { const starting = EmVer.parse(startingVersion); await effects.createDir({ path: "start9/migrations", volumeId: "main" }); for (const version in migrations) { const migrationVersion = EmVer.parse(version); if (migrationVersion.lessThanOrEqual(starting)) { await effects.writeFile({ path: `start9/migrations/${version}.complete`, volumeId: "main", toWrite: "", }); } } } } export function fromMapping( migrations: M.MigrationMapping, currentVersion: string ): T.ExpectedExports.migration { const inner = M.fromMapping(migrations, currentVersion); return async (effects: T.Effects, version: string, direction?: unknown) => { await initNoRepeat(effects, migrations, direction === "from" ? version : currentVersion); return inner(effects, version, direction); }; }