feat(model): add ref class to link ressources

This commit is contained in:
Julien Oculi 2024-05-13 16:52:33 +02:00
parent 53d6727650
commit 3f37648dfd
2 changed files with 107 additions and 0 deletions

View file

@ -1,3 +1,4 @@
import { Ref } from '@/src/models/utils/ref.ts'
import { DateString, ToJson, UUID } from '@/types.ts' import { DateString, ToJson, UUID } from '@/types.ts'
import { regex } from '@/utils.ts' import { regex } from '@/utils.ts'
@ -90,6 +91,10 @@ export class Ressource {
toString(): string { toString(): string {
return `Ressource (${JSON.stringify(this)})` return `Ressource (${JSON.stringify(this)})`
} }
toRef(): Ref<Ressource> {
return Ref.fromRessource(this)
}
} }
export interface Ressource { export interface Ressource {

102
src/models/utils/ref.ts Normal file
View file

@ -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<T extends Ressource> =
| `@ref/${T['type']}#${UUID}`
| Ref<T>
export type RefResolver<T extends Ressource> = (
ref: RefString<T>,
) => T | Promise<T>
export class Ref<T extends Ressource> extends String {
static #toString<T extends Ressource>(
{ uuid, type }: { uuid: UUID; type: T['type'] },
) {
return `@ref/${type}#${uuid}` as const
}
static parse<T extends Ressource>(string: RefString<T>) {
const [_, value] = string.split('/')
const [type, uuid] = value.split('#') as [T['type'], UUID]
return { type, uuid } as const
}
static fromRessource<T extends Ressource>(ressource: T): Ref<T> {
return new Ref<T>(ressource)
}
static fromString<T extends Ressource>(string: RefString<T>) {
return new Ref<T>(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<T>) {
return resolver(this.toString())
}
toString() {
return Ref.#toString<T>({ uuid: this.uuid, type: this.type })
}
toJSON() {
return this.toString()
}
}
export function RefDbResolver(db: Db) {
return <T extends Ressource>(ref: RefString<T>) => {
const { type, uuid } = Ref.parse(ref)
return db.ressource[type].get({ uuid })
}
}
export function RefRestResolver(endpoint: string) {
return async <T extends Ressource>(ref: RefString<T>) => {
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}"`)
}
}