UNPKG

lemon-core

Version:
506 lines (505 loc) 16.6 kB
import { Elastic6SimpleQueriable, CoreModel } from 'lemon-model'; import { StorageService } from './storage-service'; import { GeneralAPIController } from '../../controllers/general-api-controller'; /** * class: `CoreKey` * - to represent internal partition-key and model-id. */ export interface CoreKey<ModelType extends string> { /** * defualt internal partition-key */ _id?: string; /** * namespace */ ns?: string; /** * model-id */ id: string; /** * model-type */ type: ModelType; } /** * class: `CoreKeyMakeable` * - make internal key by type + id. */ export interface CoreKeyMakeable<ModelType extends string> { /** * get key object w/ internal partition-key (default as _id). * * @param type type of model * @param id id of model in type */ asKey$(type: ModelType, id: string): CoreKey<ModelType>; /** * get key-string to use for query. (USED FOR CUSTOM ID NAME); * * @param type type of model * @param id id of model in type */ asKey?(type: ModelType, id: string): string; } /** * type: ModelFilter * * @param model the new model to update * @param origin the old model (or 2nd model) * @return the updated model. */ export declare type CoreModelFilter<T> = (model: T, origin?: T) => T; /** * class: `CoreModelFilterable` * - support filters. */ export interface CoreModelFilterable<T> { afterRead: CoreModelFilter<T>; beforeSave: CoreModelFilter<T>; afterSave: CoreModelFilter<T>; beforeUpdate: CoreModelFilter<T>; afterUpdate: CoreModelFilter<T>; } /** * class: `StorageMakeable` * - makeable of `TypedStorageService` */ export interface StorageMakeable<T extends CoreModel<ModelType>, ModelType extends string> { /** * create storage-service w/ fields list. * @param type type of model * @param fields list of field (properties) * @param filter filter of model. */ makeStorageService(type: ModelType, fields: string[], filter: CoreModelFilterable<T>): TypedStorageService<T, ModelType>; } /** * class: `GeneralKeyMaker` * - use ':' as delimiter to join [ns, type, id] */ export declare class GeneralKeyMaker<ModelType extends string> implements CoreKeyMakeable<ModelType> { readonly NS: string; readonly DELIMITER: string; constructor(ns?: string, delimiter?: string); asKey$(type: ModelType, id: string): { ns: string; id: string; type: ModelType; _id: string; }; } /** * class: `GeneralModelFilter` * - general model-filter with differential update. * - to customize, override this class. */ export declare class GeneralModelFilter<T extends CoreModel<ModelType>, ModelType extends string> implements CoreModelFilterable<T> { readonly FIELDS: string[]; /** * default constructor */ constructor(fields: string[]); /** * parse `.meta` to json * @param model the current model * @param origin the origin model */ afterRead(model: T, origin?: T): T; /** * filter for before saving. * - make sure data conversion * - move the unknown fields to `.meta`. * * @param model the current model * @param origin the origin model */ beforeSave(model: T, origin?: T): T; /** * called after saving the model. * - parse `.meta` back to json object. * * @param model the saved model * @param origin the origin model. */ afterSave(model: T, origin?: T): T; /** * called before updating the model. * @param model the updated model * @param incrementals (optional) incremental fields. */ beforeUpdate(model: T, incrementals?: T): T; /** * called after updating the model. * @param model the updated model */ afterUpdate(model: T): T; /** * override this `onBeforeSave()` in sub-class. * @param model the current model * @param origin (optional) the origin model */ onBeforeSave(model: T, origin?: T): T; } /** * class: `ProxyStorageService` * - support `nextSeq()`, `doLock()`, `doRelease()` * - proxed storage-service to wrap the parent storage-service w/ more features. * - table is supposed to have internal-key as `_id` string. * * **Usage** * ```js * type MyType = '' | 'test'; * interface MyModel extends CoreModel<MyType>{ * name?: string; * } * const storage = new ProxyStorageService<MyModel, MyType>(this, 'TestTable', ['id','name']); * const $test = storage.makeTypedStorageService('test'); * ``` */ export declare class ProxyStorageService<T extends CoreModel<ModelType>, ModelType extends string> implements StorageService<T> { static readonly AUTO_SEQUENCE = 1000000; static readonly TYPE_SEQUENCE = "sequence"; readonly idName: string; readonly service: CoreKeyMakeable<ModelType>; readonly storage: StorageService<T>; readonly filters: CoreModelFilterable<T>; /** * create proxed storage-service. * * @param service service to support `CoreKeyMakeable` * @param storage table-name or the parent storage-service * @param fields list of fields. * @param filters filters of `CoreModelFilterable` * @param idName (optional) internal partition-key (default as '_id') */ constructor(service: CoreKeyMakeable<ModelType>, storage: StorageService<T> | string, fields: string[], filters?: CoreModelFilterable<T>, idName?: string); /** * factory function to create this `proxy-storage-service` * @param service key-makeable * @param table table-name * @param fields list of fields. * @param filters model filter. * @param idName (optional) internal partition-key (default as '_id') */ static create<T extends CoreModel<ModelType>, ModelType extends string>(service: CoreKeyMakeable<ModelType>, table: string, fields?: string[], filters?: CoreModelFilterable<T>, idName?: string): ProxyStorageService<T, ModelType>; /** * say hello() */ hello: () => string; /** * read by _id */ read: (_id: string) => Promise<T>; /** * read or create by _id */ readOrCreate: (_id: string, model: T) => Promise<T>; /** * save by _id */ save: (_id: string, model: T) => Promise<T>; /** * update by _id */ update: (_id: string, model: T, incrementals?: T) => Promise<T>; /** * increment by _id */ increment: (_id: string, model: T, $update?: T) => Promise<T>; /** * delete by _id */ delete: (_id: string) => Promise<T>; /** * get key-id by type+id */ asKey: (type: ModelType, id: string | number) => string; /** * get next auto-sequence number. * * @param type type of seqeunce. * @param nextInit (optional) initial next value if not exist. * @param nextStep (optional) the incremental step to get next. (default 1) */ nextSeq(type: ModelType, nextInit?: number, nextStep?: number): Promise<number>; /** * get uuid by type. * @param type */ nextUuid(type?: ModelType): Promise<string>; /** * timer to generate the current-time (msec) */ private $timer; setTimer: (timer: () => number) => () => number; getTime: () => number; /** * get time-stamp as now. */ asTime(currentTime?: number): { createdAt: number; updatedAt: number; deletedAt: number; }; /** * delete sequence-key. * @param type type of seqeunce. */ clearSeq(type: ModelType): Promise<void>; /** * read model by key + id with optional auto creation. * * @param type model-type * @param id node-id * @param $create (optional) initial model if not exist. (or throw 404 error) */ doRead(type: ModelType, id: string, $create?: T): Promise<T>; /** * delete model by id. * * @param type model-type * @param id node-id * @param destroy flag to destroy (real delete) */ doDelete(type: ModelType, id: string, destroy?: boolean): Promise<T>; /** * update model (or it will create automatically) * * @param type model-type * @param id node-id * @param node model * @param incrementals (optional) fields to increment */ doUpdate(type: ModelType, id: string, node: T, incrementals?: T): Promise<T>; /** * update model (or it will create automatically) * * @param type model-type * @param id node-id */ doIncrement(type: ModelType, id: string, $inc: T, $up: T): Promise<T>; /** * save model by checking origin node. * - use `doSave()` rather than `doUpdate()` for both create & update. * - if `$create` is null, throw 404 error it if not found. * * @param type model-type * @param id node-id * @param node node to save (or update) * @param $create (optional) initial creation model if not found. */ doSave(type: ModelType, id: string, node: T, $create?: T): Promise<T>; /** * lock data-entry by type+id w/ limited time tick * - WARN! must release lock by `doRelease()` * * `total-waited-time = tick * interval (msec)` * * @param type model-type * @param id model-id * @param tick tick count to wait. * @param interval timeout interval per each tick (in msec, default 1000 = 1sec) */ doLock(type: ModelType, id: string, tick?: number, interval?: number): Promise<boolean>; /** * release lock by resetting lock = 0. * * @param type model-type * @param id model-id */ doRelease(type: ModelType, id: string): Promise<boolean>; /** * create storage-service w/ fields list. * - idName should be `_id` * * @param table table-name or dummy file name (ex: `dummy-data.yml`). * @param fields required for dynamo table. * @param idName internal partition-key name (default '_id') */ static makeStorageService<T>(table: string, fields?: string[], idName?: string): StorageService<T>; /** * create proxy-storage-service by type * @param type model-type */ makeTypedStorageService<U extends T>(type: ModelType): TypedStorageService<U, ModelType>; } /** * class: `TypedStorageService` * - wrap id with type + id. */ export declare class TypedStorageService<T extends CoreModel<ModelType>, ModelType extends string> implements StorageService<T> { readonly type: ModelType; readonly storage: ProxyStorageService<T, ModelType>; constructor(service: ProxyStorageService<T, ModelType>, type: ModelType); /** * show self service name */ hello: () => string; /** * get next auto-sequence id in number like `1000003`. */ nextId: () => Promise<number>; /** * get uuid like `d01764cd-9ef2-41e2-9e88-68e79555c979` */ nextUuid: () => Promise<string>; /** * read model by key + id with optional auto creation. * - throws '404 NOT FOUND' if not found. * * @param id node-id */ read: (id: string | number) => Promise<T>; /** * read model by key + id with optional auto creation. * * @param id node-id * @param model initial model if not exist. (or throw 404 error) */ readOrCreate: (id: string | number, model: T) => Promise<T>; /** * update model (or it will create automatically) * * @param id node-id * @param model model to update * @param incrementals (optional) fields to increment. */ update: (id: string | number, model: T, incrementals?: T) => Promise<T>; /** * insert model w/ auto generated id * * @param model model to insert */ insert: (node: T) => Promise<T>; /** * update model (or it will create automatically) * * ```ts * //before: { count: 1 }; * const res = await storage.increment(1, { count: 2 }, { total: 2 }); * //after : { count: 3, total: 2 } * ``` * * @param id node-id * @param $increments model only with numbers */ increment: (id: string | number, $increments: T, $update?: T) => Promise<T>; /** * delete model by id. * * @param id node-id * @param destroy flag to destroy (real delete) */ delete: (id: string | number, destroy?: boolean) => Promise<T>; /** * save model by checking origin node. * - use `doSave()` rather than `doUpdate()` for both create & update. * - if `$create` is null, throw 404 error it if not found. * * @param id node-id * @param node node to save (or update) * @param $create (optional) initial creation model. */ save: (id: string | number, model: T, $create?: T) => Promise<T>; /** * lock data-entry by type+id w/ limited time tick * - WARN! must release lock by `release(id)` * * `total-waited-time = tick * interval (msec)` * * **[UPDATES]** * 1. read original node (or, throw 404 error) * 2. use internal lock. * * @param id model-id to lock * @param tick tick count to wait. * @param interval timeout interval per each tick (in msec, default 1000 = 1sec) */ lock: (id: string | number, tick?: number, interval?: number) => Promise<boolean>; /** * release lock by resetting lock = 0. * @param id model-id */ release: (id: string | number) => Promise<boolean>; /** * using `lock()`, guard func with auto lock & release. * * ```ts * const res = await storage.guard(async ()=>{ * return 'abc'; * }); * // res === 'abc' * ``` */ guard: <T_1>(id: string | number, handler: () => T_1 | Promise<T_1>, tick?: number, interval?: number) => Promise<any>; /** * make `UniqueFieldManager` for field. */ makeUniqueFieldManager: (field: string) => UniqueFieldManager<T, ModelType>; /** * make `GeneralAPIController` for REST API w/ supporting basic CRUD */ makeGeneralAPIController: (search?: Elastic6SimpleQueriable<any>, uniqueField?: string) => GeneralAPIController<this, string>; } /** * class: `ModelUtil` * - Helper functions for model. */ export declare class ModelUtil { static selfRead: <T>(self: any, key: string, defValue?: T) => T; static selfPop: <T>(self: any, key: string, defValue?: T) => T; /** * attach `.pop()` method to object. * * ```js * const data = CoreModelUtil.buildPop({'a':1}); * assert( 1 === data.pop('a) ); * const final = data.pop(); * assert( final == data ); */ static buildPop: (thiz: any, popName?: string) => any; } /** * class: `UniqueFieldManager` * - support `.{field}` is unique in typed-storage-service. * - make lookup data entry to save the reverse mapping to origin id. * - set `.stereo` as '#' to mark as lookup. (to filter out from Elastic.search()) * - set `.id` as `#{field}/{name}` or `#{name}`. * - set `.meta` as origin id. */ export declare class UniqueFieldManager<T extends CoreModel<ModelType>, ModelType extends string> { readonly type: ModelType; readonly field: string; readonly storage: TypedStorageService<T, ModelType>; constructor(storage: TypedStorageService<T, ModelType>, field?: string); hello: () => string; /** * validate value format * - just check empty string. * @param value unique value in same type+field. */ validate(value: string): boolean; /** * convert to internal id by value * @param value unique value in same type group. */ asLookupId(value: string): string; /** * lookup model by value * - use `.meta` property to link with the origin. * - mark `.stereo` as to '#' to distinguish normal. * * @param value unique value in same type group. * @param $creates (optional) create-set if not found. */ findOrCreate(value: string, $creates?: T): Promise<T>; /** * update lookup table (or create) * * @param model target model * @param value (optional) new value of model. */ updateLookup(model: T, value?: string): Promise<T>; }