ismx-nexo-node-app 0.3.24
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/js/api/Service.js +45 -0
- package/dist/js/api/ServiceRest.js +33 -0
- package/dist/js/api/ServiceRestFormal.js +97 -0
- package/dist/js/api/ServiceRestFormalTemplate.js +127 -0
- package/dist/js/business/Business.js +5 -0
- package/dist/js/business/BusinessErrors.js +101 -0
- package/dist/js/business/BusinessLogger.js +9 -0
- package/dist/js/business/BusinessProxy.js +90 -0
- package/dist/js/business/BusinessServer.js +97 -0
- package/dist/js/business/BusinessState.js +5 -0
- package/dist/js/business/BusinessThread.js +23 -0
- package/dist/js/business/utils/CryptoUtils.js +30 -0
- package/dist/js/business/utils/NumberUtils.js +8 -0
- package/dist/js/business/utils/StringUtils.js +20 -0
- package/dist/js/index.js +56 -0
- package/dist/js/repository/Repository.js +5 -0
- package/dist/js/repository/RepositoryPostgres.js +215 -0
- package/dist/js/repository/RepositoryRest.js +55 -0
- package/dist/js/repository/RepositoryRestFormal.js +46 -0
- package/dist/js/repository/utils/PostgresUtils.js +51 -0
- package/dist/js/repository/utils/QueryUtils.js +13 -0
- package/dist/types/api/Service.d.ts +45 -0
- package/dist/types/api/ServiceRest.d.ts +6 -0
- package/dist/types/api/ServiceRestFormal.d.ts +23 -0
- package/dist/types/api/ServiceRestFormalTemplate.d.ts +40 -0
- package/dist/types/business/Business.d.ts +2 -0
- package/dist/types/business/BusinessErrors.d.ts +39 -0
- package/dist/types/business/BusinessLogger.d.ts +3 -0
- package/dist/types/business/BusinessProxy.d.ts +15 -0
- package/dist/types/business/BusinessServer.d.ts +15 -0
- package/dist/types/business/BusinessState.d.ts +2 -0
- package/dist/types/business/BusinessThread.d.ts +8 -0
- package/dist/types/business/utils/CryptoUtils.d.ts +5 -0
- package/dist/types/business/utils/NumberUtils.d.ts +3 -0
- package/dist/types/business/utils/StringUtils.d.ts +5 -0
- package/dist/types/index.d.ts +35 -0
- package/dist/types/repository/Repository.d.ts +2 -0
- package/dist/types/repository/RepositoryPostgres.d.ts +75 -0
- package/dist/types/repository/RepositoryRest.d.ts +17 -0
- package/dist/types/repository/RepositoryRestFormal.d.ts +7 -0
- package/dist/types/repository/utils/PostgresUtils.d.ts +17 -0
- package/dist/types/repository/utils/QueryUtils.d.ts +5 -0
- package/package.json +35 -0
- package/src/main/node/api/Service.ts +55 -0
- package/src/main/node/api/ServiceRest.ts +19 -0
- package/src/main/node/api/ServiceRestFormal.ts +58 -0
- package/src/main/node/api/ServiceRestFormalTemplate.ts +133 -0
- package/src/main/node/business/Business.ts +3 -0
- package/src/main/node/business/BusinessErrors.ts +94 -0
- package/src/main/node/business/BusinessLogger.ts +6 -0
- package/src/main/node/business/BusinessProxy.ts +57 -0
- package/src/main/node/business/BusinessServer.ts +76 -0
- package/src/main/node/business/BusinessState.ts +4 -0
- package/src/main/node/business/BusinessThread.ts +19 -0
- package/src/main/node/business/utils/CryptoUtils.ts +32 -0
- package/src/main/node/business/utils/NumberUtils.ts +6 -0
- package/src/main/node/business/utils/StringUtils.ts +20 -0
- package/src/main/node/index.ts +45 -0
- package/src/main/node/repository/Repository.ts +3 -0
- package/src/main/node/repository/RepositoryPostgres.ts +246 -0
- package/src/main/node/repository/RepositoryRest.ts +70 -0
- package/src/main/node/repository/RepositoryRestFormal.ts +37 -0
- package/src/main/node/repository/utils/PostgresUtils.ts +59 -0
- package/src/main/node/repository/utils/QueryUtils.ts +10 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import Service, { HttpRequest, HttpResponse } from "../api/Service";
|
|
2
|
+
import Business from "./Business";
|
|
3
|
+
export default class BusinessServer extends Business {
|
|
4
|
+
private app;
|
|
5
|
+
protected onStarted?: () => any;
|
|
6
|
+
protected onRequest?: (request: HttpRequest) => any;
|
|
7
|
+
protected onResponse?: (request: HttpRequest, response: HttpResponse) => HttpResponse<any> | undefined;
|
|
8
|
+
protected onError?: (request: HttpRequest, error: Error) => HttpResponse<any> | undefined | void;
|
|
9
|
+
protected onEnd?: (request: HttpRequest, response: HttpResponse) => any;
|
|
10
|
+
constructor();
|
|
11
|
+
start(port: number): void;
|
|
12
|
+
setServices(services: Service<any, any>[]): void;
|
|
13
|
+
private publish;
|
|
14
|
+
private run;
|
|
15
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import BaseService, { HttpRequest as BaseHttpRequest, HttpResponse as BaseHttpResponse } from "./api/Service";
|
|
2
|
+
export declare const Service: typeof BaseService;
|
|
3
|
+
export interface HttpRequest extends BaseHttpRequest {
|
|
4
|
+
}
|
|
5
|
+
export declare const HttpResponse: typeof BaseHttpResponse;
|
|
6
|
+
import BaseServiceRest from "./api/ServiceRest";
|
|
7
|
+
export declare const ServiceRest: typeof BaseServiceRest;
|
|
8
|
+
import BaseServiceRestFormal, { Wrapper as BaseWrapper } from "./api/ServiceRestFormal";
|
|
9
|
+
export declare const ServiceRestFormal: typeof BaseServiceRestFormal;
|
|
10
|
+
export interface Wrapper<T> extends BaseWrapper<T> {
|
|
11
|
+
}
|
|
12
|
+
import BaseServiceRestFormalTemplate from "./api/ServiceRestFormalTemplate";
|
|
13
|
+
export declare const ServiceRestFormalTemplate: typeof BaseServiceRestFormalTemplate;
|
|
14
|
+
import BaseBusiness from "./business/Business";
|
|
15
|
+
export declare const Business: typeof BaseBusiness;
|
|
16
|
+
import BaseBusinessState from "./business/BusinessState";
|
|
17
|
+
export declare const BusinessState: typeof BaseBusinessState;
|
|
18
|
+
import BaseBusinessProxy, { Module as BaseModule } from "./business/BusinessProxy";
|
|
19
|
+
export declare const BusinessProxy: typeof BaseBusinessProxy;
|
|
20
|
+
export interface Module extends BaseModule {
|
|
21
|
+
}
|
|
22
|
+
import BaseBusinessServer from "./business/BusinessServer";
|
|
23
|
+
export declare const BusinessServer: typeof BaseBusinessServer;
|
|
24
|
+
import BaseBusinessThread from "./business/BusinessThread";
|
|
25
|
+
export declare const BusinessThread: typeof BaseBusinessThread;
|
|
26
|
+
import BaseBusinessErrors, { FormalError as BaseFormalError } from "./business/BusinessErrors";
|
|
27
|
+
export declare const BusinessErrors: typeof BaseBusinessErrors;
|
|
28
|
+
export interface FormalError extends BaseFormalError {
|
|
29
|
+
}
|
|
30
|
+
import BaseBusinessLogger from "./business/BusinessLogger";
|
|
31
|
+
export declare const BusinessLogger: typeof BaseBusinessLogger;
|
|
32
|
+
import BaseRepository from "./repository/Repository";
|
|
33
|
+
export declare const Repository: typeof BaseRepository;
|
|
34
|
+
import BaseRepositoryPostgres from "./repository/RepositoryPostgres";
|
|
35
|
+
export declare const RepositoryPostgres: typeof BaseRepositoryPostgres;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import pg, { ClientConfig, QueryResult } from "pg";
|
|
2
|
+
export interface QueryOptions {
|
|
3
|
+
schema?: string;
|
|
4
|
+
filters?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface Chain extends ClientConfig {
|
|
7
|
+
host: string;
|
|
8
|
+
port: number;
|
|
9
|
+
user: string;
|
|
10
|
+
password: string;
|
|
11
|
+
database: string;
|
|
12
|
+
schema: string;
|
|
13
|
+
}
|
|
14
|
+
export interface Column {
|
|
15
|
+
name: string;
|
|
16
|
+
default?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface Pagination<T> {
|
|
19
|
+
total: number;
|
|
20
|
+
elements: T[];
|
|
21
|
+
}
|
|
22
|
+
export type Primitive = string | number | Date | null | undefined;
|
|
23
|
+
export default abstract class RepositoryPostgres {
|
|
24
|
+
protected chain: Chain;
|
|
25
|
+
protected client: pg.Client;
|
|
26
|
+
private connected;
|
|
27
|
+
private tables;
|
|
28
|
+
private onConnectListeners;
|
|
29
|
+
private onQueryWillExecuteListeners;
|
|
30
|
+
private onQueryDidExecuteListeners;
|
|
31
|
+
private onIntrospectedListener;
|
|
32
|
+
private introspectIntervalTime;
|
|
33
|
+
private schema;
|
|
34
|
+
constructor();
|
|
35
|
+
init(chain: Chain): void;
|
|
36
|
+
isConnected(): boolean;
|
|
37
|
+
addOnConnect(listener: () => void): void;
|
|
38
|
+
addOnQueryWillExecuteListener(listener: (query: string) => void): void;
|
|
39
|
+
setIntrospectInterval(time: number): void;
|
|
40
|
+
addOnIntrospected(listener: (tables: {
|
|
41
|
+
[p: string]: Column[];
|
|
42
|
+
}) => void): void;
|
|
43
|
+
connect(connection: Chain): void;
|
|
44
|
+
native(query: string, values?: any[], options?: QueryOptions): Promise<QueryResult>;
|
|
45
|
+
query<E>(query: string, values?: any[], options?: QueryOptions): Promise<E[]>;
|
|
46
|
+
one<E>(tableName: string, id: string): Promise<E>;
|
|
47
|
+
any<E>(tableName: string, filters: {
|
|
48
|
+
[key: string]: Primitive | Array<any>;
|
|
49
|
+
}): Promise<E>;
|
|
50
|
+
find<T>(tableName: string, filters: {
|
|
51
|
+
[key: string]: Primitive | Array<any>;
|
|
52
|
+
}): Promise<T[]>;
|
|
53
|
+
all_depr<E>(tableName: string, options?: QueryOptions): Promise<E[]>;
|
|
54
|
+
add<E>(tableName: string, object: {
|
|
55
|
+
[key: string]: Primitive;
|
|
56
|
+
} | any, id?: string): Promise<E>;
|
|
57
|
+
addAll<T>(tableName: string, objects: {
|
|
58
|
+
[key: string]: Primitive;
|
|
59
|
+
}[]): Promise<T[]>;
|
|
60
|
+
update(tableName: string, id: string, object: {
|
|
61
|
+
[key: string]: Primitive;
|
|
62
|
+
}): Promise<unknown[]>;
|
|
63
|
+
page<T>(tableName: string, sortKey: string, maxResults?: number, pageNumber?: number, filters?: {
|
|
64
|
+
[key: string]: Primitive | Array<Primitive>;
|
|
65
|
+
}): Promise<Pagination<T>>;
|
|
66
|
+
count(tableName: string, filters?: {
|
|
67
|
+
[key: string]: Primitive | Array<Primitive>;
|
|
68
|
+
}): Promise<number>;
|
|
69
|
+
select(tableName: string, select: string, filters?: {
|
|
70
|
+
[key: string]: Primitive | Array<Primitive>;
|
|
71
|
+
}): Promise<string[]>;
|
|
72
|
+
del(tableName: string, id: string): Promise<void>;
|
|
73
|
+
private toQuery;
|
|
74
|
+
private introspect;
|
|
75
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Repository from './Repository';
|
|
2
|
+
import { HttpRequest } from "../api/Service";
|
|
3
|
+
export interface RestResponse<E = any> extends Response {
|
|
4
|
+
timestamp?: Date;
|
|
5
|
+
duration?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface RestOptions {
|
|
8
|
+
interceptor: (response: RestResponse) => void;
|
|
9
|
+
}
|
|
10
|
+
export default class RepositoryRest<S = any> extends Repository {
|
|
11
|
+
private readonly baseUrl;
|
|
12
|
+
private readonly interceptor?;
|
|
13
|
+
private onRequestListener?;
|
|
14
|
+
constructor(baseUrl: string, options?: RestOptions);
|
|
15
|
+
setOnRequestListener(listener: (method: string, endpoint: string, request: HttpRequest) => void): void;
|
|
16
|
+
call<E = S>(method?: string, endpoint?: string, request?: HttpRequest): Promise<RestResponse<E>>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import RepositoryRest from "./RepositoryRest";
|
|
2
|
+
import { Wrapper } from "../api/ServiceRestFormal";
|
|
3
|
+
import { HttpRequest } from "../api/Service";
|
|
4
|
+
export default class RepositoryRestFormal<S = any> extends RepositoryRest<Wrapper<S>> {
|
|
5
|
+
constructor(baseUrl: string, ...methods: RepositoryRest[]);
|
|
6
|
+
call<E = S>(method?: string, endpoint?: string, request?: HttpRequest): Promise<E>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Column, Primitive } from "../RepositoryPostgres";
|
|
2
|
+
export default abstract class PostgresUtils {
|
|
3
|
+
static stringToCamel(column: string): string;
|
|
4
|
+
static camelToSnake(column: string): string;
|
|
5
|
+
static snakeToCamel(obj: any): any;
|
|
6
|
+
static columns(table: Column[]): string[];
|
|
7
|
+
static bindOrDefault(table: Column[], object: {
|
|
8
|
+
[p: string]: Primitive;
|
|
9
|
+
}, startIndex: any): {
|
|
10
|
+
bindings: string[];
|
|
11
|
+
values: any[];
|
|
12
|
+
};
|
|
13
|
+
static valueOrDefault(columns: [string], object: any): string[];
|
|
14
|
+
static fromEntries(iterable: [string, any][]): {
|
|
15
|
+
[key: string]: any;
|
|
16
|
+
};
|
|
17
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ismx-nexo-node-app",
|
|
3
|
+
"version": "0.3.24",
|
|
4
|
+
"description": "",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "rm -rf ./dist && npx tsc",
|
|
7
|
+
"publish-patch": "rm -rf ./dist && npx tsc && git add -A && git commit -m \"patch\" && npm version patch && npm publish && git push",
|
|
8
|
+
"publish-minor": "rm -rf ./dist && npx tsc && git add -A && git commit -m \"minor\" && npm version minor && npm publish && git push",
|
|
9
|
+
"publish-major": "rm -rf ./dist && npx tsc && git add -A && git commit -m \"major\" && npm version major && npm publish && git push"
|
|
10
|
+
},
|
|
11
|
+
"main": "dist/js/index.js",
|
|
12
|
+
"types": "dist/types/index.d.ts",
|
|
13
|
+
"keywords": [],
|
|
14
|
+
"author": "",
|
|
15
|
+
"license": "ISC",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"cors": "^2.8.5",
|
|
18
|
+
"esbuild": "^0.23.1",
|
|
19
|
+
"express": "^4.21.0",
|
|
20
|
+
"node-fetch": "^2.6.11",
|
|
21
|
+
"pg": "^8.11.0",
|
|
22
|
+
"ts-node": "8.10.1"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/cors": "^2.8.17",
|
|
26
|
+
"@types/express": "^5.0.0",
|
|
27
|
+
"@types/node": "^14.0.5",
|
|
28
|
+
"@types/node-fetch": "^2.6.11",
|
|
29
|
+
"@types/pg": "^8.11.10",
|
|
30
|
+
"rollup": "^4.24.4",
|
|
31
|
+
"rollup-plugin-dts": "^6.1.1",
|
|
32
|
+
"tsx": "^4.19.1",
|
|
33
|
+
"typescript": "^5.6.3"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import BusinessServer from "../business/BusinessServer";
|
|
2
|
+
|
|
3
|
+
export interface HttpRequest<T = any> {
|
|
4
|
+
id?: string,
|
|
5
|
+
url?: string,
|
|
6
|
+
method?: string,
|
|
7
|
+
endpoint?: string;
|
|
8
|
+
timestamp?: Date,
|
|
9
|
+
params?: { [key: string]: string },
|
|
10
|
+
query?: { [key: string]: string },
|
|
11
|
+
body?: T,
|
|
12
|
+
headers?: { [key: string]: string },
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class HttpResponse<T = any> {
|
|
16
|
+
content?: T;
|
|
17
|
+
httpCode?: number;
|
|
18
|
+
headers?: { [key: string]: string };
|
|
19
|
+
|
|
20
|
+
static ok<T=any>(content: T) {
|
|
21
|
+
let response = new HttpResponse();
|
|
22
|
+
response.content = content;
|
|
23
|
+
response.httpCode = 200;
|
|
24
|
+
return response
|
|
25
|
+
}
|
|
26
|
+
static ko<T=any>(httpCode: number, content: T | undefined) {
|
|
27
|
+
let response = new HttpResponse();
|
|
28
|
+
response.content = content;
|
|
29
|
+
response.httpCode = httpCode;
|
|
30
|
+
return response
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default abstract class Service<Req, Res>
|
|
35
|
+
{
|
|
36
|
+
public static readonly services: Service<any, any>[] = []
|
|
37
|
+
|
|
38
|
+
readonly method: string;
|
|
39
|
+
|
|
40
|
+
readonly endpoint: string;
|
|
41
|
+
|
|
42
|
+
protected constructor(method: string, endpoint: string = "/") {
|
|
43
|
+
this.method = method;
|
|
44
|
+
this.endpoint = endpoint;
|
|
45
|
+
Service.services.push(this);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async serve(request: HttpRequest<Req>): Promise<HttpResponse<Res>> {
|
|
49
|
+
return HttpResponse.ok({});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static clone<R, S>(request: HttpRequest<R>, clone: { params?: { [key: string]: string }, query?: { [key: string]: string }, body?: S, headers?: { [key: string]: string } }): HttpRequest<S> {
|
|
53
|
+
return { id: request.id, url: request.url, method: request.method, timestamp: request.timestamp, params: clone.params ?? {}, query: clone.query ?? {}, body: clone.body, headers: clone.headers ?? {} };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Service, {HttpRequest, HttpResponse} from "./Service";
|
|
2
|
+
|
|
3
|
+
export default class ServiceRest<Req, Res> extends Service<Req, Res>
|
|
4
|
+
{
|
|
5
|
+
constructor(method: string, endpoint: string = "/") {
|
|
6
|
+
super(method, endpoint);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async serve(request: HttpRequest<Req>): Promise<HttpResponse<Res>>
|
|
10
|
+
{
|
|
11
|
+
let response = await this.serveRest(request)
|
|
12
|
+
response.headers = { "Content-Type": "application/json" };
|
|
13
|
+
return response;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async serveRest(request: HttpRequest<Req>): Promise<HttpResponse<Res>> {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import Service, {HttpRequest, HttpResponse} from "./Service";
|
|
2
|
+
import ServiceRest from "./ServiceRest";
|
|
3
|
+
|
|
4
|
+
export interface Wrapper<T> {
|
|
5
|
+
code: string,
|
|
6
|
+
description: string,
|
|
7
|
+
data?: T
|
|
8
|
+
debug?: Error
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default class ServiceRestFormal<Req=any, Res=any> extends ServiceRest<Req, Wrapper<Res>>
|
|
12
|
+
{
|
|
13
|
+
constructor(method: string, endpoint: string = "/") {
|
|
14
|
+
super(method, endpoint);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async serveRest(request: HttpRequest<Req>): Promise<HttpResponse<Wrapper<Res>>>
|
|
18
|
+
{
|
|
19
|
+
try {
|
|
20
|
+
let response = await this.serveRestFormal(request);
|
|
21
|
+
let content: Wrapper<Res> = ({ code: "0", description: "Service execution OK", data: response.content })
|
|
22
|
+
return Object.assign({} as HttpResponse<Wrapper<Res>>, response, {content});
|
|
23
|
+
} catch (error: any) {
|
|
24
|
+
let content: Wrapper<any> = ({ code: "-1", description: "Internal server error", debug: error.message })
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async serveRestFormal(request: HttpRequest<Req>): Promise<HttpResponse<Res>> {
|
|
30
|
+
return HttpResponse.ok(null);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async unwrap<Res>(request: Promise<HttpResponse<Wrapper<Res>>>): Promise<Res | undefined> {
|
|
34
|
+
return ServiceRestFormal.unwrap(request);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async fullSelect<Req, Res>(request: HttpRequest<Req>, api: { select: ServiceRestFormal<Req, string[]>, full: ServiceRestFormal<Req, Res> }) {
|
|
38
|
+
return ServiceRestFormal.fullSelect(request, api);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static async unwrap<Res>(request: Promise<HttpResponse<Wrapper<Res>>>): Promise<Res | undefined> {
|
|
42
|
+
let response = await request;
|
|
43
|
+
if (response.httpCode !== 200) throw response.content;
|
|
44
|
+
else return response.content?.data;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static async fullSelect<Req, Res>(request: HttpRequest<Req>, api: { select: ServiceRestFormal<Req, string[]>, full: ServiceRestFormal<Req, Res> })
|
|
48
|
+
{
|
|
49
|
+
return api.select.serve(request).then((result) => {
|
|
50
|
+
let select = result.content?.data;
|
|
51
|
+
let requests = select?.map(async (selectId) => {
|
|
52
|
+
let result = await api.full.serve(Service.clone(request, {query: {id: selectId}}));
|
|
53
|
+
return result.content?.data;
|
|
54
|
+
});
|
|
55
|
+
return Promise.all(requests ?? []);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import Service, {HttpRequest, HttpResponse} from "./Service";
|
|
2
|
+
import ServiceRestFormal, {Wrapper} from "./ServiceRestFormal";
|
|
3
|
+
import RepositoryPostgres, {Pagination} from "../repository/RepositoryPostgres";
|
|
4
|
+
import BusinessErrors from "../business/BusinessErrors";
|
|
5
|
+
|
|
6
|
+
export default class ServiceRestFormalTemplate<Req=any, Res=any>
|
|
7
|
+
{
|
|
8
|
+
readonly resource: string;
|
|
9
|
+
readonly tableName: string;
|
|
10
|
+
readonly database!: RepositoryPostgres;
|
|
11
|
+
readonly errors!: BusinessErrors;
|
|
12
|
+
|
|
13
|
+
readonly get!: ServiceRestFormal<Req, Res>;
|
|
14
|
+
readonly post!: ServiceRestFormal<Req, Res>;
|
|
15
|
+
readonly put!: ServiceRestFormal<Req, Res>;
|
|
16
|
+
readonly del!: ServiceRestFormal<Req, Res>;
|
|
17
|
+
readonly select!: ServiceRestFormal<Req, string[]>;
|
|
18
|
+
readonly exist!: ServiceRestFormal<Req, Boolean>;
|
|
19
|
+
|
|
20
|
+
readonly count!: ServiceRestFormal<Req, Number>;
|
|
21
|
+
readonly getList!: ServiceRestFormal<Req, Res[]>;
|
|
22
|
+
|
|
23
|
+
readonly postList!: ServiceRestFormal<Req, Res[]>;
|
|
24
|
+
readonly page!: ServiceRestFormal<Req, Pagination<Res>>;
|
|
25
|
+
readonly map!: ServiceRestFormal<Req, { [key:string]: Res }>;
|
|
26
|
+
|
|
27
|
+
constructor(resource: string, tableName: string) {
|
|
28
|
+
this.resource = resource;
|
|
29
|
+
this.tableName = tableName;
|
|
30
|
+
|
|
31
|
+
this.get = new ServiceRestFormal("GET", `${resource}`);
|
|
32
|
+
this.get.serveRestFormal = this.serveGet.bind(this);
|
|
33
|
+
|
|
34
|
+
this.getList = new ServiceRestFormal("GET", `${resource}/list`);
|
|
35
|
+
this.getList.serveRestFormal = this.serveGetList.bind(this);
|
|
36
|
+
|
|
37
|
+
this.postList = new ServiceRestFormal("POST", `${resource}/list`);
|
|
38
|
+
this.postList.serveRestFormal = this.servePostList.bind(this);
|
|
39
|
+
|
|
40
|
+
this.page = new ServiceRestFormal("GET", `${resource}/page`);
|
|
41
|
+
this.page.serveRestFormal = this.servePage.bind(this);
|
|
42
|
+
|
|
43
|
+
this.map = new ServiceRestFormal("GET", `${resource}/map`);
|
|
44
|
+
this.map.serveRestFormal = this.serveMap.bind(this);
|
|
45
|
+
|
|
46
|
+
this.select = new ServiceRestFormal("GET", `${resource}/select`);
|
|
47
|
+
this.select.serveRestFormal = this.serveSelect.bind(this);
|
|
48
|
+
|
|
49
|
+
this.exist = new ServiceRestFormal("GET", `${resource}/exist`);
|
|
50
|
+
this.exist.serveRestFormal = this.serveExist.bind(this);
|
|
51
|
+
|
|
52
|
+
this.count = new ServiceRestFormal("GET", `${resource}/count`);
|
|
53
|
+
this.count.serveRestFormal = this.serveCount.bind(this);
|
|
54
|
+
|
|
55
|
+
this.post = new ServiceRestFormal("POST", `${resource}`);
|
|
56
|
+
this.post.serveRestFormal = this.servePost.bind(this);
|
|
57
|
+
|
|
58
|
+
this.put = new ServiceRestFormal("PUT", `${resource}`);
|
|
59
|
+
this.put.serveRestFormal = this.servePut.bind(this);
|
|
60
|
+
|
|
61
|
+
this.del = new ServiceRestFormal("DELETE", `${resource}`);
|
|
62
|
+
this.del.serveRestFormal = this.serveDel.bind(this);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
protected async serveGet(request: HttpRequest<Req>): Promise<HttpResponse<Res>> {
|
|
66
|
+
let id = this.errors.getQuery("id", request.query)
|
|
67
|
+
let result = await this.database.one<Res>(this.tableName, id);
|
|
68
|
+
return HttpResponse.ok(result);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
protected async serveGetList(request: HttpRequest<Req>): Promise<HttpResponse<Res[]>> {
|
|
72
|
+
return HttpResponse.ok(await this.database.find<Res>(this.tableName, { ...request.query }));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
protected async servePostList(request: HttpRequest<Req>): Promise<HttpResponse<Res[]>> {
|
|
76
|
+
let list = request.body
|
|
77
|
+
if (!(list instanceof Array)) throw new Error("body must be a list");
|
|
78
|
+
return HttpResponse.ok(this.database.addAll(this.tableName, list));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
protected async servePage(request: HttpRequest<Req>): Promise<HttpResponse<Pagination<Res>>> {
|
|
82
|
+
let maxResults = this.errors.getQuery("maxResults", request.query);
|
|
83
|
+
let maxResultsNum = this.errors.isType("maxResults", maxResults, "number", Number.parseInt);
|
|
84
|
+
|
|
85
|
+
let pageNumber = this.errors.getQuery("pageNumber", request.query)
|
|
86
|
+
let pageNumberNum = this.errors.isType("pageNumber", pageNumber, "number", Number.parseInt)
|
|
87
|
+
|
|
88
|
+
let results = await this.database.page(this.tableName, "timestamp", maxResultsNum, pageNumberNum);
|
|
89
|
+
return HttpResponse.ok(results);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
protected async serveMap(request: HttpRequest<Req>): Promise<HttpResponse<{[key: string]: Res}>> {
|
|
93
|
+
let results = await this.database.find<Res>(this.tableName, { ...request.query });
|
|
94
|
+
let response: {[key: string]: Res} = {}; for (let result of results) {
|
|
95
|
+
let key = (result as any)[this.indexer];
|
|
96
|
+
if (!key) return HttpResponse.ok({});
|
|
97
|
+
else response[key] = result;
|
|
98
|
+
}
|
|
99
|
+
return HttpResponse.ok(response);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
protected indexer = "id"
|
|
103
|
+
protected async serveSelect(request: HttpRequest<Req>): Promise<HttpResponse<string[]>> {
|
|
104
|
+
let selection = request.query?.["selection"] ?? this.indexer;
|
|
105
|
+
delete request.query?.["selection"];
|
|
106
|
+
let select = await this.database.select(this.tableName, selection, { ...request.query });
|
|
107
|
+
return HttpResponse.ok(select);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
protected async serveExist(request: HttpRequest<Req>): Promise<HttpResponse<Boolean>> {
|
|
111
|
+
return HttpResponse.ok((await this.database.count(this.tableName, { ...request.query })) > 0);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
protected async serveCount(request: HttpRequest<Req>): Promise<HttpResponse<Number>> {
|
|
115
|
+
return HttpResponse.ok((await this.database.count(this.tableName, { ...request.query })));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
protected async servePost(request: HttpRequest<Req>): Promise<HttpResponse<Res>> {
|
|
119
|
+
let entity = this.database.add(this.tableName, request.body);
|
|
120
|
+
return HttpResponse.ok(entity);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
protected async servePut(request: HttpRequest<Req>): Promise<HttpResponse<Res>> {
|
|
124
|
+
return HttpResponse.ok("not implemented yet");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
protected async serveDel(request: HttpRequest<Req>): Promise<HttpResponse<Res>> {
|
|
128
|
+
return HttpResponse.ok("not implemented yet");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
protected readonly unwrap = ServiceRestFormal.unwrap
|
|
132
|
+
protected readonly fullSelect = ServiceRestFormal.fullSelect
|
|
133
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
export interface FormalError {
|
|
2
|
+
id: string;
|
|
3
|
+
code: string;
|
|
4
|
+
description: string;
|
|
5
|
+
httpCode?: number;
|
|
6
|
+
group?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default class BusinessErrors {
|
|
10
|
+
|
|
11
|
+
private errors: { [key: string]: FormalError } = {};
|
|
12
|
+
|
|
13
|
+
async init() { }
|
|
14
|
+
|
|
15
|
+
setErrors(errors: { [key: string]: FormalError }) {
|
|
16
|
+
this.errors = errors;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get(code: string, ...params: string[]) {
|
|
20
|
+
let error = this.errors[code] ?? this.getFallback();
|
|
21
|
+
let result = Object.assign({} as FormalError, error);
|
|
22
|
+
for (const p in params) result.description = result.description?.replace(`\${${p}}`, params[p])
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
except(code: string, ...params: string[]) {
|
|
27
|
+
let error = this.get(code, ...params)
|
|
28
|
+
//this.onExcept?.(error)
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
notFound(endpoint: string) {
|
|
33
|
+
this.except("0004", endpoint)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getQuery(key: string, query: {[key: string]: string}|undefined): string {
|
|
37
|
+
if (!query || !query[key]) this.except("0011", key)
|
|
38
|
+
return query![key];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
eitherQuery(keys: string[], query: { [p: string]: string }): { key: string, value: string, index: number } {
|
|
42
|
+
let result = undefined;
|
|
43
|
+
for (let i = 0; i < keys.length; i++) {
|
|
44
|
+
let key = keys[i];
|
|
45
|
+
if (query[key] !== undefined) {
|
|
46
|
+
if (!result) result = {key: key, value: query[key], index: i}
|
|
47
|
+
else this.except("0017", keys.join(" or "));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (!result) this.except("0016", keys.join(" or "));
|
|
51
|
+
return result!;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
getPath(key: string, path: {[key: string]: string}): string {
|
|
55
|
+
if (!path[key]) this.except("0012", key)
|
|
56
|
+
return path[key];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getBody<T>(body?: T): T {
|
|
60
|
+
if (!body) this.except("0013")
|
|
61
|
+
return body!;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getHeaders(key: string, headers: {[key: string]: string}): string {
|
|
65
|
+
if (!headers[key]) this.except("0015", key)
|
|
66
|
+
return headers[key];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
isType<T>(key: string, value: string, type:string, assertion: (value:string)=>T): T {
|
|
70
|
+
try { if (!assertion(value)) throw new Error(); }
|
|
71
|
+
catch { this.except("0021", key, type) }
|
|
72
|
+
return assertion(value);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
exists<T>(type: string, key: string, value: string, object?:T): T {
|
|
76
|
+
if (!object) this.except("0030", type, key, value);
|
|
77
|
+
return object!;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
isRegex(key: string, value: string, regex: string):string {
|
|
81
|
+
if (!new RegExp(regex).test(value))
|
|
82
|
+
this.except("0022", key)
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
isDate(key:string, value:string, format: string): Date {
|
|
87
|
+
this.except("0023", key, format);
|
|
88
|
+
return new Date(value);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
getFallback(): FormalError {
|
|
92
|
+
return { id: "-1", description: "Internal Server error", code: "-1", httpCode: 500 };
|
|
93
|
+
}
|
|
94
|
+
}
|