tychat-contracts 1.6.81 → 1.6.83
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/.husky/commit-msg +11 -0
- package/dist/analytics/create-patient-audit-log.dto.d.ts +39 -0
- package/dist/analytics/create-patient-audit-log.dto.d.ts.map +1 -0
- package/dist/analytics/create-patient-audit-log.dto.js +179 -0
- package/dist/analytics/index.d.ts +1 -1
- package/dist/analytics/index.d.ts.map +1 -1
- package/dist/analytics/index.js +1 -1
- package/dist/analytics/list-tenant-audit-logs-query.dto.d.ts +2 -2
- package/dist/analytics/list-tenant-audit-logs-query.dto.d.ts.map +1 -1
- package/dist/analytics/list-tenant-audit-logs-query.dto.js +5 -7
- package/dist/users/create-user.dto.d.ts +2 -1
- package/dist/users/create-user.dto.d.ts.map +1 -1
- package/dist/users/create-user.dto.js +19 -1
- package/dist/users/update-user-by-admin.dto.d.ts +2 -1
- package/dist/users/update-user-by-admin.dto.d.ts.map +1 -1
- package/dist/users/update-user-by-admin.dto.js +20 -3
- package/dist/users/updated-user.dto.d.ts +3 -1
- package/dist/users/updated-user.dto.d.ts.map +1 -1
- package/dist/users/updated-user.dto.js +14 -3
- package/dist/users/user-list-item.dto.d.ts +3 -1
- package/dist/users/user-list-item.dto.d.ts.map +1 -1
- package/dist/users/user-list-item.dto.js +14 -3
- package/dist/users/user-update-kafka-payloads.d.ts +3 -0
- package/dist/users/user-update-kafka-payloads.d.ts.map +1 -1
- package/package.json +32 -30
- package/src/analytics/create-patient-audit-log.dto.ts +144 -0
- package/src/analytics/index.ts +1 -1
- package/src/analytics/list-tenant-audit-logs-query.dto.ts +5 -7
- package/src/users/create-user.dto.ts +25 -3
- package/src/users/update-user-by-admin.dto.ts +28 -4
- package/src/users/updated-user.dto.ts +13 -4
- package/src/users/user-list-item.dto.ts +13 -4
- package/src/users/user-update-kafka-payloads.ts +3 -0
- package/src/analytics/create-tenant-audit-log.dto.ts +0 -105
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** Who performed the audited patient mutation. */
|
|
2
|
+
export declare const PATIENT_AUDIT_ACTOR_TYPES: readonly ["user", "system", "patient"];
|
|
3
|
+
export type PatientAuditActorType = (typeof PATIENT_AUDIT_ACTOR_TYPES)[number];
|
|
4
|
+
/** Domain actions persisted in patient_audit_logs. */
|
|
5
|
+
export declare const PATIENT_AUDIT_ACTIONS: readonly ["patient.created", "patient.updated", "patient.deleted"];
|
|
6
|
+
export type PatientAuditAction = (typeof PATIENT_AUDIT_ACTIONS)[number];
|
|
7
|
+
/** Single field change on a patient record. */
|
|
8
|
+
export declare class PatientAuditChangeDto {
|
|
9
|
+
field: string;
|
|
10
|
+
previousValue?: unknown;
|
|
11
|
+
newValue?: unknown;
|
|
12
|
+
}
|
|
13
|
+
/** Caller context propagated over RPC for patient write operations. */
|
|
14
|
+
export declare class PatientAuditContextDto {
|
|
15
|
+
actorType: PatientAuditActorType;
|
|
16
|
+
actorId: string;
|
|
17
|
+
actorIp?: string;
|
|
18
|
+
actorUserAgent?: string;
|
|
19
|
+
source: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Payload for persisting a tenant-scoped patient audit row.
|
|
23
|
+
*/
|
|
24
|
+
export declare class CreatePatientAuditLogDto {
|
|
25
|
+
eventId: string;
|
|
26
|
+
tenant: string;
|
|
27
|
+
actorType: PatientAuditActorType;
|
|
28
|
+
actorId: string;
|
|
29
|
+
actorIp?: string | null;
|
|
30
|
+
actorUserAgent?: string | null;
|
|
31
|
+
action: PatientAuditAction;
|
|
32
|
+
resourceId: string;
|
|
33
|
+
source: string;
|
|
34
|
+
changes: PatientAuditChangeDto[];
|
|
35
|
+
occurredAt: string;
|
|
36
|
+
}
|
|
37
|
+
/** @deprecated Use CreatePatientAuditLogDto */
|
|
38
|
+
export type CreateTenantAuditLogDto = CreatePatientAuditLogDto;
|
|
39
|
+
//# sourceMappingURL=create-patient-audit-log.dto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-patient-audit-log.dto.d.ts","sourceRoot":"","sources":["../../src/analytics/create-patient-audit-log.dto.ts"],"names":[],"mappings":"AAeA,kDAAkD;AAClD,eAAO,MAAM,yBAAyB,wCAAyC,CAAC;AAChF,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/E,sDAAsD;AACtD,eAAO,MAAM,qBAAqB,oEAIxB,CAAC;AACX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExE,+CAA+C;AAC/C,qBAAa,qBAAqB;IAKhC,KAAK,EAAE,MAAM,CAAC;IAId,aAAa,CAAC,EAAE,OAAO,CAAC;IAIxB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,uEAAuE;AACvE,qBAAa,sBAAsB;IAGjC,SAAS,EAAE,qBAAqB,CAAC;IAMjC,OAAO,EAAE,MAAM,CAAC;IAMhB,OAAO,CAAC,EAAE,MAAM,CAAC;IAMjB,cAAc,CAAC,EAAE,MAAM,CAAC;IASxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,wBAAwB;IAGnC,OAAO,EAAE,MAAM,CAAC;IAOhB,MAAM,EAAE,MAAM,CAAC;IAIf,SAAS,EAAE,qBAAqB,CAAC;IAMjC,OAAO,EAAE,MAAM,CAAC;IAMhB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAMxB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAI/B,MAAM,EAAE,kBAAkB,CAAC;IAM3B,UAAU,EAAE,MAAM,CAAC;IAMnB,MAAM,EAAE,MAAM,CAAC;IAMf,OAAO,EAAE,qBAAqB,EAAE,CAAC;IAIjC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,+CAA+C;AAC/C,MAAM,MAAM,uBAAuB,GAAG,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.CreatePatientAuditLogDto = exports.PatientAuditContextDto = exports.PatientAuditChangeDto = exports.PATIENT_AUDIT_ACTIONS = exports.PATIENT_AUDIT_ACTOR_TYPES = void 0;
|
|
13
|
+
const swagger_1 = require("@nestjs/swagger");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
const class_transformer_1 = require("class-transformer");
|
|
16
|
+
/** Who performed the audited patient mutation. */
|
|
17
|
+
exports.PATIENT_AUDIT_ACTOR_TYPES = ['user', 'system', 'patient'];
|
|
18
|
+
/** Domain actions persisted in patient_audit_logs. */
|
|
19
|
+
exports.PATIENT_AUDIT_ACTIONS = [
|
|
20
|
+
'patient.created',
|
|
21
|
+
'patient.updated',
|
|
22
|
+
'patient.deleted',
|
|
23
|
+
];
|
|
24
|
+
/** Single field change on a patient record. */
|
|
25
|
+
class PatientAuditChangeDto {
|
|
26
|
+
field;
|
|
27
|
+
previousValue;
|
|
28
|
+
newValue;
|
|
29
|
+
}
|
|
30
|
+
exports.PatientAuditChangeDto = PatientAuditChangeDto;
|
|
31
|
+
__decorate([
|
|
32
|
+
(0, swagger_1.ApiProperty)({ example: 'name' }),
|
|
33
|
+
(0, class_validator_1.IsString)(),
|
|
34
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
35
|
+
(0, class_validator_1.MaxLength)(128),
|
|
36
|
+
__metadata("design:type", String)
|
|
37
|
+
], PatientAuditChangeDto.prototype, "field", void 0);
|
|
38
|
+
__decorate([
|
|
39
|
+
(0, swagger_1.ApiPropertyOptional)({ description: 'Previous value (null on create)' }),
|
|
40
|
+
(0, class_validator_1.IsOptional)(),
|
|
41
|
+
__metadata("design:type", Object)
|
|
42
|
+
], PatientAuditChangeDto.prototype, "previousValue", void 0);
|
|
43
|
+
__decorate([
|
|
44
|
+
(0, swagger_1.ApiPropertyOptional)({ description: 'New value (null on delete of field)' }),
|
|
45
|
+
(0, class_validator_1.IsOptional)(),
|
|
46
|
+
__metadata("design:type", Object)
|
|
47
|
+
], PatientAuditChangeDto.prototype, "newValue", void 0);
|
|
48
|
+
/** Caller context propagated over RPC for patient write operations. */
|
|
49
|
+
class PatientAuditContextDto {
|
|
50
|
+
actorType;
|
|
51
|
+
actorId;
|
|
52
|
+
actorIp;
|
|
53
|
+
actorUserAgent;
|
|
54
|
+
source;
|
|
55
|
+
}
|
|
56
|
+
exports.PatientAuditContextDto = PatientAuditContextDto;
|
|
57
|
+
__decorate([
|
|
58
|
+
(0, swagger_1.ApiProperty)({ enum: exports.PATIENT_AUDIT_ACTOR_TYPES }),
|
|
59
|
+
(0, class_validator_1.IsIn)([...exports.PATIENT_AUDIT_ACTOR_TYPES]),
|
|
60
|
+
__metadata("design:type", String)
|
|
61
|
+
], PatientAuditContextDto.prototype, "actorType", void 0);
|
|
62
|
+
__decorate([
|
|
63
|
+
(0, swagger_1.ApiProperty)({ description: 'User sub, service name, or patient token id' }),
|
|
64
|
+
(0, class_validator_1.IsString)(),
|
|
65
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
66
|
+
(0, class_validator_1.MaxLength)(255),
|
|
67
|
+
__metadata("design:type", String)
|
|
68
|
+
], PatientAuditContextDto.prototype, "actorId", void 0);
|
|
69
|
+
__decorate([
|
|
70
|
+
(0, swagger_1.ApiPropertyOptional)({ example: '203.0.113.10' }),
|
|
71
|
+
(0, class_validator_1.IsOptional)(),
|
|
72
|
+
(0, class_validator_1.IsString)(),
|
|
73
|
+
(0, class_validator_1.MaxLength)(64),
|
|
74
|
+
__metadata("design:type", String)
|
|
75
|
+
], PatientAuditContextDto.prototype, "actorIp", void 0);
|
|
76
|
+
__decorate([
|
|
77
|
+
(0, swagger_1.ApiPropertyOptional)(),
|
|
78
|
+
(0, class_validator_1.IsOptional)(),
|
|
79
|
+
(0, class_validator_1.IsString)(),
|
|
80
|
+
(0, class_validator_1.MaxLength)(512),
|
|
81
|
+
__metadata("design:type", String)
|
|
82
|
+
], PatientAuditContextDto.prototype, "actorUserAgent", void 0);
|
|
83
|
+
__decorate([
|
|
84
|
+
(0, swagger_1.ApiProperty)({
|
|
85
|
+
example: 'patient_service.upsertPatientByPhone',
|
|
86
|
+
description: 'Deterministic origin of the mutation',
|
|
87
|
+
}),
|
|
88
|
+
(0, class_validator_1.IsString)(),
|
|
89
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
90
|
+
(0, class_validator_1.MaxLength)(255),
|
|
91
|
+
__metadata("design:type", String)
|
|
92
|
+
], PatientAuditContextDto.prototype, "source", void 0);
|
|
93
|
+
/**
|
|
94
|
+
* Payload for persisting a tenant-scoped patient audit row.
|
|
95
|
+
*/
|
|
96
|
+
class CreatePatientAuditLogDto {
|
|
97
|
+
eventId;
|
|
98
|
+
tenant;
|
|
99
|
+
actorType;
|
|
100
|
+
actorId;
|
|
101
|
+
actorIp;
|
|
102
|
+
actorUserAgent;
|
|
103
|
+
action;
|
|
104
|
+
resourceId;
|
|
105
|
+
source;
|
|
106
|
+
changes;
|
|
107
|
+
occurredAt;
|
|
108
|
+
}
|
|
109
|
+
exports.CreatePatientAuditLogDto = CreatePatientAuditLogDto;
|
|
110
|
+
__decorate([
|
|
111
|
+
(0, swagger_1.ApiProperty)({ description: 'Unique id for idempotent insert', format: 'uuid' }),
|
|
112
|
+
(0, class_validator_1.IsUUID)(),
|
|
113
|
+
__metadata("design:type", String)
|
|
114
|
+
], CreatePatientAuditLogDto.prototype, "eventId", void 0);
|
|
115
|
+
__decorate([
|
|
116
|
+
(0, swagger_1.ApiProperty)({ description: 'Tenant slug' }),
|
|
117
|
+
(0, class_validator_1.IsString)(),
|
|
118
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
119
|
+
(0, class_validator_1.MinLength)(1),
|
|
120
|
+
(0, class_validator_1.MaxLength)(255),
|
|
121
|
+
__metadata("design:type", String)
|
|
122
|
+
], CreatePatientAuditLogDto.prototype, "tenant", void 0);
|
|
123
|
+
__decorate([
|
|
124
|
+
(0, swagger_1.ApiProperty)({ enum: exports.PATIENT_AUDIT_ACTOR_TYPES }),
|
|
125
|
+
(0, class_validator_1.IsIn)([...exports.PATIENT_AUDIT_ACTOR_TYPES]),
|
|
126
|
+
__metadata("design:type", String)
|
|
127
|
+
], CreatePatientAuditLogDto.prototype, "actorType", void 0);
|
|
128
|
+
__decorate([
|
|
129
|
+
(0, swagger_1.ApiProperty)({ description: 'Authenticated user id, service name, or token id' }),
|
|
130
|
+
(0, class_validator_1.IsString)(),
|
|
131
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
132
|
+
(0, class_validator_1.MaxLength)(255),
|
|
133
|
+
__metadata("design:type", String)
|
|
134
|
+
], CreatePatientAuditLogDto.prototype, "actorId", void 0);
|
|
135
|
+
__decorate([
|
|
136
|
+
(0, swagger_1.ApiPropertyOptional)(),
|
|
137
|
+
(0, class_validator_1.IsOptional)(),
|
|
138
|
+
(0, class_validator_1.IsString)(),
|
|
139
|
+
(0, class_validator_1.MaxLength)(64),
|
|
140
|
+
__metadata("design:type", Object)
|
|
141
|
+
], CreatePatientAuditLogDto.prototype, "actorIp", void 0);
|
|
142
|
+
__decorate([
|
|
143
|
+
(0, swagger_1.ApiPropertyOptional)(),
|
|
144
|
+
(0, class_validator_1.IsOptional)(),
|
|
145
|
+
(0, class_validator_1.IsString)(),
|
|
146
|
+
(0, class_validator_1.MaxLength)(512),
|
|
147
|
+
__metadata("design:type", Object)
|
|
148
|
+
], CreatePatientAuditLogDto.prototype, "actorUserAgent", void 0);
|
|
149
|
+
__decorate([
|
|
150
|
+
(0, swagger_1.ApiProperty)({ enum: exports.PATIENT_AUDIT_ACTIONS }),
|
|
151
|
+
(0, class_validator_1.IsIn)([...exports.PATIENT_AUDIT_ACTIONS]),
|
|
152
|
+
__metadata("design:type", String)
|
|
153
|
+
], CreatePatientAuditLogDto.prototype, "action", void 0);
|
|
154
|
+
__decorate([
|
|
155
|
+
(0, swagger_1.ApiProperty)({ description: 'Patient UUID' }),
|
|
156
|
+
(0, class_validator_1.IsString)(),
|
|
157
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
158
|
+
(0, class_validator_1.MaxLength)(255),
|
|
159
|
+
__metadata("design:type", String)
|
|
160
|
+
], CreatePatientAuditLogDto.prototype, "resourceId", void 0);
|
|
161
|
+
__decorate([
|
|
162
|
+
(0, swagger_1.ApiProperty)({ example: 'patient_service.updatePatient' }),
|
|
163
|
+
(0, class_validator_1.IsString)(),
|
|
164
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
165
|
+
(0, class_validator_1.MaxLength)(255),
|
|
166
|
+
__metadata("design:type", String)
|
|
167
|
+
], CreatePatientAuditLogDto.prototype, "source", void 0);
|
|
168
|
+
__decorate([
|
|
169
|
+
(0, swagger_1.ApiProperty)({ type: [PatientAuditChangeDto] }),
|
|
170
|
+
(0, class_validator_1.IsArray)(),
|
|
171
|
+
(0, class_validator_1.ValidateNested)({ each: true }),
|
|
172
|
+
(0, class_transformer_1.Type)(() => PatientAuditChangeDto),
|
|
173
|
+
__metadata("design:type", Array)
|
|
174
|
+
], CreatePatientAuditLogDto.prototype, "changes", void 0);
|
|
175
|
+
__decorate([
|
|
176
|
+
(0, swagger_1.ApiProperty)({ description: 'When the mutation completed (UTC ISO-8601)' }),
|
|
177
|
+
(0, class_validator_1.IsISO8601)(),
|
|
178
|
+
__metadata("design:type", String)
|
|
179
|
+
], CreatePatientAuditLogDto.prototype, "occurredAt", void 0);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from './event-analytic.enum';
|
|
2
2
|
export * from './followup-analytic-event-type.util';
|
|
3
3
|
export * from './create-analytic-event.dto';
|
|
4
|
-
export * from './create-
|
|
4
|
+
export * from './create-patient-audit-log.dto';
|
|
5
5
|
export * from './list-tenant-audit-logs-query.dto';
|
|
6
6
|
export * from './analytics-query.dto';
|
|
7
7
|
export * from './analytics-kafka-topics';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analytics/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,qCAAqC,CAAC;AACpD,cAAc,6BAA6B,CAAC;AAC5C,cAAc
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analytics/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,qCAAqC,CAAC;AACpD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,oCAAoC,CAAC;AACnD,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC"}
|
package/dist/analytics/index.js
CHANGED
|
@@ -17,7 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./event-analytic.enum"), exports);
|
|
18
18
|
__exportStar(require("./followup-analytic-event-type.util"), exports);
|
|
19
19
|
__exportStar(require("./create-analytic-event.dto"), exports);
|
|
20
|
-
__exportStar(require("./create-
|
|
20
|
+
__exportStar(require("./create-patient-audit-log.dto"), exports);
|
|
21
21
|
__exportStar(require("./list-tenant-audit-logs-query.dto"), exports);
|
|
22
22
|
__exportStar(require("./analytics-query.dto"), exports);
|
|
23
23
|
__exportStar(require("./analytics-kafka-topics"), exports);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ParsedFilterDto } from '../filters/parsed-filter.dto';
|
|
2
2
|
/**
|
|
3
|
-
* Allowed
|
|
3
|
+
* Allowed patient_audit_logs columns for filtering (camelCase API keys).
|
|
4
4
|
*/
|
|
5
|
-
export declare const TENANT_AUDIT_LIST_FILTER_KEYS: readonly ["actorId", "action", "
|
|
5
|
+
export declare const TENANT_AUDIT_LIST_FILTER_KEYS: readonly ["actorId", "action", "resourceId", "source", "occurredAt"];
|
|
6
6
|
export type TenantAuditListFilterKeyDto = (typeof TENANT_AUDIT_LIST_FILTER_KEYS)[number];
|
|
7
7
|
export declare class ListTenantAuditLogsQueryDto {
|
|
8
8
|
page?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-tenant-audit-logs-query.dto.d.ts","sourceRoot":"","sources":["../../src/analytics/list-tenant-audit-logs-query.dto.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;GAEG;AACH,eAAO,MAAM,6BAA6B,
|
|
1
|
+
{"version":3,"file":"list-tenant-audit-logs-query.dto.d.ts","sourceRoot":"","sources":["../../src/analytics/list-tenant-audit-logs-query.dto.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;GAEG;AACH,eAAO,MAAM,6BAA6B,sEAMhC,CAAC;AACX,MAAM,MAAM,2BAA2B,GAAG,CAAC,OAAO,6BAA6B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzF,qBAAa,2BAA2B;IAMtC,IAAI,CAAC,EAAE,MAAM,CAAC;IAQd,KAAK,CAAC,EAAE,MAAM,CAAC;IAUf,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;CAC7B"}
|
|
@@ -14,15 +14,13 @@ const swagger_1 = require("@nestjs/swagger");
|
|
|
14
14
|
const class_transformer_1 = require("class-transformer");
|
|
15
15
|
const class_validator_1 = require("class-validator");
|
|
16
16
|
/**
|
|
17
|
-
* Allowed
|
|
17
|
+
* Allowed patient_audit_logs columns for filtering (camelCase API keys).
|
|
18
18
|
*/
|
|
19
19
|
exports.TENANT_AUDIT_LIST_FILTER_KEYS = [
|
|
20
20
|
'actorId',
|
|
21
21
|
'action',
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'httpPath',
|
|
25
|
-
'httpStatus',
|
|
22
|
+
'resourceId',
|
|
23
|
+
'source',
|
|
26
24
|
'occurredAt',
|
|
27
25
|
];
|
|
28
26
|
class ListTenantAuditLogsQueryDto {
|
|
@@ -51,9 +49,9 @@ __decorate([
|
|
|
51
49
|
__decorate([
|
|
52
50
|
(0, swagger_1.ApiPropertyOptional)({
|
|
53
51
|
description: 'JSON string with an array of filter objects `{ key, op, value }`. ' +
|
|
54
|
-
'Example: `[{"key":"
|
|
52
|
+
'Example: `[{"key":"action","op":"==","value":"patient.updated"}]`.',
|
|
55
53
|
type: String,
|
|
56
|
-
example: '[{"key":"
|
|
54
|
+
example: '[{"key":"action","op":"==","value":"patient.updated"}]',
|
|
57
55
|
}),
|
|
58
56
|
(0, class_validator_1.IsOptional)(),
|
|
59
57
|
__metadata("design:type", Array)
|
|
@@ -7,6 +7,7 @@ export type CreatableUserRole = (typeof CREATABLE_USER_ROLES)[number];
|
|
|
7
7
|
export declare class CreateUserDto {
|
|
8
8
|
name: string;
|
|
9
9
|
email: string;
|
|
10
|
-
|
|
10
|
+
roles?: CreatableUserRole[];
|
|
11
|
+
role?: CreatableUserRole;
|
|
11
12
|
}
|
|
12
13
|
//# sourceMappingURL=create-user.dto.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-user.dto.d.ts","sourceRoot":"","sources":["../../src/users/create-user.dto.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"create-user.dto.d.ts","sourceRoot":"","sources":["../../src/users/create-user.dto.ts"],"names":[],"mappings":"AAaA;;;GAGG;AACH,eAAO,MAAM,oBAAoB,+CAAgD,CAAC;AAClF,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,qBAAa,aAAa;IASxB,IAAI,EAAE,MAAM,CAAC;IAWb,KAAK,EAAE,MAAM,CAAC;IAgBd,KAAK,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAa5B,IAAI,CAAC,EAAE,iBAAiB,CAAC;CAC1B"}
|
|
@@ -20,6 +20,7 @@ exports.CREATABLE_USER_ROLES = ['attendant', 'health_professional'];
|
|
|
20
20
|
class CreateUserDto {
|
|
21
21
|
name;
|
|
22
22
|
email;
|
|
23
|
+
roles;
|
|
23
24
|
role;
|
|
24
25
|
}
|
|
25
26
|
exports.CreateUserDto = CreateUserDto;
|
|
@@ -48,10 +49,27 @@ __decorate([
|
|
|
48
49
|
], CreateUserDto.prototype, "email", void 0);
|
|
49
50
|
__decorate([
|
|
50
51
|
(0, swagger_1.ApiProperty)({
|
|
51
|
-
description: '
|
|
52
|
+
description: 'Papéis do usuário. Pelo menos um: atendente e/ou profissional de saúde.',
|
|
53
|
+
enum: exports.CREATABLE_USER_ROLES,
|
|
54
|
+
isArray: true,
|
|
55
|
+
example: ['attendant'],
|
|
56
|
+
}),
|
|
57
|
+
(0, class_validator_1.ValidateIf)((o) => o.role == null),
|
|
58
|
+
(0, class_validator_1.IsArray)({ message: 'roles deve ser um array' }),
|
|
59
|
+
(0, class_validator_1.ArrayMinSize)(1, { message: 'roles deve conter ao menos um papel' }),
|
|
60
|
+
(0, class_validator_1.IsIn)(exports.CREATABLE_USER_ROLES, {
|
|
61
|
+
each: true,
|
|
62
|
+
message: 'cada role deve ser attendant ou health_professional',
|
|
63
|
+
}),
|
|
64
|
+
__metadata("design:type", Array)
|
|
65
|
+
], CreateUserDto.prototype, "roles", void 0);
|
|
66
|
+
__decorate([
|
|
67
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
68
|
+
description: 'Deprecated: use roles[]. Papel único; convertido internamente para roles com um elemento.',
|
|
52
69
|
enum: exports.CREATABLE_USER_ROLES,
|
|
53
70
|
default: 'attendant',
|
|
54
71
|
}),
|
|
72
|
+
(0, class_validator_1.IsOptional)(),
|
|
55
73
|
(0, class_validator_1.IsString)(),
|
|
56
74
|
(0, class_validator_1.IsIn)(exports.CREATABLE_USER_ROLES, {
|
|
57
75
|
message: 'role deve ser attendant ou health_professional',
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export declare const ALL_USER_ROLES: readonly ["administrator", "attendant", "health_professional"];
|
|
2
2
|
export type AllUserRole = (typeof ALL_USER_ROLES)[number];
|
|
3
|
-
/** Fields an administrator may change for any user (including
|
|
3
|
+
/** Fields an administrator may change for any user (including roles). */
|
|
4
4
|
export declare class UpdateUserByAdminDto {
|
|
5
5
|
_atLeastOne?: string;
|
|
6
6
|
name?: string;
|
|
7
7
|
email?: string;
|
|
8
|
+
roles?: AllUserRole[];
|
|
8
9
|
role?: AllUserRole;
|
|
9
10
|
}
|
|
10
11
|
//# sourceMappingURL=update-user-by-admin.dto.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-user-by-admin.dto.d.ts","sourceRoot":"","sources":["../../src/users/update-user-by-admin.dto.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"update-user-by-admin.dto.d.ts","sourceRoot":"","sources":["../../src/users/update-user-by-admin.dto.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,cAAc,gEAIjB,CAAC;AACX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,yEAAyE;AACzE,qBAAa,oBAAoB;IAG/B,WAAW,CAAC,EAAE,MAAM,CAAC;IAWrB,IAAI,CAAC,EAAE,MAAM,CAAC;IAWd,KAAK,CAAC,EAAE,MAAM,CAAC;IAef,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IAYtB,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB"}
|
|
@@ -18,17 +18,18 @@ exports.ALL_USER_ROLES = [
|
|
|
18
18
|
'attendant',
|
|
19
19
|
'health_professional',
|
|
20
20
|
];
|
|
21
|
-
/** Fields an administrator may change for any user (including
|
|
21
|
+
/** Fields an administrator may change for any user (including roles). */
|
|
22
22
|
class UpdateUserByAdminDto {
|
|
23
23
|
_atLeastOne;
|
|
24
24
|
name;
|
|
25
25
|
email;
|
|
26
|
+
roles;
|
|
26
27
|
role;
|
|
27
28
|
}
|
|
28
29
|
exports.UpdateUserByAdminDto = UpdateUserByAdminDto;
|
|
29
30
|
__decorate([
|
|
30
31
|
(0, swagger_1.ApiHideProperty)(),
|
|
31
|
-
(0, at_least_one_of_decorator_1.AtLeastOneOf)(['name', 'email', 'role']),
|
|
32
|
+
(0, at_least_one_of_decorator_1.AtLeastOneOf)(['name', 'email', 'role', 'roles']),
|
|
32
33
|
__metadata("design:type", String)
|
|
33
34
|
], UpdateUserByAdminDto.prototype, "_atLeastOne", void 0);
|
|
34
35
|
__decorate([
|
|
@@ -56,7 +57,23 @@ __decorate([
|
|
|
56
57
|
], UpdateUserByAdminDto.prototype, "email", void 0);
|
|
57
58
|
__decorate([
|
|
58
59
|
(0, swagger_1.ApiPropertyOptional)({
|
|
59
|
-
description: 'User role',
|
|
60
|
+
description: 'User roles (replaces the full role set when provided)',
|
|
61
|
+
enum: exports.ALL_USER_ROLES,
|
|
62
|
+
isArray: true,
|
|
63
|
+
example: ['attendant', 'health_professional'],
|
|
64
|
+
}),
|
|
65
|
+
(0, class_validator_1.IsOptional)(),
|
|
66
|
+
(0, class_validator_1.IsArray)(),
|
|
67
|
+
(0, class_validator_1.ArrayMinSize)(1, { message: 'roles must contain at least one role when provided' }),
|
|
68
|
+
(0, class_validator_1.IsIn)(exports.ALL_USER_ROLES, {
|
|
69
|
+
each: true,
|
|
70
|
+
message: 'each role must be administrator, attendant, or health_professional',
|
|
71
|
+
}),
|
|
72
|
+
__metadata("design:type", Array)
|
|
73
|
+
], UpdateUserByAdminDto.prototype, "roles", void 0);
|
|
74
|
+
__decorate([
|
|
75
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
76
|
+
description: 'Deprecated: use roles[]. Single role replacement.',
|
|
60
77
|
enum: exports.ALL_USER_ROLES,
|
|
61
78
|
example: 'attendant',
|
|
62
79
|
}),
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { ALL_USER_ROLES } from './update-user-by-admin.dto';
|
|
1
2
|
/** User payload returned after create or update (no password). */
|
|
2
3
|
export declare class UpdatedUserDto {
|
|
3
4
|
userId: string;
|
|
4
5
|
name: string;
|
|
5
6
|
email: string;
|
|
6
|
-
|
|
7
|
+
roles: (typeof ALL_USER_ROLES)[number][];
|
|
8
|
+
role?: (typeof ALL_USER_ROLES)[number];
|
|
7
9
|
createdAt: Date;
|
|
8
10
|
updatedAt: Date;
|
|
9
11
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updated-user.dto.d.ts","sourceRoot":"","sources":["../../src/users/updated-user.dto.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"updated-user.dto.d.ts","sourceRoot":"","sources":["../../src/users/updated-user.dto.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,kEAAkE;AAClE,qBAAa,cAAc;IAMzB,MAAM,EAAE,MAAM,CAAC;IAGf,IAAI,EAAE,MAAM,CAAC;IAGb,KAAK,EAAE,MAAM,CAAC;IAQd,KAAK,EAAE,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IAOzC,IAAI,CAAC,EAAE,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;IAGvC,SAAS,EAAE,IAAI,CAAC;IAGhB,SAAS,EAAE,IAAI,CAAC;CACjB"}
|
|
@@ -11,11 +11,13 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.UpdatedUserDto = void 0;
|
|
13
13
|
const swagger_1 = require("@nestjs/swagger");
|
|
14
|
+
const update_user_by_admin_dto_1 = require("./update-user-by-admin.dto");
|
|
14
15
|
/** User payload returned after create or update (no password). */
|
|
15
16
|
class UpdatedUserDto {
|
|
16
17
|
userId;
|
|
17
18
|
name;
|
|
18
19
|
email;
|
|
20
|
+
roles;
|
|
19
21
|
role;
|
|
20
22
|
createdAt;
|
|
21
23
|
updatedAt;
|
|
@@ -39,11 +41,20 @@ __decorate([
|
|
|
39
41
|
], UpdatedUserDto.prototype, "email", void 0);
|
|
40
42
|
__decorate([
|
|
41
43
|
(0, swagger_1.ApiProperty)({
|
|
42
|
-
description: '
|
|
43
|
-
enum:
|
|
44
|
+
description: 'All roles assigned to the user',
|
|
45
|
+
enum: update_user_by_admin_dto_1.ALL_USER_ROLES,
|
|
46
|
+
isArray: true,
|
|
47
|
+
example: ['attendant'],
|
|
48
|
+
}),
|
|
49
|
+
__metadata("design:type", Array)
|
|
50
|
+
], UpdatedUserDto.prototype, "roles", void 0);
|
|
51
|
+
__decorate([
|
|
52
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
53
|
+
description: 'Deprecated primary role alias (first role in roles[])',
|
|
54
|
+
enum: update_user_by_admin_dto_1.ALL_USER_ROLES,
|
|
44
55
|
example: 'attendant',
|
|
45
56
|
}),
|
|
46
|
-
__metadata("design:type",
|
|
57
|
+
__metadata("design:type", Object)
|
|
47
58
|
], UpdatedUserDto.prototype, "role", void 0);
|
|
48
59
|
__decorate([
|
|
49
60
|
(0, swagger_1.ApiProperty)({ description: 'Creation timestamp' }),
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { ALL_USER_ROLES } from './update-user-by-admin.dto';
|
|
1
2
|
/** Public user summary returned when an administrator lists users (no password). */
|
|
2
3
|
export declare class UserListItemDto {
|
|
3
4
|
id: string;
|
|
4
5
|
name: string;
|
|
5
6
|
email: string;
|
|
6
|
-
|
|
7
|
+
roles: (typeof ALL_USER_ROLES)[number][];
|
|
8
|
+
role?: (typeof ALL_USER_ROLES)[number];
|
|
7
9
|
}
|
|
8
10
|
//# sourceMappingURL=user-list-item.dto.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-list-item.dto.d.ts","sourceRoot":"","sources":["../../src/users/user-list-item.dto.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"user-list-item.dto.d.ts","sourceRoot":"","sources":["../../src/users/user-list-item.dto.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,oFAAoF;AACpF,qBAAa,eAAe;IAM1B,EAAE,EAAE,MAAM,CAAC;IAGX,IAAI,EAAE,MAAM,CAAC;IAGb,KAAK,EAAE,MAAM,CAAC;IAQd,KAAK,EAAE,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IAOzC,IAAI,CAAC,EAAE,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;CACxC"}
|
|
@@ -11,11 +11,13 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.UserListItemDto = void 0;
|
|
13
13
|
const swagger_1 = require("@nestjs/swagger");
|
|
14
|
+
const update_user_by_admin_dto_1 = require("./update-user-by-admin.dto");
|
|
14
15
|
/** Public user summary returned when an administrator lists users (no password). */
|
|
15
16
|
class UserListItemDto {
|
|
16
17
|
id;
|
|
17
18
|
name;
|
|
18
19
|
email;
|
|
20
|
+
roles;
|
|
19
21
|
role;
|
|
20
22
|
}
|
|
21
23
|
exports.UserListItemDto = UserListItemDto;
|
|
@@ -37,9 +39,18 @@ __decorate([
|
|
|
37
39
|
], UserListItemDto.prototype, "email", void 0);
|
|
38
40
|
__decorate([
|
|
39
41
|
(0, swagger_1.ApiProperty)({
|
|
40
|
-
description: '
|
|
41
|
-
enum:
|
|
42
|
+
description: 'All roles assigned to the user',
|
|
43
|
+
enum: update_user_by_admin_dto_1.ALL_USER_ROLES,
|
|
44
|
+
isArray: true,
|
|
45
|
+
example: ['attendant'],
|
|
46
|
+
}),
|
|
47
|
+
__metadata("design:type", Array)
|
|
48
|
+
], UserListItemDto.prototype, "roles", void 0);
|
|
49
|
+
__decorate([
|
|
50
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
51
|
+
description: 'Deprecated primary role alias (first role in roles[])',
|
|
52
|
+
enum: update_user_by_admin_dto_1.ALL_USER_ROLES,
|
|
42
53
|
example: 'attendant',
|
|
43
54
|
}),
|
|
44
|
-
__metadata("design:type",
|
|
55
|
+
__metadata("design:type", Object)
|
|
45
56
|
], UserListItemDto.prototype, "role", void 0);
|
|
@@ -19,6 +19,9 @@ export interface AuthUpdateUserAdminPayload {
|
|
|
19
19
|
targetUserId: string;
|
|
20
20
|
name?: string;
|
|
21
21
|
email?: string;
|
|
22
|
+
/** Replaces the full role set when provided. */
|
|
23
|
+
roles?: ('administrator' | 'attendant' | 'health_professional')[];
|
|
24
|
+
/** @deprecated Use roles[] */
|
|
22
25
|
role?: 'administrator' | 'attendant' | 'health_professional';
|
|
23
26
|
}
|
|
24
27
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-update-kafka-payloads.d.ts","sourceRoot":"","sources":["../../src/users/user-update-kafka-payloads.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,eAAe,GAAG,WAAW,GAAG,qBAAqB,CAAC;CAC9D;AAED;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;CACtB"}
|
|
1
|
+
{"version":3,"file":"user-update-kafka-payloads.d.ts","sourceRoot":"","sources":["../../src/users/user-update-kafka-payloads.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,KAAK,CAAC,EAAE,CAAC,eAAe,GAAG,WAAW,GAAG,qBAAqB,CAAC,EAAE,CAAC;IAClE,8BAA8B;IAC9B,IAAI,CAAC,EAAE,eAAe,GAAG,WAAW,GAAG,qBAAqB,CAAC;CAC9D;AAED;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;CACtB"}
|
package/package.json
CHANGED
|
@@ -1,30 +1,32 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "tychat-contracts",
|
|
3
|
-
"version": "1.6.
|
|
4
|
-
"description": "DTOs compartilhados com class-validator (API e microserviços)",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"private": false,
|
|
8
|
-
"scripts": {
|
|
9
|
-
"build": "tsc",
|
|
10
|
-
"release": "npm version patch && npm publish",
|
|
11
|
-
"prepublishOnly": "npm run build"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"class-
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"@
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "tychat-contracts",
|
|
3
|
+
"version": "1.6.83",
|
|
4
|
+
"description": "DTOs compartilhados com class-validator (API e microserviços)",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"private": false,
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"release": "npm version patch && npm publish",
|
|
11
|
+
"prepublishOnly": "npm run build",
|
|
12
|
+
"prepare": "husky"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"class-transformer": "^0.5.1",
|
|
16
|
+
"class-validator": "^0.14.1",
|
|
17
|
+
"ioredis": "^5.10.0",
|
|
18
|
+
"jest": "^30.3.0",
|
|
19
|
+
"reflect-metadata": "*"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@nestjs/swagger": "^11.2.6",
|
|
23
|
+
"@types/jest": "^30.0.0",
|
|
24
|
+
"husky": "^9.1.7",
|
|
25
|
+
"ts-jest": "^29.4.9",
|
|
26
|
+
"typescript": "^5.7.3"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"@nestjs/swagger": "^11.2.6",
|
|
30
|
+
"reflect-metadata": "*"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import {
|
|
3
|
+
IsArray,
|
|
4
|
+
IsIn,
|
|
5
|
+
IsISO8601,
|
|
6
|
+
IsNotEmpty,
|
|
7
|
+
IsOptional,
|
|
8
|
+
IsString,
|
|
9
|
+
IsUUID,
|
|
10
|
+
MaxLength,
|
|
11
|
+
MinLength,
|
|
12
|
+
ValidateNested,
|
|
13
|
+
} from 'class-validator';
|
|
14
|
+
import { Type } from 'class-transformer';
|
|
15
|
+
|
|
16
|
+
/** Who performed the audited patient mutation. */
|
|
17
|
+
export const PATIENT_AUDIT_ACTOR_TYPES = ['user', 'system', 'patient'] as const;
|
|
18
|
+
export type PatientAuditActorType = (typeof PATIENT_AUDIT_ACTOR_TYPES)[number];
|
|
19
|
+
|
|
20
|
+
/** Domain actions persisted in patient_audit_logs. */
|
|
21
|
+
export const PATIENT_AUDIT_ACTIONS = [
|
|
22
|
+
'patient.created',
|
|
23
|
+
'patient.updated',
|
|
24
|
+
'patient.deleted',
|
|
25
|
+
] as const;
|
|
26
|
+
export type PatientAuditAction = (typeof PATIENT_AUDIT_ACTIONS)[number];
|
|
27
|
+
|
|
28
|
+
/** Single field change on a patient record. */
|
|
29
|
+
export class PatientAuditChangeDto {
|
|
30
|
+
@ApiProperty({ example: 'name' })
|
|
31
|
+
@IsString()
|
|
32
|
+
@IsNotEmpty()
|
|
33
|
+
@MaxLength(128)
|
|
34
|
+
field: string;
|
|
35
|
+
|
|
36
|
+
@ApiPropertyOptional({ description: 'Previous value (null on create)' })
|
|
37
|
+
@IsOptional()
|
|
38
|
+
previousValue?: unknown;
|
|
39
|
+
|
|
40
|
+
@ApiPropertyOptional({ description: 'New value (null on delete of field)' })
|
|
41
|
+
@IsOptional()
|
|
42
|
+
newValue?: unknown;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Caller context propagated over RPC for patient write operations. */
|
|
46
|
+
export class PatientAuditContextDto {
|
|
47
|
+
@ApiProperty({ enum: PATIENT_AUDIT_ACTOR_TYPES })
|
|
48
|
+
@IsIn([...PATIENT_AUDIT_ACTOR_TYPES])
|
|
49
|
+
actorType: PatientAuditActorType;
|
|
50
|
+
|
|
51
|
+
@ApiProperty({ description: 'User sub, service name, or patient token id' })
|
|
52
|
+
@IsString()
|
|
53
|
+
@IsNotEmpty()
|
|
54
|
+
@MaxLength(255)
|
|
55
|
+
actorId: string;
|
|
56
|
+
|
|
57
|
+
@ApiPropertyOptional({ example: '203.0.113.10' })
|
|
58
|
+
@IsOptional()
|
|
59
|
+
@IsString()
|
|
60
|
+
@MaxLength(64)
|
|
61
|
+
actorIp?: string;
|
|
62
|
+
|
|
63
|
+
@ApiPropertyOptional()
|
|
64
|
+
@IsOptional()
|
|
65
|
+
@IsString()
|
|
66
|
+
@MaxLength(512)
|
|
67
|
+
actorUserAgent?: string;
|
|
68
|
+
|
|
69
|
+
@ApiProperty({
|
|
70
|
+
example: 'patient_service.upsertPatientByPhone',
|
|
71
|
+
description: 'Deterministic origin of the mutation',
|
|
72
|
+
})
|
|
73
|
+
@IsString()
|
|
74
|
+
@IsNotEmpty()
|
|
75
|
+
@MaxLength(255)
|
|
76
|
+
source: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Payload for persisting a tenant-scoped patient audit row.
|
|
81
|
+
*/
|
|
82
|
+
export class CreatePatientAuditLogDto {
|
|
83
|
+
@ApiProperty({ description: 'Unique id for idempotent insert', format: 'uuid' })
|
|
84
|
+
@IsUUID()
|
|
85
|
+
eventId: string;
|
|
86
|
+
|
|
87
|
+
@ApiProperty({ description: 'Tenant slug' })
|
|
88
|
+
@IsString()
|
|
89
|
+
@IsNotEmpty()
|
|
90
|
+
@MinLength(1)
|
|
91
|
+
@MaxLength(255)
|
|
92
|
+
tenant: string;
|
|
93
|
+
|
|
94
|
+
@ApiProperty({ enum: PATIENT_AUDIT_ACTOR_TYPES })
|
|
95
|
+
@IsIn([...PATIENT_AUDIT_ACTOR_TYPES])
|
|
96
|
+
actorType: PatientAuditActorType;
|
|
97
|
+
|
|
98
|
+
@ApiProperty({ description: 'Authenticated user id, service name, or token id' })
|
|
99
|
+
@IsString()
|
|
100
|
+
@IsNotEmpty()
|
|
101
|
+
@MaxLength(255)
|
|
102
|
+
actorId: string;
|
|
103
|
+
|
|
104
|
+
@ApiPropertyOptional()
|
|
105
|
+
@IsOptional()
|
|
106
|
+
@IsString()
|
|
107
|
+
@MaxLength(64)
|
|
108
|
+
actorIp?: string | null;
|
|
109
|
+
|
|
110
|
+
@ApiPropertyOptional()
|
|
111
|
+
@IsOptional()
|
|
112
|
+
@IsString()
|
|
113
|
+
@MaxLength(512)
|
|
114
|
+
actorUserAgent?: string | null;
|
|
115
|
+
|
|
116
|
+
@ApiProperty({ enum: PATIENT_AUDIT_ACTIONS })
|
|
117
|
+
@IsIn([...PATIENT_AUDIT_ACTIONS])
|
|
118
|
+
action: PatientAuditAction;
|
|
119
|
+
|
|
120
|
+
@ApiProperty({ description: 'Patient UUID' })
|
|
121
|
+
@IsString()
|
|
122
|
+
@IsNotEmpty()
|
|
123
|
+
@MaxLength(255)
|
|
124
|
+
resourceId: string;
|
|
125
|
+
|
|
126
|
+
@ApiProperty({ example: 'patient_service.updatePatient' })
|
|
127
|
+
@IsString()
|
|
128
|
+
@IsNotEmpty()
|
|
129
|
+
@MaxLength(255)
|
|
130
|
+
source: string;
|
|
131
|
+
|
|
132
|
+
@ApiProperty({ type: [PatientAuditChangeDto] })
|
|
133
|
+
@IsArray()
|
|
134
|
+
@ValidateNested({ each: true })
|
|
135
|
+
@Type(() => PatientAuditChangeDto)
|
|
136
|
+
changes: PatientAuditChangeDto[];
|
|
137
|
+
|
|
138
|
+
@ApiProperty({ description: 'When the mutation completed (UTC ISO-8601)' })
|
|
139
|
+
@IsISO8601()
|
|
140
|
+
occurredAt: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** @deprecated Use CreatePatientAuditLogDto */
|
|
144
|
+
export type CreateTenantAuditLogDto = CreatePatientAuditLogDto;
|
package/src/analytics/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from './event-analytic.enum';
|
|
2
2
|
export * from './followup-analytic-event-type.util';
|
|
3
3
|
export * from './create-analytic-event.dto';
|
|
4
|
-
export * from './create-
|
|
4
|
+
export * from './create-patient-audit-log.dto';
|
|
5
5
|
export * from './list-tenant-audit-logs-query.dto';
|
|
6
6
|
export * from './analytics-query.dto';
|
|
7
7
|
export * from './analytics-kafka-topics';
|
|
@@ -4,15 +4,13 @@ import { IsInt, IsOptional, Max, Min } from 'class-validator';
|
|
|
4
4
|
import type { ParsedFilterDto } from '../filters/parsed-filter.dto';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Allowed
|
|
7
|
+
* Allowed patient_audit_logs columns for filtering (camelCase API keys).
|
|
8
8
|
*/
|
|
9
9
|
export const TENANT_AUDIT_LIST_FILTER_KEYS = [
|
|
10
10
|
'actorId',
|
|
11
11
|
'action',
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'httpPath',
|
|
15
|
-
'httpStatus',
|
|
12
|
+
'resourceId',
|
|
13
|
+
'source',
|
|
16
14
|
'occurredAt',
|
|
17
15
|
] as const;
|
|
18
16
|
export type TenantAuditListFilterKeyDto = (typeof TENANT_AUDIT_LIST_FILTER_KEYS)[number];
|
|
@@ -36,9 +34,9 @@ export class ListTenantAuditLogsQueryDto {
|
|
|
36
34
|
@ApiPropertyOptional({
|
|
37
35
|
description:
|
|
38
36
|
'JSON string with an array of filter objects `{ key, op, value }`. ' +
|
|
39
|
-
'Example: `[{"key":"
|
|
37
|
+
'Example: `[{"key":"action","op":"==","value":"patient.updated"}]`.',
|
|
40
38
|
type: String,
|
|
41
|
-
example: '[{"key":"
|
|
39
|
+
example: '[{"key":"action","op":"==","value":"patient.updated"}]',
|
|
42
40
|
})
|
|
43
41
|
@IsOptional()
|
|
44
42
|
filters?: ParsedFilterDto[];
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { ApiProperty } from '@nestjs/swagger';
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
2
|
import {
|
|
3
3
|
IsEmail,
|
|
4
4
|
IsString,
|
|
5
5
|
IsIn,
|
|
6
6
|
MinLength,
|
|
7
7
|
MaxLength,
|
|
8
|
+
IsArray,
|
|
9
|
+
ArrayMinSize,
|
|
10
|
+
IsOptional,
|
|
11
|
+
ValidateIf,
|
|
8
12
|
} from 'class-validator';
|
|
9
13
|
|
|
10
14
|
/**
|
|
@@ -37,13 +41,31 @@ export class CreateUserDto {
|
|
|
37
41
|
email: string;
|
|
38
42
|
|
|
39
43
|
@ApiProperty({
|
|
40
|
-
description:
|
|
44
|
+
description:
|
|
45
|
+
'Papéis do usuário. Pelo menos um: atendente e/ou profissional de saúde.',
|
|
46
|
+
enum: CREATABLE_USER_ROLES,
|
|
47
|
+
isArray: true,
|
|
48
|
+
example: ['attendant'],
|
|
49
|
+
})
|
|
50
|
+
@ValidateIf((o: CreateUserDto) => o.role == null)
|
|
51
|
+
@IsArray({ message: 'roles deve ser um array' })
|
|
52
|
+
@ArrayMinSize(1, { message: 'roles deve conter ao menos um papel' })
|
|
53
|
+
@IsIn(CREATABLE_USER_ROLES, {
|
|
54
|
+
each: true,
|
|
55
|
+
message: 'cada role deve ser attendant ou health_professional',
|
|
56
|
+
})
|
|
57
|
+
roles?: CreatableUserRole[];
|
|
58
|
+
|
|
59
|
+
@ApiPropertyOptional({
|
|
60
|
+
description:
|
|
61
|
+
'Deprecated: use roles[]. Papel único; convertido internamente para roles com um elemento.',
|
|
41
62
|
enum: CREATABLE_USER_ROLES,
|
|
42
63
|
default: 'attendant',
|
|
43
64
|
})
|
|
65
|
+
@IsOptional()
|
|
44
66
|
@IsString()
|
|
45
67
|
@IsIn(CREATABLE_USER_ROLES, {
|
|
46
68
|
message: 'role deve ser attendant ou health_professional',
|
|
47
69
|
})
|
|
48
|
-
role
|
|
70
|
+
role?: CreatableUserRole;
|
|
49
71
|
}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { ApiHideProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
IsEmail,
|
|
4
|
+
IsIn,
|
|
5
|
+
IsOptional,
|
|
6
|
+
IsString,
|
|
7
|
+
MaxLength,
|
|
8
|
+
MinLength,
|
|
9
|
+
IsArray,
|
|
10
|
+
ArrayMinSize,
|
|
11
|
+
} from 'class-validator';
|
|
3
12
|
import { AtLeastOneOf } from './at-least-one-of.decorator';
|
|
4
13
|
|
|
5
14
|
export const ALL_USER_ROLES = [
|
|
@@ -9,10 +18,10 @@ export const ALL_USER_ROLES = [
|
|
|
9
18
|
] as const;
|
|
10
19
|
export type AllUserRole = (typeof ALL_USER_ROLES)[number];
|
|
11
20
|
|
|
12
|
-
/** Fields an administrator may change for any user (including
|
|
21
|
+
/** Fields an administrator may change for any user (including roles). */
|
|
13
22
|
export class UpdateUserByAdminDto {
|
|
14
23
|
@ApiHideProperty()
|
|
15
|
-
@AtLeastOneOf(['name', 'email', 'role'])
|
|
24
|
+
@AtLeastOneOf(['name', 'email', 'role', 'roles'])
|
|
16
25
|
_atLeastOne?: string;
|
|
17
26
|
|
|
18
27
|
@ApiPropertyOptional({
|
|
@@ -38,7 +47,22 @@ export class UpdateUserByAdminDto {
|
|
|
38
47
|
email?: string;
|
|
39
48
|
|
|
40
49
|
@ApiPropertyOptional({
|
|
41
|
-
description: 'User role',
|
|
50
|
+
description: 'User roles (replaces the full role set when provided)',
|
|
51
|
+
enum: ALL_USER_ROLES,
|
|
52
|
+
isArray: true,
|
|
53
|
+
example: ['attendant', 'health_professional'],
|
|
54
|
+
})
|
|
55
|
+
@IsOptional()
|
|
56
|
+
@IsArray()
|
|
57
|
+
@ArrayMinSize(1, { message: 'roles must contain at least one role when provided' })
|
|
58
|
+
@IsIn(ALL_USER_ROLES, {
|
|
59
|
+
each: true,
|
|
60
|
+
message: 'each role must be administrator, attendant, or health_professional',
|
|
61
|
+
})
|
|
62
|
+
roles?: AllUserRole[];
|
|
63
|
+
|
|
64
|
+
@ApiPropertyOptional({
|
|
65
|
+
description: 'Deprecated: use roles[]. Single role replacement.',
|
|
42
66
|
enum: ALL_USER_ROLES,
|
|
43
67
|
example: 'attendant',
|
|
44
68
|
})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ApiProperty } from '@nestjs/swagger';
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import { ALL_USER_ROLES } from './update-user-by-admin.dto';
|
|
2
3
|
|
|
3
4
|
/** User payload returned after create or update (no password). */
|
|
4
5
|
export class UpdatedUserDto {
|
|
@@ -16,11 +17,19 @@ export class UpdatedUserDto {
|
|
|
16
17
|
email: string;
|
|
17
18
|
|
|
18
19
|
@ApiProperty({
|
|
19
|
-
description: '
|
|
20
|
-
enum:
|
|
20
|
+
description: 'All roles assigned to the user',
|
|
21
|
+
enum: ALL_USER_ROLES,
|
|
22
|
+
isArray: true,
|
|
23
|
+
example: ['attendant'],
|
|
24
|
+
})
|
|
25
|
+
roles: (typeof ALL_USER_ROLES)[number][];
|
|
26
|
+
|
|
27
|
+
@ApiPropertyOptional({
|
|
28
|
+
description: 'Deprecated primary role alias (first role in roles[])',
|
|
29
|
+
enum: ALL_USER_ROLES,
|
|
21
30
|
example: 'attendant',
|
|
22
31
|
})
|
|
23
|
-
role
|
|
32
|
+
role?: (typeof ALL_USER_ROLES)[number];
|
|
24
33
|
|
|
25
34
|
@ApiProperty({ description: 'Creation timestamp' })
|
|
26
35
|
createdAt: Date;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ApiProperty } from '@nestjs/swagger';
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import { ALL_USER_ROLES } from './update-user-by-admin.dto';
|
|
2
3
|
|
|
3
4
|
/** Public user summary returned when an administrator lists users (no password). */
|
|
4
5
|
export class UserListItemDto {
|
|
@@ -16,9 +17,17 @@ export class UserListItemDto {
|
|
|
16
17
|
email: string;
|
|
17
18
|
|
|
18
19
|
@ApiProperty({
|
|
19
|
-
description: '
|
|
20
|
-
enum:
|
|
20
|
+
description: 'All roles assigned to the user',
|
|
21
|
+
enum: ALL_USER_ROLES,
|
|
22
|
+
isArray: true,
|
|
23
|
+
example: ['attendant'],
|
|
24
|
+
})
|
|
25
|
+
roles: (typeof ALL_USER_ROLES)[number][];
|
|
26
|
+
|
|
27
|
+
@ApiPropertyOptional({
|
|
28
|
+
description: 'Deprecated primary role alias (first role in roles[])',
|
|
29
|
+
enum: ALL_USER_ROLES,
|
|
21
30
|
example: 'attendant',
|
|
22
31
|
})
|
|
23
|
-
role
|
|
32
|
+
role?: (typeof ALL_USER_ROLES)[number];
|
|
24
33
|
}
|
|
@@ -20,6 +20,9 @@ export interface AuthUpdateUserAdminPayload {
|
|
|
20
20
|
targetUserId: string;
|
|
21
21
|
name?: string;
|
|
22
22
|
email?: string;
|
|
23
|
+
/** Replaces the full role set when provided. */
|
|
24
|
+
roles?: ('administrator' | 'attendant' | 'health_professional')[];
|
|
25
|
+
/** @deprecated Use roles[] */
|
|
23
26
|
role?: 'administrator' | 'attendant' | 'health_professional';
|
|
24
27
|
}
|
|
25
28
|
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
-
import {
|
|
3
|
-
IsIn,
|
|
4
|
-
IsInt,
|
|
5
|
-
IsISO8601,
|
|
6
|
-
IsNotEmpty,
|
|
7
|
-
IsObject,
|
|
8
|
-
IsOptional,
|
|
9
|
-
IsString,
|
|
10
|
-
IsUUID,
|
|
11
|
-
Max,
|
|
12
|
-
MaxLength,
|
|
13
|
-
Min,
|
|
14
|
-
MinLength,
|
|
15
|
-
} from 'class-validator';
|
|
16
|
-
|
|
17
|
-
/** Outcome of the audited HTTP mutation. */
|
|
18
|
-
export const TENANT_AUDIT_OUTCOMES = ['success', 'failure'] as const;
|
|
19
|
-
export type TenantAuditOutcome = (typeof TENANT_AUDIT_OUTCOMES)[number];
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Payload for persisting a tenant-scoped user action audit row (HTTP mutation).
|
|
23
|
-
*/
|
|
24
|
-
export class CreateTenantAuditLogDto {
|
|
25
|
-
@ApiProperty({ description: 'Unique id for idempotent insert', format: 'uuid' })
|
|
26
|
-
@IsUUID()
|
|
27
|
-
eventId: string;
|
|
28
|
-
|
|
29
|
-
@ApiProperty({ description: 'Tenant slug' })
|
|
30
|
-
@IsString()
|
|
31
|
-
@IsNotEmpty()
|
|
32
|
-
@MinLength(1)
|
|
33
|
-
@MaxLength(255)
|
|
34
|
-
tenant: string;
|
|
35
|
-
|
|
36
|
-
@ApiProperty({ example: 'user', description: 'Actor category' })
|
|
37
|
-
@IsString()
|
|
38
|
-
@IsNotEmpty()
|
|
39
|
-
@MaxLength(64)
|
|
40
|
-
actorType: string;
|
|
41
|
-
|
|
42
|
-
@ApiProperty({ description: 'Authenticated user id (sub)' })
|
|
43
|
-
@IsString()
|
|
44
|
-
@IsNotEmpty()
|
|
45
|
-
@MaxLength(255)
|
|
46
|
-
actorId: string;
|
|
47
|
-
|
|
48
|
-
@ApiProperty({
|
|
49
|
-
example: 'http.mutation',
|
|
50
|
-
description: 'Deterministic action key (e.g. http.mutation)',
|
|
51
|
-
})
|
|
52
|
-
@IsString()
|
|
53
|
-
@IsNotEmpty()
|
|
54
|
-
@MaxLength(128)
|
|
55
|
-
action: string;
|
|
56
|
-
|
|
57
|
-
@ApiPropertyOptional({ example: 'http' })
|
|
58
|
-
@IsOptional()
|
|
59
|
-
@IsString()
|
|
60
|
-
@MaxLength(64)
|
|
61
|
-
targetType?: string | null;
|
|
62
|
-
|
|
63
|
-
@ApiPropertyOptional({ description: 'Route param id when present' })
|
|
64
|
-
@IsOptional()
|
|
65
|
-
@IsString()
|
|
66
|
-
@MaxLength(255)
|
|
67
|
-
targetId?: string | null;
|
|
68
|
-
|
|
69
|
-
@ApiProperty({ enum: TENANT_AUDIT_OUTCOMES })
|
|
70
|
-
@IsIn([...TENANT_AUDIT_OUTCOMES])
|
|
71
|
-
outcome: TenantAuditOutcome;
|
|
72
|
-
|
|
73
|
-
@ApiProperty({ example: 'POST' })
|
|
74
|
-
@IsString()
|
|
75
|
-
@IsNotEmpty()
|
|
76
|
-
@MaxLength(16)
|
|
77
|
-
httpMethod: string;
|
|
78
|
-
|
|
79
|
-
@ApiProperty({ example: '/patients/123' })
|
|
80
|
-
@IsString()
|
|
81
|
-
@IsNotEmpty()
|
|
82
|
-
@MaxLength(2048)
|
|
83
|
-
httpPath: string;
|
|
84
|
-
|
|
85
|
-
@ApiProperty({ example: 201 })
|
|
86
|
-
@IsInt()
|
|
87
|
-
@Min(100)
|
|
88
|
-
@Max(599)
|
|
89
|
-
httpStatus: number;
|
|
90
|
-
|
|
91
|
-
@ApiPropertyOptional({ description: 'On failure, stable error code if available' })
|
|
92
|
-
@IsOptional()
|
|
93
|
-
@IsString()
|
|
94
|
-
@MaxLength(128)
|
|
95
|
-
errorCode?: string | null;
|
|
96
|
-
|
|
97
|
-
@ApiPropertyOptional({ description: 'Minimal non-PII context' })
|
|
98
|
-
@IsOptional()
|
|
99
|
-
@IsObject()
|
|
100
|
-
metadata?: Record<string, unknown> | null;
|
|
101
|
-
|
|
102
|
-
@ApiProperty({ description: 'When the action completed (UTC ISO-8601)' })
|
|
103
|
-
@IsISO8601()
|
|
104
|
-
occurredAt: string;
|
|
105
|
-
}
|