lemon-core
Version:
Lemon Serverless Micro-Service Platform
551 lines (550 loc) • 16.8 kB
TypeScript
/**
* `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>;
};