@clairejs/server 3.15.2 → 3.16.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/.mocharc.json +3 -0
- package/dist/common/AbstractController.js +3 -0
- package/dist/common/ControllerMetadata.js +1 -0
- package/dist/common/FileOperation.js +6 -0
- package/dist/common/ServerModelMetadata.js +1 -0
- package/dist/common/Transactionable.js +17 -0
- package/dist/common/auth/AbstractPrincipalResolver.js +2 -0
- package/dist/common/auth/IPrincipal.js +1 -0
- package/dist/common/constants.js +7 -0
- package/dist/common/decorator.d.ts +2 -2
- package/dist/common/decorator.js +6 -0
- package/dist/common/request/EndpointMetadata.js +1 -0
- package/dist/common/request/HttpData.js +1 -0
- package/dist/common/request/HttpEndpoint.js +1 -0
- package/dist/common/request/JobData.js +1 -0
- package/dist/common/request/MountedEndpointInfo.js +1 -0
- package/dist/common/request/RequestOptions.js +1 -0
- package/dist/common/request/SocketData.js +1 -0
- package/dist/common/request/types.d.ts +1 -1
- package/dist/common/request/types.js +1 -0
- package/dist/controllers/FileManageController.js +90 -0
- package/dist/controllers/FileUploadController.js +64 -0
- package/dist/controllers/dto/system.js +14 -0
- package/dist/controllers/dto/upload.js +205 -0
- package/dist/http/auth/AbstractHttpAuthorizer.js +2 -0
- package/dist/http/common/HttpRequest.js +72 -0
- package/dist/http/common/HttpResponse.js +62 -0
- package/dist/http/controller/AbstractHttpController.js +21 -0
- package/dist/http/controller/AbstractHttpMiddleware.js +2 -0
- package/dist/http/controller/AbstractHttpRequestHandler.js +69 -0
- package/dist/http/controller/CrudHttpController.js +302 -0
- package/dist/http/controller/DefaultHttpRequestHandler.js +143 -0
- package/dist/http/decorators.d.ts +1 -1
- package/dist/http/decorators.js +86 -0
- package/dist/http/file-upload/AbstractFileUploadHandler.js +2 -0
- package/dist/http/file-upload/FileUploadHandler.js +41 -0
- package/dist/http/file-upload/types.d.ts +1 -1
- package/dist/http/file-upload/types.js +1 -0
- package/dist/http/repository/AbstractRepository.js +26 -0
- package/dist/http/repository/DtoRepository.d.ts +3 -3
- package/dist/http/repository/DtoRepository.js +204 -0
- package/dist/http/repository/ICrudRepository.js +1 -0
- package/dist/http/repository/ModelRepository.js +696 -0
- package/dist/http/security/AbstractAccessCondition.js +2 -0
- package/dist/http/security/access-conditions/FilterModelFieldAccessCondition.js +30 -0
- package/dist/http/security/access-conditions/MaximumQueryLimit.js +31 -0
- package/dist/http/security/cors.js +1 -0
- package/dist/http/utils.js +32 -0
- package/dist/index.js +75 -1
- package/dist/job/AbstractJobController.js +9 -0
- package/dist/job/AbstractJobRepository.js +2 -0
- package/dist/job/AbstractJobScheduler.js +48 -0
- package/dist/job/AwsJobScheduler.js +405 -0
- package/dist/job/LocalJobScheduler.js +273 -0
- package/dist/job/decorators.js +57 -0
- package/dist/job/interfaces.js +10 -0
- package/dist/logging/FileLogMedium.js +44 -0
- package/dist/services/AbstractFileService.js +28 -0
- package/dist/services/AbstractMailService.js +2 -0
- package/dist/services/AbstractService.js +3 -0
- package/dist/services/AbstractSmsService.js +2 -0
- package/dist/services/implementations/LocalFileService.js +42 -0
- package/dist/services/implementations/LocalMailService.js +27 -0
- package/dist/services/implementations/LocalSmsService.js +17 -0
- package/dist/services/implementations/S3FileService.js +107 -0
- package/dist/services/implementations/SesMailService.js +64 -0
- package/dist/socket/AbstractServerSocket.js +44 -0
- package/dist/socket/AbstractServerSocketManager.d.ts +1 -1
- package/dist/socket/AbstractServerSocketManager.js +348 -0
- package/dist/socket/AbstractSocketConnectionHandler.js +2 -0
- package/dist/socket/AbstractSocketController.d.ts +3 -3
- package/dist/socket/AbstractSocketController.js +12 -0
- package/dist/socket/AwsSocketManager.d.ts +2 -2
- package/dist/socket/AwsSocketManager.js +160 -0
- package/dist/socket/IServerSocket.js +1 -0
- package/dist/socket/LocalSocketManager.js +292 -0
- package/dist/system/ClaireServer.js +78 -0
- package/dist/system/ExpressWrapper.js +122 -0
- package/dist/system/LambdaWrapper.js +151 -0
- package/dist/system/ServerGlobalStore.js +1 -0
- package/dist/system/lamba-request-mapper.js +49 -0
- package/dist/system/locale/LocaleEntry.js +13 -0
- package/dist/system/locale/LocaleTranslation.js +47 -0
- package/dist/system/locale/decorators.js +14 -0
- package/package.json +13 -20
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getObjectMetadata } from "@clairejs/core";
|
|
2
|
+
export class AbstractRepository {
|
|
3
|
+
model;
|
|
4
|
+
modelMetadata;
|
|
5
|
+
constructor(model) {
|
|
6
|
+
this.model = model;
|
|
7
|
+
this.modelMetadata = getObjectMetadata(this.model);
|
|
8
|
+
}
|
|
9
|
+
async beforeCreating(principal, records) {
|
|
10
|
+
//-- check for fields that has URI with mapper property
|
|
11
|
+
for (const record of records) {
|
|
12
|
+
for (const field of this.modelMetadata.fields) {
|
|
13
|
+
if (field.userResolver && principal) {
|
|
14
|
+
//-- resolve user from principal
|
|
15
|
+
record[field.name] = field.userResolver(principal);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
project(records, projection) {
|
|
21
|
+
if (!projection) {
|
|
22
|
+
return records;
|
|
23
|
+
}
|
|
24
|
+
return records.map((re) => projection.reduce((collector, fieldName) => Object.assign(collector, { [fieldName]: re[fieldName] }), {}));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -3,7 +3,7 @@ import { IQueryProvider, ITransaction, QueryCondition } from "@clairejs/orm";
|
|
|
3
3
|
import { IPrincipal } from "../../common/auth/IPrincipal";
|
|
4
4
|
import { AbstractRepository } from "./AbstractRepository";
|
|
5
5
|
import { ICrudRepository } from "./ICrudRepository";
|
|
6
|
-
export
|
|
6
|
+
export type DtoDissolver<R extends Identifiable, K extends Identifiable> = (t: DeepPartial<K>) => MappingMetadata<R, any>[];
|
|
7
7
|
export interface MappingMetadata<R extends Identifiable, K extends AbstractModel> {
|
|
8
8
|
multiple: boolean;
|
|
9
9
|
modelClass: Constructor<K>;
|
|
@@ -12,7 +12,7 @@ export interface MappingMetadata<R extends Identifiable, K extends AbstractModel
|
|
|
12
12
|
rootMapping: (ks?: DeepPartial<K>[]) => DeepPartial<R>;
|
|
13
13
|
nestedMapping?: DtoDissolver<R, K>;
|
|
14
14
|
}
|
|
15
|
-
export declare const SingleMap: <R extends Identifiable
|
|
15
|
+
export declare const SingleMap: <R extends Identifiable, K extends AbstractModel>(modelClass: Constructor<K>, forwardOps: (ops?: QueryCondition<R>[] | undefined) => QueryCondition<K>[] | undefined, forwardMapping: (t: DeepPartial<K>) => DeepPartial<K> | undefined, rootMapping: (k?: DeepPartial<K> | undefined) => DeepPartial<R>, nestedMapping?: DtoDissolver<R, K> | undefined) => {
|
|
16
16
|
multiple: boolean;
|
|
17
17
|
modelClass: Constructor<K>;
|
|
18
18
|
forwardOps: (ops?: QueryCondition<R>[] | undefined) => QueryCondition<K>[] | undefined;
|
|
@@ -20,7 +20,7 @@ export declare const SingleMap: <R extends Identifiable<string | number>, K exte
|
|
|
20
20
|
rootMapping: (ks?: DeepPartial<K>[] | undefined) => DeepPartial<R>;
|
|
21
21
|
nestedMapping: DtoDissolver<R, K> | undefined;
|
|
22
22
|
};
|
|
23
|
-
export declare const MultipleMap: <R extends Identifiable
|
|
23
|
+
export declare const MultipleMap: <R extends Identifiable, K extends AbstractModel>(modelClass: Constructor<K>, forwardOps: (ops?: QueryCondition<R>[] | undefined) => QueryCondition<K>[] | undefined, forwardMapping: (t: DeepPartial<K>[]) => DeepPartial<K>[] | undefined, rootMapping: (ks?: DeepPartial<K>[] | undefined) => DeepPartial<R>, nestedMapping?: DtoDissolver<R, K> | undefined) => {
|
|
24
24
|
multiple: boolean;
|
|
25
25
|
modelClass: Constructor<K>;
|
|
26
26
|
forwardOps: (ops?: QueryCondition<R>[] | undefined) => QueryCondition<K>[] | undefined;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { deepMerge, Errors, } from "@clairejs/core";
|
|
2
|
+
import { AbstractRepository } from "./AbstractRepository";
|
|
3
|
+
import { ModelRepository } from "./ModelRepository";
|
|
4
|
+
export const SingleMap = (modelClass, forwardOps, forwardMapping, rootMapping, nestedMapping) => ({
|
|
5
|
+
multiple: false,
|
|
6
|
+
modelClass,
|
|
7
|
+
forwardOps,
|
|
8
|
+
forwardMapping: (t) => {
|
|
9
|
+
const v = forwardMapping(t[0] || {});
|
|
10
|
+
return v && [v];
|
|
11
|
+
},
|
|
12
|
+
rootMapping: (ks) => {
|
|
13
|
+
const k = ks && ks[0];
|
|
14
|
+
return rootMapping(k);
|
|
15
|
+
},
|
|
16
|
+
nestedMapping,
|
|
17
|
+
});
|
|
18
|
+
export const MultipleMap = (modelClass, forwardOps, forwardMapping, rootMapping, nestedMapping) => ({
|
|
19
|
+
multiple: true,
|
|
20
|
+
modelClass,
|
|
21
|
+
forwardOps,
|
|
22
|
+
forwardMapping,
|
|
23
|
+
rootMapping,
|
|
24
|
+
nestedMapping,
|
|
25
|
+
});
|
|
26
|
+
export class DtoRepository extends AbstractRepository {
|
|
27
|
+
model;
|
|
28
|
+
dissolver;
|
|
29
|
+
constructor(model, dissolver) {
|
|
30
|
+
super(model);
|
|
31
|
+
this.model = model;
|
|
32
|
+
this.dissolver = dissolver;
|
|
33
|
+
}
|
|
34
|
+
async getMapValue(root, currentInstancesResolver, mappers, preOperator, postOperator) {
|
|
35
|
+
for (const mapper of mappers) {
|
|
36
|
+
const currentInstances = await currentInstancesResolver(mapper);
|
|
37
|
+
let instances = preOperator
|
|
38
|
+
? await preOperator(mapper, mapper.forwardMapping(currentInstances), currentInstances)
|
|
39
|
+
: mapper.forwardMapping(currentInstances);
|
|
40
|
+
if (mapper.nestedMapping && instances) {
|
|
41
|
+
//-- resolve nested
|
|
42
|
+
for (const instance of instances) {
|
|
43
|
+
await this.getMapValue(root, currentInstancesResolver, mapper.nestedMapping(instance), preOperator, postOperator);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (postOperator) {
|
|
47
|
+
instances = await postOperator(mapper, mapper.forwardMapping(currentInstances), currentInstances);
|
|
48
|
+
}
|
|
49
|
+
//-- backward assign
|
|
50
|
+
const partial = mapper.rootMapping(instances);
|
|
51
|
+
deepMerge(root, partial);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async createMany({ principal, body, tx, }) {
|
|
55
|
+
const result = [];
|
|
56
|
+
await this.beforeCreating(principal, body.records);
|
|
57
|
+
for (const record of body.records) {
|
|
58
|
+
await this.getMapValue(record, async (mapper) => {
|
|
59
|
+
const repo = new ModelRepository(mapper.modelClass);
|
|
60
|
+
const currentInstances = await repo.getMany({
|
|
61
|
+
ops: mapper.forwardOps(),
|
|
62
|
+
queryProvider: tx,
|
|
63
|
+
});
|
|
64
|
+
return currentInstances.records;
|
|
65
|
+
}, this.dissolver(record), async (mapper, ks, currentInstances) => {
|
|
66
|
+
if (!ks) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
const repo = new ModelRepository(mapper.modelClass);
|
|
70
|
+
const ops = mapper.forwardOps();
|
|
71
|
+
if (mapper.multiple) {
|
|
72
|
+
//-- if ks contain an array of instances, we need to remove old instances and create new ones
|
|
73
|
+
await repo.deleteMany({ ops, tx });
|
|
74
|
+
//-- try creating new
|
|
75
|
+
const createManyResult = await repo.createMany({ principal, body: { records: ks }, tx });
|
|
76
|
+
return createManyResult.records;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
//-- single mapping, we need to update record if found
|
|
80
|
+
const k = ks[0];
|
|
81
|
+
if (!k) {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
if (currentInstances.length === 0) {
|
|
85
|
+
//-- record not found, create new
|
|
86
|
+
const instances = await repo.createMany({
|
|
87
|
+
principal,
|
|
88
|
+
body: { records: ks },
|
|
89
|
+
tx,
|
|
90
|
+
});
|
|
91
|
+
return instances.records;
|
|
92
|
+
}
|
|
93
|
+
else if (currentInstances.length === 1) {
|
|
94
|
+
const result = await repo.updateMany({ principal, ops, body: { update: k }, tx });
|
|
95
|
+
return [{ ...currentInstances[0], ...k, ...result.modified[0] }];
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
throw Errors.VALIDATION_ERROR(`Found multiple record when creating ${mapper.modelClass.name}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
result.push(record);
|
|
103
|
+
}
|
|
104
|
+
return { records: result };
|
|
105
|
+
}
|
|
106
|
+
async updateMany({ principal, queries, ops, body, tx, }) {
|
|
107
|
+
//-- check queries for id field
|
|
108
|
+
if (!queries?.fields?.id || queries.fields.id.length !== 1) {
|
|
109
|
+
throw Errors.VALIDATION_ERROR("Missing required id field in query");
|
|
110
|
+
}
|
|
111
|
+
const result = { ...body.update, id: queries.fields.id[0] };
|
|
112
|
+
await this.beforeCreating(principal, [result]);
|
|
113
|
+
await this.getMapValue(result, async (mapper) => {
|
|
114
|
+
const repo = new ModelRepository(mapper.modelClass);
|
|
115
|
+
const currentInstances = await repo.getMany({
|
|
116
|
+
ops: mapper.forwardOps(ops),
|
|
117
|
+
queryProvider: tx,
|
|
118
|
+
});
|
|
119
|
+
return currentInstances.records;
|
|
120
|
+
}, this.dissolver(result), async (mapper, ks, currentInstances) => {
|
|
121
|
+
if (!ks) {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
const repo = new ModelRepository(mapper.modelClass);
|
|
125
|
+
const opsx = mapper.forwardOps(ops);
|
|
126
|
+
if (mapper.multiple) {
|
|
127
|
+
//-- if ks contain an array of instances, we need to remove old instances and create new ones
|
|
128
|
+
await repo.deleteMany({ ops: opsx, tx });
|
|
129
|
+
//-- try creating new
|
|
130
|
+
const createManyResult = await repo.createMany({ principal, body: { records: ks }, tx });
|
|
131
|
+
return createManyResult.records;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
//-- if ks contain only one instance, we only need to update that instance
|
|
135
|
+
const k = ks[0];
|
|
136
|
+
if (!k) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
if (currentInstances.length == 0) {
|
|
140
|
+
//-- record not found, return empty array
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
if (currentInstances.length > 1) {
|
|
144
|
+
//-- remove all instances
|
|
145
|
+
await repo.deleteMany({ ops: opsx, tx });
|
|
146
|
+
//-- try creating new
|
|
147
|
+
await repo.createMany({ principal, body: { records: [k] }, tx });
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
//-- update current instance
|
|
151
|
+
await repo.updateMany({ principal, ops: opsx, body: { update: k }, tx });
|
|
152
|
+
}
|
|
153
|
+
return currentInstances.map((re) => ({ ...re, ...k }));
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
let projection = ["id"];
|
|
157
|
+
if (queries.returning) {
|
|
158
|
+
//-- return result
|
|
159
|
+
projection = [
|
|
160
|
+
...projection,
|
|
161
|
+
...Object.keys(body.update).filter((key) => body.update[key] !== undefined),
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
return { modified: result.id ? this.project([result], projection) : [] };
|
|
165
|
+
}
|
|
166
|
+
async getMany({ queries, ops, queryProvider, }) {
|
|
167
|
+
//-- check queries for id field
|
|
168
|
+
if (!queries?.fields?.id || queries.fields.id.length !== 1) {
|
|
169
|
+
throw Errors.VALIDATION_ERROR("Missing required id field in query");
|
|
170
|
+
}
|
|
171
|
+
const result = { id: queries.fields.id[0] };
|
|
172
|
+
await this.getMapValue(result, async () => [], this.dissolver(result), async (mapper) => {
|
|
173
|
+
const repo = new ModelRepository(mapper.modelClass);
|
|
174
|
+
//-- forward get
|
|
175
|
+
const instances = await repo.getMany({
|
|
176
|
+
ops: mapper.forwardOps(ops),
|
|
177
|
+
queryProvider,
|
|
178
|
+
});
|
|
179
|
+
return instances.records;
|
|
180
|
+
});
|
|
181
|
+
return result.id
|
|
182
|
+
? { records: this.project([result], queries.projection), total: 1 }
|
|
183
|
+
: { records: [], total: 0 };
|
|
184
|
+
}
|
|
185
|
+
async deleteMany({ queries, ops, tx, }) {
|
|
186
|
+
//-- check queries for id field
|
|
187
|
+
if (!queries?.fields?.id || queries.fields.id.length !== 1) {
|
|
188
|
+
throw Errors.VALIDATION_ERROR("Missing required id field in query");
|
|
189
|
+
}
|
|
190
|
+
const result = { id: queries.fields.id[0] };
|
|
191
|
+
await this.getMapValue(result, async () => [], this.dissolver(result), undefined, async (mapper) => {
|
|
192
|
+
const repo = new ModelRepository(mapper.modelClass);
|
|
193
|
+
//const nestedQueries = mapper.forwardQuery();
|
|
194
|
+
const nestedOps = mapper.forwardOps(ops);
|
|
195
|
+
const result = await repo.deleteMany({
|
|
196
|
+
queries: { returning: queries?.returning },
|
|
197
|
+
ops: nestedOps,
|
|
198
|
+
tx,
|
|
199
|
+
});
|
|
200
|
+
return result.modified;
|
|
201
|
+
});
|
|
202
|
+
return { modified: result.id ? [result] : [] };
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|