From d762003ea808ea09730ba68b91546d69b528d91f Mon Sep 17 00:00:00 2001 From: Xc Date: Tue, 12 Apr 2022 19:40:39 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=BA=86cache=E5=92=8Ctoken?= =?UTF-8?q?=E7=9A=84=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/FrontContext.ts | 5 -- src/cacheStore/CacheStore.ts | 83 +++++++++++++++++++++-- src/cacheStore/context.ts | 25 +++++-- src/debugStore/context.ts | 4 +- src/debugStore/debugStore.ts | 6 +- src/debugStore/index.ts | 22 ++++-- src/features/cache.ts | 126 +++++++++++++++++++++++++++++++++-- src/features/index.ts | 14 ++-- src/features/node.ts | 19 +++--- src/features/token.ts | 81 ---------------------- src/index.ts | 14 ++-- src/types/AspectProxy.ts | 1 - src/types/Feature.ts | 1 - test/test.ts | 12 ++-- 14 files changed, 271 insertions(+), 142 deletions(-) delete mode 100644 src/FrontContext.ts delete mode 100644 src/features/token.ts diff --git a/src/FrontContext.ts b/src/FrontContext.ts deleted file mode 100644 index 56cf5ae3..00000000 --- a/src/FrontContext.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { EntityDict } from 'oak-domain/lib/types/Entity'; -import { Context as BaseContext } from 'oak-memory-tree-store'; - -export class FrontContext extends BaseContext { -}; \ No newline at end of file diff --git a/src/cacheStore/CacheStore.ts b/src/cacheStore/CacheStore.ts index 2d35f93b..fd4d73f9 100644 --- a/src/cacheStore/CacheStore.ts +++ b/src/cacheStore/CacheStore.ts @@ -1,11 +1,84 @@ -import { EntityDict } from 'oak-domain/lib/types/Entity'; +import { EntityDict, OperationResult, SelectionResult } from 'oak-domain/lib/types/Entity'; import { StorageSchema } from "oak-domain/lib/types/Storage"; +import { TriggerExecutor, Checker } from 'oak-general-business'; +import { BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict'; import { TreeStore } from 'oak-memory-tree-store'; +import { CacheContext } from './context'; -export class CacheStore extends TreeStore { - constructor(storageSchema: StorageSchema) { - super(storageSchema); +export class CacheStore extends TreeStore { + private executor: TriggerExecutor; + + constructor(storageSchema: StorageSchema, initialData?: { + [T in keyof ED]?: { + [ID: string]: ED[T]['OpSchema']; + }; + }) { + super(storageSchema, true, initialData); + this.executor = new TriggerExecutor(); } - // todo + async operate( + entity: T, + operation: ED[T]['Operation'], + context: CacheContext, + params?: Object + ): Promise { + const autoCommit = !context.uuid; + let result; + if (autoCommit) { + await context.begin(); + } + try { + await this.executor.preOperation(entity, operation, context); + result = await super.operate(entity, operation, context, params); + await this.executor.postOperation(entity, operation, context); + } + catch (err) { + await context.rollback(); + throw err; + } + if (autoCommit) { + await context.commit(); + } + return result; + } + + async select( + entity: T, + selection: ED[T]['Selection'], + context: CacheContext, + params?: Object + ): Promise> { + + const autoCommit = !context.uuid; + if (autoCommit) { + await context.begin(); + } + let result; + + try { + result = await super.select(entity, selection, context, params); + } + catch (err) { + await context.rollback(); + throw err; + } + if (autoCommit) { + await context.commit(); + } + return result; + } + + async count( + entity: T, + selection: Omit, + context: CacheContext, + params?: Object + ): Promise { + throw new Error("Method not implemented."); + } + + registerChecker(checker: Checker) { + this.executor.registerChecker(checker); + } } diff --git a/src/cacheStore/context.ts b/src/cacheStore/context.ts index 3e554d3e..8dceb8ba 100644 --- a/src/cacheStore/context.ts +++ b/src/cacheStore/context.ts @@ -1,8 +1,23 @@ -import { EntityDef } from "oak-domain/lib/types/Entity"; -import { Context as BaseContext } from 'oak-memory-tree-store'; +import { EntityDict } from 'oak-domain/lib/types/Entity'; +import { RuntimeContext } from 'oak-general-business'; +import { BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict'; +import { Schema as Application } from 'oak-general-business/src/base-ed/Application/Schema'; +import { Schema as Token } from 'oak-general-business/src/base-ed/Token/Schema'; +import { Context } from 'oak-memory-tree-store'; +import { CacheStore } from './CacheStore'; -export class CacheContext extends BaseContext { +export class CacheContext extends Context implements RuntimeContext { + getApplication: () => Pick | undefined; + getToken: () => Pick | undefined; + constructor(store: CacheStore, application?: Pick, token?: Pick) { + super(store); + this.getApplication = () => application; + this.getToken = () => token; + } + + + on(event: "commit" | "rollback", callback: (context: any) => Promise): void { + throw new Error('disallow cross txn events in FrontContext'); + } }; \ No newline at end of file diff --git a/src/debugStore/context.ts b/src/debugStore/context.ts index e4e50cac..0e539432 100644 --- a/src/debugStore/context.ts +++ b/src/debugStore/context.ts @@ -8,8 +8,8 @@ import { DebugStore } from './debugStore'; import { RuntimeContext } from 'oak-general-business'; export class DebugContext extends BaseContext implements RuntimeContext { - getApplication: () => Application | undefined; - getToken: () => Token | undefined; + getApplication: () => Pick | undefined; + getToken: () => Pick | undefined; async initGetFn(applicationId?: string, tokenValue?: string) { if (applicationId) { diff --git a/src/debugStore/debugStore.ts b/src/debugStore/debugStore.ts index 547dc924..94f32c0d 100644 --- a/src/debugStore/debugStore.ts +++ b/src/debugStore/debugStore.ts @@ -3,7 +3,7 @@ import { BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict'; import { TreeStore } from 'oak-memory-tree-store'; import { DebugContext } from './context'; -import { TriggerExecutor, Trigger } from 'oak-general-business'; +import { TriggerExecutor, Trigger, Checker } from 'oak-general-business'; import { StorageSchema } from "oak-domain/lib/types/Storage"; export class DebugStore extends TreeStore { @@ -88,5 +88,9 @@ export class DebugStore extends TreeStor registerTrigger(trigger: Trigger) { this.executor.registerTrigger(trigger); } + + registerChecker(checker: Checker) { + this.executor.registerChecker(checker); + } } diff --git a/src/debugStore/index.ts b/src/debugStore/index.ts index 8ec55b33..5ac8e55a 100644 --- a/src/debugStore/index.ts +++ b/src/debugStore/index.ts @@ -4,9 +4,8 @@ import { DebugContext } from './context'; import { FormCreateData, Selection, EntityDict } from "oak-domain/lib/types/Entity"; import { BaseEntityDict as BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict'; import { StorageSchema } from 'oak-domain/lib/types/Storage'; -import { Trigger, TriggerExecutor } from 'oak-general-business'; -import { data as generalData, triggers as generalTriggers } from 'oak-general-business'; -import { FrontContext } from '../FrontContext'; +import { Checker, Trigger, TriggerExecutor } from 'oak-general-business'; +import { data as generalData, triggers as generalTriggers, checkers as generalCheckers } from 'oak-general-business'; async function initDataInStore(store: DebugStore, initialData?: { [T in keyof ED]?: Array>; @@ -36,19 +35,30 @@ async function initDataInStore(store: De } -export function createDebugStore(storageSchema: StorageSchema, triggers?: Array>, initialData?: { +export function createDebugStore( + storageSchema: StorageSchema, + triggers?: Array>, + checkers?: Array>, + initialData?: { [T in keyof ED]?: Array>; }){ const executor = new TriggerExecutor(); const store = new DebugStore(executor, storageSchema); - generalTriggers.forEach( - ele => store.registerTrigger(ele) + (generalTriggers).forEach( + ele => store.registerTrigger(ele as any) ); triggers?.forEach( ele => store.registerTrigger(ele) ); + generalCheckers.forEach( + ele => store.registerChecker(ele as any) + ); + checkers?.forEach( + ele => store.registerChecker(ele) + ); + // 如果有物化存储的数据使用此数据,否则使用initialData初始化debugStore initDataInStore(store, initialData); return store; diff --git a/src/features/cache.ts b/src/features/cache.ts index 869d929e..80bb49e5 100644 --- a/src/features/cache.ts +++ b/src/features/cache.ts @@ -1,18 +1,118 @@ import { DeduceSelection, EntityDict, OperateParams, OpRecord } from 'oak-domain/lib/types/Entity'; -import { Aspect } from 'oak-general-business'; +import { Aspect, Checker, checkers as generalCheckers } from 'oak-general-business'; import { Action, Feature } from '../types/Feature'; import { assign } from 'lodash'; -import { FrontContext } from '../FrontContext'; +import { CacheContext } from '../cacheStore/context'; +import { BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict'; import { CacheStore } from '../cacheStore/CacheStore'; +import { StorageSchema } from 'oak-domain/lib/types/Storage'; +import { Schema as Token } from 'oak-general-business/lib/base-ed/Token/Schema'; +import { Schema as Application } from 'oak-general-business/lib/base-ed/Application/Schema'; -export class Cache>> extends Feature { +export class Cache>> extends Feature { cacheStore: CacheStore; + applicationId: string; + tokenValue?: string; + token?: Pick; + application?: Pick; - constructor(cacheStore: CacheStore) { + private async getConstansData() { + await this.getAspectProxy()?.operate({ + entity: 'application', + operation: { + data: { + id: 1, + systemId: 1, + system: { + id: 1, + }, + }, + filter: { + id: this.applicationId, + }, + action: 'select', + } + }); + if (this.tokenValue) { + await this.getAspectProxy()?.operate({ + entity: 'token', + operation: { + action: 'select', + data: { + id: 1, + userId: 1, + user: { + id: 1, + nickname: 1, + }, + playerId: 1, + player: { + id: 1, + nickname: 1, + }, + }, + filter: { + id: this.tokenValue, + }, + } + }); + } + } + + private async setConstants(getData?: true) { + if (getData) { + await this.getConstansData(); + } + const context = new CacheContext(this.cacheStore, this.application, this.token); + if (this.tokenValue) { + const { result } = await this.cacheStore.select('token', { + data: { + id: 1, + userId: 1, + playerId: 1, + }, + filter: { + id: this.tokenValue, + } + }, context); + this.token = result[0] as any; + } + if (this.applicationId) { + const { result: [application] } = await this.cacheStore.select('application', { + data: { + id: 1, + systemId: 1, + system: { + id: 1, + }, + }, + filter: { + id: this.applicationId, + } + }, context); + this.application = application as any; + } + await context.commit(); + } + + constructor(storageSchema: StorageSchema, applicationId: string, checkers?: Array>, tokenValue?: string) { + const cacheStore = new CacheStore(storageSchema); + generalCheckers.forEach( + (checker) => cacheStore.registerChecker(checker as any) + ); + if (checkers) { + checkers.forEach( + (checker) => cacheStore.registerChecker(checker) + ); + } super(); this.cacheStore = cacheStore; + this.applicationId = applicationId; + this.tokenValue = tokenValue; + this.setConstants(true); } + @Action refresh(entity: T, selection: ED[T]['Selection'], params?: object) { return this.getAspectProxy().operate({ @@ -24,7 +124,7 @@ export class Cache>> @Action async sync(records: OpRecord[]) { - const context = new FrontContext(this.cacheStore); + const context = new CacheContext(this.cacheStore); try { await this.cacheStore.sync(records, context); } @@ -37,7 +137,7 @@ export class Cache>> @Action async operate(entity: T, operation: ED[T]['Operation'], commit: boolean = true, params?: OperateParams) { - const context = new FrontContext(this.cacheStore); + const context = new CacheContext(this.cacheStore); let result: Awaited>; try { result = await this.cacheStore.operate(entity, operation, context, params); @@ -55,9 +155,17 @@ export class Cache>> return result; } + + @Action + async loginByPassword(mobile: string, password: string) { + this.tokenValue = await this.getAspectProxy().loginByPassword({ mobile, password }); + await this.setConstants(); + return; + } + async get(options: { entity: T, selection: ED[T]['Selection'], params?: object }) { const { entity, selection, params } = options; - const context = new FrontContext(this.cacheStore); + const context = new CacheContext(this.cacheStore); const { result } = await this.cacheStore.select(entity, selection, context, params); return result; } @@ -65,4 +173,8 @@ export class Cache>> judgeRelation(entity: keyof ED, attr: string) { return this.cacheStore.judgeRelation(entity, attr); } + + getTokenValue() { + return this.tokenValue; + } } diff --git a/src/features/index.ts b/src/features/index.ts index b0323729..26bfe73e 100644 --- a/src/features/index.ts +++ b/src/features/index.ts @@ -1,24 +1,25 @@ import { EntityDict } from 'oak-domain/lib/types/Entity'; import { BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict'; -import { Aspect } from 'oak-general-business'; +import { Aspect, Checker } from 'oak-general-business'; import { Cache } from './cache'; import { Location } from './location'; -import { Token } from './token'; import { RunningNode } from './node'; import { CacheStore } from '../cacheStore/CacheStore'; +import { StorageSchema } from 'oak-domain/lib/types/Storage'; export function initialize>> (cacheStore: CacheStore): BasicFeatures { - const cache = new Cache(cacheStore); + AD extends Record>> ( + storageSchema: StorageSchema, + applicationId: string, + checkers?: Array>): BasicFeatures { + const cache = new Cache(storageSchema, applicationId, checkers); const location = new Location(); - const token = new Token(cache as any); const runningNode = new RunningNode(cache); return { cache, location, - token, runningNode, }; } @@ -26,6 +27,5 @@ export function initialize>> = { cache: Cache; location: Location; - token: Token; runningNode: RunningNode; }; diff --git a/src/features/node.ts b/src/features/node.ts index 8720bfa8..ad2886fe 100644 --- a/src/features/node.ts +++ b/src/features/node.ts @@ -1,5 +1,6 @@ import { set, cloneDeep, pull, unset } from 'lodash'; -import { DeduceCreateOperation, DeduceFilter, DeduceOperation, DeduceSelection, DeduceUpdateOperation, EntityDict, EntityShape, FormCreateData, OperationResult, OpRecord, SelectionResult } from 'oak-domain/lib/types/Entity'; +import { DeduceCreateOperation, DeduceFilter, DeduceOperation, DeduceSelection, DeduceUpdateOperation, EntityDict, EntityShape } from 'oak-domain/lib/types/Entity'; +import { BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict'; import { Aspect } from 'oak-general-business'; import { combineFilters } from 'oak-domain/lib/store/filter'; import { Action, Feature } from '../types/Feature'; @@ -95,7 +96,7 @@ const DEFAULT_PAGINATION: Pagination = { more: true, } -class ListNode>, T extends keyof ED> extends Node{ +class ListNode>, T extends keyof ED> extends Node{ private ids: string[]; protected children: SingleNode[]; protected value: Partial[]; @@ -240,7 +241,7 @@ declare type AttrFilter = { [K in keyof SH]: any; }; -class SingleNode>, T extends keyof ED> extends Node{ +class SingleNode>, T extends keyof ED> extends Node{ private id?: string; private value?: Partial; private children: { @@ -266,15 +267,15 @@ class SingleNode>, T } } - composeOperation(): DeduceOperation | undefined { - if (!this.isDirty()) { + composeOperation(action2?: string): DeduceOperation | undefined { + if (!this.isDirty() && !action2) { return; } const action = this.action === 'create' ? { action: 'create', data: cloneDeep(this.updateData) || {}, } as DeduceCreateOperation : { - action: this.action || 'update', + action: action2 || this.action || 'update', data: cloneDeep(this.updateData) || {}, filter: { id: this.id!, @@ -413,7 +414,7 @@ class SingleNode>, T } -export class RunningNode>> extends Feature { +export class RunningNode>> extends Feature { private cache: Cache; private schema?: StorageSchema; private root: Record | ListNode>; @@ -732,9 +733,9 @@ export class RunningNode { - tokenValue?: string; - cache: Cache; - - constructor(cache: Cache) { - super(); - this.cache = cache; - } - getValue() { - return this.tokenValue; - } - - async getToken() { - const [token] = await this.cache.get({ - entity: 'token', - selection: { - data: { - id: 1, - userId: 1, - user: { - id: 1, - name: 1, - nickname: 1, - }, - playerId: 1, - player: { - id: 1, - name: 1, - nickname: 1, - }, - }, - filter: { - id: this.tokenValue, - }, - }}); - return token; - } - - /* async get(type: 'value' | 'token') { - if (type === 'value') { - return this.tokenValue; - } - if (!this.tokenValue) { - return undefined; - } - const [token] = await this.cache.get({ - entity: 'token', - selection: { - data: { - id: 1, - userId: 1, - user: { - id: 1, - name: 1, - nickname: 1, - }, - playerId: 1, - player: { - id: 1, - name: 1, - nickname: 1, - }, - }, - filter: { - id: this.tokenValue, - }, - }}); - return token; - } */ - - @Action - async loginByPassword(mobile: string, password: string) { - this.tokenValue = await this.getAspectProxy().loginByPassword({ mobile, password }); - return; - } -} diff --git a/src/index.ts b/src/index.ts index 514e998b..f39c00fd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import { StorageSchema } from 'oak-domain/lib/types/Storage'; -import { Trigger } from "oak-general-business"; +import { Checker, Trigger } from "oak-general-business"; import { BaseEntityDict as BaseEntityDict } from 'oak-general-business/lib/base-ed/EntityDict'; import { aspectDict as basicAspectDict } from 'oak-general-business'; @@ -19,6 +19,7 @@ function createAspectProxy>>( storageSchema: StorageSchema, triggers: Array>, + checkers: Array>, applicationId: string, features: BasicFeatures & FD, aspectDict?: AD, @@ -31,11 +32,11 @@ function createAspectProxy): (p: Parameters[0]) => ReturnType => { return async (params: Parameters[0]) => { - const tokenValue = await features.token.getValue(); + const tokenValue = features.cache.getTokenValue(); const runningContext = new DebugContext(debugStore, applicationId, tokenValue); await runningContext.begin(); @@ -70,12 +71,12 @@ export function initialize) => FD, triggers?: Array>, + checkers?: Array>, aspectDict?: AD, initialData?: { [T in keyof ED]?: Array; }) { - const cacheStore = new CacheStore(storageSchema); - const basicFeatures = createBasicFeatures(cacheStore); + const basicFeatures = createBasicFeatures(storageSchema, applicationId, checkers); basicFeatures.runningNode.setStorageSchema(storageSchema); const userDefinedfeatures = createFeatures(basicFeatures); @@ -88,7 +89,7 @@ export function initialize(storageSchema, triggers || [], + const aspectProxy = createAspectProxy(storageSchema, triggers || [], checkers || [], applicationId, features, aspectDict, initialData); keys(features).forEach( @@ -107,7 +108,6 @@ export function initialize>> = { [K in keyof AD]: (p: Parameters[0]) => ReturnType; diff --git a/src/types/Feature.ts b/src/types/Feature.ts index 2c4af254..8df6fcd0 100644 --- a/src/types/Feature.ts +++ b/src/types/Feature.ts @@ -2,7 +2,6 @@ import { pull } from 'lodash'; import { Aspect } from 'oak-general-business'; import { EntityDict } from 'oak-domain/lib/types/Entity'; import { aspectDict as basicAspectDict} from 'oak-general-business'; -import { FrontContext } from '../FrontContext'; import { AspectProxy } from './AspectProxy'; diff --git a/test/test.ts b/test/test.ts index 3feac25a..d5f59e1b 100644 --- a/test/test.ts +++ b/test/test.ts @@ -1,7 +1,9 @@ -import { unset } from 'lodash'; +type A = { + name: 'bbb' + [k: string]: string; +}; -const test = [, 'xc', 1234]; -unset(test, 1); -console.log(test[1]); +type B = keyof A; -console.log('xc'.lastIndexOf('.')); + +const b: B = 1; \ No newline at end of file