@ruiapp/rapid-core 0.0.2 → 0.1.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.
- package/dist/core/request.d.ts +4 -2
- package/dist/core/server.d.ts +1 -0
- package/dist/dataAccess/dataAccessor.d.ts +2 -2
- package/dist/index.js +231 -147
- package/dist/queryBuilder/queryBuilder.d.ts +1 -0
- package/dist/server.d.ts +2 -1
- package/dist/types.d.ts +2 -1
- package/package.json +1 -1
- package/src/bootstrapApplicationConfig.ts +3 -3
- package/src/core/request.ts +36 -5
- package/src/core/response.ts +2 -2
- package/src/core/routeContext.ts +5 -0
- package/src/core/routesBuilder.ts +12 -1
- package/src/core/server.ts +1 -0
- package/src/dataAccess/dataAccessor.ts +2 -1
- package/src/plugins/authManager/routes/getMyProfile.ts +1 -1
- package/src/plugins/authManager/routes/signin.ts +1 -1
- package/src/plugins/authManager/routes/signout.ts +1 -1
- package/src/plugins/dataManager/mod.ts +2 -2
- package/src/plugins/metaManager/mod.ts +3 -3
- package/src/plugins/webhooks/mod.ts +3 -3
- package/src/queryBuilder/queryBuilder.ts +15 -9
- package/src/server.ts +37 -1
- package/src/types.ts +2 -1
package/dist/core/request.d.ts
CHANGED
|
@@ -3,13 +3,15 @@ export declare const GlobalRequest: {
|
|
|
3
3
|
prototype: Request;
|
|
4
4
|
};
|
|
5
5
|
export interface RapidRequestBody {
|
|
6
|
-
type:
|
|
6
|
+
type: "form-data" | "json";
|
|
7
7
|
value: any;
|
|
8
8
|
}
|
|
9
9
|
export declare class RapidRequest {
|
|
10
|
+
#private;
|
|
10
11
|
method: string;
|
|
11
12
|
url: URL;
|
|
13
|
+
headers: Headers;
|
|
12
14
|
hasBody: boolean;
|
|
13
|
-
constructor(
|
|
15
|
+
constructor(req: Request);
|
|
14
16
|
body(): RapidRequestBody;
|
|
15
17
|
}
|
package/dist/core/server.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface IRpdServer {
|
|
|
12
12
|
getHttpHandlerByCode(code: string): HttpRequestHandler | undefined;
|
|
13
13
|
getDataAccessor<T = any>(options: GetDataAccessorOptions): IRpdDataAccessor<T>;
|
|
14
14
|
getApplicationConfig(): RpdApplicationConfig;
|
|
15
|
+
appendApplicationConfig(config: Partial<RpdApplicationConfig>): any;
|
|
15
16
|
getModel(options: GetModelOptions): RpdDataModel | undefined;
|
|
16
17
|
registerEventHandler<K extends keyof RpdServerEventTypes>(eventName: K, listener: (...args: RpdServerEventTypes[K]) => void): this;
|
|
17
18
|
emitEvent<K extends keyof RpdServerEventTypes>(eventName: K, sender: IPluginInstance, payload: RpdServerEventTypes[K][1]): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CountEntityOptions, FindEntityOptions, IRpdDataAccessor, RpdDataModel } from "~/types";
|
|
1
|
+
import { CountEntityOptions, FindEntityOptions, IRpdDataAccessor, RpdDataModel, IDatabaseAccessor } from "~/types";
|
|
2
2
|
import QueryBuilder from "~/queryBuilder/queryBuilder";
|
|
3
3
|
export interface IDataAccessorOptions {
|
|
4
4
|
model: RpdDataModel;
|
|
@@ -6,7 +6,7 @@ export interface IDataAccessorOptions {
|
|
|
6
6
|
}
|
|
7
7
|
export default class DataAccessor<T = any> implements IRpdDataAccessor<T> {
|
|
8
8
|
#private;
|
|
9
|
-
constructor(options: IDataAccessorOptions);
|
|
9
|
+
constructor(databaseAccessor: IDatabaseAccessor, options: IDataAccessorOptions);
|
|
10
10
|
getModel(): RpdDataModel;
|
|
11
11
|
create(entity: Partial<T>): Promise<T>;
|
|
12
12
|
updateById(id: any, entity: Partial<T>): Promise<{
|
package/dist/index.js
CHANGED
|
@@ -41,7 +41,8 @@ class DataAccessor {
|
|
|
41
41
|
#model;
|
|
42
42
|
#queryBuilder;
|
|
43
43
|
#databaseAccessor;
|
|
44
|
-
constructor(options) {
|
|
44
|
+
constructor(databaseAccessor, options) {
|
|
45
|
+
this.#databaseAccessor = databaseAccessor;
|
|
45
46
|
this.#queryBuilder = options.queryBuilder;
|
|
46
47
|
this.#model = options.model;
|
|
47
48
|
}
|
|
@@ -153,6 +154,7 @@ class QueryBuilder {
|
|
|
153
154
|
}
|
|
154
155
|
select(model, options) {
|
|
155
156
|
const ctx = {
|
|
157
|
+
builder: this,
|
|
156
158
|
params: [],
|
|
157
159
|
};
|
|
158
160
|
let { properties, filters, orderBy, pagination } = options;
|
|
@@ -191,6 +193,7 @@ class QueryBuilder {
|
|
|
191
193
|
}
|
|
192
194
|
count(model, options) {
|
|
193
195
|
const ctx = {
|
|
196
|
+
builder: this,
|
|
194
197
|
params: [],
|
|
195
198
|
};
|
|
196
199
|
let { filters } = options;
|
|
@@ -208,6 +211,7 @@ class QueryBuilder {
|
|
|
208
211
|
insert(model, options) {
|
|
209
212
|
const params = [];
|
|
210
213
|
const ctx = {
|
|
214
|
+
builder: this,
|
|
211
215
|
params,
|
|
212
216
|
};
|
|
213
217
|
const { entity } = options;
|
|
@@ -242,6 +246,7 @@ class QueryBuilder {
|
|
|
242
246
|
update(model, options) {
|
|
243
247
|
const params = [];
|
|
244
248
|
const ctx = {
|
|
249
|
+
builder: this,
|
|
245
250
|
params,
|
|
246
251
|
};
|
|
247
252
|
let { entity, filters } = options;
|
|
@@ -279,6 +284,7 @@ class QueryBuilder {
|
|
|
279
284
|
delete(model, options) {
|
|
280
285
|
const params = [];
|
|
281
286
|
const ctx = {
|
|
287
|
+
builder: this,
|
|
282
288
|
params,
|
|
283
289
|
};
|
|
284
290
|
let { filters } = options;
|
|
@@ -353,7 +359,7 @@ function buildLogicalFilterQuery(level, ctx, filter) {
|
|
|
353
359
|
return command;
|
|
354
360
|
}
|
|
355
361
|
function buildUnaryFilterQuery(ctx, filter) {
|
|
356
|
-
let command =
|
|
362
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
357
363
|
if (filter.operator === "null") {
|
|
358
364
|
command += " IS NULL";
|
|
359
365
|
}
|
|
@@ -363,7 +369,7 @@ function buildUnaryFilterQuery(ctx, filter) {
|
|
|
363
369
|
return command;
|
|
364
370
|
}
|
|
365
371
|
function buildInFilterQuery(ctx, filter) {
|
|
366
|
-
let command =
|
|
372
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
367
373
|
if (filter.operator === "in") {
|
|
368
374
|
command += " = ";
|
|
369
375
|
}
|
|
@@ -375,49 +381,49 @@ function buildInFilterQuery(ctx, filter) {
|
|
|
375
381
|
return command;
|
|
376
382
|
}
|
|
377
383
|
function buildContainsFilterQuery(ctx, filter) {
|
|
378
|
-
let command =
|
|
384
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
379
385
|
command += " LIKE ";
|
|
380
386
|
ctx.params.push(`%${filter.value}%`);
|
|
381
387
|
command += "$" + ctx.params.length;
|
|
382
388
|
return command;
|
|
383
389
|
}
|
|
384
390
|
function buildNotContainsFilterQuery(ctx, filter) {
|
|
385
|
-
let command =
|
|
391
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
386
392
|
command += " NOT LIKE ";
|
|
387
393
|
ctx.params.push(`%${filter.value}%`);
|
|
388
394
|
command += "$" + ctx.params.length;
|
|
389
395
|
return command;
|
|
390
396
|
}
|
|
391
397
|
function buildStartsWithFilterQuery(ctx, filter) {
|
|
392
|
-
let command =
|
|
398
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
393
399
|
command += " LIKE ";
|
|
394
400
|
ctx.params.push(`${filter.value}%`);
|
|
395
401
|
command += "$" + ctx.params.length;
|
|
396
402
|
return command;
|
|
397
403
|
}
|
|
398
404
|
function buildNotStartsWithFilterQuery(ctx, filter) {
|
|
399
|
-
let command =
|
|
405
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
400
406
|
command += " NOT LIKE ";
|
|
401
407
|
ctx.params.push(`${filter.value}%`);
|
|
402
408
|
command += "$" + ctx.params.length;
|
|
403
409
|
return command;
|
|
404
410
|
}
|
|
405
411
|
function buildEndsWithFilterQuery(ctx, filter) {
|
|
406
|
-
let command =
|
|
412
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
407
413
|
command += " LIKE ";
|
|
408
414
|
ctx.params.push(`%${filter.value}`);
|
|
409
415
|
command += "$" + ctx.params.length;
|
|
410
416
|
return command;
|
|
411
417
|
}
|
|
412
418
|
function buildNotEndsWithFilterQuery(ctx, filter) {
|
|
413
|
-
let command =
|
|
419
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
414
420
|
command += " NOT LIKE ";
|
|
415
421
|
ctx.params.push(`%${filter.value}`);
|
|
416
422
|
command += "$" + ctx.params.length;
|
|
417
423
|
return command;
|
|
418
424
|
}
|
|
419
425
|
function buildRelationalFilterQuery(ctx, filter) {
|
|
420
|
-
let command =
|
|
426
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
421
427
|
command += relationalOperatorsMap.get(filter.operator);
|
|
422
428
|
ctx.params.push(filter.value);
|
|
423
429
|
command += "$" + ctx.params.length;
|
|
@@ -1124,10 +1130,10 @@ async function handleEntityDeleteEvent(server, sender, payload) {
|
|
|
1124
1130
|
async function configureModels$3(server, applicationConfig) {
|
|
1125
1131
|
try {
|
|
1126
1132
|
const models = await listCollections(server, applicationConfig);
|
|
1127
|
-
|
|
1133
|
+
server.appendApplicationConfig({ models });
|
|
1128
1134
|
}
|
|
1129
1135
|
catch (ex) {
|
|
1130
|
-
console.warn("Failed to loading existing meta of models.", ex
|
|
1136
|
+
console.warn("Failed to loading existing meta of models.", ex);
|
|
1131
1137
|
}
|
|
1132
1138
|
}
|
|
1133
1139
|
function listCollections(server, applicationConfig) {
|
|
@@ -1808,7 +1814,7 @@ const routeConfigs = [
|
|
|
1808
1814
|
},
|
|
1809
1815
|
{
|
|
1810
1816
|
code: "updateById",
|
|
1811
|
-
method: "
|
|
1817
|
+
method: "patch",
|
|
1812
1818
|
endpoint: "/:id",
|
|
1813
1819
|
handlerCode: "updateCollectionEntityById",
|
|
1814
1820
|
},
|
|
@@ -1846,7 +1852,7 @@ async function configureRoutes$3(server, applicationConfig) {
|
|
|
1846
1852
|
code: `${namespace}.${singularCode}.${routeConfig.code}`,
|
|
1847
1853
|
type: "RESTful",
|
|
1848
1854
|
method: routeConfig.method,
|
|
1849
|
-
endpoint:
|
|
1855
|
+
endpoint: `/${namespace}/${pluralCode}${routeConfig.endpoint}`,
|
|
1850
1856
|
handlers: [
|
|
1851
1857
|
{
|
|
1852
1858
|
code: routeConfig.handlerCode,
|
|
@@ -1953,11 +1959,21 @@ var listMetaRoutes = /*#__PURE__*/Object.freeze({
|
|
|
1953
1959
|
|
|
1954
1960
|
async function buildRoutes(server, applicationConfig) {
|
|
1955
1961
|
const router = new Router__default["default"]();
|
|
1962
|
+
let baseUrl = server.config.baseUrl;
|
|
1963
|
+
if (baseUrl) {
|
|
1964
|
+
if (baseUrl.endsWith('/')) {
|
|
1965
|
+
baseUrl = baseUrl.substring(0, baseUrl.length - 1);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
else {
|
|
1969
|
+
baseUrl = "";
|
|
1970
|
+
}
|
|
1956
1971
|
applicationConfig.routes.forEach((routeConfig) => {
|
|
1957
1972
|
if (routeConfig.type !== "RESTful") {
|
|
1958
1973
|
return;
|
|
1959
1974
|
}
|
|
1960
|
-
|
|
1975
|
+
const routePath = baseUrl + routeConfig.endpoint;
|
|
1976
|
+
router[routeConfig.method](routePath, async (routerContext, next) => {
|
|
1961
1977
|
const { request, params } = routerContext;
|
|
1962
1978
|
let search = request.url.search;
|
|
1963
1979
|
if (search && search.startsWith("?")) {
|
|
@@ -2170,9 +2186,9 @@ async function initPlugin$1(plugin, server) {
|
|
|
2170
2186
|
_plugin$1 = plugin;
|
|
2171
2187
|
}
|
|
2172
2188
|
async function configureModels$2(server, applicationConfig) {
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
}
|
|
2189
|
+
server.appendApplicationConfig({
|
|
2190
|
+
models: pluginConfig.models,
|
|
2191
|
+
});
|
|
2176
2192
|
}
|
|
2177
2193
|
async function registerEventHandlers$2(server) {
|
|
2178
2194
|
const events = [
|
|
@@ -2631,7 +2647,7 @@ var getMyProfile = {
|
|
|
2631
2647
|
code: "",
|
|
2632
2648
|
type: "RESTful",
|
|
2633
2649
|
method: "get",
|
|
2634
|
-
endpoint: "/
|
|
2650
|
+
endpoint: "/me",
|
|
2635
2651
|
handlers: [
|
|
2636
2652
|
{
|
|
2637
2653
|
code: "getMyProfile",
|
|
@@ -2645,7 +2661,7 @@ var signin = {
|
|
|
2645
2661
|
code: "",
|
|
2646
2662
|
type: "RESTful",
|
|
2647
2663
|
method: "post",
|
|
2648
|
-
endpoint: "/
|
|
2664
|
+
endpoint: "/signin",
|
|
2649
2665
|
handlers: [
|
|
2650
2666
|
{
|
|
2651
2667
|
code: "createSession",
|
|
@@ -2659,7 +2675,7 @@ var signout = {
|
|
|
2659
2675
|
code: "",
|
|
2660
2676
|
type: "RESTful",
|
|
2661
2677
|
method: "get",
|
|
2662
|
-
endpoint: "/
|
|
2678
|
+
endpoint: "/signout",
|
|
2663
2679
|
handlers: [
|
|
2664
2680
|
{
|
|
2665
2681
|
code: "deleteSession",
|
|
@@ -2863,8 +2879,8 @@ class RapidResponse {
|
|
|
2863
2879
|
headers;
|
|
2864
2880
|
constructor(body, init) {
|
|
2865
2881
|
this.body = body;
|
|
2866
|
-
this.headers = new Headers(init
|
|
2867
|
-
this.status = init
|
|
2882
|
+
this.headers = new Headers(init?.headers);
|
|
2883
|
+
this.status = init?.status;
|
|
2868
2884
|
}
|
|
2869
2885
|
json(obj, status, headers) {
|
|
2870
2886
|
const body = JSON.stringify(obj);
|
|
@@ -2904,7 +2920,11 @@ class RouteContext {
|
|
|
2904
2920
|
this.request = request;
|
|
2905
2921
|
this.state = {};
|
|
2906
2922
|
this.response = new RapidResponse();
|
|
2923
|
+
// `method` and `path` are used by `koa-tree-router` to match route
|
|
2924
|
+
this.method = request.method;
|
|
2925
|
+
this.path = request.url.pathname;
|
|
2907
2926
|
}
|
|
2927
|
+
// `koa-tree-router` uses this method to set headers
|
|
2908
2928
|
set(headerName, headerValue) {
|
|
2909
2929
|
this.response.headers.set(headerName, headerValue);
|
|
2910
2930
|
}
|
|
@@ -2918,133 +2938,43 @@ class RouteContext {
|
|
|
2918
2938
|
|
|
2919
2939
|
const GlobalRequest = global.Request;
|
|
2920
2940
|
class RapidRequest {
|
|
2941
|
+
#raw;
|
|
2942
|
+
#bodyParsed;
|
|
2943
|
+
#body;
|
|
2921
2944
|
method;
|
|
2922
2945
|
url;
|
|
2946
|
+
headers;
|
|
2923
2947
|
hasBody;
|
|
2924
|
-
constructor(
|
|
2948
|
+
constructor(req) {
|
|
2949
|
+
this.#raw = req;
|
|
2950
|
+
this.method = req.method;
|
|
2951
|
+
this.url = new URL(req.url);
|
|
2952
|
+
this.headers = req.headers;
|
|
2953
|
+
this.hasBody = false;
|
|
2925
2954
|
}
|
|
2926
2955
|
body() {
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
this.#eventManager = new EventManager();
|
|
2947
|
-
this.#middlewares = [];
|
|
2948
|
-
this.#bootstrapApplicationConfig = options.applicationConfig;
|
|
2949
|
-
this.#applicationConfig = {};
|
|
2950
|
-
this.#httpHandlersMapByCode = new Map();
|
|
2951
|
-
this.#databaseAccessor = options.databaseAccessor;
|
|
2952
|
-
this.queryBuilder = new QueryBuilder({
|
|
2953
|
-
dbDefaultSchema: options.databaseConfig.dbDefaultSchema,
|
|
2954
|
-
});
|
|
2955
|
-
this.databaseConfig = options.databaseConfig;
|
|
2956
|
-
this.config = options.serverConfig;
|
|
2957
|
-
}
|
|
2958
|
-
getApplicationConfig() {
|
|
2959
|
-
return this.#applicationConfig;
|
|
2960
|
-
}
|
|
2961
|
-
registerHttpHandler(plugin, options) {
|
|
2962
|
-
const handler = ___namespace.bind(options.handler, null, plugin);
|
|
2963
|
-
this.#httpHandlersMapByCode.set(options.code, handler);
|
|
2964
|
-
}
|
|
2965
|
-
getHttpHandlerByCode(code) {
|
|
2966
|
-
return this.#httpHandlersMapByCode.get(code);
|
|
2967
|
-
}
|
|
2968
|
-
registerMiddleware(middleware) {
|
|
2969
|
-
this.#middlewares.push(middleware);
|
|
2970
|
-
}
|
|
2971
|
-
getDataAccessor(options) {
|
|
2972
|
-
const { namespace, singularCode } = options;
|
|
2973
|
-
// TODO: Should reuse the and DataAccessor instance
|
|
2974
|
-
const model = this.getModel(options);
|
|
2975
|
-
if (!model) {
|
|
2976
|
-
throw new Error(`Data model ${namespace}.${singularCode} not found.`);
|
|
2977
|
-
}
|
|
2978
|
-
const dataAccessor = new DataAccessor({
|
|
2979
|
-
model,
|
|
2980
|
-
queryBuilder: this.queryBuilder,
|
|
2981
|
-
});
|
|
2982
|
-
return dataAccessor;
|
|
2983
|
-
}
|
|
2984
|
-
getModel(options) {
|
|
2985
|
-
if (options.namespace) {
|
|
2986
|
-
return this.#applicationConfig?.models.find((e) => e.namespace === options.namespace && e.singularCode === options.singularCode);
|
|
2987
|
-
}
|
|
2988
|
-
return this.#applicationConfig?.models.find((e) => e.singularCode === options.singularCode);
|
|
2989
|
-
}
|
|
2990
|
-
registerEventHandler(eventName, listener) {
|
|
2991
|
-
this.#eventManager.on(eventName, listener);
|
|
2992
|
-
return this;
|
|
2993
|
-
}
|
|
2994
|
-
async emitEvent(eventName, sender, payload) {
|
|
2995
|
-
console.log(`Emit event "${eventName}"`, payload);
|
|
2996
|
-
await this.#eventManager.emit(eventName, sender, payload);
|
|
2997
|
-
// TODO: should move this logic into metaManager
|
|
2998
|
-
// if (
|
|
2999
|
-
// (eventName === "entity.create" || eventName === "entity.update" ||
|
|
3000
|
-
// eventName === "entity.delete") &&
|
|
3001
|
-
// payload.namespace === "meta"
|
|
3002
|
-
// ) {
|
|
3003
|
-
// await this.configureApplication();
|
|
3004
|
-
// }
|
|
3005
|
-
}
|
|
3006
|
-
async start() {
|
|
3007
|
-
console.log("Starting rapid server...");
|
|
3008
|
-
await loadPlugins();
|
|
3009
|
-
await initPlugins(this);
|
|
3010
|
-
await registerMiddlewares(this);
|
|
3011
|
-
await registerHttpHandlers(this);
|
|
3012
|
-
await registerEventHandlers(this);
|
|
3013
|
-
await registerMessageHandlers(this);
|
|
3014
|
-
await registerTaskProcessors(this);
|
|
3015
|
-
await this.configureApplication();
|
|
3016
|
-
console.log(`Application ready.`);
|
|
3017
|
-
await onApplicationReady(this, this.#applicationConfig);
|
|
3018
|
-
}
|
|
3019
|
-
async configureApplication() {
|
|
3020
|
-
this.#applicationConfig = ___namespace.merge({}, this.#bootstrapApplicationConfig);
|
|
3021
|
-
await onLoadingApplication(this, this.#applicationConfig);
|
|
3022
|
-
await configureModels(this, this.#applicationConfig);
|
|
3023
|
-
await configureModelProperties(this, this.#applicationConfig);
|
|
3024
|
-
await configureRoutes(this, this.#applicationConfig);
|
|
3025
|
-
// TODO: check application configuration.
|
|
3026
|
-
await onApplicationLoaded(this, this.#applicationConfig);
|
|
3027
|
-
this.#buildedRoutes = await buildRoutes(this, this.#applicationConfig);
|
|
3028
|
-
}
|
|
3029
|
-
async queryDatabaseObject(sql, params) {
|
|
3030
|
-
return await this.#databaseAccessor.queryDatabaseObject(sql, params);
|
|
3031
|
-
}
|
|
3032
|
-
async tryQueryDatabaseObject(sql, params) {
|
|
3033
|
-
try {
|
|
3034
|
-
return await this.queryDatabaseObject(sql, params);
|
|
3035
|
-
}
|
|
3036
|
-
catch (err) {
|
|
3037
|
-
console.error(err.message);
|
|
2956
|
+
if (this.#bodyParsed) {
|
|
2957
|
+
return this.#body;
|
|
2958
|
+
}
|
|
2959
|
+
this.#bodyParsed = true;
|
|
2960
|
+
if (this.method === "post" || this.method === "put" || this.method === "patch") {
|
|
2961
|
+
this.hasBody = true;
|
|
2962
|
+
const contentType = this.headers.get("Content-Type");
|
|
2963
|
+
if (contentType.includes("json")) {
|
|
2964
|
+
this.#body = {
|
|
2965
|
+
type: "json",
|
|
2966
|
+
value: this.#raw.json(),
|
|
2967
|
+
};
|
|
2968
|
+
}
|
|
2969
|
+
else if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
2970
|
+
this.#body = {
|
|
2971
|
+
type: "form-data",
|
|
2972
|
+
value: this.#raw.formData(),
|
|
2973
|
+
};
|
|
2974
|
+
}
|
|
3038
2975
|
}
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
get middlewares() {
|
|
3042
|
-
return this.#middlewares;
|
|
3043
|
-
}
|
|
3044
|
-
async handleRequest(request, next) {
|
|
3045
|
-
const routeContext = new RouteContext(new RapidRequest(request));
|
|
3046
|
-
await this.#buildedRoutes(routeContext, next);
|
|
3047
|
-
return routeContext.response.getResponse();
|
|
2976
|
+
this.#body = null;
|
|
2977
|
+
return this.#body;
|
|
3048
2978
|
}
|
|
3049
2979
|
}
|
|
3050
2980
|
|
|
@@ -3535,7 +3465,7 @@ var bootstrapApplicationConfig = {
|
|
|
3535
3465
|
code: "meta.model.list",
|
|
3536
3466
|
type: "RESTful",
|
|
3537
3467
|
method: "get",
|
|
3538
|
-
endpoint: "/
|
|
3468
|
+
endpoint: "/_meta/models",
|
|
3539
3469
|
handlers: [
|
|
3540
3470
|
{
|
|
3541
3471
|
code: "listMetaModels",
|
|
@@ -3548,7 +3478,7 @@ var bootstrapApplicationConfig = {
|
|
|
3548
3478
|
code: "meta.model.getDetail",
|
|
3549
3479
|
type: "RESTful",
|
|
3550
3480
|
method: "get",
|
|
3551
|
-
endpoint: "/
|
|
3481
|
+
endpoint: "/_meta/models/:namespace/:singularCode",
|
|
3552
3482
|
handlers: [
|
|
3553
3483
|
{
|
|
3554
3484
|
code: "getMetaModelDetail",
|
|
@@ -3561,7 +3491,7 @@ var bootstrapApplicationConfig = {
|
|
|
3561
3491
|
code: "meta.route.list",
|
|
3562
3492
|
type: "RESTful",
|
|
3563
3493
|
method: "get",
|
|
3564
|
-
endpoint: "/
|
|
3494
|
+
endpoint: "/_meta/routes",
|
|
3565
3495
|
handlers: [
|
|
3566
3496
|
{
|
|
3567
3497
|
code: "listMetaRoutes",
|
|
@@ -3576,6 +3506,160 @@ var bootstrapApplicationConfig$1 = /*#__PURE__*/Object.freeze({
|
|
|
3576
3506
|
'default': bootstrapApplicationConfig
|
|
3577
3507
|
});
|
|
3578
3508
|
|
|
3509
|
+
class RapidServer {
|
|
3510
|
+
#eventManager;
|
|
3511
|
+
#middlewares;
|
|
3512
|
+
#bootstrapApplicationConfig;
|
|
3513
|
+
#applicationConfig;
|
|
3514
|
+
#httpHandlersMapByCode;
|
|
3515
|
+
#databaseAccessor;
|
|
3516
|
+
queryBuilder;
|
|
3517
|
+
config;
|
|
3518
|
+
databaseConfig;
|
|
3519
|
+
#buildedRoutes;
|
|
3520
|
+
constructor(options) {
|
|
3521
|
+
this.#eventManager = new EventManager();
|
|
3522
|
+
this.#middlewares = [];
|
|
3523
|
+
this.#bootstrapApplicationConfig = options.applicationConfig || bootstrapApplicationConfig;
|
|
3524
|
+
this.#applicationConfig = {};
|
|
3525
|
+
this.#httpHandlersMapByCode = new Map();
|
|
3526
|
+
this.#databaseAccessor = options.databaseAccessor;
|
|
3527
|
+
this.queryBuilder = new QueryBuilder({
|
|
3528
|
+
dbDefaultSchema: options.databaseConfig.dbDefaultSchema,
|
|
3529
|
+
});
|
|
3530
|
+
this.databaseConfig = options.databaseConfig;
|
|
3531
|
+
this.config = options.serverConfig;
|
|
3532
|
+
}
|
|
3533
|
+
getApplicationConfig() {
|
|
3534
|
+
return this.#applicationConfig;
|
|
3535
|
+
}
|
|
3536
|
+
appendApplicationConfig(config) {
|
|
3537
|
+
const { models, routes } = config;
|
|
3538
|
+
if (models) {
|
|
3539
|
+
for (const model of models) {
|
|
3540
|
+
const originalModel = ___namespace.find(this.#applicationConfig.models, (item) => item.singularCode == model.singularCode);
|
|
3541
|
+
if (originalModel) {
|
|
3542
|
+
originalModel.name = model.name;
|
|
3543
|
+
const originalProperties = originalModel.properties;
|
|
3544
|
+
for (const property of model.properties) {
|
|
3545
|
+
const originalProperty = ___namespace.find(originalProperties, (item) => item.code == property.code);
|
|
3546
|
+
if (originalProperty) {
|
|
3547
|
+
originalProperty.name = property.name;
|
|
3548
|
+
}
|
|
3549
|
+
else {
|
|
3550
|
+
originalProperties.push(property);
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
else {
|
|
3555
|
+
this.#applicationConfig.models.push(model);
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3559
|
+
if (routes) {
|
|
3560
|
+
for (const route of routes) {
|
|
3561
|
+
const originalRoute = ___namespace.find(this.#applicationConfig.routes, (item) => item.code == route.code);
|
|
3562
|
+
if (originalRoute) {
|
|
3563
|
+
originalRoute.name = route.name;
|
|
3564
|
+
originalRoute.handlers = route.handlers;
|
|
3565
|
+
}
|
|
3566
|
+
else {
|
|
3567
|
+
this.#applicationConfig.routes.push(route);
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
registerHttpHandler(plugin, options) {
|
|
3573
|
+
const handler = ___namespace.bind(options.handler, null, plugin);
|
|
3574
|
+
this.#httpHandlersMapByCode.set(options.code, handler);
|
|
3575
|
+
}
|
|
3576
|
+
getHttpHandlerByCode(code) {
|
|
3577
|
+
return this.#httpHandlersMapByCode.get(code);
|
|
3578
|
+
}
|
|
3579
|
+
registerMiddleware(middleware) {
|
|
3580
|
+
this.#middlewares.push(middleware);
|
|
3581
|
+
}
|
|
3582
|
+
getDataAccessor(options) {
|
|
3583
|
+
const { namespace, singularCode } = options;
|
|
3584
|
+
// TODO: Should reuse the and DataAccessor instance
|
|
3585
|
+
const model = this.getModel(options);
|
|
3586
|
+
if (!model) {
|
|
3587
|
+
throw new Error(`Data model ${namespace}.${singularCode} not found.`);
|
|
3588
|
+
}
|
|
3589
|
+
const dataAccessor = new DataAccessor(this.#databaseAccessor, {
|
|
3590
|
+
model,
|
|
3591
|
+
queryBuilder: this.queryBuilder,
|
|
3592
|
+
});
|
|
3593
|
+
return dataAccessor;
|
|
3594
|
+
}
|
|
3595
|
+
getModel(options) {
|
|
3596
|
+
if (options.namespace) {
|
|
3597
|
+
return this.#applicationConfig?.models.find((e) => e.namespace === options.namespace && e.singularCode === options.singularCode);
|
|
3598
|
+
}
|
|
3599
|
+
return this.#applicationConfig?.models.find((e) => e.singularCode === options.singularCode);
|
|
3600
|
+
}
|
|
3601
|
+
registerEventHandler(eventName, listener) {
|
|
3602
|
+
this.#eventManager.on(eventName, listener);
|
|
3603
|
+
return this;
|
|
3604
|
+
}
|
|
3605
|
+
async emitEvent(eventName, sender, payload) {
|
|
3606
|
+
console.log(`Emit event "${eventName}"`, payload);
|
|
3607
|
+
await this.#eventManager.emit(eventName, sender, payload);
|
|
3608
|
+
// TODO: should move this logic into metaManager
|
|
3609
|
+
// if (
|
|
3610
|
+
// (eventName === "entity.create" || eventName === "entity.update" ||
|
|
3611
|
+
// eventName === "entity.delete") &&
|
|
3612
|
+
// payload.namespace === "meta"
|
|
3613
|
+
// ) {
|
|
3614
|
+
// await this.configureApplication();
|
|
3615
|
+
// }
|
|
3616
|
+
}
|
|
3617
|
+
async start() {
|
|
3618
|
+
console.log("Starting rapid server...");
|
|
3619
|
+
await loadPlugins();
|
|
3620
|
+
await initPlugins(this);
|
|
3621
|
+
await registerMiddlewares(this);
|
|
3622
|
+
await registerHttpHandlers(this);
|
|
3623
|
+
await registerEventHandlers(this);
|
|
3624
|
+
await registerMessageHandlers(this);
|
|
3625
|
+
await registerTaskProcessors(this);
|
|
3626
|
+
await this.configureApplication();
|
|
3627
|
+
console.log(`Application ready.`);
|
|
3628
|
+
await onApplicationReady(this, this.#applicationConfig);
|
|
3629
|
+
}
|
|
3630
|
+
async configureApplication() {
|
|
3631
|
+
this.#applicationConfig = ___namespace.merge({}, this.#bootstrapApplicationConfig);
|
|
3632
|
+
await onLoadingApplication(this, this.#applicationConfig);
|
|
3633
|
+
await configureModels(this, this.#applicationConfig);
|
|
3634
|
+
await configureModelProperties(this, this.#applicationConfig);
|
|
3635
|
+
await configureRoutes(this, this.#applicationConfig);
|
|
3636
|
+
// TODO: check application configuration.
|
|
3637
|
+
await onApplicationLoaded(this, this.#applicationConfig);
|
|
3638
|
+
this.#buildedRoutes = await buildRoutes(this, this.#applicationConfig);
|
|
3639
|
+
}
|
|
3640
|
+
async queryDatabaseObject(sql, params) {
|
|
3641
|
+
return await this.#databaseAccessor.queryDatabaseObject(sql, params);
|
|
3642
|
+
}
|
|
3643
|
+
async tryQueryDatabaseObject(sql, params) {
|
|
3644
|
+
try {
|
|
3645
|
+
return await this.queryDatabaseObject(sql, params);
|
|
3646
|
+
}
|
|
3647
|
+
catch (err) {
|
|
3648
|
+
console.error(err.message);
|
|
3649
|
+
}
|
|
3650
|
+
return [];
|
|
3651
|
+
}
|
|
3652
|
+
get middlewares() {
|
|
3653
|
+
return this.#middlewares;
|
|
3654
|
+
}
|
|
3655
|
+
async handleRequest(request, next) {
|
|
3656
|
+
const routeContext = new RouteContext(new RapidRequest(request));
|
|
3657
|
+
console.log('routeContext', routeContext);
|
|
3658
|
+
await this.#buildedRoutes(routeContext, next);
|
|
3659
|
+
return routeContext.response.getResponse();
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
|
|
3579
3663
|
fixBigIntJSONSerialize();
|
|
3580
3664
|
|
|
3581
3665
|
exports.GlobalRequest = GlobalRequest;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CountEntityOptions, DeleteEntityOptions, EntityFilterOptions, FindEntityOptions, CreateEntityOptions, RpdDataModel, UpdateEntityOptions, QuoteTableOptions } from "../types";
|
|
2
2
|
export interface BuildQueryContext {
|
|
3
|
+
builder: QueryBuilder;
|
|
3
4
|
params: any[];
|
|
4
5
|
}
|
|
5
6
|
export interface InitQueryBuilderOptions {
|
package/dist/server.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export interface InitServerOptions {
|
|
|
6
6
|
databaseAccessor: IDatabaseAccessor;
|
|
7
7
|
databaseConfig: IDatabaseConfig;
|
|
8
8
|
serverConfig: RapidServerConfig;
|
|
9
|
-
applicationConfig
|
|
9
|
+
applicationConfig?: RpdApplicationConfig;
|
|
10
10
|
}
|
|
11
11
|
export declare class RapidServer implements IRpdServer {
|
|
12
12
|
#private;
|
|
@@ -15,6 +15,7 @@ export declare class RapidServer implements IRpdServer {
|
|
|
15
15
|
databaseConfig: IDatabaseConfig;
|
|
16
16
|
constructor(options: InitServerOptions);
|
|
17
17
|
getApplicationConfig(): RpdApplicationConfig;
|
|
18
|
+
appendApplicationConfig(config: Partial<RpdApplicationConfig>): void;
|
|
18
19
|
registerHttpHandler(plugin: IPluginInstance, options: IPluginHttpHandler): void;
|
|
19
20
|
getHttpHandlerByCode(code: string): HttpRequestHandler;
|
|
20
21
|
registerMiddleware(middleware: any): void;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type RapidServerConfig = {
|
|
2
|
+
baseUrl?: string;
|
|
2
3
|
sessionCookieName: string;
|
|
3
4
|
jwtKey: string;
|
|
4
5
|
localFileStoragePath: string;
|
|
@@ -246,7 +247,7 @@ export interface RpdRoute {
|
|
|
246
247
|
endpoint: string;
|
|
247
248
|
handlers: RpdHttpHandler[];
|
|
248
249
|
}
|
|
249
|
-
export type RpdHttpMethod = "get" | "post" | "put" | "delete";
|
|
250
|
+
export type RpdHttpMethod = "get" | "post" | "put" | "delete" | "patch";
|
|
250
251
|
export interface RpdHttpHandler {
|
|
251
252
|
code: string;
|
|
252
253
|
config: any;
|
package/package.json
CHANGED
|
@@ -487,7 +487,7 @@ export default {
|
|
|
487
487
|
code: "meta.model.list",
|
|
488
488
|
type: "RESTful",
|
|
489
489
|
method: "get",
|
|
490
|
-
endpoint: "/
|
|
490
|
+
endpoint: "/_meta/models",
|
|
491
491
|
handlers: [
|
|
492
492
|
{
|
|
493
493
|
code: "listMetaModels",
|
|
@@ -500,7 +500,7 @@ export default {
|
|
|
500
500
|
code: "meta.model.getDetail",
|
|
501
501
|
type: "RESTful",
|
|
502
502
|
method: "get",
|
|
503
|
-
endpoint: "/
|
|
503
|
+
endpoint: "/_meta/models/:namespace/:singularCode",
|
|
504
504
|
handlers: [
|
|
505
505
|
{
|
|
506
506
|
code: "getMetaModelDetail",
|
|
@@ -513,7 +513,7 @@ export default {
|
|
|
513
513
|
code: "meta.route.list",
|
|
514
514
|
type: "RESTful",
|
|
515
515
|
method: "get",
|
|
516
|
-
endpoint: "/
|
|
516
|
+
endpoint: "/_meta/routes",
|
|
517
517
|
handlers: [
|
|
518
518
|
{
|
|
519
519
|
code: "listMetaRoutes",
|
package/src/core/request.ts
CHANGED
|
@@ -1,23 +1,54 @@
|
|
|
1
1
|
export const GlobalRequest = global.Request;
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
|
|
4
5
|
export interface RapidRequestBody {
|
|
5
|
-
type:
|
|
6
|
+
type: "form-data" | "json";
|
|
6
7
|
value: any;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export class RapidRequest {
|
|
11
|
+
#raw: Request;
|
|
12
|
+
#bodyParsed: boolean;
|
|
13
|
+
#body: RapidRequestBody;
|
|
10
14
|
method: string;
|
|
11
15
|
url: URL;
|
|
16
|
+
headers: Headers;
|
|
12
17
|
hasBody: boolean;
|
|
13
18
|
|
|
14
|
-
constructor(
|
|
19
|
+
constructor(req: Request) {
|
|
20
|
+
this.#raw = req;
|
|
21
|
+
this.method = req.method;
|
|
22
|
+
this.url = new URL(req.url);
|
|
23
|
+
this.headers = req.headers;
|
|
24
|
+
this.hasBody = false;
|
|
15
25
|
}
|
|
16
26
|
|
|
17
27
|
body(): RapidRequestBody {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
28
|
+
if (this.#bodyParsed) {
|
|
29
|
+
return this.#body;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
this.#bodyParsed = true;
|
|
33
|
+
|
|
34
|
+
if (this.method === "post" || this.method === "put" || this.method === "patch") {
|
|
35
|
+
this.hasBody = true;
|
|
36
|
+
|
|
37
|
+
const contentType = this.headers.get("Content-Type");
|
|
38
|
+
if (contentType.includes("json")) {
|
|
39
|
+
this.#body = {
|
|
40
|
+
type: "json",
|
|
41
|
+
value: this.#raw.json(),
|
|
42
|
+
};
|
|
43
|
+
} else if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
44
|
+
this.#body = {
|
|
45
|
+
type: "form-data",
|
|
46
|
+
value: this.#raw.formData(),
|
|
47
|
+
}
|
|
48
|
+
}
|
|
21
49
|
}
|
|
50
|
+
|
|
51
|
+
this.#body = null;
|
|
52
|
+
return this.#body;
|
|
22
53
|
}
|
|
23
54
|
}
|
package/src/core/response.ts
CHANGED
|
@@ -40,8 +40,8 @@ export class RapidResponse {
|
|
|
40
40
|
|
|
41
41
|
constructor(body?: BodyInit, init?: ResponseInit) {
|
|
42
42
|
this.body = body;
|
|
43
|
-
this.headers = new Headers(init
|
|
44
|
-
this.status = init
|
|
43
|
+
this.headers = new Headers(init?.headers);
|
|
44
|
+
this.status = init?.status;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
json(
|
package/src/core/routeContext.ts
CHANGED
|
@@ -18,8 +18,13 @@ export class RouteContext {
|
|
|
18
18
|
this.request = request;
|
|
19
19
|
this.state = {};
|
|
20
20
|
this.response = new RapidResponse();
|
|
21
|
+
|
|
22
|
+
// `method` and `path` are used by `koa-tree-router` to match route
|
|
23
|
+
this.method = request.method;
|
|
24
|
+
this.path = request.url.pathname;
|
|
21
25
|
}
|
|
22
26
|
|
|
27
|
+
// `koa-tree-router` uses this method to set headers
|
|
23
28
|
set(headerName: string, headerValue: string) {
|
|
24
29
|
this.response.headers.set(headerName, headerValue);
|
|
25
30
|
}
|
|
@@ -12,13 +12,24 @@ export async function buildRoutes(
|
|
|
12
12
|
) {
|
|
13
13
|
const router = new Router();
|
|
14
14
|
|
|
15
|
+
let baseUrl = server.config.baseUrl;
|
|
16
|
+
if (baseUrl) {
|
|
17
|
+
if (baseUrl.endsWith('/')) {
|
|
18
|
+
baseUrl = baseUrl.substring(0, baseUrl.length - 1);
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
baseUrl = "";
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
applicationConfig.routes.forEach((routeConfig) => {
|
|
16
25
|
if (routeConfig.type !== "RESTful") {
|
|
17
26
|
return;
|
|
18
27
|
}
|
|
19
28
|
|
|
29
|
+
const routePath = baseUrl + routeConfig.endpoint;
|
|
30
|
+
|
|
20
31
|
(router as any)[routeConfig.method](
|
|
21
|
-
|
|
32
|
+
routePath,
|
|
22
33
|
async (routerContext: RouteContext, next: Next) => {
|
|
23
34
|
const { request, params } = routerContext;
|
|
24
35
|
|
package/src/core/server.ts
CHANGED
|
@@ -24,6 +24,7 @@ export interface IRpdServer {
|
|
|
24
24
|
options: GetDataAccessorOptions,
|
|
25
25
|
): IRpdDataAccessor<T>;
|
|
26
26
|
getApplicationConfig(): RpdApplicationConfig;
|
|
27
|
+
appendApplicationConfig(config: Partial<RpdApplicationConfig>);
|
|
27
28
|
getModel(options: GetModelOptions): RpdDataModel | undefined;
|
|
28
29
|
registerEventHandler<K extends keyof RpdServerEventTypes>(
|
|
29
30
|
eventName: K,
|
|
@@ -20,7 +20,8 @@ export default class DataAccessor<T = any> implements IRpdDataAccessor<T> {
|
|
|
20
20
|
#queryBuilder: QueryBuilder;
|
|
21
21
|
#databaseAccessor: IDatabaseAccessor;
|
|
22
22
|
|
|
23
|
-
constructor(options: IDataAccessorOptions) {
|
|
23
|
+
constructor(databaseAccessor: IDatabaseAccessor, options: IDataAccessorOptions) {
|
|
24
|
+
this.#databaseAccessor = databaseAccessor;
|
|
24
25
|
this.#queryBuilder = options.queryBuilder;
|
|
25
26
|
this.#model = options.model;
|
|
26
27
|
}
|
|
@@ -80,7 +80,7 @@ const routeConfigs: {
|
|
|
80
80
|
},
|
|
81
81
|
{
|
|
82
82
|
code: "updateById",
|
|
83
|
-
method: "
|
|
83
|
+
method: "patch",
|
|
84
84
|
endpoint: "/:id",
|
|
85
85
|
handlerCode: "updateCollectionEntityById",
|
|
86
86
|
},
|
|
@@ -127,7 +127,7 @@ export async function configureRoutes(
|
|
|
127
127
|
code: `${namespace}.${singularCode}.${routeConfig.code}`,
|
|
128
128
|
type: "RESTful",
|
|
129
129
|
method: routeConfig.method,
|
|
130
|
-
endpoint:
|
|
130
|
+
endpoint: `/${namespace}/${pluralCode}${routeConfig.endpoint}`,
|
|
131
131
|
handlers: [
|
|
132
132
|
{
|
|
133
133
|
code: routeConfig.handlerCode,
|
|
@@ -155,10 +155,10 @@ export async function configureModels(
|
|
|
155
155
|
applicationConfig: RpdApplicationConfig,
|
|
156
156
|
) {
|
|
157
157
|
try {
|
|
158
|
-
const models = await listCollections(server, applicationConfig);
|
|
159
|
-
|
|
158
|
+
const models: RpdDataModel[] = await listCollections(server, applicationConfig);
|
|
159
|
+
server.appendApplicationConfig({ models });
|
|
160
160
|
} catch (ex) {
|
|
161
|
-
console.warn("Failed to loading existing meta of models.", ex
|
|
161
|
+
console.warn("Failed to loading existing meta of models.", ex);
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
164
|
|
|
@@ -44,9 +44,9 @@ export async function configureModels(
|
|
|
44
44
|
server: IRpdServer,
|
|
45
45
|
applicationConfig: RpdApplicationConfig,
|
|
46
46
|
) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
47
|
+
server.appendApplicationConfig({
|
|
48
|
+
models: pluginConfig.models,
|
|
49
|
+
});
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export async function registerEventHandlers(server: IRpdServer) {
|
|
@@ -32,6 +32,7 @@ const relationalOperatorsMap = new Map<EntityFilterRelationalOperators, string>(
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
export interface BuildQueryContext {
|
|
35
|
+
builder: QueryBuilder;
|
|
35
36
|
params: any[];
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -63,6 +64,7 @@ export default class QueryBuilder {
|
|
|
63
64
|
|
|
64
65
|
select(model: RpdDataModel, options: FindEntityOptions) {
|
|
65
66
|
const ctx: BuildQueryContext = {
|
|
67
|
+
builder: this,
|
|
66
68
|
params: [],
|
|
67
69
|
};
|
|
68
70
|
let { properties, filters, orderBy, pagination } = options;
|
|
@@ -107,6 +109,7 @@ export default class QueryBuilder {
|
|
|
107
109
|
|
|
108
110
|
count(model: RpdDataModel, options: CountEntityOptions) {
|
|
109
111
|
const ctx: BuildQueryContext = {
|
|
112
|
+
builder: this,
|
|
110
113
|
params: [],
|
|
111
114
|
};
|
|
112
115
|
let { filters } = options;
|
|
@@ -128,6 +131,7 @@ export default class QueryBuilder {
|
|
|
128
131
|
insert(model: RpdDataModel, options: CreateEntityOptions) {
|
|
129
132
|
const params: any[] = [];
|
|
130
133
|
const ctx: BuildQueryContext = {
|
|
134
|
+
builder: this,
|
|
131
135
|
params,
|
|
132
136
|
};
|
|
133
137
|
const { entity } = options;
|
|
@@ -171,6 +175,7 @@ export default class QueryBuilder {
|
|
|
171
175
|
update(model: RpdDataModel, options: UpdateEntityOptions) {
|
|
172
176
|
const params: any[] = [];
|
|
173
177
|
const ctx: BuildQueryContext = {
|
|
178
|
+
builder: this,
|
|
174
179
|
params,
|
|
175
180
|
};
|
|
176
181
|
let { entity, filters } = options;
|
|
@@ -218,6 +223,7 @@ export default class QueryBuilder {
|
|
|
218
223
|
delete(model: RpdDataModel, options: DeleteEntityOptions) {
|
|
219
224
|
const params: any[] = [];
|
|
220
225
|
const ctx: BuildQueryContext = {
|
|
226
|
+
builder: this,
|
|
221
227
|
params,
|
|
222
228
|
};
|
|
223
229
|
let { filters } = options;
|
|
@@ -305,7 +311,7 @@ function buildUnaryFilterQuery(
|
|
|
305
311
|
ctx: BuildQueryContext,
|
|
306
312
|
filter: FindEntityUnaryFilterOptions,
|
|
307
313
|
) {
|
|
308
|
-
let command =
|
|
314
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
309
315
|
if (filter.operator === "null") {
|
|
310
316
|
command += " IS NULL";
|
|
311
317
|
} else {
|
|
@@ -318,7 +324,7 @@ function buildInFilterQuery(
|
|
|
318
324
|
ctx: BuildQueryContext,
|
|
319
325
|
filter: FindEntitySetFilterOptions,
|
|
320
326
|
) {
|
|
321
|
-
let command =
|
|
327
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
322
328
|
|
|
323
329
|
if (filter.operator === "in") {
|
|
324
330
|
command += " = ";
|
|
@@ -335,7 +341,7 @@ function buildContainsFilterQuery(
|
|
|
335
341
|
ctx: BuildQueryContext,
|
|
336
342
|
filter: FindEntityRelationalFilterOptions,
|
|
337
343
|
) {
|
|
338
|
-
let command =
|
|
344
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
339
345
|
|
|
340
346
|
command += " LIKE ";
|
|
341
347
|
ctx.params.push(`%${filter.value}%`);
|
|
@@ -348,7 +354,7 @@ function buildNotContainsFilterQuery(
|
|
|
348
354
|
ctx: BuildQueryContext,
|
|
349
355
|
filter: FindEntityRelationalFilterOptions,
|
|
350
356
|
) {
|
|
351
|
-
let command =
|
|
357
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
352
358
|
|
|
353
359
|
command += " NOT LIKE ";
|
|
354
360
|
ctx.params.push(`%${filter.value}%`);
|
|
@@ -361,7 +367,7 @@ function buildStartsWithFilterQuery(
|
|
|
361
367
|
ctx: BuildQueryContext,
|
|
362
368
|
filter: FindEntityRelationalFilterOptions,
|
|
363
369
|
) {
|
|
364
|
-
let command =
|
|
370
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
365
371
|
|
|
366
372
|
command += " LIKE ";
|
|
367
373
|
ctx.params.push(`${filter.value}%`);
|
|
@@ -374,7 +380,7 @@ function buildNotStartsWithFilterQuery(
|
|
|
374
380
|
ctx: BuildQueryContext,
|
|
375
381
|
filter: FindEntityRelationalFilterOptions,
|
|
376
382
|
) {
|
|
377
|
-
let command =
|
|
383
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
378
384
|
|
|
379
385
|
command += " NOT LIKE ";
|
|
380
386
|
ctx.params.push(`${filter.value}%`);
|
|
@@ -387,7 +393,7 @@ function buildEndsWithFilterQuery(
|
|
|
387
393
|
ctx: BuildQueryContext,
|
|
388
394
|
filter: FindEntityRelationalFilterOptions,
|
|
389
395
|
) {
|
|
390
|
-
let command =
|
|
396
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
391
397
|
|
|
392
398
|
command += " LIKE ";
|
|
393
399
|
ctx.params.push(`%${filter.value}`);
|
|
@@ -400,7 +406,7 @@ function buildNotEndsWithFilterQuery(
|
|
|
400
406
|
ctx: BuildQueryContext,
|
|
401
407
|
filter: FindEntityRelationalFilterOptions,
|
|
402
408
|
) {
|
|
403
|
-
let command =
|
|
409
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
404
410
|
|
|
405
411
|
command += " NOT LIKE ";
|
|
406
412
|
ctx.params.push(`%${filter.value}`);
|
|
@@ -413,7 +419,7 @@ function buildRelationalFilterQuery(
|
|
|
413
419
|
ctx: BuildQueryContext,
|
|
414
420
|
filter: FindEntityRelationalFilterOptions,
|
|
415
421
|
) {
|
|
416
|
-
let command =
|
|
422
|
+
let command = ctx.builder.quoteObject(filter.field);
|
|
417
423
|
|
|
418
424
|
command += relationalOperatorsMap.get(filter.operator);
|
|
419
425
|
|
package/src/server.ts
CHANGED
|
@@ -63,6 +63,41 @@ export class RapidServer implements IRpdServer {
|
|
|
63
63
|
return this.#applicationConfig;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
appendApplicationConfig(config: Partial<RpdApplicationConfig>) {
|
|
67
|
+
const { models, routes } = config;
|
|
68
|
+
if (models) {
|
|
69
|
+
for (const model of models) {
|
|
70
|
+
const originalModel = _.find(this.#applicationConfig.models, (item) => item.singularCode == model.singularCode);
|
|
71
|
+
if (originalModel) {
|
|
72
|
+
originalModel.name = model.name;
|
|
73
|
+
const originalProperties = originalModel.properties;
|
|
74
|
+
for (const property of model.properties) {
|
|
75
|
+
const originalProperty = _.find(originalProperties, (item) => item.code == property.code);
|
|
76
|
+
if (originalProperty) {
|
|
77
|
+
originalProperty.name = property.name;
|
|
78
|
+
} else {
|
|
79
|
+
originalProperties.push(property);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
this.#applicationConfig.models.push(model);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (routes) {
|
|
89
|
+
for (const route of routes) {
|
|
90
|
+
const originalRoute = _.find(this.#applicationConfig.routes, (item) => item.code == route.code);
|
|
91
|
+
if (originalRoute) {
|
|
92
|
+
originalRoute.name = route.name;
|
|
93
|
+
originalRoute.handlers = route.handlers;
|
|
94
|
+
} else {
|
|
95
|
+
this.#applicationConfig.routes.push(route);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
66
101
|
registerHttpHandler(
|
|
67
102
|
plugin: IPluginInstance,
|
|
68
103
|
options: IPluginHttpHandler,
|
|
@@ -89,7 +124,7 @@ export class RapidServer implements IRpdServer {
|
|
|
89
124
|
throw new Error(`Data model ${namespace}.${singularCode} not found.`);
|
|
90
125
|
}
|
|
91
126
|
|
|
92
|
-
const dataAccessor = new DataAccessor<T>({
|
|
127
|
+
const dataAccessor = new DataAccessor<T>(this.#databaseAccessor, {
|
|
93
128
|
model,
|
|
94
129
|
queryBuilder: this.queryBuilder as QueryBuilder,
|
|
95
130
|
});
|
|
@@ -186,6 +221,7 @@ export class RapidServer implements IRpdServer {
|
|
|
186
221
|
|
|
187
222
|
async handleRequest(request: Request, next: Next) {
|
|
188
223
|
const routeContext = new RouteContext(new RapidRequest(request));
|
|
224
|
+
console.log('routeContext', routeContext)
|
|
189
225
|
await this.#buildedRoutes(routeContext, next);
|
|
190
226
|
return routeContext.response.getResponse();
|
|
191
227
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type RapidServerConfig = {
|
|
2
|
+
baseUrl?: string;
|
|
2
3
|
sessionCookieName: string;
|
|
3
4
|
jwtKey: string;
|
|
4
5
|
localFileStoragePath: string;
|
|
@@ -303,7 +304,7 @@ export interface RpdRoute {
|
|
|
303
304
|
handlers: RpdHttpHandler[];
|
|
304
305
|
}
|
|
305
306
|
|
|
306
|
-
export type RpdHttpMethod = "get" | "post" | "put" | "delete";
|
|
307
|
+
export type RpdHttpMethod = "get" | "post" | "put" | "delete" | "patch";
|
|
307
308
|
|
|
308
309
|
export interface RpdHttpHandler {
|
|
309
310
|
code: string;
|