@tstdl/base 0.93.123 → 0.93.125
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/document-management/server/drizzle/{0000_silly_chimera.sql → 0000_complex_black_bird.sql} +1 -1
- package/document-management/server/drizzle/meta/0000_snapshot.json +3 -3
- package/document-management/server/drizzle/meta/_journal.json +2 -2
- package/notification/api/notification.api.d.ts +26 -6
- package/notification/api/notification.api.js +15 -4
- package/notification/client/notification-client.d.ts +6 -0
- package/notification/client/notification-client.js +13 -3
- package/notification/models/in-app-notification.model.d.ts +9 -3
- package/notification/models/in-app-notification.model.js +32 -11
- package/notification/models/notification-log.model.js +2 -3
- package/notification/server/api/notification.api-controller.d.ts +1 -0
- package/notification/server/api/notification.api-controller.js +7 -1
- package/notification/server/drizzle/{0000_oval_rage.sql → 0000_wise_pyro.sql} +22 -4
- package/notification/server/drizzle/meta/0000_snapshot.json +249 -37
- package/notification/server/drizzle/meta/_journal.json +2 -2
- package/notification/server/module.d.ts +5 -0
- package/notification/server/module.js +6 -1
- package/notification/server/providers/in-app-channel-provider.js +1 -0
- package/notification/server/schemas.d.ts +3 -2
- package/notification/server/schemas.js +3 -2
- package/notification/server/services/notification.service.d.ts +11 -6
- package/notification/server/services/notification.service.js +138 -42
- package/notification/tests/notification-api.test.js +8 -1
- package/notification/tests/notification-flow.test.js +41 -4
- package/orm/server/drizzle/schema-converter.js +5 -3
- package/orm/tests/schema-converter.test.js +1 -0
- package/package.json +1 -1
package/document-management/server/drizzle/{0000_silly_chimera.sql → 0000_complex_black_bird.sql}
RENAMED
|
@@ -384,4 +384,4 @@ CREATE INDEX "request_type_id_idx" ON "document_management"."request" USING btre
|
|
|
384
384
|
CREATE INDEX "request_state_idx" ON "document_management"."request" USING btree ("state");--> statement-breakpoint
|
|
385
385
|
CREATE INDEX "request_collection_assignment_collection_id_idx" ON "document_management"."request_collection_assignment" USING btree ("collection_id");--> statement-breakpoint
|
|
386
386
|
CREATE INDEX "document_type_validation_type_id_idx" ON "document_management"."document_type_validation" USING btree ("type_id");--> statement-breakpoint
|
|
387
|
-
CREATE UNIQUE INDEX "
|
|
387
|
+
CREATE UNIQUE INDEX "workflow_document_id_partial_idx" ON "document_management"."workflow" USING btree ("document_id") WHERE "document_management"."workflow"."state" <> 'completed';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"id": "
|
|
2
|
+
"id": "ff5ec8ad-f61c-4ce5-b858-b28db663121f",
|
|
3
3
|
"prevId": "00000000-0000-0000-0000-000000000000",
|
|
4
4
|
"version": "7",
|
|
5
5
|
"dialect": "postgresql",
|
|
@@ -2756,8 +2756,8 @@
|
|
|
2756
2756
|
}
|
|
2757
2757
|
},
|
|
2758
2758
|
"indexes": {
|
|
2759
|
-
"
|
|
2760
|
-
"name": "
|
|
2759
|
+
"workflow_document_id_partial_idx": {
|
|
2760
|
+
"name": "workflow_document_id_partial_idx",
|
|
2761
2761
|
"columns": [
|
|
2762
2762
|
{
|
|
2763
2763
|
"expression": "document_id",
|
|
@@ -14,7 +14,7 @@ export declare const notificationApiDefinition: {
|
|
|
14
14
|
};
|
|
15
15
|
credentials: true;
|
|
16
16
|
dataStream: {
|
|
17
|
-
idProvider: (
|
|
17
|
+
idProvider: () => string;
|
|
18
18
|
};
|
|
19
19
|
};
|
|
20
20
|
types: {
|
|
@@ -29,9 +29,19 @@ export declare const notificationApiDefinition: {
|
|
|
29
29
|
parameters: import("../../schema/index.js").ObjectSchema<{
|
|
30
30
|
offset?: number | undefined;
|
|
31
31
|
limit?: number | undefined;
|
|
32
|
-
after?: string | undefined;
|
|
32
|
+
after?: string | number | undefined;
|
|
33
33
|
unreadOnly?: boolean | undefined;
|
|
34
|
-
|
|
34
|
+
}>;
|
|
35
|
+
result: import("../../schema/index.js").ArraySchema<InAppNotificationView<Record<string, import("../models/notification-log.model.js").NotificationDefinition<import("../../types/types.js").ObjectLiteral, import("../../types/types.js").ObjectLiteral>>>>;
|
|
36
|
+
credentials: true;
|
|
37
|
+
};
|
|
38
|
+
listArchivedInApp: {
|
|
39
|
+
resource: string;
|
|
40
|
+
method: "GET";
|
|
41
|
+
parameters: import("../../schema/index.js").ObjectSchema<{
|
|
42
|
+
offset?: number | undefined;
|
|
43
|
+
limit?: number | undefined;
|
|
44
|
+
after?: string | number | undefined;
|
|
35
45
|
}>;
|
|
36
46
|
result: import("../../schema/index.js").ArraySchema<InAppNotificationView<Record<string, import("../models/notification-log.model.js").NotificationDefinition<import("../../types/types.js").ObjectLiteral, import("../../types/types.js").ObjectLiteral>>>>;
|
|
37
47
|
credentials: true;
|
|
@@ -120,7 +130,7 @@ declare const _NotificationApiClient: import("../../api/client/index.js").ApiCli
|
|
|
120
130
|
};
|
|
121
131
|
credentials: true;
|
|
122
132
|
dataStream: {
|
|
123
|
-
idProvider: (
|
|
133
|
+
idProvider: () => string;
|
|
124
134
|
};
|
|
125
135
|
};
|
|
126
136
|
types: {
|
|
@@ -135,9 +145,19 @@ declare const _NotificationApiClient: import("../../api/client/index.js").ApiCli
|
|
|
135
145
|
parameters: import("../../schema/index.js").ObjectSchema<{
|
|
136
146
|
offset?: number | undefined;
|
|
137
147
|
limit?: number | undefined;
|
|
138
|
-
after?: string | undefined;
|
|
148
|
+
after?: string | number | undefined;
|
|
139
149
|
unreadOnly?: boolean | undefined;
|
|
140
|
-
|
|
150
|
+
}>;
|
|
151
|
+
result: import("../../schema/index.js").ArraySchema<InAppNotificationView<Record<string, import("../models/notification-log.model.js").NotificationDefinition<import("../../types/types.js").ObjectLiteral, import("../../types/types.js").ObjectLiteral>>>>;
|
|
152
|
+
credentials: true;
|
|
153
|
+
};
|
|
154
|
+
listArchivedInApp: {
|
|
155
|
+
resource: string;
|
|
156
|
+
method: "GET";
|
|
157
|
+
parameters: import("../../schema/index.js").ObjectSchema<{
|
|
158
|
+
offset?: number | undefined;
|
|
159
|
+
limit?: number | undefined;
|
|
160
|
+
after?: string | number | undefined;
|
|
141
161
|
}>;
|
|
142
162
|
result: import("../../schema/index.js").ArraySchema<InAppNotificationView<Record<string, import("../models/notification-log.model.js").NotificationDefinition<import("../../types/types.js").ObjectLiteral, import("../../types/types.js").ObjectLiteral>>>>;
|
|
143
163
|
credentials: true;
|
|
@@ -7,8 +7,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import { compileClient } from '../../api/client/index.js';
|
|
8
8
|
import { defineApi } from '../../api/types.js';
|
|
9
9
|
import { ReplaceClass } from '../../injector/decorators.js';
|
|
10
|
-
import { array, boolean, enumeration, literal, number, object, optional, record, string } from '../../schema/index.js';
|
|
10
|
+
import { array, boolean, enumeration, literal, number, object, optional, record, string, union } from '../../schema/index.js';
|
|
11
11
|
import { DataStream } from '../../sse/data-stream.js';
|
|
12
|
+
import { currentTimestamp } from '../../utils/date-time.js';
|
|
12
13
|
import { InAppNotificationView } from '../models/in-app-notification.model.js';
|
|
13
14
|
import { NotificationChannel } from '../models/index.js';
|
|
14
15
|
export const notificationApiDefinition = defineApi({
|
|
@@ -20,7 +21,7 @@ export const notificationApiDefinition = defineApi({
|
|
|
20
21
|
result: (DataStream),
|
|
21
22
|
credentials: true,
|
|
22
23
|
dataStream: {
|
|
23
|
-
idProvider: (
|
|
24
|
+
idProvider: () => currentTimestamp().toString(),
|
|
24
25
|
},
|
|
25
26
|
},
|
|
26
27
|
types: {
|
|
@@ -35,9 +36,19 @@ export const notificationApiDefinition = defineApi({
|
|
|
35
36
|
parameters: object({
|
|
36
37
|
limit: optional(number()),
|
|
37
38
|
offset: optional(number()),
|
|
38
|
-
after: optional(string()),
|
|
39
|
+
after: optional(union(number(), string())),
|
|
39
40
|
unreadOnly: optional(boolean()),
|
|
40
|
-
|
|
41
|
+
}),
|
|
42
|
+
result: array(InAppNotificationView),
|
|
43
|
+
credentials: true,
|
|
44
|
+
},
|
|
45
|
+
listArchivedInApp: {
|
|
46
|
+
resource: 'in-app/archived',
|
|
47
|
+
method: 'GET',
|
|
48
|
+
parameters: object({
|
|
49
|
+
limit: optional(number()),
|
|
50
|
+
offset: optional(number()),
|
|
51
|
+
after: optional(union(number(), string())),
|
|
41
52
|
}),
|
|
42
53
|
result: array(InAppNotificationView),
|
|
43
54
|
credentials: true,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NotificationApiClient } from '../../notification/api/index.js';
|
|
2
2
|
import type { InAppNotificationView, NotificationDefinitionMap } from '../../notification/models/index.js';
|
|
3
|
+
import type { NotificationStreamItem } from '../../notification/types.js';
|
|
3
4
|
type NotificationState<Definitions extends NotificationDefinitionMap = NotificationDefinitionMap> = {
|
|
4
5
|
notifications: InAppNotificationView<Definitions>[];
|
|
5
6
|
unreadCount: number;
|
|
@@ -7,6 +8,9 @@ type NotificationState<Definitions extends NotificationDefinitionMap = Notificat
|
|
|
7
8
|
export declare class NotificationClient<Definitions extends NotificationDefinitionMap = NotificationDefinitionMap> {
|
|
8
9
|
#private;
|
|
9
10
|
readonly api: NotificationApiClient;
|
|
11
|
+
readonly stream$: import("rxjs").Observable<NotificationStreamItem<Definitions>>;
|
|
12
|
+
/** Emits whenever a new notification is received via the stream. */
|
|
13
|
+
readonly onNewNotification$: import("rxjs").Observable<InAppNotificationView<Definitions>>;
|
|
10
14
|
readonly state$: import("rxjs").Observable<NotificationState<Definitions>>;
|
|
11
15
|
readonly notifications$: import("rxjs").Observable<InAppNotificationView<Definitions>[]>;
|
|
12
16
|
readonly unreadCount$: import("rxjs").Observable<number>;
|
|
@@ -15,6 +19,8 @@ export declare class NotificationClient<Definitions extends NotificationDefiniti
|
|
|
15
19
|
readonly notifications: import("../../signals/api.js").Signal<InAppNotificationView<Definitions>[]>;
|
|
16
20
|
readonly unreadCount: import("../../signals/api.js").Signal<number>;
|
|
17
21
|
readonly types: import("../../signals/api.js").Signal<Record<keyof Definitions, string>>;
|
|
22
|
+
readonly unreadNotifications: import("../../signals/api.js").Signal<InAppNotificationView<Definitions>[]>;
|
|
23
|
+
readonly readNotifications: import("../../signals/api.js").Signal<InAppNotificationView<Definitions>[]>;
|
|
18
24
|
loadNext(count?: number): void;
|
|
19
25
|
}
|
|
20
26
|
export {};
|
|
@@ -4,23 +4,31 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { concatMap, defer, from, map, merge, of, scan, shareReplay, Subject, switchAll, switchMap } from 'rxjs';
|
|
7
|
+
import { concatMap, defer, EMPTY, filter, from, map, merge, of, scan, share, shareReplay, Subject, switchAll, switchMap } from 'rxjs';
|
|
8
8
|
import { AuthenticationClientService } from '../../authentication/client/authentication.service.js';
|
|
9
9
|
import { Singleton } from '../../injector/decorators.js';
|
|
10
10
|
import { inject } from '../../injector/inject.js';
|
|
11
11
|
import { NotificationApiClient } from '../../notification/api/index.js';
|
|
12
12
|
import { forceCast } from '../../rxjs-utils/cast.js';
|
|
13
13
|
import { computed, toSignal } from '../../signals/api.js';
|
|
14
|
-
import { isDefined, isUndefined } from '../../utils/type-guards.js';
|
|
14
|
+
import { isDefined, isNotNull, isNull, isUndefined } from '../../utils/type-guards.js';
|
|
15
15
|
let NotificationClient = class NotificationClient {
|
|
16
16
|
#pagination$ = new Subject();
|
|
17
17
|
#authenticationService = inject(AuthenticationClientService);
|
|
18
18
|
api = inject(NotificationApiClient);
|
|
19
|
+
stream$ = this.#authenticationService.sessionId$.pipe(switchMap((sessionId) => {
|
|
20
|
+
if (isUndefined(sessionId)) {
|
|
21
|
+
return EMPTY;
|
|
22
|
+
}
|
|
23
|
+
return defer(() => from(this.api.stream())).pipe(switchAll(), forceCast());
|
|
24
|
+
}), share());
|
|
25
|
+
/** Emits whenever a new notification is received via the stream. */
|
|
26
|
+
onNewNotification$ = this.stream$.pipe(filter((item) => isDefined(item.notification)), map((item) => item.notification));
|
|
19
27
|
state$ = this.#authenticationService.sessionId$.pipe(switchMap((sessionId) => {
|
|
20
28
|
if (isUndefined(sessionId)) {
|
|
21
29
|
return of({ notifications: [], unreadCount: 0 });
|
|
22
30
|
}
|
|
23
|
-
return merge(defer(() => from(this.api.listInApp({ limit: 20 }))).pipe(map((notifications) => ({ type: 'set-notifications', notifications: notifications }))), defer(() => from(this.api.unreadCount())).pipe(map((unreadCount) => ({ type: 'set-unread-count', unreadCount }))),
|
|
31
|
+
return merge(defer(() => from(this.api.listInApp({ limit: 20 }))).pipe(map((notifications) => ({ type: 'set-notifications', notifications: notifications }))), defer(() => from(this.api.unreadCount())).pipe(map((unreadCount) => ({ type: 'set-unread-count', unreadCount }))), this.stream$.pipe(switchMap((item) => {
|
|
24
32
|
const actions = [];
|
|
25
33
|
if (isDefined(item.unreadCount)) {
|
|
26
34
|
actions.push({ type: 'set-unread-count', unreadCount: item.unreadCount });
|
|
@@ -90,6 +98,8 @@ let NotificationClient = class NotificationClient {
|
|
|
90
98
|
notifications = computed(() => this.state().notifications);
|
|
91
99
|
unreadCount = computed(() => this.state().unreadCount);
|
|
92
100
|
types = toSignal(this.types$, { initialValue: {} });
|
|
101
|
+
unreadNotifications = computed(() => this.notifications().filter((notification) => isNull(notification.readTimestamp)));
|
|
102
|
+
readNotifications = computed(() => this.notifications().filter((notification) => isNotNull(notification.readTimestamp)));
|
|
93
103
|
loadNext(count = 20) {
|
|
94
104
|
const current = this.notifications();
|
|
95
105
|
const after = current[current.length - 1]?.id;
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { TenantBaseEntity, type Uuid } from '../../orm/index.js';
|
|
2
2
|
import type { Timestamp } from '../../orm/types.js';
|
|
3
3
|
import { type NotificationDefinitionMap, type NotificationLogView } from './notification-log.model.js';
|
|
4
|
-
export declare class
|
|
5
|
-
static readonly entityName = "InAppNotification";
|
|
4
|
+
export declare abstract class InAppNotificationBase extends TenantBaseEntity {
|
|
6
5
|
userId: Uuid;
|
|
7
6
|
logId: Uuid;
|
|
7
|
+
timestamp: Timestamp;
|
|
8
8
|
readTimestamp: Timestamp | null;
|
|
9
9
|
archiveTimestamp: Timestamp | null;
|
|
10
10
|
}
|
|
11
|
-
export declare class
|
|
11
|
+
export declare class InAppNotification extends InAppNotificationBase {
|
|
12
|
+
static readonly entityName = "InAppNotification";
|
|
13
|
+
}
|
|
14
|
+
export declare class InAppNotificationArchive extends InAppNotificationBase {
|
|
15
|
+
static readonly entityName = "InAppNotificationArchive";
|
|
16
|
+
}
|
|
17
|
+
export declare class InAppNotificationView<Definitions extends NotificationDefinitionMap = NotificationDefinitionMap> extends InAppNotificationBase {
|
|
12
18
|
notification: NotificationLogView<Definitions>;
|
|
13
19
|
}
|
|
14
20
|
export declare function toInAppNotificationView<Definitions extends NotificationDefinitionMap = NotificationDefinitionMap>(inAppNotification: InAppNotification, notification: NotificationLogView<Definitions>): InAppNotificationView<Definitions>;
|
|
@@ -7,43 +7,64 @@ 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 { isNotNull, isNull } from 'drizzle-orm';
|
|
10
11
|
import { User } from '../../authentication/index.js';
|
|
11
|
-
import { ForeignKey, Index, TenantBaseEntity, TenantReference, TimestampProperty, UuidProperty } from '../../orm/index.js';
|
|
12
|
+
import { ForeignKey, Index, TenantBaseEntity, TenantReference, TimestampProperty, Unique, UuidProperty } from '../../orm/index.js';
|
|
12
13
|
import { Property } from '../../schema/index.js';
|
|
13
14
|
import { NotificationLogEntity } from './notification-log.model.js';
|
|
14
15
|
import { NotificationTable } from './notification-table.js';
|
|
15
|
-
|
|
16
|
-
static entityName = 'InAppNotification';
|
|
16
|
+
export class InAppNotificationBase extends TenantBaseEntity {
|
|
17
17
|
userId;
|
|
18
18
|
logId;
|
|
19
|
-
|
|
19
|
+
timestamp;
|
|
20
20
|
readTimestamp;
|
|
21
21
|
archiveTimestamp;
|
|
22
|
-
}
|
|
22
|
+
}
|
|
23
23
|
__decorate([
|
|
24
24
|
UuidProperty(),
|
|
25
25
|
TenantReference(() => User),
|
|
26
26
|
__metadata("design:type", String)
|
|
27
|
-
],
|
|
27
|
+
], InAppNotificationBase.prototype, "userId", void 0);
|
|
28
28
|
__decorate([
|
|
29
29
|
UuidProperty(),
|
|
30
30
|
__metadata("design:type", String)
|
|
31
|
-
],
|
|
31
|
+
], InAppNotificationBase.prototype, "logId", void 0);
|
|
32
|
+
__decorate([
|
|
33
|
+
TimestampProperty(),
|
|
34
|
+
__metadata("design:type", Number)
|
|
35
|
+
], InAppNotificationBase.prototype, "timestamp", void 0);
|
|
32
36
|
__decorate([
|
|
33
37
|
TimestampProperty({ nullable: true }),
|
|
34
38
|
__metadata("design:type", Object)
|
|
35
|
-
],
|
|
39
|
+
], InAppNotificationBase.prototype, "readTimestamp", void 0);
|
|
36
40
|
__decorate([
|
|
37
41
|
TimestampProperty({ nullable: true }),
|
|
38
42
|
__metadata("design:type", Object)
|
|
39
|
-
],
|
|
43
|
+
], InAppNotificationBase.prototype, "archiveTimestamp", void 0);
|
|
44
|
+
let InAppNotification = class InAppNotification extends InAppNotificationBase {
|
|
45
|
+
static entityName = 'InAppNotification';
|
|
46
|
+
};
|
|
40
47
|
InAppNotification = __decorate([
|
|
41
48
|
NotificationTable({ name: 'in_app' }),
|
|
42
49
|
ForeignKey(() => NotificationLogEntity, ['tenantId', 'logId', 'userId'], ['tenantId', 'id', 'userId']),
|
|
43
|
-
|
|
50
|
+
Unique(['tenantId', 'userId', 'logId']),
|
|
51
|
+
Index(['tenantId', 'userId', ['timestamp', 'desc'], ['logId', 'desc']]),
|
|
52
|
+
Index(['tenantId', 'userId', ['timestamp', 'desc'], ['logId', 'desc']], { where: (table) => isNull(table.readTimestamp) }),
|
|
53
|
+
Index(['tenantId', 'userId', 'readTimestamp'], { where: (table) => isNotNull(table.readTimestamp) }) // todo: include id as soon as drizzle supports this to make it a covering index for queries that fetch read notifications
|
|
54
|
+
,
|
|
55
|
+
Index(['timestamp'], { using: 'brin' })
|
|
44
56
|
], InAppNotification);
|
|
45
57
|
export { InAppNotification };
|
|
46
|
-
|
|
58
|
+
let InAppNotificationArchive = class InAppNotificationArchive extends InAppNotificationBase {
|
|
59
|
+
static entityName = 'InAppNotificationArchive';
|
|
60
|
+
};
|
|
61
|
+
InAppNotificationArchive = __decorate([
|
|
62
|
+
NotificationTable({ name: 'in_app_archive' }),
|
|
63
|
+
Index(['tenantId', 'userId', ['timestamp', 'desc'], ['logId', 'desc']]),
|
|
64
|
+
Index(['tenantId', 'userId', ['archiveTimestamp', 'desc']]) // todo: include id as soon as drizzle supports this to make it a covering index for queries that fetch archived notifications
|
|
65
|
+
], InAppNotificationArchive);
|
|
66
|
+
export { InAppNotificationArchive };
|
|
67
|
+
export class InAppNotificationView extends InAppNotificationBase {
|
|
47
68
|
notification;
|
|
48
69
|
}
|
|
49
70
|
__decorate([
|
|
@@ -9,7 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
};
|
|
10
10
|
import { Subject, User } from '../../authentication/index.js';
|
|
11
11
|
import { defineEnum } from '../../enumeration/index.js';
|
|
12
|
-
import {
|
|
12
|
+
import { JsonProperty, Reference, TenantBaseEntity, TenantReference, TimestampProperty, Unique, UuidProperty } from '../../orm/index.js';
|
|
13
13
|
import { Enumeration, Integer, StringProperty } from '../../schema/index.js';
|
|
14
14
|
import { NotificationTable } from './notification-table.js';
|
|
15
15
|
import { NotificationType } from './notification-type.model.js';
|
|
@@ -79,7 +79,6 @@ __decorate([
|
|
|
79
79
|
], NotificationLogEntity.prototype, "payload", void 0);
|
|
80
80
|
NotificationLogEntity = __decorate([
|
|
81
81
|
NotificationTable({ name: 'log' }),
|
|
82
|
-
Unique(['tenantId', '
|
|
83
|
-
Index(['tenantId', 'userId', 'timestamp'])
|
|
82
|
+
Unique(['tenantId', 'userId', 'id'])
|
|
84
83
|
], NotificationLogEntity);
|
|
85
84
|
export { NotificationLogEntity };
|
|
@@ -10,6 +10,7 @@ export declare class NotificationApiController implements ApiController<Notifica
|
|
|
10
10
|
stream({ abortSignal, getToken, serverSentEvents: { lastEventId } }: ApiRequestContext<NotificationApiDefinition, 'stream'>): ApiServerResult<NotificationApiDefinition, 'stream'>;
|
|
11
11
|
types(): Promise<Record<string, string>>;
|
|
12
12
|
listInApp({ parameters, getToken }: ApiRequestContext<NotificationApiDefinition, 'listInApp'>): Promise<any>;
|
|
13
|
+
listArchivedInApp({ parameters, getToken }: ApiRequestContext<NotificationApiDefinition, 'listArchivedInApp'>): Promise<any>;
|
|
13
14
|
markRead({ parameters, getToken }: ApiRequestContext<NotificationApiDefinition, 'markRead'>): Promise<'ok'>;
|
|
14
15
|
markAllRead({ getToken }: ApiRequestContext<NotificationApiDefinition, 'markAllRead'>): Promise<'ok'>;
|
|
15
16
|
archive({ parameters, getToken }: ApiRequestContext<NotificationApiDefinition, 'archive'>): Promise<'ok'>;
|
|
@@ -24,7 +24,9 @@ let NotificationApiController = class NotificationApiController {
|
|
|
24
24
|
abortSignal.addEventListener('abort', () => this.sseService.unregister(token.payload.tenant, token.payload.subject, source));
|
|
25
25
|
try {
|
|
26
26
|
if (isDefined(lastEventId)) {
|
|
27
|
-
const
|
|
27
|
+
const lastEventIdNumber = Number(lastEventId);
|
|
28
|
+
const stateTimestamp = Number.isNaN(lastEventIdNumber) ? 0 : lastEventIdNumber;
|
|
29
|
+
const { unreadCount, missedNotifications, readIds, archiveIds } = await this.notificationService.getCatchupData(token.payload.tenant, token.payload.subject, stateTimestamp);
|
|
28
30
|
yield { unreadCount };
|
|
29
31
|
for (const readId of readIds) {
|
|
30
32
|
yield { readId };
|
|
@@ -49,6 +51,10 @@ let NotificationApiController = class NotificationApiController {
|
|
|
49
51
|
const token = await getToken();
|
|
50
52
|
return await this.notificationService.listInApp(token.payload.tenant, token.payload.subject, parameters);
|
|
51
53
|
}
|
|
54
|
+
async listArchivedInApp({ parameters, getToken }) {
|
|
55
|
+
const token = await getToken();
|
|
56
|
+
return await this.notificationService.listArchivedInApp(token.payload.tenant, token.payload.subject, parameters);
|
|
57
|
+
}
|
|
52
58
|
async markRead({ parameters, getToken }) {
|
|
53
59
|
const token = await getToken();
|
|
54
60
|
await this.notificationService.markRead(token.payload.tenant, token.payload.subject, parameters.id);
|
|
@@ -6,9 +6,22 @@ CREATE TABLE "notification"."in_app" (
|
|
|
6
6
|
"tenant_id" uuid NOT NULL,
|
|
7
7
|
"user_id" uuid NOT NULL,
|
|
8
8
|
"log_id" uuid NOT NULL,
|
|
9
|
+
"timestamp" timestamp with time zone NOT NULL,
|
|
10
|
+
"read_timestamp" timestamp with time zone,
|
|
11
|
+
"archive_timestamp" timestamp with time zone,
|
|
12
|
+
CONSTRAINT "in_app_tenant_id_id_pk" PRIMARY KEY("tenant_id","id"),
|
|
13
|
+
CONSTRAINT "in_app_tenant_id_user_id_log_id_unique" UNIQUE("tenant_id","user_id","log_id")
|
|
14
|
+
);
|
|
15
|
+
--> statement-breakpoint
|
|
16
|
+
CREATE TABLE "notification"."in_app_archive" (
|
|
17
|
+
"id" uuid DEFAULT gen_random_uuid() NOT NULL,
|
|
18
|
+
"tenant_id" uuid NOT NULL,
|
|
19
|
+
"user_id" uuid NOT NULL,
|
|
20
|
+
"log_id" uuid NOT NULL,
|
|
21
|
+
"timestamp" timestamp with time zone NOT NULL,
|
|
9
22
|
"read_timestamp" timestamp with time zone,
|
|
10
23
|
"archive_timestamp" timestamp with time zone,
|
|
11
|
-
CONSTRAINT "
|
|
24
|
+
CONSTRAINT "in_app_archive_tenant_id_id_pk" PRIMARY KEY("tenant_id","id")
|
|
12
25
|
);
|
|
13
26
|
--> statement-breakpoint
|
|
14
27
|
CREATE TABLE "notification"."log" (
|
|
@@ -23,7 +36,7 @@ CREATE TABLE "notification"."log" (
|
|
|
23
36
|
"trigger_subject_id" uuid NOT NULL,
|
|
24
37
|
"payload" jsonb,
|
|
25
38
|
CONSTRAINT "log_tenant_id_id_pk" PRIMARY KEY("tenant_id","id"),
|
|
26
|
-
CONSTRAINT "
|
|
39
|
+
CONSTRAINT "log_tenant_id_user_id_id_unique" UNIQUE("tenant_id","user_id","id")
|
|
27
40
|
);
|
|
28
41
|
--> statement-breakpoint
|
|
29
42
|
CREATE TABLE "notification"."preference" (
|
|
@@ -74,11 +87,16 @@ CREATE TABLE "notification"."web_push_subscription" (
|
|
|
74
87
|
--> statement-breakpoint
|
|
75
88
|
ALTER TABLE "notification"."in_app" ADD CONSTRAINT "in_app_id_user_fkey" FOREIGN KEY ("tenant_id","user_id") REFERENCES "authentication"."user"("tenant_id","id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
76
89
|
ALTER TABLE "notification"."in_app" ADD CONSTRAINT "in_app_tenantId_logId_userId_log_fkey" FOREIGN KEY ("tenant_id","log_id","user_id") REFERENCES "notification"."log"("tenant_id","id","user_id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
90
|
+
ALTER TABLE "notification"."in_app_archive" ADD CONSTRAINT "in_app_archive_id_user_fkey" FOREIGN KEY ("tenant_id","user_id") REFERENCES "authentication"."user"("tenant_id","id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
77
91
|
ALTER TABLE "notification"."log" ADD CONSTRAINT "log_type_type_key_fk" FOREIGN KEY ("type") REFERENCES "notification"."type"("key") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
78
92
|
ALTER TABLE "notification"."log" ADD CONSTRAINT "log_id_user_fkey" FOREIGN KEY ("tenant_id","user_id") REFERENCES "authentication"."user"("tenant_id","id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
79
93
|
ALTER TABLE "notification"."log" ADD CONSTRAINT "log_id_subject_fkey" FOREIGN KEY ("tenant_id","trigger_subject_id") REFERENCES "authentication"."subject"("tenant_id","id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
80
94
|
ALTER TABLE "notification"."preference" ADD CONSTRAINT "preference_type_type_key_fk" FOREIGN KEY ("type") REFERENCES "notification"."type"("key") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
81
95
|
ALTER TABLE "notification"."preference" ADD CONSTRAINT "preference_id_user_fkey" FOREIGN KEY ("tenant_id","user_id") REFERENCES "authentication"."user"("tenant_id","id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
82
96
|
ALTER TABLE "notification"."web_push_subscription" ADD CONSTRAINT "web_push_subscription_id_user_fkey" FOREIGN KEY ("tenant_id","user_id") REFERENCES "authentication"."user"("tenant_id","id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
83
|
-
CREATE INDEX "
|
|
84
|
-
CREATE INDEX "
|
|
97
|
+
CREATE INDEX "in_app_timestamp_idx" ON "notification"."in_app" USING brin ("timestamp");--> statement-breakpoint
|
|
98
|
+
CREATE INDEX "in_app_tenant_id_user_id_read_timestamp_partial_idx" ON "notification"."in_app" USING btree ("tenant_id","user_id","read_timestamp") WHERE "notification"."in_app"."read_timestamp" is not null;--> statement-breakpoint
|
|
99
|
+
CREATE INDEX "in_app_tenant_id_user_id_timestamp_log_id_partial_idx" ON "notification"."in_app" USING btree ("tenant_id","user_id","timestamp" DESC NULLS LAST,"log_id" DESC NULLS LAST) WHERE "notification"."in_app"."read_timestamp" is null;--> statement-breakpoint
|
|
100
|
+
CREATE INDEX "in_app_tenant_id_user_id_timestamp_log_id_idx" ON "notification"."in_app" USING btree ("tenant_id","user_id","timestamp" DESC NULLS LAST,"log_id" DESC NULLS LAST);--> statement-breakpoint
|
|
101
|
+
CREATE INDEX "in_app_archive_tenant_id_user_id_archive_timestamp_idx" ON "notification"."in_app_archive" USING btree ("tenant_id","user_id","archive_timestamp" DESC NULLS LAST);--> statement-breakpoint
|
|
102
|
+
CREATE INDEX "in_app_archive_tenant_id_user_id_timestamp_log_id_idx" ON "notification"."in_app_archive" USING btree ("tenant_id","user_id","timestamp" DESC NULLS LAST,"log_id" DESC NULLS LAST);
|