UNPKG

@webda/core

Version:

Expose API with Lambda

1,008 lines (1,007 loc) 27.3 kB
import { Counter, EventWithContext, Histogram } from "../core.js"; import { ConfigurationProvider, WebdaError } from "../index.js"; import { Constructor, CoreModel, CoreModelDefinition, FilterAttributes } from "../models/coremodel.js"; import { Service, ServiceParameters } from "../services/service.js"; import { OperationContext, WebContext } from "../utils/context.js"; import { HttpMethodType } from "../utils/httpcontext.js"; import { WebdaQL } from "./webdaql/query.js"; export declare class StoreNotFoundError extends WebdaError.CodeError { constructor(uuid: string, storeName: string); } export declare class UpdateConditionFailError extends WebdaError.CodeError { constructor(uuid: string, conditionField: string, condition: string | Date); } interface EventStore { /** * Target object */ object: CoreModel; /** * Object id */ object_id: string; /** * Store emitting */ store: Store; /** * Context of the operation */ context?: OperationContext; } /** * Event called before save of an object */ export interface EventStoreSave extends EventStore { } /** * Event called after save of an object */ export interface EventStoreSaved extends EventStoreSave { } /** * Event called before delete of an object */ export interface EventStoreDelete extends EventStore { } /** * Event called after delete of an object */ export interface EventStoreDeleted extends EventStoreDelete { } /** * Event called on retrieval of an object */ export interface EventStoreGet extends EventStore { } /** * Event called before action on an object */ export interface EventStoreAction extends EventWithContext { /** * Name of the action */ action: string; /** * Target object unless it is a global action */ object?: CoreModel; /** * Model of the object if global action */ model?: CoreModelDefinition; /** * Emitting store */ store: Store; } /** * Event called after action on an object */ export interface EventStoreActioned extends EventStoreAction { /** * Result of the action */ result: any; } /** * Event called before update of an object */ export interface EventStoreUpdate extends EventStore { /** * Update content */ update: any; } /** * Event called after update of an object */ export interface EventStoreUpdated extends EventStoreUpdate { /** * Object uuid */ object_id: string; /** * Object before update */ previous: any; } /** * Event called before patch update of an object */ export interface EventStorePatchUpdate extends EventStoreUpdate { } /** * Event called after patch update of an object */ export interface EventStorePatchUpdated extends EventStoreUpdated { } /** * Event called after partial update of an object */ export interface EventStorePartialUpdated<T extends CoreModel = CoreModel> { /** * Object uuid */ object_id: string; /** * Emitting store */ store: Store<T>; /** * Update date */ updateDate?: Date; /** * Info on the update */ partial_update: { /** * If incremental update */ increments?: { /** * Increment value */ value: number; /** * Property to increment */ property: string; }[]; /** * Add item to a collection */ addItem?: { /** * Item to add */ value: any; /** * Collection name */ property: string; /** * Index to add */ index: number; }; /** * If this is a patch */ patch?: any; /** * Delete an item from collection */ deleteItem?: { /** * Collection name */ property: string; /** * Index in the collection */ index: number; }; deleteAttribute?: string; }; } /** * Event sent when a query on the store is emitted */ export interface EventStoreQuery { /** * Request sent */ query: string; /** * Emitting store */ store: Store; /** * The parsed query by our grammar */ parsedQuery: WebdaQL.Query; /** * Context in which the query was run */ context: OperationContext; } /** * Event sent when query is resolved */ export interface EventStoreQueried extends EventStoreQuery { /** * Results from the query */ results: CoreModel[]; /** * The next continuation token */ continuationToken: string; } /** * Event sent when object is created via POST request */ export interface EventStoreWebCreate extends EventWithContext { /** * Properties for the object */ values: any; /** * Target object */ object: CoreModel; /** * Object id */ object_id: string; /** * Emitting store */ store: Store; } /** * Event sent when object is retrieved via GET request */ export interface EventStoreWebGet extends EventWithContext { /** * Emitting store */ store: Store; /** * Target object */ object: CoreModel; } /** * Event sent when object is retrieved via GET request */ export interface EventStoreWebGetNotFound extends EventWithContext { /** * Emitting store */ store: Store; /** * Target object */ uuid: string; } /** * Event sent when object is updated via PUT request */ export interface EventStoreWebUpdate extends EventWithContext { /** * Type of update */ method: "PATCH" | "PUT"; /** * Updates to do on the object */ updates: any; /** * Target object */ object: CoreModel; /** * Emitting store */ store: Store; } /** * Event sent when object is deleted via DELETE request */ export interface EventStoreWebDelete extends EventWithContext { /** * Object uuid */ object_id: string; /** * Emitting store */ store: Store; } /** * @deprecated Store should not be exposed directly anymore * You should use the DomainService instead */ export type StoreExposeParameters = { /** * URL endpoint to use to expose REST Resources API * * @default service.getName().toLowerCase() */ url?: string; /** * You can restrict any part of the CRUD * * @default {} */ restrict?: { /** * Do not expose the POST */ create?: boolean; /** * Do not expose the PUT and PATCH */ update?: boolean; /** * Do not expose the GET */ get?: boolean; /** * Do not expose the DELETE */ delete?: boolean; /** * Do not expose the query endpoint */ query?: boolean; }; /** * For confidentiality sometimes you might prefer to expose query through PUT * To avoid GET logging * * @default "GET" */ queryMethod?: "PUT" | "GET"; }; /** * Represent a query result on the Store */ export interface StoreFindResult<T> { /** * Current result objects */ results: T[]; /** * Continuation Token if more results are available */ continuationToken?: string; /** * Remaining filtering to do as current store cannot filter * * If `true`, no more filtering is required apart from permissions * If `filter === undefined`, a full postquery filtering will happen * Otherwise filter.eval while be used on every results */ filter?: WebdaQL.Expression | true; } /** * Store parameter */ export declare class StoreParameters extends ServiceParameters { /** * Webda model to use within the Store * * @default "Webda/CoreModel" */ model?: string; /** * Additional models * * Allow this store to manage other models * * @default [] */ additionalModels?: string[]; /** * async delete */ asyncDelete: boolean; /** * Expose the service to an urls * * @deprecated will probably be removed in 4.0 in favor of Expose annotation */ expose?: StoreExposeParameters; /** * Allow to load object that does not have the type data * * If set to true, then the Store will only managed the defined _model and no * model extending this one * * @default false */ strict?: boolean; /** * When __type model not found, use the model * If strict is setup this parameter is not used * * @default true */ defaultModel?: boolean; /** * If set, Store will ignore the __type * * @default false */ forceModel?: boolean; /** * Slow query threshold * * @default 30000 */ slowQueryThreshold: number; /** * Model Aliases to allow easier rename of Model */ modelAliases?: { [key: string]: string; }; /** * Disable default memory cache */ noCache?: boolean; constructor(params: any, service: Service<any>); } export type StoreEvents = { "Store.PartialUpdated": EventStorePartialUpdated; "Store.Save": EventStoreSave; "Store.Saved": EventStoreSaved; "Store.PatchUpdate": EventStorePatchUpdate; "Store.PatchUpdated": EventStorePatchUpdated; "Store.Update": EventStoreUpdate; "Store.Updated": EventStoreUpdated; "Store.Delete": EventStoreDelete; "Store.Deleted": EventStoreDeleted; "Store.Get": EventStoreGet; "Store.Query": EventStoreQuery; "Store.Queried": EventStoreQueried; "Store.WebCreate": EventStoreWebCreate; "Store.Action": EventStoreAction; "Store.Actioned": EventStoreActioned; "Store.WebUpdate": EventStoreWebUpdate; "Store.WebGetNotFound": EventStoreWebGetNotFound; "Store.WebGet": EventStoreWebGet; "Store.WebDelete": EventStoreWebDelete; }; /** * A mapping service allow to link two object together * * Therefore they need to handle the cascadeDelete */ export interface MappingService<T = any> { newModel(object: any): T; } /** * This class handle NoSQL storage and mapping (duplication) between NoSQL object * TODO Create the mapping documentation * * It use basic CRUD, and can expose those 4 to through HTTP * * It emits events : * Store.Save: Before saving the object * Store.Saved: After saving the object * Store.Update: Before updating the object * Store.Updated: After updating the object * Store.Delete: Before deleting the object * Store.Deleted: After deleting the object * Store.Get: When getting the object * Store.Action: When an action will be done on an object * Store.Actioned: When an action has been done on an object * * Mapping: * * * Parameters * * map: { ... } * expose: { // Enable the HTTP exposure * url: '', // The url to expose to by default it is service name in lowercase ( users for example ) * restrict: { * create: true, // Don't expose the POST /users * update: true, // Don't expose the PUT /users/{uuid} * delete: true, // Don't expose the DELETE /users/{uuid} * get: true // Don't expose the GET /users/{uuid} * } * } * @category CoreServices */ declare abstract class Store<T extends CoreModel = CoreModel, K extends StoreParameters = StoreParameters, E extends StoreEvents = StoreEvents> extends Service<K, E> implements ConfigurationProvider, MappingService<T> { /** * Cache store */ _cacheStore: Store<T>; /** * Contain the reverse map */ _reverseMap: { mapper: MappingService; property: string; }[]; /** * Contains the current model */ _model: CoreModelDefinition<T>; /** * Store teh manager hierarchy with their depth */ _modelsHierarchy: { [key: string]: number; }; /** * Contains the current model type */ _modelType: string; /** * Contain the model uuid field */ protected _uuidField: string; /** * Add metrics counter * ' UNION SELECT name, tbl_name as email, "" as col1, "" as col2, "" as col3, "" as col4, "" as col5, "" as col6, "" as col7, "" as col8 FROM sqlite_master -- * {"email":"' UNION SELECT name as profileImage, tbl_name as email, '' AS column3 FROM sqlite_master --","password":"we"} */ metrics: { cache_invalidations: Counter; operations_total: Counter; slow_queries_total: Counter; cache_hits: Counter; queries: Histogram; }; /** * Load the parameters for a service */ abstract loadParameters(params: any): StoreParameters; /** * Retrieve the Model * * @throws Error if model is not found */ computeParameters(): void; logSlowQuery(_query: string, _reason: string, _time: number): void; /** * Invalidate a cache entry * @param uid */ invalidateCache(uid: string): Promise<void>; /** * @override */ initMetrics(): void; /** * Return Store current model * @returns */ getModel(): CoreModelDefinition; /** * Return if a model is handled by the store * @param model * @return distance from the managed class -1 means not managed, 0 manage exactly this model, >0 manage an ancestor model * */ handleModel(model: Constructor<CoreModel> | CoreModel): number; /** * Get From Cache or main * @param uuid * @param raiseIfNotFound * @returns */ _getFromCache(uuid: string, raiseIfNotFound?: boolean): Promise<T>; /** * Get object from store * @param uid * @returns */ getObject(uid: string): Promise<T>; /** * @override */ getUrl(url: string, methods: HttpMethodType[]): string; static getOpenAPI(): void; /** * @inheritdoc */ initRoutes(): void; /** * OVerwrite the model * Used mainly in test */ setModel(model: CoreModelDefinition<T>): void; /** * We should ignore exception from the store */ cacheStorePatchException(): void; /** * Init a model from the current stored data * * Initial the reverse map as well * * @param object * @returns */ protected initModel(object?: any): T; /** * Get a new model with this data preloaded * @param object * @returns */ newModel(object?: any): T; /** * Add reverse map information * * @param prop * @param cascade * @param store */ addReverseMap(prop: string, store: MappingService): void; /** * Increment attributes of an object * * @param uid * @param info * @returns */ incrementAttributes<FK extends FilterAttributes<T, number>>(uid: string, info: { property: FK; value: number; }[]): Promise<Date>; /** * Helper function that call incrementAttributes * @param uid * @param prop * @param value * @returns */ incrementAttribute<FK extends FilterAttributes<T, number>>(uid: string, prop: FK, value: number): Promise<Date>; /** * Add or update an item to an array in the model * * @param uid of the model * @param prop of the model to add in * @param item to add in the array * @param index if specified update item in this index * @param itemWriteCondition value of the condition to test (in case of update) * @param itemWriteConditionField field to read the condition from (in case of update) */ upsertItemToCollection<FK extends FilterAttributes<T, Array<any>>>(uid: string, prop: FK, item: any, index?: number, itemWriteCondition?: any, itemWriteConditionField?: string): Promise<Date>; /** * Remove an item from an array in the model * * @param uid of the model * @param prop of the model to remove from * @param index of the item to remove in the array * @param itemWriteCondition value of the condition * @param itemWriteConditionField field to read the condition from */ deleteItemFromCollection<FK extends FilterAttributes<T, Array<any>>>(uid: string, prop: FK, index: number, itemWriteCondition: any, itemWriteConditionField?: string): Promise<Date>; /** * Iterate through the results * * This can be resource consuming * * @param query * @param context */ iterate(query?: string, context?: OperationContext): AsyncGenerator<T>; /** * Query all the results * * * @param query * @param context * @returns * @deprecated use iterate instead */ queryAll(query: string, context?: OperationContext): Promise<T[]>; /** * Check that __type Comparison is only used with = and CONTAINS * If CONTAINS is used, move __type to __types * If __type = store._model, remove it */ queryTypeUpdater(query: WebdaQL.Query): WebdaQL.Query; /** * Query store with WebdaQL * @param query * @param context to apply permission */ query(query: string, context?: OperationContext): Promise<{ results: T[]; continuationToken?: string; }>; /** * Expose query to http */ httpQuery(ctx: WebContext): Promise<void>; /** * Handle StoreEvent and update cache based on it * Then emit the event, it allows the cache to be updated * before listeners are called * * @param event * @param data */ emitStoreEvent<Key extends keyof StoreEvents>(event: Key, data: E[Key] & { emitterId?: string; }): Promise<void>; /** * Save an object * * @param {Object} Object to save * @param {String} Uuid to use, if not specified take the object.uuid or generate one if not found * @return {Promise} with saved object * * Might want to rename to create */ save(object: any, ctx?: OperationContext): Promise<T>; /** * * @param object * @param ctx * @returns */ create(object: any, ctx?: OperationContext): Promise<any>; /** * Patch an object * * @param object * @param reverseMap * @returns */ patch<FK extends keyof T>(object: Partial<T>, reverseMap?: boolean, conditionField?: FK | null, conditionValue?: any): Promise<T | undefined>; /** * Check if an UpdateCondition is met * @param model * @param conditionField * @param condition * @param uid */ checkUpdateCondition<CK extends keyof T>(model: T, conditionField?: CK, condition?: any, uid?: string): void; /** * Check if an UpdateCondition is met * @param model * @param conditionField * @param condition * @param uid */ checkCollectionUpdateCondition<FK extends FilterAttributes<T, Array<any>>, CK extends keyof T>(model: T, collection: FK, conditionField?: CK, condition?: any, index?: number): void; /** * Update conditionally * @param uuid * @param updates * @param conditionField * @param condition */ conditionalPatch<CK extends keyof T>(uuid: string, updates: Partial<T>, conditionField: CK, condition: any): Promise<boolean>; /** * * @param model * @param prop * @param item * @param index * @param itemWriteCondition * @param itemWriteConditionField * @param updateDate */ simulateUpsertItemToCollection<FK extends FilterAttributes<T, Array<any>>>(model: T, prop: FK, item: any, updateDate: Date, index?: number, itemWriteCondition?: any, itemWriteConditionField?: string): Promise<void>; /** * Update an object * * If no attribute can be updated then return undefined * * @param {Object} Object to save * @param {Boolean} reverseMap internal use only, for disable map resolution * @return {Promise} with saved object */ update<CK extends keyof T>(object: any, reverseMap?: boolean, partial?: boolean, conditionField?: CK | null, conditionValue?: any): Promise<T | undefined>; /** * Manage the store migration for __type case sensitivity */ v3Migration(): Promise<void>; /** * */ recomputeTypeShortId(): Promise<void>; /** * Ensure model aliases are not used in this store * * So alias can be cleaned */ cleanModelAliases(): Promise<void>; /** * Recompute type case */ recomputeTypeCase(): Promise<void>; /** * Recompute the __types for all objects (storeMigration.Registry.typesCompute) */ recomputeTypes(): Promise<void>; /** * Delete a migration * @param name */ cancelMigration(name: string): Promise<void>; /** * Get a migration * @param name */ getMigration(name: string): Promise<any>; /** * Add a migration mechanism to store * @param name * @param patcher */ migration(name: string, patcher: (object: T) => Promise<Partial<T> | (() => Promise<void>) | undefined>, batchSize?: number): Promise<void>; /** * Remove an attribute from an object * * @param uuid * @param attribute * @returns */ removeAttribute<CK extends keyof T>(uuid: string, attribute: CK, itemWriteCondition?: any, itemWriteConditionField?: CK): Promise<void>; /** * Cascade delete a related object * * @param obj * @param uuid * @returns */ cascadeDelete(obj: CoreModel, _uuid: string): Promise<any>; /** * Delete an object from the store without condition nor async * @param uid to delete * @returns */ forceDelete(uid: string): Promise<void>; /** * Delete an object * * @param {String} uuid to delete * @param {Boolean} delete sync even if asyncDelete is active * @return {Promise} the deletion promise */ delete<CK extends keyof T>(uid: string | T, writeCondition?: any, writeConditionField?: CK, sync?: boolean): Promise<void>; /** * By default we cannot know if the store will trigger or not * * @param id * @param callback */ canTriggerConfiguration(_id: string, _callback: () => void): boolean; /** * Provide a way to store configuration in store * @param {string} id * @returns {Promise<Map<string, any>>} */ getConfiguration(id: string): Promise<{ [key: string]: any; }>; /** * Upsert the uuid object * @param uuid * @param data */ put(uuid: string, data: Partial<T>): Promise<T>; /** * Get an object * * @param {String} uuid to get * @return {Promise} the object retrieved ( can be undefined if not found ) */ get(uid: string, ctx?: OperationContext, defaultValue?: any): Promise<T>; /** * Set one attribute in an object * * this is an helper function that calls patch * * @param uid of the object * @param property to update1 * @param value new value * @returns */ setAttribute<CK extends keyof T>(uid: string, property: CK, value: any): Promise<void>; /** * @override */ protected simulateFind(query: WebdaQL.Query, uuids: string[]): Promise<StoreFindResult<T>>; getOpenApiReplacements(): { modelName: string; }; /** * Handle POST * @param ctx */ httpCreate(ctx: WebContext): Promise<void>; /** * Create a new object based on the context * @param ctx * @param model */ operationCreate(ctx: OperationContext, model: string): Promise<void>; /** * Handle obect action * @param ctx */ httpAction(ctx: WebContext, actionMethod?: string): Promise<void>; /** * Handle collection action * @param ctx */ httpGlobalAction(ctx: WebContext, model?: CoreModelDefinition): Promise<void>; /** * Handle HTTP Update for an object * * @param ctx context of the request */ httpUpdate(ctx: WebContext): Promise<void>; /** * Handle GET on object * * @param ctx context of the request */ httpGet(ctx: WebContext): Promise<void>; /** * Handle HTTP request * * @param ctx context of the request * @returns */ httpDelete(ctx: WebContext): Promise<void>; /** * Return the model uuid field */ getUuidField(): string; /** * Check if an object exists * @abstract * @params {String} uuid of the object or the object */ exists(uid: string | CoreModel): Promise<boolean>; /** * Search within the store */ abstract find(query: WebdaQL.Query): Promise<StoreFindResult<T>>; /** * Check if an object exists * @abstract */ abstract _exists(uid: string): Promise<boolean>; /** * The underlying store should recheck writeCondition only if it does not require * another get() * * @param uid * @param writeCondition * @param itemWriteConditionField */ protected abstract _delete(uid: string, writeCondition?: any, itemWriteConditionField?: string): Promise<void>; /** * Retrieve an element from the store * * @param uid to retrieve * @param raiseIfNotFound raise an StoreNotFound exception if not found */ protected abstract _get(uid: string, raiseIfNotFound?: boolean): Promise<T>; /** * Get an object * * @param {Array} uuid to gets if undefined then retrieve the all table * @return {Promise} the objects retrieved ( can be [] if not found ) */ abstract getAll(list?: string[]): Promise<T[]>; protected abstract _update(object: any, uid: string, itemWriteCondition?: any, itemWriteConditionField?: string): Promise<any>; protected abstract _patch(object: any, uid: string, itemWriteCondition?: any, itemWriteConditionField?: string): Promise<any>; protected abstract _removeAttribute(uuid: string, attribute: string, itemWriteCondition?: any, itemWriteConditionField?: string): Promise<void>; /** * Save within the store * @param object */ protected abstract _save(object: T): Promise<any>; /** * Increment the attribute * @param uid * @param prop * @param value * @param updateDate */ protected abstract _incrementAttributes(uid: string, params: { property: string; value: number; }[], updateDate: Date): Promise<any>; protected abstract _upsertItemToCollection(uid: string, prop: string, item: any, index: number, itemWriteCondition: any, itemWriteConditionField: string, updateDate: Date): Promise<any>; protected abstract _deleteItemFromCollection(uid: string, prop: string, index: number, itemWriteCondition: any, itemWriteConditionField: string, updateDate: Date): Promise<any>; } export { Store };