import { type Component, type Reply } from '@toa.io/core' import { type Remotes } from './Remotes' import { Mapping } from './Mapping' import { type Context } from './Context' import type * as RTD from './RTD' import type * as http from './HTTP' export class Endpoint implements RTD.Endpoint { private readonly endpoint: string private readonly mapping: Mapping private readonly discovery: Promise private remote: Component | null = null public constructor (endpoint: string, mapping: Mapping, discovery: Promise) { this.endpoint = endpoint this.mapping = mapping this.discovery = discovery } public async call (body: any, query: http.Query, parameters: RTD.Parameter[]): Promise { const request = this.mapping.fit(body, query, parameters) this.remote ??= await this.discovery return await this.remote.invoke(this.endpoint, request) } public async close (): Promise { this.remote ??= await this.discovery await this.remote.disconnect(INTERRUPT) } } export class EndpointsFactory implements RTD.EndpointsFactory { private readonly remotes: Remotes public constructor (remotes: Remotes) { this.remotes = remotes } public create (method: RTD.syntax.Method, context: Context): Endpoint { if (method.mapping === undefined) throw new Error('Cannot create Endpoint without mapping.') const mapping = Mapping.create(method.mapping.query) const namespace = method.mapping.namespace ?? context.extension?.namespace const component = method.mapping.component ?? context.extension?.component if (namespace === undefined || component === undefined) throw new Error('Annotation endpoints must be fully qualified.') const discovery = this.remotes.discover(namespace, component) return new Endpoint(method.mapping.endpoint, mapping, discovery) } } const INTERRUPT = true