UNPKG

lemon-core

Version:
551 lines (550 loc) 16.8 kB
/** * `abstract-service.ts` * - common service design pattern to build micro-service backend. * * @author Tim Hong <tim@lemoncloud.io> * @date 2021-02-23 initial version * @author Steve <steve@lemoncloud.io> * @date 2022-02-18 optimized search w/ ES6.8 * @date 2022-02-22 optimized w/ `lemon-core#3.0` and `@elastic/elasticsearch` * @date 2022-02-24 use `$id` in elastic-search as `_id` in dynamo-table. * @date 2022-03-15 optimized w/ `AbstractProxy` * @date 2022-03-17 optimized w/ `lemon-core#3.0.2` and use `env.ES6_DOCTYPE` * @date 2022-03-31 optimized w/ unit test spec. * @date 2022-05-19 optimized `CacheService` w/ typed key. * * @origin see `lemon-accounts-api/src/service/core-service.ts` * @copyright (C) 2021 LemonCloud Co Ltd. - All Rights Reserved. */ import { AbstractManager, CacheService, CoreModel, CoreModelFilterable, DynamoOption, DynamoStreamEvent, Elastic6Option, Elastic6QueryService, Elastic6Service, GeneralKeyMaker, NextContext, NextIdentityAccess, StorageMakeable } from '../cores/'; import elasticsearch from '@elastic/elasticsearch'; /** * authentication helper - get identity-id from context * @param context the current request context. */ export declare function asIdentityId(context: NextContext): string | undefined; /** * extract field names from models * - only fields start with lowercase, or all upper. */ export declare const filterFields: (fields: string[], base?: string[]) => string[]; /** * interface `ModelSynchronizer` */ export interface ModelSynchronizer<T extends CoreModel<string> = CoreModel<string>> { /** * callback for filtering items * @param id * @param item */ filter?(id: string, item: T): boolean; /** * callback invoked before synchronization * @param id * @param eventName * @param item * @param diff * @param prev */ onBeforeSync?(id: string, eventName: DynamoStreamEvent, item: T, diff?: string[], prev?: T): Promise<void>; /** * callback invoked after synchronization * @param id * @param eventName * @param item * @param diff * @param prev */ onAfterSync?(id: string, eventName: DynamoStreamEvent, item: T, diff?: string[], prev?: T): Promise<void>; } /** * abstract class `CoreService` * - common abstract to build user service * * @abstract */ export declare abstract class CoreService<Model extends CoreModel<ModelType>, ModelType extends string> extends GeneralKeyMaker<ModelType> implements StorageMakeable<Model, ModelType> { /** dynamo table name */ readonly tableName: string; /** global index name of elasticsearch */ readonly idName: string; /** (optional) current timestamp */ protected current: number; /** * constructor * @param tableName target table-name (or .yml dummy file-name) * @param ns namespace of dataset * @param idName must be `_id` unless otherwise */ protected constructor(tableName?: string, ns?: string, idName?: string); /** * override current time */ setCurrent: (current: number) => number; /** * get the current dynamo-options. */ get dynamoOptions(): DynamoOption; /** * create storage-service w/ fields list. */ makeStorageService<T extends Model>(type: ModelType, fields: string[], filter: CoreModelFilterable<T>): import("../cores/").TypedStorageService<T, ModelType>; } /** * class: `CoreManager` * - shared core manager for all model * * @abstract */ export declare abstract class CoreManager<Model extends CoreModel<ModelType>, ModelType extends string, Service extends CoreService<Model, ModelType>> extends AbstractManager<Model, Service, ModelType> { /** * constructor * @protected */ protected constructor(type: ModelType, parent: Service, fields: string[], uniqueField?: string); /** * say hello() */ hello: () => string; /** * get existence of model * @param id */ exists(id: string): Promise<boolean>; /** * find model - retrieve or null * @param id model-id */ find(id: string): Promise<Model | null>; /** * get model by key * @param key global id(like primary-key) */ findByKey(key: string): Promise<Model | null>; /** * batch get models * - retrieve multi models per each id * - must be matched with idList in sequence order. * * @param idList list of id * @param parrallel (optional) in parrallel size */ getMulti(idList: string[], parrallel?: number): Promise<(Model | null)[]>; /** * batch get models in map by idName */ getMulti$(idList: string[], idName?: string, parrallel?: number): Promise<{ [id: string]: Model; }>; /** * get by unique field value * @param uniqueValue */ getByUniqueField(uniqueValue: string): Promise<Model>; /** * find model by unique field value - retrieve or null * @param uniqueValue */ findByUniqueField(uniqueValue: string): Promise<Model | null>; /** * prepare model * - override `AbstractManager.prepare()` */ prepare(id: string, $def?: Model, isCreate?: boolean): Promise<Model>; /** * update model * - override 'AbstractManager.insert()' * * @deprecated use `AbstractProxy` */ insert(model: Model, initSeq?: number): Promise<Model>; /** * create or update model * @param id model id * @param model model data */ save(id: string, model: Model): Promise<Model>; /** * update model * - override 'AbstractManager.update()' */ update(id: string, model: Model, $inc?: Model): Promise<Model>; /** * update or create model * - override 'AbstractManager.updateOrCreate()' */ updateOrCreate(id: string, model: Model, $inc?: Model): Promise<Model>; /** * delete model * - override 'AbstractManager.delete()' */ delete(id: string, destroy?: boolean): Promise<Model>; /** * prepare default-model when creation * @param $def base-model */ protected prepareDefault($def: Model): Model; /** * update lookup and delete old one if exists */ protected updateLookup(id: string, model: Model, $org?: Model): Promise<void>; } /** * proxy of manager * - save model internally, and update only if changed properties. * Model extends CoreModel<ModelType>, ModelType extends string */ export declare class ManagerProxy<Model extends CoreModel<ModelType>, Manager extends CoreManager<Model, ModelType, CoreService<Model, ModelType>>, ModelType extends string = string> { readonly $mgr: Manager; constructor(proxy: AbstractProxy<string, CoreService<Model, ModelType>>, mgr: Manager); /** * store the origin model. * - `null` means `404 not found` */ protected readonly _org: { [key: string]: Model; }; /** * store the updated one. */ protected readonly _new: { [key: string]: Model; }; /** * get storage linked. */ get storage(): import("../cores/").TypedStorageService<Model, ModelType>; /** * read the origin node (cloned not to change). */ org(id: string, raw?: boolean): Model; /** * check if already read. */ has(id: string): boolean; /** * read the node. * @param id object-id * @param defaultOrThrow (optional) create if not exists, or flag to throw error */ get(id: string, defaultOrThrow?: Model | boolean): Promise<Model>; /** * 객체 정규화 시킴. * - null 에 대해서는 특별히 처리. */ normal: (N: Model) => Model; /** * override w/ model * @param $org the origin model by `.get(id)` * @param model the new model. */ override: ($org: Model, model: Model) => Model; /** * update the node. */ set(id: string, model: Model): Promise<Model>; /** * increment the field of Object[id] * !WARN! this incremented properties should NOT be updated later. */ inc(id: string, model: Model): Promise<Model>; /** * get all the updated node. * * @param onlyUpdated flag to return the only updated set. (useful to check whether to update really!) */ alls(onlyUpdated?: boolean, onlyValid?: boolean): { [key: string]: Model; }; } /** * class: `AbstractProxy` * - common abstract based class for Proxy */ export declare abstract class AbstractProxy<U extends string, T extends CoreService<CoreModel<U>, U>> { /** parrallel factor */ readonly parrallel: number; /** (internal) current context */ readonly context: NextContext; /** (internal) backend-service */ readonly service: T; /** (internal) cache service instance */ readonly cache?: CacheService; /** * constructor of proxy. * @param service user service instance * @param parrallel parrallel count (default 2) * @param cacheScope prefix of cache-key (like `lemon:SS:` or `lemon:SS:user`) */ constructor(context: NextContext, service: T, parrallel?: number, cacheScope?: string); /** * say hello(). */ hello: () => string; /** * list of manager-proxy */ protected _proxies: ManagerProxy<any, any>[]; /** * get all proxies in list. */ protected get allProxies(): ManagerProxy<any, any, string>[]; /** * register this. * * @return size of proxies. */ register(mgr: ManagerProxy<any, any>): number; /** * save all updates by each proxies. * - 업데이트할 항목을 모두 저장함 * * @param options running parameters. */ saveAllUpdates(options?: { /** (optional) the parrallel factor */ parrallel?: number; /** (optional) flag to use only valid value (not null) (default true) */ onlyValid?: boolean; }): Promise<any[]>; /** * report via slack. */ report: (title: string, data: any) => Promise<string>; /** * featch identity-acess from `lemon-accounts-api` */ protected fetchIdentityAccess(identityId: string, domain?: string): Promise<{ identityId: string; $identity: NextIdentityAccess<any>; }>; /** * the cached identity model */ protected _identity: { [key: string]: NextIdentityAccess; }; /** * fetch(or load) identity. * * @param identityId id to find * @param force (optional) force to reload if not available * @returns the cached identity-access */ getIdentity$(identityId: string, force?: boolean): Promise<NextIdentityAccess>; /** * get current identity-id */ getCurrentIdentityId(throwable?: boolean): Promise<string>; /** * get the current identity object (or throw access-error) */ getCurrentIdentity$(throwable?: boolean): Promise<NextIdentityAccess>; } /** * type `SearchResult` */ export interface SearchResult<T, U = any> { /** * total count of items searched */ total: number; /** * item list */ list: T[]; /** * pagination cursor */ last?: string[]; /** * aggregation result */ aggregations?: U; } /** * class `Elastic6Synchronizer` * - listen DynamoDBStream events and index into Elasticsearch */ export declare class Elastic6Synchronizer { /** * model synchronizer map * @private */ private readonly synchronizerMap; /** * default model synchronizer * @private */ private readonly defModelSynchronizer; /** * constructor * @param elastic elastic6-service instance * @param dynamoOptions dynamo options */ constructor(elastic: Elastic6Service, dynamoOptions: DynamoOption | { tableName: string; }); /** * set synchronizer for the model * @param type the model-type * @param handler (optional) custom synchronizer. */ enableSynchronization(type: string, handler?: ModelSynchronizer): void; /** * internal callback for filtering * @private */ private filter; /** * internal callback on before synchronization * @private */ private onBeforeSync; /** * internal callback on after synchronization * @private */ private onAfterSync; } /** * class `ElasticInstance` * - to manipulate the shared Elasticsearch resources. */ export declare class Elastic6Instance { /** * Elasticsearch client */ readonly client?: elasticsearch.Client; /** * Elastic6Service instance */ readonly elastic?: Elastic6Service; /** * Elastic6QueryService instance */ readonly query?: Elastic6QueryService<any>; /** * Elastic6Synchronizer instance */ readonly synchronizer?: Elastic6Synchronizer; /** * default constructor */ constructor({ endpoint, indexName, esVersion, esDocType, tableName, autocompleteFields, }: { /** url endpoint */ endpoint: string; /** name of index */ indexName: string; /** ES engine version(6.2 ~ 7.x) */ esVersion: string; /** doc-type (only valid under 6.2) */ esDocType: string; /** dynamo-table to sync */ tableName: string; /** field to make auto-complele */ autocompleteFields: string[]; }); /** * read the current elastic6-option. */ get options(): Elastic6Option; /** * create Elasticsearch index w/ custom settings */ createIndex(): Promise<any>; /** * destroy Elasticsearch index */ destroyIndex(): Promise<any>; /** * display index settings and mappings */ describeIndex(): Promise<any>; /** * multi get * @param _ids _id list */ mget<T>(_ids: string[]): Promise<(T | null)[]>; /** * search raw query * @param body Elasticsearch Query DSL * @param params see 'search_type' in Elasticsearch documentation */ search<T>(body: any, params?: { indexName?: string; searchType?: 'query_then_fetch' | 'dfs_query_then_fetch'; }): Promise<SearchResult<T>>; /** * create async generator that yields items queried until last * @param body Elasticsearch Query DSL * @param searchType see 'search_type' in Elasticsearch documentation */ generateSearchResult<T>(body: any, searchType?: 'query_then_fetch' | 'dfs_query_then_fetch'): AsyncGenerator<T[], void, unknown>; } /** * from Elasticsearch document to model item * - replace the elastic's `$id` field to `_id` of dynamo-table. * * @param _source from elastic-search * @param idName (optional) global id of elastic. (default is `$id`) */ export declare function sourceToItem<T>(_source: T, idName?: string): T; /** * const `$ES6` * - default instance as a singleton by env configuration. */ export declare const $ES6: { /** * Elasticsearch client */ readonly client?: elasticsearch.Client; /** * Elastic6Service instance */ readonly elastic?: Elastic6Service; /** * Elastic6QueryService instance */ readonly query?: Elastic6QueryService<any>; /** * Elastic6Synchronizer instance */ readonly synchronizer?: Elastic6Synchronizer; /** * read the current elastic6-option. */ readonly options: Elastic6Option; /** * create Elasticsearch index w/ custom settings */ createIndex(): Promise<any>; /** * destroy Elasticsearch index */ destroyIndex(): Promise<any>; /** * display index settings and mappings */ describeIndex(): Promise<any>; /** * multi get * @param _ids _id list */ mget<T>(_ids: string[]): Promise<T[]>; /** * search raw query * @param body Elasticsearch Query DSL * @param params see 'search_type' in Elasticsearch documentation */ search<T_1>(body: any, params?: { indexName?: string; searchType?: 'query_then_fetch' | 'dfs_query_then_fetch'; }): Promise<SearchResult<T_1, any>>; /** * create async generator that yields items queried until last * @param body Elasticsearch Query DSL * @param searchType see 'search_type' in Elasticsearch documentation */ generateSearchResult<T_2>(body: any, searchType?: 'query_then_fetch' | 'dfs_query_then_fetch'): AsyncGenerator<T_2[], void, unknown>; };