@tstdl/base 0.92.98 → 0.92.100
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/api/server/middlewares/cors.middleware.js +7 -8
- package/document-management/api/document-management.api.d.ts +4 -0
- package/document-management/models/document.model.d.ts +1 -0
- package/document-management/models/document.model.js +6 -1
- package/document-management/models/service-models/document.service-model.d.ts +2 -0
- package/document-management/models/service-models/document.service-model.js +1 -1
- package/document-management/models/service-models/document.view-model.d.ts +8 -0
- package/document-management/models/service-models/document.view-model.js +12 -1
- package/document-management/server/services/document-management.service.d.ts +12 -1
- package/document-management/server/services/document-management.service.js +114 -24
- package/message-bus/message-bus-base.d.ts +0 -1
- package/message-bus/message-bus-base.js +2 -9
- package/orm/decorators.d.ts +5 -4
- package/orm/server/drizzle/schema-converter.js +3 -2
- package/package.json +2 -2
- package/queue/mongo/queue.d.ts +3 -2
- package/queue/mongo/queue.js +8 -2
- package/queue/postgres/job.model.js +3 -3
- package/queue/postgres/queue.d.ts +3 -0
- package/queue/postgres/queue.js +13 -6
- package/queue/queue.d.ts +6 -0
- package/utils/try-ignore.d.ts +4 -4
- package/utils/try-ignore.js +2 -2
|
@@ -2,7 +2,6 @@ import { resolveApiEndpointDataProvider } from '../../../api/types.js';
|
|
|
2
2
|
import { toArray } from '../../../utils/array/array.js';
|
|
3
3
|
import { isDefined } from '../../../utils/type-guards.js';
|
|
4
4
|
export function corsMiddleware(options = {}) {
|
|
5
|
-
// eslint-disable-next-line max-statements, @typescript-eslint/no-shadow
|
|
6
5
|
async function corsMiddleware(context, next) {
|
|
7
6
|
try {
|
|
8
7
|
await next();
|
|
@@ -15,18 +14,18 @@ export function corsMiddleware(options = {}) {
|
|
|
15
14
|
const cors = { ...options.default, ...endpointDefinition?.cors };
|
|
16
15
|
if (isOptions) {
|
|
17
16
|
const allowMethods = (await resolveApiEndpointDataProvider(request, context, cors.accessControlAllowMethods)) ?? [...context.api.endpoints.keys()].join(', ');
|
|
18
|
-
response.headers.
|
|
17
|
+
response.headers.set('Access-Control-Allow-Methods', allowMethods);
|
|
19
18
|
if (isDefined(cors.accessControlAllowHeaders) && !request.headers.has('Access-Control-Allow-Headers')) {
|
|
20
19
|
const value = await resolveApiEndpointDataProvider(request, context, cors.accessControlAllowHeaders);
|
|
21
|
-
response.headers.
|
|
20
|
+
response.headers.set('Access-Control-Allow-Headers', value);
|
|
22
21
|
}
|
|
23
22
|
if (isDefined(cors.accessControlExposeHeaders) && !request.headers.has('Access-Control-Expose-Headers')) {
|
|
24
23
|
const value = await resolveApiEndpointDataProvider(request, context, cors.accessControlExposeHeaders);
|
|
25
|
-
response.headers.
|
|
24
|
+
response.headers.set('Access-Control-Expose-Headers', value);
|
|
26
25
|
}
|
|
27
26
|
if (isDefined(cors.accessControlMaxAge) && !request.headers.has('Access-Control-Max-Age')) {
|
|
28
27
|
const value = await resolveApiEndpointDataProvider(request, context, cors.accessControlMaxAge);
|
|
29
|
-
response.headers.
|
|
28
|
+
response.headers.set('Access-Control-Max-Age', value);
|
|
30
29
|
}
|
|
31
30
|
}
|
|
32
31
|
if (!request.headers.has('Access-Control-Allow-Credentials')) {
|
|
@@ -34,19 +33,19 @@ export function corsMiddleware(options = {}) {
|
|
|
34
33
|
? await resolveApiEndpointDataProvider(request, context, cors.accessControlAllowCredentials)
|
|
35
34
|
: endpointDefinition?.credentials;
|
|
36
35
|
if (allowCredentials == true) {
|
|
37
|
-
response.headers.
|
|
36
|
+
response.headers.set('Access-Control-Allow-Credentials', 'true');
|
|
38
37
|
}
|
|
39
38
|
}
|
|
40
39
|
if (isDefined(cors.accessControlAllowOrigin) && !response.headers.has('Access-Control-Allow-Origin')) {
|
|
41
40
|
const value = await resolveApiEndpointDataProvider(request, context, cors.accessControlAllowOrigin);
|
|
42
|
-
response.headers.
|
|
41
|
+
response.headers.set('Access-Control-Allow-Origin', value);
|
|
43
42
|
}
|
|
44
43
|
if (isDefined(cors.autoAccessControlAllowOrigin) && !response.headers.has('Access-Control-Allow-Origin')) {
|
|
45
44
|
const value = await resolveApiEndpointDataProvider(request, context, cors.autoAccessControlAllowOrigin);
|
|
46
45
|
const origin = request.headers.tryGetSingle('Origin');
|
|
47
46
|
const allowed = isDefined(value) && toArray(value).includes(origin);
|
|
48
47
|
if (allowed) {
|
|
49
|
-
response.headers.
|
|
48
|
+
response.headers.set('Access-Control-Allow-Origin', origin);
|
|
50
49
|
}
|
|
51
50
|
}
|
|
52
51
|
}
|
|
@@ -98,6 +98,7 @@ export declare const documentManagementApiDefinition: {
|
|
|
98
98
|
typeId: import("../../orm/schemas/uuid.js").Uuid | null;
|
|
99
99
|
subtitle: string | null;
|
|
100
100
|
pages: number | null;
|
|
101
|
+
validated: boolean;
|
|
101
102
|
originalFileName: string | null;
|
|
102
103
|
collectionIds: string | string[];
|
|
103
104
|
properties?: {
|
|
@@ -374,6 +375,7 @@ export declare const documentManagementApiDefinition: {
|
|
|
374
375
|
typeId?: import("../../orm/types.js").Uuid | null | undefined;
|
|
375
376
|
subtitle?: string | null | undefined;
|
|
376
377
|
pages?: number | null | undefined;
|
|
378
|
+
validated?: boolean | undefined;
|
|
377
379
|
properties?: {
|
|
378
380
|
propertyId: import("../../orm/schemas/uuid.js").Uuid;
|
|
379
381
|
value: unknown;
|
|
@@ -488,6 +490,7 @@ declare const _DocumentManagementApi: import("../../api/index.js").ApiClient<{
|
|
|
488
490
|
typeId: import("../../orm/schemas/uuid.js").Uuid | null;
|
|
489
491
|
subtitle: string | null;
|
|
490
492
|
pages: number | null;
|
|
493
|
+
validated: boolean;
|
|
491
494
|
originalFileName: string | null;
|
|
492
495
|
collectionIds: string | string[];
|
|
493
496
|
properties?: {
|
|
@@ -764,6 +767,7 @@ declare const _DocumentManagementApi: import("../../api/index.js").ApiClient<{
|
|
|
764
767
|
typeId?: import("../../orm/types.js").Uuid | null | undefined;
|
|
765
768
|
subtitle?: string | null | undefined;
|
|
766
769
|
pages?: number | null | undefined;
|
|
770
|
+
validated?: boolean | undefined;
|
|
767
771
|
properties?: {
|
|
768
772
|
propertyId: import("../../orm/schemas/uuid.js").Uuid;
|
|
769
773
|
value: unknown;
|
|
@@ -10,7 +10,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
import { References } from '../../orm/decorators.js';
|
|
11
11
|
import { Entity } from '../../orm/entity.js';
|
|
12
12
|
import { NumericDate, Uuid } from '../../orm/types.js';
|
|
13
|
-
import { Array, Integer, string, StringProperty } from '../../schema/index.js';
|
|
13
|
+
import { Array, BooleanProperty, Integer, string, StringProperty } from '../../schema/index.js';
|
|
14
14
|
import { DocumentFile } from './document-file.model.js';
|
|
15
15
|
import { DocumentManagementTable } from './document-management-table.js';
|
|
16
16
|
import { DocumentType } from './document-type.model.js';
|
|
@@ -23,6 +23,7 @@ let Document = class Document extends Entity {
|
|
|
23
23
|
date;
|
|
24
24
|
summary;
|
|
25
25
|
tags;
|
|
26
|
+
validated;
|
|
26
27
|
};
|
|
27
28
|
__decorate([
|
|
28
29
|
Uuid(),
|
|
@@ -58,6 +59,10 @@ __decorate([
|
|
|
58
59
|
Array(string(), { nullable: true }),
|
|
59
60
|
__metadata("design:type", Object)
|
|
60
61
|
], Document.prototype, "tags", void 0);
|
|
62
|
+
__decorate([
|
|
63
|
+
BooleanProperty(),
|
|
64
|
+
__metadata("design:type", Boolean)
|
|
65
|
+
], Document.prototype, "validated", void 0);
|
|
61
66
|
Document = __decorate([
|
|
62
67
|
DocumentManagementTable()
|
|
63
68
|
], Document);
|
|
@@ -22,6 +22,7 @@ export declare const createDocumentParametersSchema: import("../../../schema/ind
|
|
|
22
22
|
typeId: import("../../../orm/schemas/uuid.js").Uuid | null;
|
|
23
23
|
subtitle: string | null;
|
|
24
24
|
pages: number | null;
|
|
25
|
+
validated: boolean;
|
|
25
26
|
originalFileName: string | null;
|
|
26
27
|
collectionIds: string | string[];
|
|
27
28
|
properties?: {
|
|
@@ -45,6 +46,7 @@ export declare const updateDocumentParametersSchema: import("../../../schema/ind
|
|
|
45
46
|
typeId?: import("../../../orm/types.js").Uuid | null | undefined;
|
|
46
47
|
subtitle?: string | null | undefined;
|
|
47
48
|
pages?: number | null | undefined;
|
|
49
|
+
validated?: boolean | undefined;
|
|
48
50
|
properties?: {
|
|
49
51
|
propertyId: import("../../../orm/schemas/uuid.js").Uuid;
|
|
50
52
|
value: unknown;
|
|
@@ -15,7 +15,7 @@ import { Document } from '../document.model.js';
|
|
|
15
15
|
export const metadataParameterSchema = optional(partial(pick(EntityMetadata, 'attributes')));
|
|
16
16
|
export const metadataParameterObjectSchema = object({ metadata: metadataParameterSchema });
|
|
17
17
|
export const setDocumentPropertyParametersSchema = assign(pick(DocumentPropertyValueBase, ['propertyId']), object({ value: unknown() }), metadataParameterObjectSchema);
|
|
18
|
-
export const createDocumentParametersSchema = assign(pick(Document, ['typeId', 'title', 'subtitle', 'pages', 'date', 'summary', 'tags']), pick(DocumentFile, ['originalFileName']), object({
|
|
18
|
+
export const createDocumentParametersSchema = assign(pick(Document, ['typeId', 'title', 'subtitle', 'pages', 'date', 'summary', 'tags', 'validated']), pick(DocumentFile, ['originalFileName']), object({
|
|
19
19
|
collectionIds: oneOrMany(string()),
|
|
20
20
|
properties: optional(array(setDocumentPropertyParametersSchema))
|
|
21
21
|
}), metadataParameterObjectSchema);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type EnumType } from '../../../enumeration/enumeration.js';
|
|
1
2
|
import { Timestamp } from '../../../orm/types.js';
|
|
2
3
|
import { DocumentCategory } from '../document-category.model.js';
|
|
3
4
|
import { DocumentCollection } from '../document-collection.model.js';
|
|
@@ -7,6 +8,12 @@ import { DocumentRequestFile } from '../document-request-file.model.js';
|
|
|
7
8
|
import { DocumentRequest } from '../document-request.model.js';
|
|
8
9
|
import { DocumentType } from '../document-type.model.js';
|
|
9
10
|
import { Document } from '../document.model.js';
|
|
11
|
+
export declare const ExtractionStatus: {
|
|
12
|
+
readonly Pending: "pending";
|
|
13
|
+
readonly Extracting: "extracting";
|
|
14
|
+
readonly Error: "error";
|
|
15
|
+
};
|
|
16
|
+
export type ExtractionStatus = EnumType<typeof ExtractionStatus>;
|
|
10
17
|
export declare class DocumentCollectionView extends DocumentCollection {
|
|
11
18
|
name: string | null;
|
|
12
19
|
group: string | null;
|
|
@@ -18,6 +25,7 @@ export declare class DocumentViewCollectionAssignment {
|
|
|
18
25
|
export declare class DocumentView extends Document {
|
|
19
26
|
collectionAssignments: DocumentViewCollectionAssignment[];
|
|
20
27
|
properties: DocumentPropertyValue[];
|
|
28
|
+
extractionStatus: ExtractionStatus | null;
|
|
21
29
|
}
|
|
22
30
|
export declare class DocumentRequestView extends DocumentRequest {
|
|
23
31
|
collectionIds: string[];
|
|
@@ -7,8 +7,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
+
import { defineEnum } from '../../../enumeration/enumeration.js';
|
|
10
11
|
import { Timestamp } from '../../../orm/types.js';
|
|
11
|
-
import { Array, StringProperty } from '../../../schema/index.js';
|
|
12
|
+
import { Array, Enumeration, StringProperty } from '../../../schema/index.js';
|
|
12
13
|
import { DocumentCategory } from '../document-category.model.js';
|
|
13
14
|
import { DocumentCollection } from '../document-collection.model.js';
|
|
14
15
|
import { DocumentFile } from '../document-file.model.js';
|
|
@@ -17,6 +18,11 @@ import { DocumentRequestFile } from '../document-request-file.model.js';
|
|
|
17
18
|
import { DocumentRequest } from '../document-request.model.js';
|
|
18
19
|
import { DocumentType } from '../document-type.model.js';
|
|
19
20
|
import { Document } from '../document.model.js';
|
|
21
|
+
export const ExtractionStatus = defineEnum('ExtractionStatus', {
|
|
22
|
+
Pending: 'pending',
|
|
23
|
+
Extracting: 'extracting',
|
|
24
|
+
Error: 'error'
|
|
25
|
+
});
|
|
20
26
|
export class DocumentCollectionView extends DocumentCollection {
|
|
21
27
|
name;
|
|
22
28
|
group;
|
|
@@ -44,6 +50,7 @@ __decorate([
|
|
|
44
50
|
export class DocumentView extends Document {
|
|
45
51
|
collectionAssignments;
|
|
46
52
|
properties;
|
|
53
|
+
extractionStatus;
|
|
47
54
|
}
|
|
48
55
|
__decorate([
|
|
49
56
|
Array(DocumentViewCollectionAssignment),
|
|
@@ -53,6 +60,10 @@ __decorate([
|
|
|
53
60
|
Array(DocumentPropertyValue),
|
|
54
61
|
__metadata("design:type", Array)
|
|
55
62
|
], DocumentView.prototype, "properties", void 0);
|
|
63
|
+
__decorate([
|
|
64
|
+
Enumeration(ExtractionStatus, { nullable: true }),
|
|
65
|
+
__metadata("design:type", Object)
|
|
66
|
+
], DocumentView.prototype, "extractionStatus", void 0);
|
|
56
67
|
export class DocumentRequestView extends DocumentRequest {
|
|
57
68
|
collectionIds;
|
|
58
69
|
requestFiles;
|
|
@@ -2,11 +2,13 @@ import type { RequireExactlyOne, Stringified } from 'type-fest';
|
|
|
2
2
|
import type { CancellationSignal } from '../../../cancellation/token.js';
|
|
3
3
|
import { type AfterResolveContext, afterResolve } from '../../../injector/index.js';
|
|
4
4
|
import { Logger } from '../../../logger/logger.js';
|
|
5
|
+
import { MessageBus } from '../../../message-bus/message-bus.js';
|
|
5
6
|
import { ObjectStorage } from '../../../object-storage/index.js';
|
|
6
7
|
import type { Query } from '../../../orm/index.js';
|
|
7
8
|
import { type Transaction } from '../../../orm/server/index.js';
|
|
8
9
|
import { Queue } from '../../../queue/queue.js';
|
|
9
10
|
import type { OneOrMany, Record } from '../../../types.js';
|
|
11
|
+
import { type Observable } from 'rxjs';
|
|
10
12
|
import { type AddOrArchiveDocumentToOrFromCollectionParameters, type ApplyDocumentRequestsTemplateParameters, type ApproveDocumentRequestFileParameters, type AssignPropertyToTypeParameters, type CategoryAndTypesView, type CreateCollectionParameters, type CreateDocumentCategoryParameters, type CreateDocumentParameters, type CreateDocumentPropertyParameters, type CreateDocumentRequestFileParameters, type CreateDocumentRequestParameters, type CreateDocumentRequestTemplateParameters, type CreateDocumentRequestsTemplateParameters, type CreateDocumentTypeParameters, type DeleteDocumentRequestFileParameters, type DeleteDocumentRequestParameters, type DeleteDocumentRequestTemplateParameters, type DeleteDocumentRequestsTemplateParameters, Document, DocumentCategory, DocumentCollection, DocumentCollectionDocument, DocumentFile, type DocumentManagementData, DocumentProperty, DocumentPropertyDataType, DocumentPropertyValue, DocumentRequest, DocumentRequestAssignmentTask, DocumentRequestAssignmentTaskCollection, DocumentRequestAssignmentTaskPropertyValue, DocumentRequestCollection, DocumentRequestFile, DocumentRequestFilePropertyValue, DocumentRequestTemplate, DocumentRequestsTemplate, type DocumentRequestsTemplateData, DocumentType, DocumentTypeProperty, type LoadDataCollectionsMetadataParameters, type RejectDocumentRequestFileParameters, type RequestFilesStats, type SetDocumentPropertiesParameters, type UpdateDocumentParameters, type UpdateDocumentRequestFileParameters, type UpdateDocumentRequestParameters, type UpdateDocumentRequestTemplateParameters, type UpdateDocumentRequestsTemplateParameters } from '../../models/index.js';
|
|
11
13
|
import { DocumentManagementAncillaryService } from './document-management-ancillary.service.js';
|
|
12
14
|
type DocumentInformationExtractionPropertyResult = {
|
|
@@ -56,8 +58,10 @@ export declare class DocumentManagementService extends DocumentManagementService
|
|
|
56
58
|
protected readonly fileObjectStorage: ObjectStorage;
|
|
57
59
|
protected readonly extractionQueue: Queue<ExtractionJobData>;
|
|
58
60
|
protected readonly assignmentQueue: Queue<AssignmentJobData>;
|
|
61
|
+
protected readonly collectionChangeMessageBus: MessageBus<string[] | undefined>;
|
|
59
62
|
protected readonly logger: Logger;
|
|
60
63
|
[afterResolve](_: unknown, { cancellationSignal }: AfterResolveContext<any>): void;
|
|
64
|
+
changes$(collectionIds: OneOrMany<string>): Observable<void>;
|
|
61
65
|
processQueues(cancellationSignal: CancellationSignal): void;
|
|
62
66
|
resolveNames<const T extends (DocumentCollection | string)[]>(...collectionsOrIds: T): Promise<Stringified<T>>;
|
|
63
67
|
resolveNamesMap(...collectionsOrIds: (DocumentCollection | string)[]): Promise<Record<string, string>>;
|
|
@@ -80,7 +84,7 @@ export declare class DocumentManagementService extends DocumentManagementService
|
|
|
80
84
|
createDocumentRequestTemplate(parameters: CreateDocumentRequestTemplateParameters): Promise<DocumentRequestTemplate>;
|
|
81
85
|
updateDocumentRequestTemplate({ id, typeId, requiredFilesCount, comment, metadata }: UpdateDocumentRequestTemplateParameters): Promise<DocumentRequestTemplate>;
|
|
82
86
|
deleteDocumentRequestTemplate(parameters: DeleteDocumentRequestTemplateParameters): Promise<DocumentRequestTemplate>;
|
|
83
|
-
createDocument({ typeId, title, subtitle, pages, date, summary, tags, originalFileName, collectionIds, properties, metadata }: CreateDocumentParameters, content: Uint8Array | ReadableStream<Uint8Array>): Promise<Document>;
|
|
87
|
+
createDocument({ typeId, title, subtitle, pages, date, summary, tags, validated, originalFileName, collectionIds, properties, metadata }: CreateDocumentParameters, content: Uint8Array | ReadableStream<Uint8Array>): Promise<Document>;
|
|
84
88
|
approveDocumentRequestFile({ id, approvalComment, documentMetadata, requestFileMetadata }: ApproveDocumentRequestFileParameters): Promise<Document>;
|
|
85
89
|
rejectDocumentRequestFile({ id, approvalComment, metadata }: RejectDocumentRequestFileParameters): Promise<void>;
|
|
86
90
|
updateDocumentRequestFile({ id, title, approvalComment, metadata }: UpdateDocumentRequestFileParameters): Promise<DocumentRequestFile>;
|
|
@@ -108,5 +112,12 @@ export declare class DocumentManagementService extends DocumentManagementService
|
|
|
108
112
|
protected getDocumentFileContent(fileId: string): Promise<Uint8Array>;
|
|
109
113
|
protected getDocumentFileContentStream(fileId: string): ReadableStream<Uint8Array>;
|
|
110
114
|
protected getDocumentFileContentObjectUrl(title: string, file: DocumentFile, download: boolean): Promise<string>;
|
|
115
|
+
protected publishChange({ collectionIds, documentIds, requestIds, requestFileIds, requestAssignmentTaskIds: requestAssignmentTasksIds }: {
|
|
116
|
+
collectionIds?: string[];
|
|
117
|
+
documentIds?: string[];
|
|
118
|
+
requestIds?: string[];
|
|
119
|
+
requestFileIds?: string[];
|
|
120
|
+
requestAssignmentTaskIds?: string[];
|
|
121
|
+
}): void;
|
|
111
122
|
}
|
|
112
123
|
export {};
|
|
@@ -63,11 +63,12 @@ import { AiService } from '../../../ai/index.js';
|
|
|
63
63
|
import { Enumerable } from '../../../enumerable/index.js';
|
|
64
64
|
import { BadRequestError } from '../../../errors/index.js';
|
|
65
65
|
import { TemporaryFile, getMimeType, getMimeTypeExtensions } from '../../../file/index.js';
|
|
66
|
-
import { Singleton, afterResolve, inject
|
|
66
|
+
import { Singleton, afterResolve, inject } from '../../../injector/index.js';
|
|
67
67
|
import { Logger } from '../../../logger/logger.js';
|
|
68
|
+
import { MessageBus } from '../../../message-bus/message-bus.js';
|
|
68
69
|
import { ObjectStorage } from '../../../object-storage/index.js';
|
|
69
70
|
import { TRANSACTION_TIMESTAMP, arrayAgg, coalesce, getEntityMap, jsonAgg, toJsonb } from '../../../orm/index.js';
|
|
70
|
-
import { DatabaseConfig,
|
|
71
|
+
import { DatabaseConfig, getRepository, injectRepository } from '../../../orm/server/index.js';
|
|
71
72
|
import { getPdfPageCount } from '../../../pdf/index.js';
|
|
72
73
|
import { Queue } from '../../../queue/queue.js';
|
|
73
74
|
import { array, boolean, enumeration, integer, nullable, number, object, string } from '../../../schema/index.js';
|
|
@@ -81,9 +82,11 @@ import { readBinaryStream } from '../../../utils/stream/index.js';
|
|
|
81
82
|
import { tryIgnoreLogAsync } from '../../../utils/try-ignore.js';
|
|
82
83
|
import { assertBooleanPass, assertDefined, assertDefinedPass, assertNotNullPass, assertNumberPass, assertStringPass, isBoolean, isDefined, isNotNull, isNull, isNumber, isString, isUint8Array, isUndefined } from '../../../utils/type-guards.js';
|
|
83
84
|
import { millisecondsPerMinute } from '../../../utils/units.js';
|
|
85
|
+
import { union } from 'drizzle-orm/pg-core';
|
|
86
|
+
import { debounceTime, filter, map } from 'rxjs';
|
|
84
87
|
import { Document, DocumentCategory, DocumentCollection, DocumentCollectionDocument, DocumentFile, DocumentProperty, DocumentPropertyDataType, DocumentPropertyValue, DocumentRequest, DocumentRequestAssignmentTask, DocumentRequestAssignmentTaskCollection, DocumentRequestAssignmentTaskPropertyValue, DocumentRequestCollection, DocumentRequestFile, DocumentRequestFilePropertyValue, DocumentRequestTemplate, DocumentRequestsTemplate, DocumentType, DocumentTypeProperty } from '../../models/index.js';
|
|
85
88
|
import { DocumentManagementConfig } from '../module.js';
|
|
86
|
-
import { documentCategory, documentProperty, documentRequest, documentRequestAssignmentTask, documentRequestAssignmentTaskCollection, documentRequestAssignmentTaskPropertyValue, documentRequestCollection, documentRequestFile, documentType } from '../schemas.js';
|
|
89
|
+
import { documentCategory, documentCollectionDocument, documentProperty, documentRequest, documentRequestAssignmentTask, documentRequestAssignmentTaskCollection, documentRequestAssignmentTaskPropertyValue, documentRequestCollection, documentRequestFile, documentType } from '../schemas.js';
|
|
87
90
|
import { DocumentManagementAncillaryService } from './document-management-ancillary.service.js';
|
|
88
91
|
const defaultGenerationOptions = {
|
|
89
92
|
model: 'gemini-2.0-flash',
|
|
@@ -116,6 +119,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
116
119
|
fileObjectStorage = inject(ObjectStorage, inject(DocumentManagementConfig).fileObjectStorageModule);
|
|
117
120
|
extractionQueue = inject((Queue), { name: 'DocumentManagement:extraction', processTimeout: 15 * millisecondsPerMinute });
|
|
118
121
|
assignmentQueue = inject((Queue), { name: 'DocumentManagement:assignment', processTimeout: 15 * millisecondsPerMinute });
|
|
122
|
+
collectionChangeMessageBus = inject((MessageBus), 'DocumentManagement:collectionChange');
|
|
119
123
|
logger = inject(Logger, DocumentManagementService_1.name);
|
|
120
124
|
[afterResolve](_, { cancellationSignal }) {
|
|
121
125
|
if (this.isFork) {
|
|
@@ -123,6 +127,10 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
123
127
|
}
|
|
124
128
|
this.processQueues(cancellationSignal);
|
|
125
129
|
}
|
|
130
|
+
changes$(collectionIds) {
|
|
131
|
+
const collectionIdsArray = toArray(collectionIds);
|
|
132
|
+
return this.collectionChangeMessageBus.allMessages$.pipe(filter((message) => isUndefined(message) || collectionIdsArray.some((id) => message.includes(id))), debounceTime(250), map(() => undefined));
|
|
133
|
+
}
|
|
126
134
|
processQueues(cancellationSignal) {
|
|
127
135
|
this.extractionQueue.process({ concurrency: 1, cancellationSignal }, async (job) => {
|
|
128
136
|
const [entry] = objectEntries(job.data);
|
|
@@ -168,11 +176,12 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
168
176
|
]);
|
|
169
177
|
const documentIds = collectionDocuments.map((document) => document.documentId);
|
|
170
178
|
const requestIds = requestCollections.map((requestCollection) => requestCollection.requestId);
|
|
171
|
-
const [documents, requests, requestFiles, propertyValues] = await Promise.all([
|
|
179
|
+
const [documents, requests, requestFiles, propertyValues, extractionJobs] = await Promise.all([
|
|
172
180
|
this.documentService.withTransaction(transaction).loadManyByQuery({ id: { $in: documentIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
|
|
173
181
|
this.documentRequestService.withTransaction(transaction).loadManyByQuery({ id: { $in: requestIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
|
|
174
182
|
this.documentRequestFileService.withTransaction(transaction).loadManyByQuery({ requestId: { $in: requestIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
|
|
175
|
-
this.documentPropertyValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } })
|
|
183
|
+
this.documentPropertyValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } }),
|
|
184
|
+
this.extractionQueue.getByTags(documentIds)
|
|
176
185
|
]);
|
|
177
186
|
const documentFileIds = documents.map((document) => document.fileId);
|
|
178
187
|
const requestFileIds = requestFiles.map((requestFile) => requestFile.fileId);
|
|
@@ -184,11 +193,21 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
184
193
|
name: collectionsMetadata?.[collection.id]?.name ?? null,
|
|
185
194
|
group: collectionsMetadata?.[collection.id]?.group ?? null
|
|
186
195
|
}));
|
|
187
|
-
const documentViews = documents.map((document) =>
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
196
|
+
const documentViews = documents.map((document) => {
|
|
197
|
+
const job = extractionJobs.find((job) => job.tag == document.id);
|
|
198
|
+
return {
|
|
199
|
+
...document,
|
|
200
|
+
collectionAssignments: collectionDocuments.filter((collectionDocument) => collectionDocument.documentId == document.id).map(({ collectionId, archiveTimestamp }) => ({ collectionId, archiveTimestamp })),
|
|
201
|
+
properties: valuesMap.get(document.id) ?? [],
|
|
202
|
+
extractionStatus: isUndefined(job)
|
|
203
|
+
? null
|
|
204
|
+
: isNull(job.lastDequeueTimestamp)
|
|
205
|
+
? 'pending'
|
|
206
|
+
: (job.tries >= this.extractionQueue.maxTries) && (job.lastDequeueTimestamp >= (currentTimestamp() + this.extractionQueue.processTimeout))
|
|
207
|
+
? 'error'
|
|
208
|
+
: 'extracting'
|
|
209
|
+
};
|
|
210
|
+
});
|
|
192
211
|
const requestViews = requests.map((request) => ({
|
|
193
212
|
...request,
|
|
194
213
|
collectionIds: requestCollections.filter((requestCollection) => requestCollection.requestId == request.id).map((requestCollection) => requestCollection.collectionId),
|
|
@@ -287,11 +306,12 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
287
306
|
async deleteDocumentRequestTemplate(parameters) {
|
|
288
307
|
return this.documentRequestTemplateService.delete(parameters.id);
|
|
289
308
|
}
|
|
290
|
-
async createDocument({ typeId, title, subtitle, pages, date, summary, tags, originalFileName, collectionIds, properties, metadata }, content) {
|
|
309
|
+
async createDocument({ typeId, title, subtitle, pages, date, summary, tags, validated, originalFileName, collectionIds, properties, metadata }, content) {
|
|
310
|
+
const collectionIdsArray = toArray(collectionIds);
|
|
291
311
|
const document = await this.documentService.transaction(async (_, transaction) => {
|
|
292
312
|
const documentFile = await this.createDocumentFile(content, originalFileName, transaction);
|
|
293
|
-
const document = await this.documentService.withTransaction(transaction).insert({ fileId: documentFile.id, typeId, title, subtitle, pages, date, summary, tags, metadata });
|
|
294
|
-
for (const collectionId of
|
|
313
|
+
const document = await this.documentService.withTransaction(transaction).insert({ fileId: documentFile.id, typeId, title, subtitle, pages, date, summary, tags, metadata, validated });
|
|
314
|
+
for (const collectionId of collectionIdsArray) {
|
|
295
315
|
await this.documentCollectionDocumentService.withTransaction(transaction).insert({ collectionId, documentId: document.id, archiveTimestamp: null });
|
|
296
316
|
}
|
|
297
317
|
if (isDefined(properties)) {
|
|
@@ -299,7 +319,8 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
299
319
|
}
|
|
300
320
|
return document;
|
|
301
321
|
});
|
|
302
|
-
void tryIgnoreLogAsync(async () => this.extractionQueue.enqueue({ documentId: document.id }
|
|
322
|
+
void tryIgnoreLogAsync(this.logger, async () => this.extractionQueue.enqueue({ documentId: document.id }, { tag: document.id }));
|
|
323
|
+
this.publishChange({ collectionIds: collectionIdsArray });
|
|
303
324
|
return document;
|
|
304
325
|
}
|
|
305
326
|
async approveDocumentRequestFile({ id, approvalComment, documentMetadata, requestFileMetadata }) {
|
|
@@ -319,6 +340,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
319
340
|
date: requestFile.date,
|
|
320
341
|
summary: requestFile.summary,
|
|
321
342
|
tags: requestFile.tags,
|
|
343
|
+
validated: true,
|
|
322
344
|
metadata: documentMetadata
|
|
323
345
|
});
|
|
324
346
|
for (const { collectionId } of requestCollections) {
|
|
@@ -329,16 +351,19 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
329
351
|
});
|
|
330
352
|
}
|
|
331
353
|
async rejectDocumentRequestFile({ id, approvalComment, metadata }) {
|
|
332
|
-
|
|
354
|
+
const requestFile = await this.documentRequestFileService.transaction(async (_, transaction) => {
|
|
333
355
|
const requestFile = await this.documentRequestFileService.withTransaction(transaction).load(id);
|
|
334
356
|
if (requestFile.approval == true) {
|
|
335
357
|
throw new BadRequestError('Document request file already accepted.');
|
|
336
358
|
}
|
|
337
|
-
|
|
359
|
+
return this.documentRequestFileService.withTransaction(transaction).update(id, { approval: false, approvalComment, approvalTimestamp: TRANSACTION_TIMESTAMP, metadata });
|
|
338
360
|
});
|
|
361
|
+
this.publishChange({ requestIds: [requestFile.requestId] });
|
|
339
362
|
}
|
|
340
363
|
async updateDocumentRequestFile({ id, title, approvalComment, metadata }) {
|
|
341
|
-
|
|
364
|
+
const result = await this.documentRequestFileService.update(id, { title, approvalComment, metadata });
|
|
365
|
+
this.publishChange({ requestIds: [result.requestId] });
|
|
366
|
+
return result;
|
|
342
367
|
}
|
|
343
368
|
async deleteDocumentRequestFile({ id, metadata }) {
|
|
344
369
|
const requestFile = await this.documentRequestFileService.load(id);
|
|
@@ -346,6 +371,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
346
371
|
throw new BadRequestError('Only pending request files can be deleted.');
|
|
347
372
|
}
|
|
348
373
|
await this.documentRequestFileService.delete(id, metadata);
|
|
374
|
+
this.publishChange({ requestIds: [requestFile.requestId] });
|
|
349
375
|
}
|
|
350
376
|
async createDocumentFile(content, originalFileName, transaction) {
|
|
351
377
|
const contentAsUint8Array = isUint8Array(content) ? content : await readBinaryStream(content);
|
|
@@ -376,7 +402,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
376
402
|
throw new BadRequestError('Maximum amount of allowed files reached.');
|
|
377
403
|
}
|
|
378
404
|
const file = await this.createDocumentFile(content, originalFileName, transaction);
|
|
379
|
-
|
|
405
|
+
const result = await this.documentRequestFileService
|
|
380
406
|
.withTransaction(transaction)
|
|
381
407
|
.insert({
|
|
382
408
|
requestId,
|
|
@@ -393,20 +419,24 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
393
419
|
approvalTimestamp: null,
|
|
394
420
|
metadata
|
|
395
421
|
});
|
|
422
|
+
return result;
|
|
396
423
|
});
|
|
397
|
-
void tryIgnoreLogAsync(async () => this.extractionQueue.enqueue({ requestFileId: documentRequestFile.id })
|
|
424
|
+
void tryIgnoreLogAsync(this.logger, async () => this.extractionQueue.enqueue({ requestFileId: documentRequestFile.id }));
|
|
425
|
+
this.publishChange({ requestIds: [documentRequestFile.requestId] });
|
|
398
426
|
return documentRequestFile;
|
|
399
427
|
}
|
|
400
428
|
async createDocumentRequest(parameters, transaction) {
|
|
401
429
|
if (parameters.collectionIds.length == 0) {
|
|
402
430
|
throw new BadRequestError('No target collectionId specified.');
|
|
403
431
|
}
|
|
404
|
-
|
|
432
|
+
const result = await this.documentRequestService.useTransaction(transaction, async (_, tx) => {
|
|
405
433
|
const request = await this.documentRequestService.withTransaction(tx).insert({ ...parameters, completed: false });
|
|
406
434
|
const newDocumentRequestCollections = parameters.collectionIds.map((collectionId) => ({ requestId: request.id, collectionId }));
|
|
407
435
|
await this.documentRequestCollectionService.withTransaction(tx).insertMany(newDocumentRequestCollections);
|
|
408
436
|
return request;
|
|
409
437
|
});
|
|
438
|
+
this.publishChange({ collectionIds: parameters.collectionIds });
|
|
439
|
+
return result;
|
|
410
440
|
}
|
|
411
441
|
async createDocumentRequestAssignmentTask({ originalFileName, collectionIds }, content) {
|
|
412
442
|
if (collectionIds.length == 0) {
|
|
@@ -428,7 +458,8 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
428
458
|
});
|
|
429
459
|
const newTaksCollections = collectionIds.map((collectionId) => ({ requestAssignmentTaskId: task.id, collectionId }));
|
|
430
460
|
await this.documentRequestAssignmentTaskCollectionService.withTransaction(transaction).insertMany(newTaksCollections);
|
|
431
|
-
void tryIgnoreLogAsync(async () => this.extractionQueue.enqueue({ requestAssignmentTaskId: task.id })
|
|
461
|
+
void tryIgnoreLogAsync(this.logger, async () => this.extractionQueue.enqueue({ requestAssignmentTaskId: task.id }));
|
|
462
|
+
this.publishChange({ collectionIds });
|
|
432
463
|
return task;
|
|
433
464
|
});
|
|
434
465
|
}
|
|
@@ -440,6 +471,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
440
471
|
await this.setPropertyValues({ documentId, properties: propertyUpdates }, tx);
|
|
441
472
|
}
|
|
442
473
|
});
|
|
474
|
+
this.publishChange({ documentIds: [documentId] });
|
|
443
475
|
}
|
|
444
476
|
async updateDocumentRequest(parameters, transaction) {
|
|
445
477
|
const { id: documentRequestId, ...update } = parameters;
|
|
@@ -451,6 +483,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
451
483
|
}
|
|
452
484
|
}
|
|
453
485
|
await this.documentRequestService.withOptionalTransaction(transaction).update(documentRequestId, update);
|
|
486
|
+
this.publishChange({ requestIds: [documentRequestId] });
|
|
454
487
|
}
|
|
455
488
|
async deleteDocumentRequest({ id, metadata }, transaction) {
|
|
456
489
|
const requestFiles = await this.documentRequestFileService.loadManyByQuery({ requestId: id });
|
|
@@ -459,6 +492,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
459
492
|
throw new BadRequestError('Cannot delete requests which has approved files.');
|
|
460
493
|
}
|
|
461
494
|
await this.documentRequestService.withOptionalTransaction(transaction).delete(id, metadata);
|
|
495
|
+
this.publishChange({ requestIds: [id] });
|
|
462
496
|
}
|
|
463
497
|
async setPropertyValues({ documentId, requestFileId, requestAssignmentTaskId, properties: items }, transaction) {
|
|
464
498
|
if ((items.length == 0)) {
|
|
@@ -497,12 +531,19 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
497
531
|
.upsertMany(['requestAssignmentTaskId', 'propertyId'], propertyItems.map((propertyItem) => ({ ...propertyItem, requestAssignmentTaskId })));
|
|
498
532
|
}
|
|
499
533
|
});
|
|
534
|
+
this.publishChange({
|
|
535
|
+
documentIds: isDefined(documentId) ? [documentId] : undefined,
|
|
536
|
+
requestFileIds: isDefined(requestFileId) ? [requestFileId] : undefined,
|
|
537
|
+
requestAssignmentTaskIds: isDefined(requestAssignmentTaskId) ? [requestAssignmentTaskId] : undefined
|
|
538
|
+
});
|
|
500
539
|
}
|
|
501
540
|
async addDocumentToCollection(parameters, transaction) {
|
|
502
541
|
await this.documentCollectionDocumentService.withOptionalTransaction(transaction).upsert(['collectionId', 'documentId'], { ...parameters, archiveTimestamp: null });
|
|
542
|
+
this.publishChange({ documentIds: [parameters.documentId] });
|
|
503
543
|
}
|
|
504
544
|
async archiveDocument({ collectionId, documentId, metadata }) {
|
|
505
545
|
await this.documentCollectionDocumentService.updateByQuery({ collectionId, documentId }, { archiveTimestamp: TRANSACTION_TIMESTAMP, metadata });
|
|
546
|
+
this.publishChange({ documentIds: [documentId] });
|
|
506
547
|
}
|
|
507
548
|
async createProperty(parameters) {
|
|
508
549
|
return this.documentPropertyService.insert(parameters);
|
|
@@ -514,9 +555,10 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
514
555
|
const document = await this.documentService.load(documentId);
|
|
515
556
|
const { properties, ...extractionResult } = await this.extractFileInformation(document.fileId, document.typeId);
|
|
516
557
|
await this.documentService.transaction(async (documentService, transaction) => {
|
|
517
|
-
await documentService.update(document.id, extractionResult);
|
|
558
|
+
await documentService.update(document.id, { ...extractionResult, validated: false });
|
|
518
559
|
await this.setPropertyValues({ documentId, properties: properties }, transaction);
|
|
519
560
|
});
|
|
561
|
+
this.publishChange({ documentIds: [documentId] });
|
|
520
562
|
}
|
|
521
563
|
async enrichDocumentRequestFile(requestFileId) {
|
|
522
564
|
const requestFile = await this.documentRequestFileService.load(requestFileId);
|
|
@@ -526,6 +568,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
526
568
|
await documentRequestFileService.update(requestFile.id, extractionResult);
|
|
527
569
|
await this.setPropertyValues({ requestFileId, properties: properties }, transaction);
|
|
528
570
|
});
|
|
571
|
+
this.publishChange({ requestIds: [request.id] });
|
|
529
572
|
}
|
|
530
573
|
async enrichDocumentRequestAssignmentTask(requestAssignmentTaskId) {
|
|
531
574
|
const task = await this.documentRequestAssignmentTaskService.load(requestAssignmentTaskId);
|
|
@@ -534,7 +577,8 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
534
577
|
await documentRequestAssignmentTaskService.update(task.id, extractionResult);
|
|
535
578
|
await this.setPropertyValues({ requestAssignmentTaskId, properties: properties }, transaction);
|
|
536
579
|
});
|
|
537
|
-
void tryIgnoreLogAsync(async () => this.assignmentQueue.enqueue({ requestAssignmentTaskId })
|
|
580
|
+
void tryIgnoreLogAsync(this.logger, async () => this.assignmentQueue.enqueue({ requestAssignmentTaskId }));
|
|
581
|
+
this.publishChange({ requestAssignmentTaskIds: [requestAssignmentTaskId] });
|
|
538
582
|
}
|
|
539
583
|
async assignDocumentRequest(requestAssignmentTaskId) {
|
|
540
584
|
const session = this.documentRequestAssignmentTaskCollectionService.session;
|
|
@@ -656,6 +700,7 @@ Ordne die Datei unter "file" der passenden Anforderungen unter "requests" zu. Gi
|
|
|
656
700
|
});
|
|
657
701
|
await this.setPropertyValues({ requestFileId: requestFile.id, properties: task.properties }, transaction);
|
|
658
702
|
});
|
|
703
|
+
this.publishChange({ collectionIds: requestsCollectionIds });
|
|
659
704
|
}
|
|
660
705
|
async extractFileInformation(fileId, assumeTypeId) {
|
|
661
706
|
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
@@ -665,7 +710,7 @@ Ordne die Datei unter "file" der passenden Anforderungen unter "requests" zu. Gi
|
|
|
665
710
|
const tmpFile = __addDisposableResource(env_1, await TemporaryFile.from(fileContentStream), true);
|
|
666
711
|
const filePart = await this.#aiService.processFile({ path: tmpFile.path, mimeType: file.mimeType });
|
|
667
712
|
const pages = file.mimeType.includes('pdf')
|
|
668
|
-
? await tryIgnoreLogAsync(async () => getPdfPageCount(tmpFile.path),
|
|
713
|
+
? await tryIgnoreLogAsync(this.logger, async () => getPdfPageCount(tmpFile.path), null)
|
|
669
714
|
: null;
|
|
670
715
|
const types = await this.documentTypeService.session
|
|
671
716
|
.select({
|
|
@@ -797,6 +842,51 @@ Antworte auf deutsch.`
|
|
|
797
842
|
'Response-Content-Disposition': `${disposition}; filename = "${encodeURIComponent(filename)}"`
|
|
798
843
|
});
|
|
799
844
|
}
|
|
845
|
+
publishChange({ collectionIds, documentIds, requestIds, requestFileIds, requestAssignmentTaskIds: requestAssignmentTasksIds }) {
|
|
846
|
+
void tryIgnoreLogAsync(this.logger, async () => {
|
|
847
|
+
const queries = [
|
|
848
|
+
isUndefined(documentIds)
|
|
849
|
+
? undefined
|
|
850
|
+
: this.session
|
|
851
|
+
.selectDistinct({ collectionId: documentCollectionDocument.collectionId })
|
|
852
|
+
.from(documentCollectionDocument)
|
|
853
|
+
.where(inArray(documentCollectionDocument.documentId, documentIds)),
|
|
854
|
+
isUndefined(requestIds)
|
|
855
|
+
? undefined
|
|
856
|
+
: this.session
|
|
857
|
+
.selectDistinct({ collectionId: documentRequestCollection.collectionId })
|
|
858
|
+
.from(documentRequestCollection)
|
|
859
|
+
.where(inArray(documentRequestCollection.requestId, requestIds)),
|
|
860
|
+
isUndefined(requestFileIds)
|
|
861
|
+
? undefined
|
|
862
|
+
: this.session
|
|
863
|
+
.selectDistinct({ collectionId: documentRequestCollection.collectionId })
|
|
864
|
+
.from(documentRequestFile)
|
|
865
|
+
.innerJoin(documentRequestCollection, eq(documentRequestCollection.requestId, documentRequestFile.requestId))
|
|
866
|
+
.where(inArray(documentRequestFile.requestId, requestFileIds)),
|
|
867
|
+
isUndefined(requestAssignmentTasksIds)
|
|
868
|
+
? undefined
|
|
869
|
+
: this.session
|
|
870
|
+
.selectDistinct({ collectionId: documentRequestAssignmentTaskCollection.collectionId })
|
|
871
|
+
.from(documentRequestAssignmentTaskCollection)
|
|
872
|
+
.where(inArray(documentRequestAssignmentTaskCollection.requestAssignmentTaskId, requestAssignmentTasksIds))
|
|
873
|
+
];
|
|
874
|
+
const filteredQueries = queries.filter(isDefined);
|
|
875
|
+
const mappings = (filteredQueries.length == 1)
|
|
876
|
+
? await filteredQueries[0]
|
|
877
|
+
: (filteredQueries.length > 1)
|
|
878
|
+
? await union(filteredQueries[0], filteredQueries[1], ...filteredQueries.slice(2))
|
|
879
|
+
: [];
|
|
880
|
+
const allCollectionIds = distinct([
|
|
881
|
+
...(collectionIds ?? []),
|
|
882
|
+
...mappings.map((mapping) => mapping.collectionId)
|
|
883
|
+
]);
|
|
884
|
+
if (allCollectionIds.length == 0) {
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
this.collectionChangeMessageBus.publishAndForget(allCollectionIds);
|
|
888
|
+
});
|
|
889
|
+
}
|
|
800
890
|
};
|
|
801
891
|
DocumentManagementService = DocumentManagementService_1 = __decorate([
|
|
802
892
|
Singleton({
|
|
@@ -17,7 +17,6 @@ export declare abstract class MessageBusBase<T> extends MessageBus<T> {
|
|
|
17
17
|
publishAndForget(message: T): void;
|
|
18
18
|
publish(message: T): Promise<void>;
|
|
19
19
|
[disposeAsync](): Promise<void>;
|
|
20
|
-
private _publishAndForget;
|
|
21
20
|
/**
|
|
22
21
|
* publish messages to other instances
|
|
23
22
|
* @param message message to send to other instances
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defer, merge, share, Subject, takeUntil } from 'rxjs';
|
|
2
|
+
import { tryIgnoreLogAsync } from '../utils/try-ignore.js';
|
|
2
3
|
import { CancellationToken } from '../cancellation/token.js';
|
|
3
4
|
import { disposeAsync } from '../disposable/disposable.js';
|
|
4
5
|
import { MessageBus } from './message-bus.js';
|
|
@@ -17,7 +18,7 @@ export class MessageBusBase extends MessageBus {
|
|
|
17
18
|
this.allMessages$ = merge(this.messages$, this.publishSubject);
|
|
18
19
|
}
|
|
19
20
|
publishAndForget(message) {
|
|
20
|
-
void this.
|
|
21
|
+
void tryIgnoreLogAsync(this.logger, async () => this.publish(message));
|
|
21
22
|
}
|
|
22
23
|
async publish(message) {
|
|
23
24
|
if (this.disposeToken.isSet) {
|
|
@@ -34,12 +35,4 @@ export class MessageBusBase extends MessageBus {
|
|
|
34
35
|
this.publishSubject.complete();
|
|
35
36
|
return this._dispose();
|
|
36
37
|
}
|
|
37
|
-
async _publishAndForget(message) {
|
|
38
|
-
try {
|
|
39
|
-
await this.publish(message);
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
this.logger.error(error);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
38
|
}
|
package/orm/decorators.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { Entity, EntityType } from './entity.js';
|
|
|
5
5
|
import type { PgTableFromType } from './server/types.js';
|
|
6
6
|
type IndexMethod = LiteralUnion<'hash' | 'btree' | 'gist' | 'spgist' | 'gin' | 'brin' | 'hnsw' | 'ivfflat', string>;
|
|
7
7
|
type NamingStrategy = 'abbreviated-table';
|
|
8
|
+
type Columns<T> = [Extract<keyof T, string>, ...Extract<keyof T, string>[]];
|
|
8
9
|
export type CheckBuilder<T extends Entity = any> = (table: PgTableFromType<EntityType<T>>) => SQL;
|
|
9
10
|
export type OrmTableReflectionData = {
|
|
10
11
|
name?: string;
|
|
@@ -69,9 +70,9 @@ type TableOptions = Partial<Pick<OrmTableReflectionData, 'name' | 'schema'>>;
|
|
|
69
70
|
export declare function Table(name?: string, options?: TypedOmit<TableOptions, 'schema'>): ClassDecorator;
|
|
70
71
|
export declare function Table(options?: TableOptions): ClassDecorator;
|
|
71
72
|
export declare function Unique(name?: string, options?: UniqueReflectionData['options']): PropertyDecorator;
|
|
72
|
-
export declare function Unique<T>(name: string | undefined, columns:
|
|
73
|
-
export declare function Unique<T>(columns:
|
|
73
|
+
export declare function Unique<T>(name: string | undefined, columns: Columns<T>, options?: UniqueReflectionData['options']): ClassDecorator;
|
|
74
|
+
export declare function Unique<T>(columns: Columns<T>, options?: UniqueReflectionData['options']): ClassDecorator;
|
|
74
75
|
export declare function Index(name?: string, options?: IndexReflectionData['options']): PropertyDecorator;
|
|
75
|
-
export declare function Index(name: string, columns:
|
|
76
|
-
export declare function Index(columns:
|
|
76
|
+
export declare function Index<T>(name: string, columns: Columns<T>, options?: IndexReflectionData['options']): ClassDecorator;
|
|
77
|
+
export declare function Index<T>(columns: Columns<T>, options?: IndexReflectionData['options']): ClassDecorator;
|
|
77
78
|
export {};
|
|
@@ -35,8 +35,9 @@ export function _getDrizzleTableFromType(type, fallbackSchemaName) {
|
|
|
35
35
|
tableReflectionDatas.push(tableReflectionData);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
+
const mergedTableReflectionData = tableReflectionDatas.reduceRight((merged, data) => ({ ...merged, ...data }), {});
|
|
38
39
|
const tableReflectionData = tableReflectionDatas[0];
|
|
39
|
-
const schema = assertDefinedPass(
|
|
40
|
+
const schema = assertDefinedPass(mergedTableReflectionData.schema ?? fallbackSchemaName, 'Table schema not provided');
|
|
40
41
|
const tableName = tableReflectionData?.name ?? getDefaultTableName(type);
|
|
41
42
|
const dbSchema = getDbSchema(schema);
|
|
42
43
|
const columnDefinitions = getPostgresColumnEntries(type, dbSchema, tableName);
|
|
@@ -64,7 +65,7 @@ export function _getDrizzleTableFromType(type, fallbackSchemaName) {
|
|
|
64
65
|
function buildPrimaryKey(table) {
|
|
65
66
|
const columns = primaryKeyColumnDefinitions.map((columnDefinition) => getColumn(table, columnDefinition.name));
|
|
66
67
|
return primaryKey({
|
|
67
|
-
name:
|
|
68
|
+
name: mergedTableReflectionData.compundPrimaryKeyName ?? getPrimaryKeyName(tableName, columns, { naming: mergedTableReflectionData.compundPrimaryKeyNaming }),
|
|
68
69
|
columns
|
|
69
70
|
});
|
|
70
71
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tstdl/base",
|
|
3
|
-
"version": "0.92.
|
|
3
|
+
"version": "0.92.100",
|
|
4
4
|
"author": "Patrick Hein",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -177,7 +177,7 @@
|
|
|
177
177
|
"playwright": "^1.51",
|
|
178
178
|
"preact": "^10.26",
|
|
179
179
|
"preact-render-to-string": "^6.5",
|
|
180
|
-
"undici": "^7.
|
|
180
|
+
"undici": "^7.5",
|
|
181
181
|
"urlpattern-polyfill": "^10.0"
|
|
182
182
|
},
|
|
183
183
|
"peerDependenciesMeta": {
|
package/queue/mongo/queue.d.ts
CHANGED
|
@@ -8,9 +8,9 @@ export declare class MongoQueue<T = unknown> extends Queue<T> {
|
|
|
8
8
|
private readonly repository;
|
|
9
9
|
private readonly lock;
|
|
10
10
|
private readonly queueKey;
|
|
11
|
-
private readonly processTimeout;
|
|
12
|
-
private readonly maxTries;
|
|
13
11
|
private readonly messageBus;
|
|
12
|
+
readonly processTimeout: number;
|
|
13
|
+
readonly maxTries: number;
|
|
14
14
|
constructor(repository: MongoJobRepository<T>, lock: Lock, messageBusProvider: MessageBusProvider, key: string, config?: QueueConfig);
|
|
15
15
|
enqueue(data: T, options?: EnqueueOneOptions): Promise<Job<T>>;
|
|
16
16
|
enqueueMany(items: EnqueueManyItem<T>[], options?: EnqueueManyOptions & {
|
|
@@ -24,6 +24,7 @@ export declare class MongoQueue<T = unknown> extends Queue<T> {
|
|
|
24
24
|
countByTag(tag: JobTag): Promise<number>;
|
|
25
25
|
get(id: string): Promise<Job<T> | undefined>;
|
|
26
26
|
getByTag(tag: JobTag): Promise<Job<T>[]>;
|
|
27
|
+
getByTags(tags: JobTag[]): Promise<Job<T>[]>;
|
|
27
28
|
cancel(id: string): Promise<void>;
|
|
28
29
|
cancelMany(ids: string[]): Promise<void>;
|
|
29
30
|
cancelByTag(tag: JobTag): Promise<void>;
|
package/queue/mongo/queue.js
CHANGED
|
@@ -36,9 +36,9 @@ let MongoQueue = class MongoQueue extends Queue {
|
|
|
36
36
|
repository;
|
|
37
37
|
lock;
|
|
38
38
|
queueKey;
|
|
39
|
+
messageBus;
|
|
39
40
|
processTimeout;
|
|
40
41
|
maxTries;
|
|
41
|
-
messageBus;
|
|
42
42
|
constructor(repository, lock, messageBusProvider, key, config) {
|
|
43
43
|
super();
|
|
44
44
|
this.repository = repository;
|
|
@@ -122,6 +122,9 @@ let MongoQueue = class MongoQueue extends Queue {
|
|
|
122
122
|
async getByTag(tag) {
|
|
123
123
|
return this.repository.loadManyByFilter({ queue: this.queueKey, tag });
|
|
124
124
|
}
|
|
125
|
+
async getByTags(tags) {
|
|
126
|
+
return this.repository.loadManyByFilter({ queue: this.queueKey, tag: { $in: tags } });
|
|
127
|
+
}
|
|
125
128
|
async cancel(id) {
|
|
126
129
|
await this.repository.deleteByFilter({ queue: this.queueKey, jobId: id });
|
|
127
130
|
}
|
|
@@ -237,7 +240,10 @@ function toModelJob(mongoJob) {
|
|
|
237
240
|
id: mongoJob.jobId,
|
|
238
241
|
priority: mongoJob.priority,
|
|
239
242
|
tag: mongoJob.tag,
|
|
240
|
-
data: mongoJob.data
|
|
243
|
+
data: mongoJob.data,
|
|
244
|
+
enqueueTimestamp: mongoJob.enqueueTimestamp,
|
|
245
|
+
lastDequeueTimestamp: mongoJob.lastDequeueTimestamp,
|
|
246
|
+
tries: mongoJob.tries
|
|
241
247
|
};
|
|
242
248
|
return job;
|
|
243
249
|
}
|
|
@@ -7,9 +7,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
-
import { Table } from '../../orm/decorators.js';
|
|
10
|
+
import { Index, Table } from '../../orm/decorators.js';
|
|
11
11
|
import { EntityWithoutMetadata } from '../../orm/entity.js';
|
|
12
|
-
import { Integer, Json, Timestamp
|
|
12
|
+
import { Integer, Json, Timestamp } from '../../orm/index.js';
|
|
13
13
|
import { StringProperty } from '../../schema/index.js';
|
|
14
14
|
let PostgresJob = class PostgresJob extends EntityWithoutMetadata {
|
|
15
15
|
queue;
|
|
@@ -50,6 +50,6 @@ __decorate([
|
|
|
50
50
|
], PostgresJob.prototype, "data", void 0);
|
|
51
51
|
PostgresJob = __decorate([
|
|
52
52
|
Table('job'),
|
|
53
|
-
|
|
53
|
+
Index(['queue', 'tag'])
|
|
54
54
|
], PostgresJob);
|
|
55
55
|
export { PostgresJob };
|
|
@@ -3,6 +3,8 @@ import type { ObjectLiteral } from '../../types.js';
|
|
|
3
3
|
import { Queue, type EnqueueManyItem, type EnqueueManyOptions, type EnqueueOneOptions, type Job, type JobTag } from '../queue.js';
|
|
4
4
|
export declare class PostgresQueue<T extends ObjectLiteral> extends Queue<T> {
|
|
5
5
|
#private;
|
|
6
|
+
readonly processTimeout: number;
|
|
7
|
+
readonly maxTries: number;
|
|
6
8
|
enqueue(data: T, options?: EnqueueOneOptions): Promise<Job<T>>;
|
|
7
9
|
enqueueMany(items: EnqueueManyItem<T>[], options?: EnqueueManyOptions & {
|
|
8
10
|
returnJobs?: false;
|
|
@@ -15,6 +17,7 @@ export declare class PostgresQueue<T extends ObjectLiteral> extends Queue<T> {
|
|
|
15
17
|
countByTag(tag: JobTag): Promise<number>;
|
|
16
18
|
get(id: string): Promise<Job<T> | undefined>;
|
|
17
19
|
getByTag(tag: JobTag): Promise<Job<T>[]>;
|
|
20
|
+
getByTags(tags: JobTag[]): Promise<Job<T>[]>;
|
|
18
21
|
cancel(id: string): Promise<void>;
|
|
19
22
|
cancelMany(ids: string[]): Promise<void>;
|
|
20
23
|
cancelByTag(tag: JobTag): Promise<void>;
|
package/queue/postgres/queue.js
CHANGED
|
@@ -12,7 +12,7 @@ import { MessageBus } from '../../message-bus/index.js';
|
|
|
12
12
|
import { interval, RANDOM_UUID, TRANSACTION_TIMESTAMP } from '../../orm/index.js';
|
|
13
13
|
import { DatabaseConfig, EntityRepositoryConfig, injectRepository } from '../../orm/server/index.js';
|
|
14
14
|
import { cancelableTimeout } from '../../utils/timing.js';
|
|
15
|
-
import { isDefined, isString } from '../../utils/type-guards.js';
|
|
15
|
+
import { isDefined, isString, isUndefined } from '../../utils/type-guards.js';
|
|
16
16
|
import { millisecondsPerSecond } from '../../utils/units.js';
|
|
17
17
|
import { defaultQueueConfig, Queue, UniqueTagStrategy } from '../queue.js';
|
|
18
18
|
import { PostgresJob } from './job.model.js';
|
|
@@ -21,11 +21,11 @@ import { job } from './schemas.js';
|
|
|
21
21
|
let PostgresQueue = class PostgresQueue extends Queue {
|
|
22
22
|
#repository = injectRepository(PostgresJob);
|
|
23
23
|
#config = injectArgument(this);
|
|
24
|
-
#processTimeout = (isString(this.#config) ? undefined : this.#config.processTimeout) ?? defaultQueueConfig.processTimeout;
|
|
25
|
-
#maxTries = (isString(this.#config) ? undefined : this.#config.maxTries) ?? defaultQueueConfig.maxTries;
|
|
26
24
|
#queueName = isString(this.#config) ? this.#config : this.#config.name;
|
|
27
25
|
#messageBus = inject((MessageBus), `PostgresQueue:${this.#queueName}`);
|
|
28
26
|
#keepOldUpdate = { id: sql `${job.id}` };
|
|
27
|
+
processTimeout = (isString(this.#config) ? undefined : this.#config.processTimeout) ?? defaultQueueConfig.processTimeout;
|
|
28
|
+
maxTries = (isString(this.#config) ? undefined : this.#config.maxTries) ?? defaultQueueConfig.maxTries;
|
|
29
29
|
#takeNewUpdate = {
|
|
30
30
|
id: RANDOM_UUID,
|
|
31
31
|
queue: this.#queueName,
|
|
@@ -36,7 +36,7 @@ let PostgresQueue = class PostgresQueue extends Queue {
|
|
|
36
36
|
lastDequeueTimestamp: null,
|
|
37
37
|
data: sql `excluded.data`
|
|
38
38
|
};
|
|
39
|
-
#dequeueQuery = and(eq(job.queue, this.#queueName), lt(job.tries, this
|
|
39
|
+
#dequeueQuery = and(eq(job.queue, this.#queueName), lt(job.tries, this.maxTries), or(isSqlNull(job.lastDequeueTimestamp), lte(sql `${job.lastDequeueTimestamp} + ${interval(this.processTimeout, 'milliseconds')}`, TRANSACTION_TIMESTAMP)));
|
|
40
40
|
#dequeueUpdate = {
|
|
41
41
|
tries: sql `${job.tries} + 1`,
|
|
42
42
|
lastDequeueTimestamp: TRANSACTION_TIMESTAMP
|
|
@@ -57,8 +57,12 @@ let PostgresQueue = class PostgresQueue extends Queue {
|
|
|
57
57
|
}));
|
|
58
58
|
const update = (options?.uniqueTag == UniqueTagStrategy.TakeNew)
|
|
59
59
|
? this.#takeNewUpdate
|
|
60
|
-
:
|
|
61
|
-
|
|
60
|
+
: (options?.uniqueTag == UniqueTagStrategy.KeepOld)
|
|
61
|
+
? this.#keepOldUpdate
|
|
62
|
+
: undefined;
|
|
63
|
+
const jobs = isUndefined(update)
|
|
64
|
+
? await this.#repository.insertMany(newEntities)
|
|
65
|
+
: await this.#repository.upsertMany(['queue', 'tag'], newEntities, update);
|
|
62
66
|
this.#messageBus.publishAndForget();
|
|
63
67
|
return jobs;
|
|
64
68
|
}
|
|
@@ -74,6 +78,9 @@ let PostgresQueue = class PostgresQueue extends Queue {
|
|
|
74
78
|
async getByTag(tag) {
|
|
75
79
|
return this.#repository.loadManyByQuery({ queue: this.#queueName, tag });
|
|
76
80
|
}
|
|
81
|
+
async getByTags(tags) {
|
|
82
|
+
return this.#repository.loadManyByQuery({ queue: this.#queueName, tag: { $in: tags } });
|
|
83
|
+
}
|
|
77
84
|
async cancel(id) {
|
|
78
85
|
await this.#repository.hardDeleteByQuery({ queue: this.#queueName, id });
|
|
79
86
|
}
|
package/queue/queue.d.ts
CHANGED
|
@@ -14,6 +14,9 @@ export type Job<T> = {
|
|
|
14
14
|
priority: number;
|
|
15
15
|
tag: JobTag;
|
|
16
16
|
data: T;
|
|
17
|
+
enqueueTimestamp: number;
|
|
18
|
+
lastDequeueTimestamp: number | null;
|
|
19
|
+
tries: number;
|
|
17
20
|
};
|
|
18
21
|
export declare const defaultJobPriority = 1000;
|
|
19
22
|
export declare enum UniqueTagStrategy {
|
|
@@ -46,6 +49,8 @@ export type QueueArgument = string | (QueueConfig & {
|
|
|
46
49
|
export declare const defaultQueueConfig: Required<QueueConfig>;
|
|
47
50
|
export declare abstract class Queue<T> implements Resolvable<QueueArgument> {
|
|
48
51
|
readonly [resolveArgumentType]: QueueArgument;
|
|
52
|
+
abstract readonly processTimeout: number;
|
|
53
|
+
abstract readonly maxTries: number;
|
|
49
54
|
batch(): QueueEnqueueBatch<T>;
|
|
50
55
|
abstract enqueue(data: T, options?: EnqueueOneOptions): Promise<Job<T>>;
|
|
51
56
|
abstract enqueueMany(items: EnqueueManyItem<T>[], options?: EnqueueManyOptions & {
|
|
@@ -59,6 +64,7 @@ export declare abstract class Queue<T> implements Resolvable<QueueArgument> {
|
|
|
59
64
|
abstract countByTag(tag: JobTag): Promise<number>;
|
|
60
65
|
abstract get(id: string): Promise<Job<T> | undefined>;
|
|
61
66
|
abstract getByTag(tag: JobTag): Promise<Job<T>[]>;
|
|
67
|
+
abstract getByTags(tags: JobTag[]): Promise<Job<T>[]>;
|
|
62
68
|
abstract cancel(id: string): Promise<void>;
|
|
63
69
|
abstract cancelMany(ids: string[]): Promise<void>;
|
|
64
70
|
abstract cancelByTag(tag: JobTag): Promise<void>;
|
package/utils/try-ignore.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export declare function tryIgnore<R>(fn: () => R): R;
|
|
|
3
3
|
export declare function tryIgnore<R, F>(fn: () => R, fallback: F): R | F;
|
|
4
4
|
export declare function tryIgnoreAsync<R>(fn: () => Promise<R>): Promise<R>;
|
|
5
5
|
export declare function tryIgnoreAsync<R, F>(fn: () => Promise<R>, fallback: F): Promise<F>;
|
|
6
|
-
export declare function tryIgnoreLog<R>(fn: () => R
|
|
7
|
-
export declare function tryIgnoreLog<R, F>(fn: () => R,
|
|
8
|
-
export declare function tryIgnoreLogAsync<R>(fn: () => Promise<R
|
|
9
|
-
export declare function tryIgnoreLogAsync<R, F>(fn: () => Promise<R>,
|
|
6
|
+
export declare function tryIgnoreLog<R>(logger: Logger, fn: () => R): R;
|
|
7
|
+
export declare function tryIgnoreLog<R, F>(logger: Logger, fn: () => R, fallback: F): R | F;
|
|
8
|
+
export declare function tryIgnoreLogAsync<R>(logger: Logger, fn: () => Promise<R>): Promise<R>;
|
|
9
|
+
export declare function tryIgnoreLogAsync<R, F>(logger: Logger, fn: () => Promise<R>, fallback: F): Promise<F>;
|
package/utils/try-ignore.js
CHANGED
|
@@ -15,7 +15,7 @@ export async function tryIgnoreAsync(fn, fallback) {
|
|
|
15
15
|
return fallback;
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
export function tryIgnoreLog(
|
|
18
|
+
export function tryIgnoreLog(logger, fn, fallback) {
|
|
19
19
|
try {
|
|
20
20
|
return fn();
|
|
21
21
|
}
|
|
@@ -24,7 +24,7 @@ export function tryIgnoreLog(fn, logger, fallback) {
|
|
|
24
24
|
return fallback;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
-
export async function tryIgnoreLogAsync(
|
|
27
|
+
export async function tryIgnoreLogAsync(logger, fn, fallback) {
|
|
28
28
|
try {
|
|
29
29
|
const value = await fn();
|
|
30
30
|
return value;
|