From 3f37648dfd68b3e4b876de8be20cff5bf5c15d73 Mon Sep 17 00:00:00 2001 From: Julien Oculi Date: Mon, 13 May 2024 16:52:33 +0200 Subject: [PATCH] feat(model): :sparkles: add `ref` class to link ressources --- src/models/src/ressource.ts | 5 ++ src/models/utils/ref.ts | 102 ++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 src/models/utils/ref.ts diff --git a/src/models/src/ressource.ts b/src/models/src/ressource.ts index 326d5a0..e070c5b 100644 --- a/src/models/src/ressource.ts +++ b/src/models/src/ressource.ts @@ -1,3 +1,4 @@ +import { Ref } from '@/src/models/utils/ref.ts' import { DateString, ToJson, UUID } from '@/types.ts' import { regex } from '@/utils.ts' @@ -90,6 +91,10 @@ export class Ressource { toString(): string { return `Ressource (${JSON.stringify(this)})` } + + toRef(): Ref { + return Ref.fromRessource(this) + } } export interface Ressource { diff --git a/src/models/utils/ref.ts b/src/models/utils/ref.ts new file mode 100644 index 0000000..586d67d --- /dev/null +++ b/src/models/utils/ref.ts @@ -0,0 +1,102 @@ +import { Db } from '@/mod.ts' +import type { UUID } from '@/types.ts' +import { + Credential, + Group, + Machine, + type Ressource, + Service, + User, +} from '@models' + +export type RefString = + | `@ref/${T['type']}#${UUID}` + | Ref +export type RefResolver = ( + ref: RefString, +) => T | Promise +export class Ref extends String { + static #toString( + { uuid, type }: { uuid: UUID; type: T['type'] }, + ) { + return `@ref/${type}#${uuid}` as const + } + + static parse(string: RefString) { + const [_, value] = string.split('/') + const [type, uuid] = value.split('#') as [T['type'], UUID] + + return { type, uuid } as const + } + + static fromRessource(ressource: T): Ref { + return new Ref(ressource) + } + + static fromString(string: RefString) { + return new Ref(Ref.parse(string)) + } + + private constructor({ uuid, type }: { uuid: UUID; type: T['type'] }) { + super(Ref.#toString({ uuid, type })) + this.#type = type + this.#uuid = uuid + } + + #type: T['type'] + #uuid: UUID + + get type() { + return this.#type + } + + get uuid() { + return this.#uuid + } + + ref(resolver: RefResolver) { + return resolver(this.toString()) + } + + toString() { + return Ref.#toString({ uuid: this.uuid, type: this.type }) + } + + toJSON() { + return this.toString() + } +} + +export function RefDbResolver(db: Db) { + return (ref: RefString) => { + const { type, uuid } = Ref.parse(ref) + return db.ressource[type].get({ uuid }) + } +} + +export function RefRestResolver(endpoint: string) { + return async (ref: RefString) => { + const { type, uuid } = Ref.parse(ref) + const url = new URL(`${type}/${uuid}`, endpoint) + const response = await fetch(url) + const json = await response.json() + + if (type === 'user') { + return User.fromJSON(json) + } + if (type === 'machine') { + return Machine.fromJSON(json) + } + if (type === 'service') { + return Service.fromJSON(json) + } + if (type === 'group') { + return Group.fromJSON(json) + } + if (type === 'credential') { + return Credential.fromJSON(json) + } + + throw new TypeError(`unknown ref type "${type}"`) + } +}