增加了SetFilters等功能

This commit is contained in:
Xu Chang 2022-04-09 17:25:41 +08:00
parent dd67a999c7
commit af4e19e43a
2 changed files with 174 additions and 89 deletions

View File

@ -1,5 +1,5 @@
import { set, cloneDeep, pull, unset } from 'lodash';
import { DeduceCreateOperation, DeduceFilter, DeduceOperation, DeduceUpdateOperation, EntityDict, EntityShape, FormCreateData, OperationResult, OpRecord, SelectionResult } from 'oak-domain/lib/types/Entity';
import { DeduceCreateOperation, DeduceFilter, DeduceOperation, DeduceSelection, DeduceUpdateOperation, EntityDict, EntityShape, FormCreateData, OperationResult, OpRecord, SelectionResult } from 'oak-domain/lib/types/Entity';
import { Aspect } from 'oak-general-business/lib/types/Aspect';
import { combineFilters } from 'oak-domain/lib/store/filter';
import { Action, Feature } from '../types/Feature';
@ -82,6 +82,10 @@ export class Node<ED extends EntityDict, AD extends Record<string, Aspect<ED>>,
getParent() {
return this.parent;
}
getProjection() {
return this.projection!;
}
}
const DEFAULT_PAGINATION: Pagination = {
@ -162,6 +166,10 @@ class ListNode<ED extends EntityDict, AD extends Record<string, Aspect<ED>>, T e
return combineFilters(this.filters);
}
setFilters(filters: DeduceFilter<ED[T]['Schema']>[]) {
this.filters = filters;
}
async refresh(cache: Cache<ED, AD>) {
const { filters, sorter, pagination, entity, projection } = this;
assert(projection);
@ -265,7 +273,7 @@ class SingleNode<ED extends EntityDict, AD extends Record<string, Aspect<ED>>, T
const action = this.action === 'create' ? {
action: 'create',
data: cloneDeep(this.updateData) || {},
} as DeduceCreateOperation<ED[T]['Schema']>: {
} as DeduceCreateOperation<ED[T]['Schema']> : {
action: this.action || 'update',
data: cloneDeep(this.updateData) || {},
filter: {
@ -290,7 +298,7 @@ class SingleNode<ED extends EntityDict, AD extends Record<string, Aspect<ED>>, T
[path]: node,
});
}
removeChild(path: string) {
const { children } = this;
assert(children[path]);
@ -311,21 +319,21 @@ class SingleNode<ED extends EntityDict, AD extends Record<string, Aspect<ED>>, T
// 新建对象并关联
assert(!this.value || this.value.entity);
const id = v4({ random: await getRandomValues(16) });
/* await cache.operate(this.entity, {
action: 'update',
data: {
entity: path as any,
[path]: {
action: 'create',
data: {
id,
},
},
},
filter: {
id: this.id!,
}
} as any); */
/* await cache.operate(this.entity, {
action: 'update',
data: {
entity: path as any,
[path]: {
action: 'create',
data: {
id,
},
},
},
filter: {
id: this.id!,
}
} as any); */
node = new SingleNode(path as any, `${this.fullPath}.${path}`, this.schema, undefined, this, id, 'create');
}
}
@ -417,7 +425,7 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
}
async createNode<T extends keyof ED>(path: string, parent?: string,
entity?: T, isList?: boolean, isPicker?: boolean, projection?: ED[T]['Selection']['data'],id?: string,
entity?: T, isList?: boolean, isPicker?: boolean, projection?: ED[T]['Selection']['data'], id?: string,
pagination?: Pagination, filters?: DeduceFilter<ED[T]['Schema']>[],
sorter?: ED[T]['Selection']['sorter']) {
let node: ListNode<ED, AD, T> | SingleNode<ED, AD, T>;
@ -432,16 +440,16 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
node = new ListNode<ED, AD, T>(entity2 as T, fullPath, this.schema!, projection, parentNode, pagination, filters, sorter);
}
else {
/* let id2: string = id || v4({ random: await getRandomValues(16) });
if (!id) {
// 如果!isList并且没有id说明是create在这里先插入cache
await context.rowStore.operate(entity2, {
action: 'create',
data: {
id: id2,
} as FormCreateData<ED[T]['OpSchema']>,
}, context);
} */
/* let id2: string = id || v4({ random: await getRandomValues(16) });
if (!id) {
// 如果!isList并且没有id说明是create在这里先插入cache
await context.rowStore.operate(entity2, {
action: 'create',
data: {
id: id2,
} as FormCreateData<ED[T]['OpSchema']>,
}, context);
} */
node = new SingleNode<ED, AD, T>(entity2 as T, fullPath, this.schema!, projection, parentNode, id, !id ? 'create' : undefined);
}
if (parentNode) {
@ -472,20 +480,20 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
}
}
@Action
async refresh(path: string) {
const node = await this.findNode(path);
await node.refresh(this.cache);
setStorageSchema(schema: StorageSchema<ED>) {
this.schema = schema;
}
private async applyAction<T extends keyof ED> (entity: T, value: Partial<ED[T]['Schema']> | Partial<ED[T]['Schema']>[] | undefined,
operation: DeduceOperation<ED[T]['Schema']> | DeduceOperation<ED[T]['Schema']>[]) : Promise<Partial<ED[T]['Schema']> | Partial<ED[T]['Schema']>[] | undefined> {
private async applyAction<T extends keyof ED>(
entity: T, value: Partial<ED[T]['Schema']> | Partial<ED[T]['Schema']>[] | undefined,
operation: DeduceOperation<ED[T]['Schema']> | DeduceOperation<ED[T]['Schema']>[],
projection: DeduceSelection<ED[T]['Schema']>['data']): Promise<Partial<ED[T]['Schema']> | Partial<ED[T]['Schema']>[] | undefined> {
if (operation instanceof Array) {
assert(value instanceof Array);
for (const action of operation) {
switch (action.action) {
case 'create': {
value.push(await this.applyAction(entity, undefined, action) as Partial<ED[T]['Schema']>);
value.push(await this.applyAction(entity, undefined, action, projection) as Partial<ED[T]['Schema']>);
}
case 'remove': {
const { filter } = action;
@ -501,8 +509,8 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
const row = value.find(
ele => ele.id === filter!.id
);
await this.applyAction(entity, row!, action);
}
await this.applyAction(entity, row!, action, projection);
}
}
}
return value;
@ -511,10 +519,10 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
if (value instanceof Array) {
// todo 这里还有种可能不是对所有的行只对指定id的行操作
return (await Promise.all(value.map(
async (row) => await this.applyAction(entity, row, operation)
async (row) => await this.applyAction(entity, row, operation, projection)
))).filter(
ele => !!ele
) as Partial<ED[T]['Schema']> [];
) as Partial<ED[T]['Schema']>[];
}
else {
const { action, data } = operation;
@ -526,51 +534,91 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
}
else if (relation === 2) {
// 基于entity/entityId的多对一
set(row, attr, await this.applyAction(attr, row[attr], actionData[attr]!));
if (row[attr]) {
assign(row, {
entity: attr,
entityId: row[attr]!['id'],
});
}
else {
assign(row, {
entity: undefined,
entityId: undefined,
});
if (projection[attr]) {
set(row, attr, await this.applyAction(attr, row[attr], actionData[attr]!, projection[attr]!));
if (row[attr]) {
assign(row, {
entity: attr,
entityId: row[attr]!['id'],
});
}
else {
assign(row, {
entity: undefined,
entityId: undefined,
});
}
}
}
else if (typeof relation === 'string') {
set(row, attr, await this.applyAction(relation, row[attr], actionData[attr]!));
if (row[attr]) {
assign(row, {
[`${attr}Id`]: row[attr]!['id'],
});
}
else {
assign(row, {
[`${attr}Id`]: undefined,
});
if (projection[attr]) {
set(row, attr, await this.applyAction(relation, row[attr], actionData[attr]!, projection[attr]!));
if (row[attr]) {
assign(row, {
[`${attr}Id`]: row[attr]!['id'],
});
}
else {
assign(row, {
[`${attr}Id`]: undefined,
});
}
}
}
else {
assert(relation instanceof Array);
set(row, attr, await this.applyAction(relation[0], row[attr], actionData[attr]!));
row[attr]!.forEach(
(ele: ED[keyof ED]['Schema']) => {
if (relation[1]) {
assign(ele, {
[relation[1]]: row.id,
});
if (projection[attr]) {
set(row, attr, await this.applyAction(relation[0], row[attr], actionData[attr]!, projection[attr]!));
row[attr]!.forEach(
(ele: ED[keyof ED]['Schema']) => {
if (relation[1]) {
assign(ele, {
[relation[1]]: row.id,
});
}
else {
assign(ele, {
entity,
entityId: row.id,
});
}
}
else {
assign(ele, {
entity,
entityId: row.id,
});
);
}
}
}
// 这里有一种额外的情况actionData中更新了某项外键而projection中有相应的请求
for (const attr in projection) {
if (!actionData.hasOwnProperty(attr)) {
const relation = judgeRelation(this.schema!, entity, attr);
if (relation === 2 &&
(actionData.hasOwnProperty('entity') || actionData.hasOwnProperty('entityId'))) {
const entity = actionData.entity || row.entity!;
const entityId = actionData.entityId || row.entityId!;
const [entityRow] = await this.cache.get({
entity,
selection: {
data: projection[attr]!,
filter: {
id: entityId,
} as any,
}
}
)
});
set(row, attr, entityRow);
}
else if (typeof relation === 'string' && actionData.hasOwnProperty(`${attr}Id`)) {
const [entityRow] = await this.cache.get({
entity: relation,
selection: {
data: projection[attr]!,
filter: {
id: actionData[`${attr}Id`],
} as any,
}
});
set(row, attr, entityRow);
}
}
}
}
@ -578,7 +626,7 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
case 'create': {
assert(!value);
const row = {
id: v4({ random: await getRandomValues(16)}),
id: v4({ random: await getRandomValues(16) }),
} as Partial<ED[T]['Schema']>;
await applyUpsert(row, data as DeduceUpdateOperation<ED[T]['Schema']>['data']);
return row;
@ -600,7 +648,8 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
let value = await node.getValue(this.cache);
if (node.isDirty()) {
const operation = node.composeAction();
value = (await this.applyAction(node.getEntity(), value, operation!));
const projection = node.getProjection();
value = (await this.applyAction(node.getEntity(), value, operation!, projection as any));
}
if (value instanceof Array) {
@ -616,13 +665,12 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
while (iter < paths.length) {
const childPath = paths[iter];
node = (await node.getChild(childPath))!;
iter ++;
iter++;
}
return node;
}
@Action
async setUpdateData(path: string, attr: string, value: any) {
protected async setUpdateDataInner(path: string, attr: string, value: any) {
let node = await this.findNode(path);
const attrSplit = attr.split('.');
let idx = 0;
@ -650,8 +698,33 @@ export class RunningNode<ED extends EntityDict, AD extends Record<string, Aspect
}
}
setStorageSchema(schema: StorageSchema<ED>) {
this.schema = schema;
@Action
async setUpdateData(path: string, attr: string, value: any) {
await this.setUpdateDataInner(path, attr, value);
}
@Action
async setMultipleData(path: string, data: [string, any][]) {
for (const d of data) {
await this.setUpdateDataInner(path, d[0], d[1]);
}
}
@Action
async refresh(path: string) {
const node = await this.findNode(path);
await node.refresh(this.cache);
}
@Action
async setFilters<T extends keyof ED>(path: string, filters: DeduceFilter<ED[T]['Schema']>[], refresh: boolean = true) {
const node = await this.findNode(path);
if (node instanceof ListNode) {
node.setFilters(filters);
if (refresh) {
await node.refresh(this.cache);
}
}
}
}

View File

@ -48,7 +48,7 @@ function createAspectProxy<ED extends BaseEntityDict & EntityDict,
}
else {
// todo initialData
const debugStore = createDebugStore(storageSchema, triggers, initialData);
const debugStore = createDebugStore(storageSchema, triggers, initialData);
const connectAspectToDebugStore = (aspect: Aspect<ED>): (p: Parameters<typeof aspect>[0]) => ReturnType<typeof aspect> => {
return async (params: Parameters<typeof aspect>[0]) => {
@ -85,11 +85,23 @@ function createAspectProxy<ED extends BaseEntityDict & EntityDict,
}
const getToken = () => token;
const runningContext = new DebugRuntimeContext(debugStore, getApplication, getToken)
const result = aspect(params, runningContext);
context.rowStore.sync(runningContext.opRecords, context);
return result;
const runningContext = new DebugRuntimeContext(debugStore, getApplication, getToken);
await runningContext.begin();
let aspectCompeleted = false;
try {
const result = await aspect(params, runningContext);
await runningContext.commit();
aspectCompeleted = true;
await context.rowStore.sync(runningContext.opRecords, context);
return result;
}
catch(err) {
if (!aspectCompeleted) {
await runningContext.rollback();
}
throw err;
}
}
};