@clairejs/server 3.22.0 → 3.22.2
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/README.md
CHANGED
|
@@ -2,10 +2,7 @@ import { ModelFieldMetadata, ModelMetadata } from "@clairejs/core";
|
|
|
2
2
|
import { IPrincipal } from "./auth/IPrincipal";
|
|
3
3
|
import { UriMapperHandler } from "../http/file-upload/types";
|
|
4
4
|
export interface ServerModelFieldMetadata extends ModelFieldMetadata {
|
|
5
|
-
uriMapper?:
|
|
6
|
-
mapper: UriMapperHandler;
|
|
7
|
-
subkeys: string[];
|
|
8
|
-
};
|
|
5
|
+
uriMapper?: UriMapperHandler;
|
|
9
6
|
userResolver?: (principal: IPrincipal) => any;
|
|
10
7
|
}
|
|
11
8
|
export interface ServerModelMetadata extends ModelMetadata {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Constructor, HttpMethod,
|
|
1
|
+
import { type Constructor, HttpMethod, Identifiable } from "@clairejs/core";
|
|
2
2
|
import { type UriMapperHandler } from "./file-upload/types";
|
|
3
3
|
import { type HttpResponse } from "./common/HttpResponse";
|
|
4
4
|
import { type IPrincipal } from "../common/auth/IPrincipal";
|
|
@@ -21,7 +21,7 @@ export declare const Queries: () => (prototype: AbstractHttpController, property
|
|
|
21
21
|
export declare const Headers: () => (prototype: AbstractHttpController, propertyKey: string, paramIndex: number) => void;
|
|
22
22
|
export declare const Socket: () => (prototype: AbstractHttpController, propertyKey: string, paramIndex: number) => void;
|
|
23
23
|
export declare const Raw: () => (prototype: AbstractHttpController, propertyKey: string, paramIndex: number) => void;
|
|
24
|
-
export declare const UriMapper: (mapper: UriMapperHandler) => <T extends
|
|
24
|
+
export declare const UriMapper: (mapper: UriMapperHandler) => <T extends Identifiable>(prototype: T, propertyKey: keyof T) => void;
|
|
25
25
|
/**
|
|
26
26
|
* Current User decorator only has effect when being used with ICrudRepository.
|
|
27
27
|
* */
|
package/dist/http/decorators.js
CHANGED
|
@@ -61,9 +61,8 @@ export const Headers = () => RequestDeco("headers");
|
|
|
61
61
|
export const Socket = () => RequestDeco("socket");
|
|
62
62
|
export const Raw = () => RequestDeco("raw");
|
|
63
63
|
export const UriMapper = (mapper) => (prototype, propertyKey) => {
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
field.uriMapper = { mapper, subkeys: keys };
|
|
64
|
+
const field = initFieldMetadata(prototype, propertyKey);
|
|
65
|
+
field.uriMapper = mapper;
|
|
67
66
|
};
|
|
68
67
|
/**
|
|
69
68
|
* Current User decorator only has effect when being used with ICrudRepository.
|
|
@@ -1,34 +1,14 @@
|
|
|
1
1
|
import { DataType, getModelById, getServiceProvider, RangeQueryDto, uniqueReducer, leanData, getSystemLocale, Errors, omitData, MODEL_FIELD_SEPARATOR, } from "@clairejs/core";
|
|
2
|
-
import { getDirectFields } from "@clairejs/orm";
|
|
2
|
+
import { getDirectFields, getSafeUpdate, } from "@clairejs/orm";
|
|
3
3
|
import { AbstractFileUploadHandler } from "../file-upload/AbstractFileUploadHandler";
|
|
4
4
|
import { AbstractRepository } from "./AbstractRepository";
|
|
5
5
|
import { LocaleTranslation } from "../../system/locale/LocaleTranslation";
|
|
6
6
|
import { LocaleEntry } from "../../system/locale/LocaleEntry";
|
|
7
|
-
const resolveUris = (record, field
|
|
8
|
-
return field.vectorProps
|
|
9
|
-
? field.vectorProps.elementDataType === DataType.STRING
|
|
10
|
-
? record[field.name]
|
|
11
|
-
: record[field.name].map((obj) => subkeys.reduce((value, key) => value[key], obj))
|
|
12
|
-
: [record[field.name]];
|
|
7
|
+
const resolveUris = (record, field) => {
|
|
8
|
+
return field.vectorProps?.elementDataType === DataType.STRING ? record[field.name] : [record[field.name]];
|
|
13
9
|
};
|
|
14
|
-
const assignUrls = (record, field,
|
|
15
|
-
|
|
16
|
-
if (field.vectorProps.elementDataType === DataType.STRING) {
|
|
17
|
-
record[field.name] = urls;
|
|
18
|
-
}
|
|
19
|
-
else {
|
|
20
|
-
record[field.name].forEach((obj, index) => {
|
|
21
|
-
let currentObj = obj;
|
|
22
|
-
for (let i = 0; i < subkeys.length - 1; i++) {
|
|
23
|
-
currentObj = currentObj[subkeys[i]];
|
|
24
|
-
}
|
|
25
|
-
currentObj[subkeys[subkeys.length - 1]] = urls[index];
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
record[field.name] = urls[0];
|
|
31
|
-
}
|
|
10
|
+
const assignUrls = (record, field, urls) => {
|
|
11
|
+
record[field.name] = field.vectorProps?.elementDataType === DataType.STRING ? urls : urls[0];
|
|
32
12
|
};
|
|
33
13
|
export class ModelRepository extends AbstractRepository {
|
|
34
14
|
model;
|
|
@@ -157,12 +137,18 @@ export class ModelRepository extends AbstractRepository {
|
|
|
157
137
|
//-- update record value and not persist yet
|
|
158
138
|
return newUri;
|
|
159
139
|
};
|
|
160
|
-
|
|
161
|
-
for (const field of
|
|
140
|
+
const mapRecords = (records, modelMetadata) => {
|
|
141
|
+
for (const field of modelMetadata.fields) {
|
|
162
142
|
for (const record of records) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
143
|
+
const value = record[field.name];
|
|
144
|
+
if (!value)
|
|
145
|
+
continue;
|
|
146
|
+
if (field.elementDto) {
|
|
147
|
+
mapRecords(field.vectorProps ? value : [value], field.elementDto);
|
|
148
|
+
}
|
|
149
|
+
else if (field.uriMapper) {
|
|
150
|
+
const mapper = field.uriMapper;
|
|
151
|
+
const tmpUris = resolveUris(record, field);
|
|
166
152
|
if (!tmpUris.length) {
|
|
167
153
|
continue;
|
|
168
154
|
}
|
|
@@ -171,11 +157,14 @@ export class ModelRepository extends AbstractRepository {
|
|
|
171
157
|
}
|
|
172
158
|
operations.push((async () => {
|
|
173
159
|
const urls = await Promise.all(tmpUris.map((uri, index) => uriHandler(uri, index, mapper, getSystemLocale())));
|
|
174
|
-
assignUrls(record, field,
|
|
160
|
+
assignUrls(record, field, urls);
|
|
175
161
|
})());
|
|
176
162
|
}
|
|
177
163
|
}
|
|
178
164
|
}
|
|
165
|
+
};
|
|
166
|
+
if (fileUploadHandler) {
|
|
167
|
+
mapRecords(records, this.modelMetadata);
|
|
179
168
|
}
|
|
180
169
|
//-- await all operations once to save time
|
|
181
170
|
await Promise.all(operations);
|
|
@@ -192,22 +181,30 @@ export class ModelRepository extends AbstractRepository {
|
|
|
192
181
|
return;
|
|
193
182
|
}
|
|
194
183
|
const mappingOperations = [];
|
|
195
|
-
|
|
196
|
-
for (const
|
|
197
|
-
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
184
|
+
const mapRecords = (records, modelMetadata) => {
|
|
185
|
+
for (const record of records) {
|
|
186
|
+
for (const field of modelMetadata.fields) {
|
|
187
|
+
const value = record[field.name];
|
|
188
|
+
if (!value)
|
|
189
|
+
continue;
|
|
190
|
+
if (field.elementDto && value) {
|
|
191
|
+
mapRecords(field.vectorProps ? value : [value], field.elementDto);
|
|
192
|
+
}
|
|
193
|
+
else if (field.uriMapper) {
|
|
194
|
+
const uris = resolveUris(record, field);
|
|
195
|
+
mappingOperations.push((async () => {
|
|
196
|
+
const urls = await Promise.all(uris.map(async (uri) => {
|
|
197
|
+
return field.mimeProps?.public
|
|
198
|
+
? await fileUploadHandler.resolvePublicUrl(uri)
|
|
199
|
+
: await fileUploadHandler.resolvePrivateUrl(uri);
|
|
200
|
+
}));
|
|
201
|
+
assignUrls(record, field, urls);
|
|
202
|
+
})());
|
|
203
|
+
}
|
|
208
204
|
}
|
|
209
205
|
}
|
|
210
|
-
}
|
|
206
|
+
};
|
|
207
|
+
mapRecords(records, this.modelMetadata);
|
|
211
208
|
await Promise.all(mappingOperations);
|
|
212
209
|
}
|
|
213
210
|
async createMany({ principal, body, tx, logger, }) {
|
|
@@ -284,7 +281,11 @@ export class ModelRepository extends AbstractRepository {
|
|
|
284
281
|
: [];
|
|
285
282
|
await this.beforeReturning(records);
|
|
286
283
|
const projection = this.modelMetadata.fields
|
|
287
|
-
.filter((field) => !field.multiLocaleColumn &&
|
|
284
|
+
.filter((field) => !field.multiLocaleColumn &&
|
|
285
|
+
(field.pk ||
|
|
286
|
+
field.serverValue ||
|
|
287
|
+
field.mimeProps ||
|
|
288
|
+
field.vectorProps?.elementDataType === DataType.OBJECT))
|
|
288
289
|
.map((field) => field.name);
|
|
289
290
|
records = this.project(records, projection);
|
|
290
291
|
//-- then create records for has many fields
|
|
@@ -335,9 +336,13 @@ export class ModelRepository extends AbstractRepository {
|
|
|
335
336
|
const allConditions = ops || [];
|
|
336
337
|
const hasManyFields = this.modelMetadata.fields.filter((f) => !!f.hasMany);
|
|
337
338
|
const systemLocale = getSystemLocale();
|
|
339
|
+
const relevantIdentifiableVectorFields = this.modelMetadata.fields.filter((field) => field.vectorProps?.elementDataType === DataType.OBJECT &&
|
|
340
|
+
field.elementDto?.fields.some((f) => f.pk) &&
|
|
341
|
+
body.update[field.name]);
|
|
338
342
|
//-- does not update multi locale columns
|
|
339
343
|
const directUpdateFields = getDirectFields(this.modelMetadata).filter((f) => !f.multiLocaleColumn &&
|
|
340
|
-
(body.update[f.name] !== undefined || (f.isMultiLocale && !!systemLocale))
|
|
344
|
+
(body.update[f.name] !== undefined || (f.isMultiLocale && !!systemLocale)) &&
|
|
345
|
+
!relevantIdentifiableVectorFields.includes(f));
|
|
341
346
|
const updatedFields = Object.keys(body.update);
|
|
342
347
|
const localeOfFields = this.modelMetadata.fields.filter((f) => updatedFields.includes(f.name) && f.multiLocaleColumn);
|
|
343
348
|
const cleanUp = await this.uriHandling([body.update]);
|
|
@@ -364,42 +369,57 @@ export class ModelRepository extends AbstractRepository {
|
|
|
364
369
|
const nestedQueries = this.getNestedQueries(queries);
|
|
365
370
|
let modified = [];
|
|
366
371
|
let updatedRecords = [];
|
|
372
|
+
const projections = [
|
|
373
|
+
...(queries?.returning || []),
|
|
374
|
+
...relevantIdentifiableVectorFields.map((f) => f.name),
|
|
375
|
+
"id",
|
|
376
|
+
];
|
|
367
377
|
if (nestedQueries.length) {
|
|
368
378
|
const tobeUpdated = await this.db
|
|
369
379
|
.use(this.model, tx)
|
|
370
|
-
.
|
|
371
|
-
modified = tobeUpdated.
|
|
380
|
+
.getRecords(condition, { projection: projections }, nestedQueries);
|
|
381
|
+
modified = tobeUpdated.map((r) => r.id);
|
|
372
382
|
if (modified.length) {
|
|
373
383
|
if (directUpdateFields.length) {
|
|
374
384
|
updatedRecords = await this.db
|
|
375
385
|
.use(this.model, tx)
|
|
376
|
-
.updateMany({ _in: { id: modified } }, directUpdate,
|
|
377
|
-
...(queries?.returning || []),
|
|
378
|
-
"id",
|
|
379
|
-
]);
|
|
386
|
+
.updateMany({ _in: { id: modified } }, directUpdate, projections);
|
|
380
387
|
}
|
|
381
388
|
else {
|
|
382
|
-
updatedRecords = tobeUpdated
|
|
389
|
+
updatedRecords = tobeUpdated;
|
|
383
390
|
}
|
|
384
391
|
}
|
|
385
392
|
}
|
|
386
393
|
else {
|
|
387
394
|
if (directUpdateFields.length) {
|
|
388
|
-
updatedRecords = await this.db
|
|
389
|
-
.use(this.model, tx)
|
|
390
|
-
.updateMany(condition, directUpdate, [...(queries?.returning || []), "id"]);
|
|
395
|
+
updatedRecords = await this.db.use(this.model, tx).updateMany(condition, directUpdate, projections);
|
|
391
396
|
modified = updatedRecords.map((re) => re.id);
|
|
392
397
|
}
|
|
393
398
|
else {
|
|
394
|
-
const tobeUpdated = await this.db
|
|
395
|
-
.use(this.model, tx)
|
|
396
|
-
.getMany(condition, { projection: [...(queries?.returning || []), "id"] });
|
|
399
|
+
const tobeUpdated = await this.db.use(this.model, tx).getMany(condition, { projection: projections });
|
|
397
400
|
modified = tobeUpdated.records.map((r) => r.id);
|
|
398
401
|
updatedRecords = tobeUpdated.records;
|
|
399
402
|
}
|
|
400
403
|
}
|
|
404
|
+
for (const field of relevantIdentifiableVectorFields) {
|
|
405
|
+
const newValue = body.update[field.name];
|
|
406
|
+
for (const record of updatedRecords) {
|
|
407
|
+
const oldValue = record[field.name];
|
|
408
|
+
const tobeUpdated = newValue.map((newV) => {
|
|
409
|
+
const foundOld = oldValue?.find((v) => v.id === newV.id);
|
|
410
|
+
return {
|
|
411
|
+
...foundOld,
|
|
412
|
+
...newV,
|
|
413
|
+
};
|
|
414
|
+
});
|
|
415
|
+
const updatedRecord = await this.db
|
|
416
|
+
.use(this.model, tx)
|
|
417
|
+
.updateById(record.id, { [field.name]: tobeUpdated }, [field.name]);
|
|
418
|
+
Object.assign(record, updatedRecord);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
401
421
|
//-- body.update here had been modified by uri handling
|
|
402
|
-
const records = updatedRecords.map((re) => ({ ...re, ...body.update,
|
|
422
|
+
const records = updatedRecords.map((re) => ({ ...re, ...getSafeUpdate(body.update, this.modelMetadata) }));
|
|
403
423
|
//-- update translations
|
|
404
424
|
if (localeOfFields.length) {
|
|
405
425
|
//-- check if there is missing locale entry for localeFields
|
|
@@ -510,19 +530,10 @@ export class ModelRepository extends AbstractRepository {
|
|
|
510
530
|
.concat(updatedToBeKept.map((r) => r.modified[0]));
|
|
511
531
|
theRecord[field.name] = (field.hasMany?.single ? finalInnerRecords[0] : finalInnerRecords);
|
|
512
532
|
}
|
|
513
|
-
let projection = ["id"];
|
|
514
|
-
if (queries?.returning) {
|
|
515
|
-
//-- return result
|
|
516
|
-
projection = [
|
|
517
|
-
...projection,
|
|
518
|
-
...Object.keys(directUpdate).filter((key) => directUpdate[key] !== undefined),
|
|
519
|
-
"lastModified",
|
|
520
|
-
];
|
|
521
|
-
}
|
|
522
533
|
//-- ok clean up
|
|
523
534
|
cleanUp().catch((err) => logger?.error("Error in clean up", err));
|
|
524
535
|
await this.beforeReturning(records);
|
|
525
|
-
return { modified: this.project(records,
|
|
536
|
+
return { modified: this.project(records, projections) };
|
|
526
537
|
}
|
|
527
538
|
async getMany({ queries, ops, tx, logger: _logger, }) {
|
|
528
539
|
const conditions = ops || [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clairejs/server",
|
|
3
|
-
"version": "3.22.
|
|
3
|
+
"version": "3.22.2",
|
|
4
4
|
"description": "Claire server NodeJs framework written in Typescript.",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"@clairejs/client": "^3.4.4",
|
|
37
|
-
"@clairejs/core": "^3.8.
|
|
38
|
-
"@clairejs/orm": "^3.16.
|
|
37
|
+
"@clairejs/core": "^3.8.10",
|
|
38
|
+
"@clairejs/orm": "^3.16.15"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/cookie-parser": "^1.4.3",
|