UNPKG

@webda/core

Version:

Expose API with Lambda

740 lines (739 loc) 20.5 kB
import { WorkerLogLevel, WorkerOutput } from "@webda/workout"; import Ajv, { ErrorObject } from "ajv"; import * as events from "events"; import { OpenAPIV3 } from "openapi-types"; import { Counter, CounterConfiguration, Gauge, GaugeConfiguration, Histogram, HistogramConfiguration } from "prom-client"; import { Writable } from "stream"; import { Application, Configuration } from "./application.js"; import { BinaryService, ContextProvider, ContextProviderInfo, GlobalContext, HttpContext, Logger, OperationContext, RegExpValidator, Service, Store, WebContext, WebdaQL } from "./index.js"; import { Constructor, CoreModel, CoreModelDefinition } from "./models/coremodel.js"; import { RouteInfo, Router } from "./router.js"; import CryptoService from "./services/cryptoservice.js"; export declare class EventEmitterUtils { static emit(eventEmitter: events.EventEmitter, event: string | number | symbol, data: any): boolean; static emitSync(eventEmitter: events.EventEmitter, event: string | number | symbol, data: any): Promise<any[]>; /** * Display a message if the listener takes too long * @param start */ static elapse(start: number): void; } /** * Copy from https://github.com/ajv-validator/ajv/blob/master/lib/runtime/validation_error.ts * It is not exported by ajv */ export declare class ValidationError extends Error { readonly errors: Partial<ErrorObject>[]; readonly ajv: true; readonly validation: true; constructor(errors: Partial<ErrorObject>[]); } /** * Operation */ export declare class OperationError extends Error { operation: string; type: "Unknown" | "PermissionDenied" | "InvalidInput"; constructor(operation: string, type: "Unknown" | "PermissionDenied" | "InvalidInput"); } /** * Define an operation within webda app */ export interface OperationDefinition { /** * Id of the operation */ id: string; /** * Name of the schema that defines operation input */ input?: string; /** * Name of the schema that defines operation output */ output?: string; /** * WebdaQL to execute on session to know if * operation is available to user */ permission?: string; /** * Service implementing the operation */ service: string; /** * Method implementing the operation */ method: string; } /** * Define an operation within webda app */ export interface OperationDefinitionInfo extends OperationDefinition { /** * Contains the parse permission query */ permissionQuery?: WebdaQL.QueryValidator; } /** * */ export type RegistryEntry<T = any> = CoreModel & T; /** * Ensure all events store the context in the same place */ export interface EventWithContext<T extends OperationContext = OperationContext> { context: T; } /** * RequestFilter allow a service which implement it to control incoming request * * If one of the filter replies with "true" then the request will go through */ export interface RequestFilter<T extends WebContext = WebContext> { /** * Return true if the request should be allowed * * @param context to check for */ checkRequest(context: T, type: "CORS" | "AUTH"): Promise<boolean>; } /** * Filter request based on their origin * * @category CoreFeatures */ export declare class OriginFilter implements RequestFilter<WebContext> { regexs: RegExpValidator; constructor(origins: string[]); /** * * @param context * @returns */ checkRequest(context: WebContext): Promise<boolean>; } /** * Authorize requests based on the website */ export declare class WebsiteOriginFilter implements RequestFilter<WebContext> { websites: string[]; constructor(website: any); checkRequest(context: WebContext): Promise<boolean>; } export declare function Bean(constructor: Function): void; export type CoreEvents = { /** * Emitted when new result is sent */ "Webda.Result": EventWithContext<WebContext>; /** * Emitted when new request comes in */ "Webda.Request": EventWithContext<WebContext>; /** * Emitted when a request does not match any route */ "Webda.404": EventWithContext<WebContext>; /** * Emitted when Services have been initialized */ "Webda.Init.Services": { [key: string]: Service; }; /** * Emitted when Services have been created */ "Webda.Create.Services": { [key: string]: Service; }; /** * Emitted when Core is initialized */ "Webda.Init": Configuration; /** * Emitted whenever a new Context is created */ "Webda.NewContext": { context: OperationContext; info: ContextProviderInfo; }; /** * Sent when route is added to context */ "Webda.UpdateContextRoute": { context: WebContext; }; [key: string]: unknown; }; type NoSchemaResult = null; type SchemaValidResult = true; /** * This is the main class of the framework, it handles the routing, the services initialization and resolution * * @class Core * @category CoreFeatures */ export declare class Core<E extends CoreEvents = CoreEvents> extends events.EventEmitter { /** * Webda Services * @hidden */ protected services: { [key: string]: Service; }; /** * Application that generates this Core */ protected application: Application; /** * Router that will route http request in */ protected router: Router; /** * If Core is already initiated */ protected _initiated: boolean; /** * Services who failed to create or initialize */ protected failedServices: { [key: string]: any; }; /** * Init promise to ensure, webda is initiated * Used for init() method */ protected _init: Promise<void>; /** * Configuration loaded from webda.config.json * @hidden */ protected configuration: Configuration; /** * JSON Schema validator instance */ protected _ajv: Ajv; /** * JSON Schema registry * Save if a schema was added to Ajv already */ protected _ajvSchemas: { [key: string]: true; }; /** * Current executor */ protected _currentExecutor: any; protected _configFile: string; /** * Contains the current initialization process */ protected _initPromise: Promise<void>; /** * When the Core was initialized */ protected _initTime: number; /** * Console logger * @hidden */ protected logger: Logger; /** * Request Filter registry * * Added via [[Webda.registerRequestFilter]] * See [[CorsFilter]] */ protected _requestFilters: RequestFilter<WebContext>[]; /** * CORS Filter registry * * Added via [[Webda.registerCORSRequestFilter]] * See [[CorsFilter]] */ protected _requestCORSFilters: RequestFilter<WebContext>[]; /** * Worker output * * @see @webda/workout */ private workerOutput; /** * Store the instance id */ private instanceId; /** * Application registry */ protected registry: Store<RegistryEntry>; /** * Manage encryption within the application */ protected cryptoService: CryptoService; /** * Contains all operations defined by services */ protected operations: { [key: string]: OperationDefinitionInfo; }; /** * Cache for model to store resolution */ private _modelStoresCache; /** * Cache for model to store resolution */ private _modelBinariesCache; /** * Store the Core singleton * * The main storage is on the process itself * This second storage allow to identify dual import */ private static singleton; /** * True if the dual import warning has been sent */ private _dualImportWarn; /** * Registered context providers */ private _contextProviders; /** * System context */ protected globalContext: GlobalContext; /** * */ interuptables: { cancel: () => Promise<void>; }[]; /** * @params {Object} config - The configuration Object, if undefined will load the configuration file */ constructor(application?: Application); /** * Register a cancelable process * @param interuptable */ static registerInteruptableProcess(interuptable: { cancel: () => Promise<void>; }): void; /** * Unregister a cancelable process * @param interuptable */ static unregisterInteruptableProcess(interuptable: { cancel: () => Promise<void>; }): void; /** * Get the current script location * @returns */ private getScriptUrl; /** * Return information on the singleton import and version * @param singleton * @returns */ private static getSingletonInfo; /** * Get the singleton of Webda Core * @returns */ static get(): Core; /** * Enforce a specific store for a model * * Useful in some specific case when you want to update store dynamically * * @param model * @param store */ setModelStore(model: Constructor<CoreModel>, store: Store): void; /** * Get the store assigned to this model * @param model * @returns */ getModelStore<T extends CoreModel>(modelOrConstructor: Constructor<T> | T): Store<T>; /** * Get the service that manage a model * @param modelOrConstructor * @param attribute * @returns */ getBinaryStore<T extends CoreModel>(modelOrConstructor: Constructor<T> | T, attribute: string): BinaryService; /** * Return Core instance id * * It is a random generated string */ getInstanceId(): string; /** * Get absolute url with subpath * @param subpath */ getApiUrl(subpath?: string): string; /** * Return application path with subpath * * Helper that redirect to this.application.getAppPath * * @param subpath * @returns */ getAppPath(subpath?: string): string; /** * Retrieve all detected modules definition */ getModules(): import("./application").CachedModule; /** * Return application definition */ getApplication(): Application; /** * Get WorkerOutput */ getWorkerOutput(): WorkerOutput; /** * Init one service * @param service */ protected initService(service: string): Promise<void>; /** * Get an object from the application based on its full uuid * @param fullUuid * @param partials */ getModelObject<T extends CoreModel = CoreModel>(fullUuid: string, partials?: any): Promise<T>; /** * Init Webda * * It will resolve Services init method and autolink */ init(): Promise<void>; /** * Pause for time ms * * @param time ms */ static sleep(time: any): Promise<void>; /** * Check if an operation can be executed with the current context * Not checking the input use `checkOperation` instead to check everything * @param context * @param operationId * @throws OperationError if operation is unknown * @returns true if operation can be executed */ checkOperationPermission(context: OperationContext, operationId: string): boolean; /** * Check if an operation can be executed with the current context * @param context * @param operationId */ checkOperation(context: OperationContext, operationId: string): Promise<void>; /** * Call an operation within the framework */ callOperation(context: OperationContext, operationId: string): Promise<any>; /** * Get available operations * @returns */ listOperations(): { [key: string]: Omit<OperationDefinition, "service" | "method">; }; /** * Register a new operation within the app * @param operationId * @param definition */ registerOperation(operationId: string, definition: OperationDefinition): void; /** * Register a request filtering * * Will apply to all requests regardless of the devMode * @param filter */ registerRequestFilter(filter: RequestFilter<WebContext>): void; /** * Register a CORS request filtering * * Does not apply in devMode * @param filter */ registerCORSFilter(filter: RequestFilter<WebContext>): void; /** * Register a new context provider * @param provider */ registerContextProvider(provider: ContextProvider): void; /** * Validate the object with schema * * @param schema path to use * @param object to validate */ validateSchema(webdaObject: CoreModel | string, object: any, ignoreRequired?: boolean): NoSchemaResult | SchemaValidResult; /** * Return webda current version * * @returns package version * @since 0.4.0 */ getVersion(): string; /** * To define the locales just add a locales: ['en-GB', 'fr-FR'] in your host global configuration * * @return The configured locales or "en-GB" if none are defined */ getLocales(): string[]; /** * Get a Logger for a class * @param clazz */ getLogger(clazz: string | Service): Logger; /** * Add a route dynamicaly * * @param {String} url of the route can contains dynamic part like {uuid} * @param {Object} info the type of executor */ addRoute(url: string, info: RouteInfo): void; /** * Remove a route dynamicly * * @param {String} url to remove */ removeRoute(url: string): void; /** * Return current Router object */ getRouter(): Router; /** * Check for a service name and return the wanted singleton or undefined if none found * * @param {String} name The service name to retrieve */ getService<T extends Service>(name?: string): T; /** * Return a map of defined services * @returns {{}} */ getServices(): { [key: string]: Service; }; /** * Return a map of services that extends type * @param type The type of implementation * @returns {{}} */ getServicesOfType<T extends Service>(type?: Constructor<T, [Core, string, any]>): { [key: string]: T; }; getConfiguration(): Configuration; /** * Return a map of defined stores * @returns {{}} */ getStores(): { [key: string]: Store; }; /** * Return a map of defined models * @returns {{}} */ getModels(): { [key: string]: CoreModelDefinition; }; /** * Check for a model name and return the wanted class or throw exception if none found * * @param {String} name The model name to retrieve */ getModel<T extends CoreModel = CoreModel>(name: any): CoreModelDefinition<T>; /** * Add to context information and executor based on the http context */ updateContextWithRoute(ctx: WebContext): boolean; /** * Flush the headers to the response, no more header modification is possible after that * * This method should set the `context.setFlushedHeaders()` and use of `context.hasFlushedHeaders()` * * @abstract */ flushHeaders(_context: WebContext): void; /** * Flush the entire response to the client */ flush(_context: WebContext): void; /** * Return if Webda is in debug mode */ isDebug(): boolean; /** * Return the global parameters of a domain */ getGlobalParams(): any; /** * Get the system context * @returns */ getGlobalContext(): GlobalContext; /** * Set the system context * @param context */ setGlobalContext(context: GlobalContext): void; /** * Reinit one service * @param service */ protected reinitService(service: string): Promise<void>; /** * Reinit all services with updated parameters * @param updates * @returns */ reinit(updates: any): Promise<void>; /** * Get a full resolved service parameter * * @param service * @param configuration * @returns */ getServiceParams(service: string, configuration?: { parameters?: any; services: any; }): any; protected createService(services: any, service: string): void; getBeans(): any; /** * @hidden * */ protected createServices(excludes?: string[]): void; /** * A registry is a predefined store * @returns */ getRegistry(): Store<RegistryEntry>; /** * Return the crypto service * @returns */ getCrypto(): CryptoService; /** * Stop all services */ stop(): Promise<void>; protected jsonFilter(key: string, value: any): any; static getMachineId(): string; /** * Init services and Beans along with Routes */ initStatics(): void; /** * Get a context based on the info * @param info * @returns */ newContext<T extends OperationContext>(info: ContextProviderInfo, noInit?: boolean): Promise<OperationContext>; /** * Create a new context for a request * * @class Service * @param httpContext THe HTTP request context * @param stream - The request output stream if any * @return A new context object to pass along */ newWebContext<T extends WebContext>(httpContext: HttpContext, stream?: Writable, noInit?: boolean): Promise<T>; /** * Convert an object to JSON using the Webda json filter * * @class Service * @param {Object} object - The object to export * @return {String} The export of the strip object ( removed all attribute with _ ) */ toPublicJSON(object: any): string; /** * Return a UUID * * @param format to return different type of format * Plan to implement base64 and maybe base85 */ getUuid(format?: "ascii" | "base64" | "hex" | "binary" | "uuid"): string; /** * @override */ emit<K extends keyof E>(eventType: K | symbol | string, event?: E[K], ...data: any[]): boolean; /** * Emit the event with data and wait for Promise to finish if listener returned a Promise */ emitSync<K extends keyof E>(eventType: K | symbol, event?: E[K], ...data: any[]): Promise<any[]>; /** * Type the listener part * @param event * @param listener * @param queue * @returns */ on<Key extends keyof E>(event: Key | symbol, listener: (evt: E[Key]) => void): this; /** * Logs * @param level * @param args */ log(level: WorkerLogLevel, ...args: any[]): void; /** * Retrieve a global parameter */ parameter(name: string): any; /** * Verify if a request can be done * * @param context Context of the request */ protected checkRequest(ctx: WebContext): Promise<boolean>; /** * Verify if an origin is allowed to do request on the API * * @param context Context of the request */ protected checkCORSRequest(ctx: WebContext): Promise<boolean>; /** * Export OpenAPI * @param skipHidden * @returns */ exportOpenAPI(skipHidden?: boolean): OpenAPIV3.Document; /** * Get a metric object * * Use the Service.getMetric method if possible * * This is map from prometheus 3 types of metrics * Our hope is that we can adapt them to export to other * metrics system if needed * * @param type * @param configuration * @returns */ getMetric<T = Gauge | Counter | Histogram>(type: Constructor<T, [MetricConfiguration<T>]>, configuration: MetricConfiguration<T>): T; } /** * Generic type for metric */ export type MetricConfiguration<T = Counter | Gauge | Histogram, K extends string = string> = T extends Counter ? CounterConfiguration<K> : T extends Gauge ? GaugeConfiguration<K> : HistogramConfiguration<K>; /** * Export a Registry type alias */ export type Registry<T extends CoreModel = RegistryEntry> = Store<T>; export { Counter, Gauge, Histogram };