oak-backend-base 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ import { AppLoader as GeneralAppLoader, RowStore, Context, EntityDict } from "oak-domain/lib/types";
2
+ import { MySQLConfiguration } from 'oak-db/lib/MySQL/types/Configuration';
3
+ export declare class AppLoader<ED extends EntityDict, Cxt extends Context<ED>> extends GeneralAppLoader<ED, Cxt> {
4
+ private dbStore;
5
+ private aspectDict;
6
+ private contextBuilder;
7
+ constructor(path: string, contextBuilder: (scene?: string) => (store: RowStore<ED, Cxt>) => Cxt, dbConfig: MySQLConfiguration);
8
+ mount(initialize?: true): Promise<void>;
9
+ unmount(): Promise<void>;
10
+ execAspect(name: string, context: Cxt, params?: any): Promise<any>;
11
+ initialize(dropIfExists?: boolean): Promise<void>;
12
+ getStore(): RowStore<ED, Cxt>;
13
+ }
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AppLoader = void 0;
7
+ const actionDef_1 = require("oak-domain/lib/store/actionDef");
8
+ const types_1 = require("oak-domain/lib/types");
9
+ const DbStore_1 = require("./DbStore");
10
+ const index_1 = __importDefault(require("oak-common-aspect/lib/index"));
11
+ function initTriggers(dbStore, path) {
12
+ const { triggers } = require(`${path}/lib/triggers/index`);
13
+ const { checkers } = require(`${path}/lib/checkers/index`);
14
+ const { ActionDefDict } = require(`${path}/lib/oak-app-domain/ActionDefDict`);
15
+ const { triggers: adTriggers, checkers: adCheckers } = (0, actionDef_1.analyzeActionDefDict)(dbStore.getSchema(), ActionDefDict);
16
+ triggers.forEach((trigger) => dbStore.registerTrigger(trigger));
17
+ adTriggers.forEach((trigger) => dbStore.registerTrigger(trigger));
18
+ checkers.forEach((checker) => dbStore.registerChecker(checker));
19
+ adCheckers.forEach((checker) => dbStore.registerChecker(checker));
20
+ }
21
+ class AppLoader extends types_1.AppLoader {
22
+ dbStore;
23
+ aspectDict;
24
+ contextBuilder;
25
+ constructor(path, contextBuilder, dbConfig) {
26
+ super(path);
27
+ const { storageSchema } = require(`${path}/lib/oak-app-domain/Storage`);
28
+ this.aspectDict = Object.assign({}, index_1.default, require(`${path}/lib/aspects/index`).aspectDict);
29
+ this.dbStore = new DbStore_1.DbStore(storageSchema, contextBuilder, dbConfig);
30
+ this.contextBuilder = contextBuilder;
31
+ }
32
+ async mount(initialize) {
33
+ const { path } = this;
34
+ if (!initialize) {
35
+ initTriggers(this.dbStore, path);
36
+ }
37
+ this.dbStore.connect();
38
+ }
39
+ async unmount() {
40
+ this.dbStore.disconnect();
41
+ }
42
+ async execAspect(name, context, params) {
43
+ const fn = this.aspectDict[name];
44
+ if (!fn) {
45
+ throw new Error(`不存在的接口名称: ${name}`);
46
+ }
47
+ return await fn(params, context);
48
+ }
49
+ async initialize(dropIfExists) {
50
+ await this.dbStore.initialize(dropIfExists);
51
+ const { data } = require(`${this.path}/lib/data/index`);
52
+ const context = this.contextBuilder()(this.dbStore);
53
+ await context.begin();
54
+ for (const entity in data) {
55
+ let rows = data[entity];
56
+ if (entity === 'area') {
57
+ // 对area暂时处理一下
58
+ rows = require('./data/area.json');
59
+ }
60
+ await this.dbStore.operate(entity, {
61
+ data: rows,
62
+ action: 'create',
63
+ }, context);
64
+ console.log(`data in ${entity} initialized!`);
65
+ }
66
+ await context.commit();
67
+ this.dbStore.disconnect();
68
+ }
69
+ getStore() {
70
+ return this.dbStore;
71
+ }
72
+ }
73
+ exports.AppLoader = AppLoader;
@@ -0,0 +1,6 @@
1
+ import { RowStore } from 'oak-domain/lib/types';
2
+ import { GeneralRuntimeContext } from 'oak-general-business';
3
+ import { EntityDict } from 'oak-general-business/lib/general-app-domain';
4
+ export declare class Context<ED extends EntityDict> extends GeneralRuntimeContext<ED> {
5
+ static FromCxtStr(cxtStr?: string): <ED extends EntityDict>(store: RowStore<ED, Context<ED>>) => Context<ED>;
6
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Context = void 0;
4
+ const oak_general_business_1 = require("oak-general-business");
5
+ class Context extends oak_general_business_1.GeneralRuntimeContext {
6
+ static FromCxtStr(cxtStr) {
7
+ const { token, applicationId, scene } = cxtStr ? oak_general_business_1.GeneralRuntimeContext.fromString(cxtStr) : {
8
+ token: undefined,
9
+ applicationId: undefined,
10
+ scene: undefined,
11
+ };
12
+ return (store) => {
13
+ const context = new Context(store, applicationId);
14
+ context.setScene(scene);
15
+ context.setToken(token);
16
+ return context;
17
+ };
18
+ }
19
+ }
20
+ exports.Context = Context;
@@ -0,0 +1,13 @@
1
+ import { MysqlStore, MySqlSelectOption, MysqlOperateOption } from 'oak-db';
2
+ import { EntityDict, Context, StorageSchema, SelectionResult, Trigger, Checker, SelectRowShape, RowStore } from 'oak-domain/lib/types';
3
+ import { MySQLConfiguration } from 'oak-db/lib/MySQL/types/Configuration';
4
+ export declare class DbStore<ED extends EntityDict, Cxt extends Context<ED>> extends MysqlStore<ED, Cxt> {
5
+ private executor;
6
+ constructor(storageSchema: StorageSchema<ED>, contextBuilder: (scene?: string) => (store: RowStore<ED, Cxt>) => Cxt, mysqlConfiguration: MySQLConfiguration);
7
+ protected cascadeUpdate<T extends keyof ED>(entity: T, operation: ED[T]['Create'] | ED[T]['Update'] | ED[T]['Remove'], context: Cxt, option?: MysqlOperateOption): Promise<import("oak-domain/lib/types").OperationResult<ED>>;
8
+ protected cascadeSelect<T extends keyof ED, S extends ED[T]["Selection"]>(entity: T, selection: S, context: Cxt, option?: MySqlSelectOption): Promise<SelectRowShape<ED[T]['Schema'], S['data']>[]>;
9
+ operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Cxt, params?: MysqlOperateOption): Promise<import("oak-domain/lib/types").OperationResult<ED>>;
10
+ select<T extends keyof ED, S extends ED[T]['Selection']>(entity: T, selection: S, context: Cxt, params?: MySqlSelectOption): Promise<SelectionResult<ED[T]["Schema"], S["data"]>>;
11
+ registerTrigger<T extends keyof ED>(trigger: Trigger<ED, T, Cxt>): void;
12
+ registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>): void;
13
+ }
package/lib/DbStore.js ADDED
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DbStore = void 0;
4
+ const oak_db_1 = require("oak-db");
5
+ const TriggerExecutor_1 = require("oak-domain/lib/store/TriggerExecutor");
6
+ class DbStore extends oak_db_1.MysqlStore {
7
+ executor;
8
+ constructor(storageSchema, contextBuilder, mysqlConfiguration) {
9
+ super(storageSchema, mysqlConfiguration);
10
+ this.executor = new TriggerExecutor_1.TriggerExecutor(async (scene) => contextBuilder(scene)(this));
11
+ }
12
+ async cascadeUpdate(entity, operation, context, option) {
13
+ await this.executor.preOperation(entity, operation, context, option);
14
+ const result = super.cascadeUpdate(entity, operation, context, option);
15
+ await this.executor.postOperation(entity, operation, context, option);
16
+ return result;
17
+ }
18
+ async cascadeSelect(entity, selection, context, option) {
19
+ const selection2 = Object.assign({
20
+ action: 'select',
21
+ }, selection);
22
+ if (!option?.ignoreTrigger) {
23
+ await this.executor.preOperation(entity, selection2, context, option);
24
+ }
25
+ const result = await super.cascadeSelect(entity, selection2, context, option);
26
+ if (!option?.ignoreTrigger) {
27
+ await this.executor.postOperation(entity, selection2, context, option, result);
28
+ }
29
+ return result;
30
+ }
31
+ async operate(entity, operation, context, params) {
32
+ const autoCommit = !context.getCurrentTxnId();
33
+ let result;
34
+ if (autoCommit) {
35
+ await context.begin();
36
+ }
37
+ try {
38
+ result = await super.operate(entity, operation, context, params);
39
+ }
40
+ catch (err) {
41
+ await context.rollback();
42
+ throw err;
43
+ }
44
+ if (autoCommit) {
45
+ await context.commit();
46
+ }
47
+ return result;
48
+ }
49
+ async select(entity, selection, context, params) {
50
+ const autoCommit = !context.getCurrentTxnId();
51
+ if (autoCommit) {
52
+ await context.begin();
53
+ }
54
+ let result;
55
+ try {
56
+ result = await super.select(entity, selection, context, params);
57
+ }
58
+ catch (err) {
59
+ await context.rollback();
60
+ throw err;
61
+ }
62
+ if (autoCommit) {
63
+ await context.commit();
64
+ }
65
+ return result;
66
+ }
67
+ registerTrigger(trigger) {
68
+ this.executor.registerTrigger(trigger);
69
+ }
70
+ registerChecker(checker) {
71
+ this.executor.registerChecker(checker);
72
+ }
73
+ }
74
+ exports.DbStore = DbStore;
package/lib/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { Context as BackendContext } from './BackendContext';
2
+ export { AppLoader } from './AppLoader';
package/lib/index.js ADDED
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AppLoader = exports.BackendContext = void 0;
4
+ var BackendContext_1 = require("./BackendContext");
5
+ Object.defineProperty(exports, "BackendContext", { enumerable: true, get: function () { return BackendContext_1.Context; } });
6
+ var AppLoader_1 = require("./AppLoader");
7
+ Object.defineProperty(exports, "AppLoader", { enumerable: true, get: function () { return AppLoader_1.AppLoader; } });
@@ -0,0 +1,3 @@
1
+ export declare type GenerateIdOption = {
2
+ shuffle?: boolean;
3
+ };
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const uuid_1 = require("uuid");
4
+ async function generateNewId(option) {
5
+ if (option?.shuffle && process.env.NODE_ENV === 'development') {
6
+ return (0, uuid_1.v4)();
7
+ }
8
+ return (0, uuid_1.v1)();
9
+ }
10
+ Object.assign(global, {
11
+ generateNewId,
12
+ });
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "oak-backend-base",
3
+ "version": "1.0.0",
4
+ "description": "oak-backend-base",
5
+ "main": "src/index",
6
+ "author": {
7
+ "name": "XuChang"
8
+ },
9
+ "scripts": {
10
+ "test": "ts-node test/test.ts",
11
+ "build": "tsc"
12
+ },
13
+ "dependencies": {
14
+ "koa": "^2.13.4",
15
+ "koa-body": "^5.0.0",
16
+ "koa-router": "^10.1.1",
17
+ "lodash": "^4.17.21",
18
+ "mysql": "^2.18.1",
19
+ "mysql2": "^2.3.3",
20
+ "oak-db": "^1.0.0",
21
+ "oak-domain": "^1.0.0",
22
+ "oak-general-business": "^1.0.0",
23
+ "oak-common-aspect": "^1.0.0",
24
+ "uuid": "^8.3.2"
25
+ },
26
+ "license": "ISC",
27
+ "devDependencies": {
28
+ "@types/koa": "^2.13.4",
29
+ "@types/koa-router": "^7.4.4",
30
+ "@types/node": "^17.0.40",
31
+ "@types/uuid": "^8.3.4",
32
+ "ts-node": "^10.8.1",
33
+ "typescript": "^4.7.3"
34
+ }
35
+ }
@@ -0,0 +1,85 @@
1
+ import { analyzeActionDefDict } from "oak-domain/lib/store/actionDef";
2
+ import { AppLoader as GeneralAppLoader, Trigger, Checker, Aspect, RowStore, Context, EntityDict } from "oak-domain/lib/types";
3
+ import { DbStore } from "./DbStore";
4
+ import generalAspectDict from 'oak-common-aspect/lib/index';
5
+ import { MySQLConfiguration } from 'oak-db/lib/MySQL/types/Configuration';
6
+
7
+ function initTriggers<ED extends EntityDict, Cxt extends Context<ED>>(dbStore: DbStore<ED, Cxt>, path: string) {
8
+ const { triggers } = require(`${path}/lib/triggers/index`);
9
+ const { checkers } = require(`${path}/lib/checkers/index`);
10
+ const { ActionDefDict } = require(`${path}/lib/oak-app-domain/ActionDefDict`);
11
+
12
+ const { triggers: adTriggers, checkers: adCheckers } = analyzeActionDefDict(dbStore.getSchema(), ActionDefDict);
13
+ triggers.forEach(
14
+ (trigger: Trigger<ED, keyof ED, Cxt>) => dbStore.registerTrigger(trigger)
15
+ );
16
+ adTriggers.forEach(
17
+ (trigger) => dbStore.registerTrigger(trigger)
18
+ );
19
+ checkers.forEach(
20
+ (checker: Checker<ED, keyof ED, Cxt>) => dbStore.registerChecker(checker)
21
+ );
22
+ adCheckers.forEach(
23
+ (checker) => dbStore.registerChecker(checker)
24
+ );
25
+ }
26
+
27
+
28
+ export class AppLoader<ED extends EntityDict, Cxt extends Context<ED>> extends GeneralAppLoader<ED, Cxt> {
29
+ private dbStore: DbStore<ED, Cxt>;
30
+ private aspectDict: Record<string, Aspect<ED, Cxt>>;
31
+ private contextBuilder: (scene?: string) => (store: RowStore<ED, Cxt>) => Cxt;
32
+ constructor(path: string, contextBuilder: (scene?: string) => (store: RowStore<ED, Cxt>) => Cxt, dbConfig: MySQLConfiguration) {
33
+ super(path);
34
+ const { storageSchema } = require(`${path}/lib/oak-app-domain/Storage`);
35
+ this.aspectDict = Object.assign({}, generalAspectDict, require(`${path}/lib/aspects/index`).aspectDict);
36
+ this.dbStore = new DbStore<ED, Cxt>(storageSchema, contextBuilder, dbConfig);
37
+ this.contextBuilder = contextBuilder;
38
+ }
39
+
40
+ async mount(initialize?: true) {
41
+ const { path } = this;
42
+ if (!initialize) {
43
+ initTriggers(this.dbStore, path);
44
+ }
45
+ this.dbStore.connect();
46
+ }
47
+
48
+ async unmount() {
49
+ this.dbStore.disconnect();
50
+ }
51
+
52
+ async execAspect(name: string, context: Cxt, params?: any): Promise<any> {
53
+ const fn = this.aspectDict[name];
54
+ if (!fn) {
55
+ throw new Error(`不存在的接口名称: ${name}`);
56
+ }
57
+ return await fn(params, context);
58
+ }
59
+
60
+ async initialize(dropIfExists?: boolean) {
61
+ await this.dbStore.initialize(dropIfExists);
62
+
63
+ const { data } = require(`${this.path}/lib/data/index`);
64
+ const context = this.contextBuilder()(this.dbStore);
65
+ await context.begin();
66
+ for (const entity in data) {
67
+ let rows = data[entity];
68
+ if (entity === 'area') {
69
+ // 对area暂时处理一下
70
+ rows = require('./data/area.json');
71
+ }
72
+ await this.dbStore.operate(entity as keyof ED, {
73
+ data: rows,
74
+ action: 'create',
75
+ } as any, context);
76
+ console.log(`data in ${entity} initialized!`);
77
+ }
78
+ await context.commit();
79
+ this.dbStore.disconnect();
80
+ }
81
+
82
+ getStore(): RowStore<ED, Cxt> {
83
+ return this.dbStore;
84
+ }
85
+ }
@@ -0,0 +1,23 @@
1
+ import { RowStore } from 'oak-domain/lib/types';
2
+ import { GeneralRuntimeContext } from 'oak-general-business';
3
+ import { EntityDict } from 'oak-general-business/lib/general-app-domain';
4
+
5
+ export class Context<ED extends EntityDict> extends GeneralRuntimeContext<ED> {
6
+ static FromCxtStr(cxtStr?: string){
7
+ const {
8
+ token,
9
+ applicationId,
10
+ scene
11
+ } = cxtStr ? GeneralRuntimeContext.fromString(cxtStr) : {
12
+ token: undefined,
13
+ applicationId: undefined,
14
+ scene: undefined,
15
+ };
16
+ return <ED extends EntityDict>(store: RowStore<ED, Context<ED>>) => {
17
+ const context = new Context<ED>(store, applicationId);
18
+ context.setScene(scene);
19
+ context.setToken(token);
20
+ return context;
21
+ };
22
+ }
23
+ }
package/src/DbStore.ts ADDED
@@ -0,0 +1,94 @@
1
+ import { MysqlStore, MySqlSelectOption, MysqlOperateOption } from 'oak-db';
2
+ import { EntityDict, Context, StorageSchema, OperateOption, SelectionResult, Trigger, Checker, SelectRowShape, RowStore } from 'oak-domain/lib/types';
3
+ import { TriggerExecutor } from 'oak-domain/lib/store/TriggerExecutor';
4
+ import { MySQLConfiguration, } from 'oak-db/lib/MySQL/types/Configuration';
5
+
6
+
7
+ export class DbStore<ED extends EntityDict, Cxt extends Context<ED>> extends MysqlStore<ED, Cxt> {
8
+ private executor: TriggerExecutor<ED, Cxt>;
9
+
10
+ constructor(storageSchema: StorageSchema<ED>, contextBuilder: (scene?: string) => (store: RowStore<ED, Cxt>) => Cxt, mysqlConfiguration: MySQLConfiguration) {
11
+ super(storageSchema, mysqlConfiguration);
12
+ this.executor = new TriggerExecutor(async (scene) => contextBuilder(scene)(this));
13
+ }
14
+
15
+
16
+ protected async cascadeUpdate<T extends keyof ED>(entity: T, operation: ED[T]['Create'] | ED[T]['Update'] | ED[T]['Remove'], context: Cxt, option?: MysqlOperateOption) {
17
+ await this.executor.preOperation(entity, operation, context, option);
18
+ const result = super.cascadeUpdate(entity, operation, context, option);
19
+ await this.executor.postOperation(entity, operation, context, option);
20
+ return result;
21
+ }
22
+
23
+ protected async cascadeSelect<T extends keyof ED, S extends ED[T]["Selection"]>(entity: T, selection: S, context: Cxt, option?: MySqlSelectOption): Promise<SelectRowShape<ED[T]['Schema'], S['data']>[]> {
24
+ const selection2 = Object.assign({
25
+ action: 'select',
26
+ }, selection) as ED[T]['Operation'];
27
+
28
+ if (!option?.ignoreTrigger) {
29
+ await this.executor.preOperation(entity, selection2, context, option);
30
+ }
31
+ const result = await super.cascadeSelect(entity, selection2 as any, context, option);
32
+ if (!option?.ignoreTrigger) {
33
+ await this.executor.postOperation(entity, selection2, context, option, result);
34
+ }
35
+ return result;
36
+ }
37
+
38
+ async operate<T extends keyof ED>(
39
+ entity: T,
40
+ operation: ED[T]['Operation'],
41
+ context: Cxt,
42
+ params?: MysqlOperateOption
43
+ ) {
44
+ const autoCommit = !context.getCurrentTxnId();
45
+ let result;
46
+ if (autoCommit) {
47
+ await context.begin();
48
+ }
49
+ try {
50
+ result = await super.operate(entity, operation, context, params);
51
+ }
52
+ catch (err) {
53
+ await context.rollback();
54
+ throw err;
55
+ }
56
+ if (autoCommit) {
57
+ await context.commit();
58
+ }
59
+ return result;
60
+ }
61
+
62
+ async select<T extends keyof ED, S extends ED[T]['Selection']>(
63
+ entity: T,
64
+ selection: S,
65
+ context: Cxt,
66
+ params?: MySqlSelectOption
67
+ ) {
68
+ const autoCommit = !context.getCurrentTxnId();
69
+ if (autoCommit) {
70
+ await context.begin();
71
+ }
72
+ let result: SelectionResult<ED[T]['Schema'], S['data']>;
73
+
74
+ try {
75
+ result = await super.select(entity, selection, context, params);
76
+ }
77
+ catch (err) {
78
+ await context.rollback();
79
+ throw err;
80
+ }
81
+ if (autoCommit) {
82
+ await context.commit();
83
+ }
84
+ return result;
85
+ }
86
+
87
+ registerTrigger<T extends keyof ED>(trigger: Trigger<ED, T, Cxt>) {
88
+ this.executor.registerTrigger(trigger);
89
+ }
90
+
91
+ registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>) {
92
+ this.executor.registerChecker(checker);
93
+ }
94
+ }