@dismissible/nestjs-dismissible 0.0.2-canary.8976e84.0 → 0.0.2-canary.d2f56d7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -67
- package/package.json +4 -4
- package/src/api/dismissible-item-response.dto.ts +0 -8
- package/src/api/dismissible-item.mapper.spec.ts +0 -12
- package/src/api/dismissible-item.mapper.ts +2 -8
- package/src/api/index.ts +3 -0
- package/src/api/use-cases/dismiss/dismiss.controller.spec.ts +1 -2
- package/src/api/use-cases/dismiss/dismiss.controller.ts +8 -8
- package/src/api/use-cases/get-or-create/get-or-create.controller.spec.ts +2 -42
- package/src/api/use-cases/get-or-create/get-or-create.controller.ts +10 -56
- package/src/api/use-cases/get-or-create/index.ts +0 -1
- package/src/api/use-cases/restore/restore.controller.spec.ts +1 -2
- package/src/api/use-cases/restore/restore.controller.ts +8 -8
- package/src/api/validation/index.ts +2 -0
- package/src/api/validation/param-validation.pipe.spec.ts +317 -0
- package/src/api/validation/param-validation.pipe.ts +42 -0
- package/src/api/validation/param.decorators.ts +32 -0
- package/src/core/dismissible-core.service.spec.ts +75 -29
- package/src/core/dismissible-core.service.ts +40 -24
- package/src/core/dismissible.service.spec.ts +111 -25
- package/src/core/dismissible.service.ts +115 -49
- package/src/core/hook-runner.service.spec.ts +486 -53
- package/src/core/hook-runner.service.ts +144 -18
- package/src/core/index.ts +0 -1
- package/src/core/lifecycle-hook.interface.ts +56 -10
- package/src/core/service-responses.interface.ts +9 -9
- package/src/dismissible.module.integration.spec.ts +685 -0
- package/src/dismissible.module.ts +6 -10
- package/src/events/dismissible.events.ts +16 -39
- package/src/index.ts +1 -0
- package/src/request/request-context.decorator.ts +1 -0
- package/src/request/request-context.interface.ts +6 -0
- package/src/response/http-exception-filter.spec.ts +213 -0
- package/src/response/http-exception-filter.ts +3 -3
- package/src/testing/factories.ts +5 -8
- package/src/utils/dismissible.helper.ts +2 -2
- package/src/validation/dismissible-input.dto.ts +47 -0
- package/src/validation/index.ts +1 -0
- package/tsconfig.json +3 -0
- package/tsconfig.spec.json +12 -0
- package/src/api/use-cases/get-or-create/get-or-create.request.dto.ts +0 -17
- package/src/core/create-options.ts +0 -9
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
IHookResult,
|
|
6
6
|
} from './lifecycle-hook.interface';
|
|
7
7
|
import { DISMISSIBLE_LOGGER, IDismissibleLogger } from '@dismissible/nestjs-logger';
|
|
8
|
-
import {
|
|
8
|
+
import { DismissibleItemDto } from '@dismissible/nestjs-dismissible-item';
|
|
9
9
|
import { IRequestContext } from '../request/request-context.interface';
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -32,13 +32,13 @@ export interface IHookRunResult {
|
|
|
32
32
|
* Service responsible for running lifecycle hooks.
|
|
33
33
|
*/
|
|
34
34
|
@Injectable()
|
|
35
|
-
export class HookRunner
|
|
36
|
-
private readonly sortedHooks: IDismissibleLifecycleHook
|
|
35
|
+
export class HookRunner {
|
|
36
|
+
private readonly sortedHooks: IDismissibleLifecycleHook[];
|
|
37
37
|
|
|
38
38
|
constructor(
|
|
39
39
|
@Optional()
|
|
40
40
|
@Inject(DISMISSIBLE_HOOKS)
|
|
41
|
-
hooks: IDismissibleLifecycleHook
|
|
41
|
+
hooks: IDismissibleLifecycleHook[] = [],
|
|
42
42
|
@Inject(DISMISSIBLE_LOGGER)
|
|
43
43
|
private readonly logger: IDismissibleLogger,
|
|
44
44
|
) {
|
|
@@ -46,29 +46,68 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
|
|
|
46
46
|
this.sortedHooks = [...hooks].sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
// ─────────────────────────────────────────────────────────────────
|
|
50
|
+
// Global Request Hooks
|
|
51
|
+
// ─────────────────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Run pre-request hooks (global - runs at start of any operation).
|
|
55
|
+
* Use for authentication, rate limiting, request validation.
|
|
56
|
+
*/
|
|
57
|
+
async runPreRequest(
|
|
58
|
+
itemId: string,
|
|
59
|
+
userId: string,
|
|
60
|
+
context?: IRequestContext,
|
|
61
|
+
): Promise<IHookRunResult> {
|
|
62
|
+
return this.runPreHooks('onBeforeRequest', itemId, userId, context);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Run post-request hooks (global - runs at end of any operation).
|
|
67
|
+
* Use for audit logging, metrics, cleanup.
|
|
68
|
+
*/
|
|
69
|
+
async runPostRequest(
|
|
70
|
+
itemId: string,
|
|
71
|
+
item: DismissibleItemDto,
|
|
72
|
+
userId: string,
|
|
73
|
+
context?: IRequestContext,
|
|
74
|
+
): Promise<void> {
|
|
75
|
+
await this.runPostHooks('onAfterRequest', itemId, item, userId, context);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ─────────────────────────────────────────────────────────────────
|
|
79
|
+
// Get Hooks
|
|
80
|
+
// ─────────────────────────────────────────────────────────────────
|
|
81
|
+
|
|
49
82
|
/**
|
|
50
|
-
* Run pre-
|
|
83
|
+
* Run pre-get hooks (when item exists and is about to be returned).
|
|
84
|
+
* Receives the item for access control based on item state.
|
|
51
85
|
*/
|
|
52
|
-
async
|
|
86
|
+
async runPreGet(
|
|
53
87
|
itemId: string,
|
|
88
|
+
item: DismissibleItemDto,
|
|
54
89
|
userId: string,
|
|
55
90
|
context?: IRequestContext,
|
|
56
91
|
): Promise<IHookRunResult> {
|
|
57
|
-
return this.
|
|
92
|
+
return this.runPreHooksWithItem('onBeforeGet', itemId, item, userId, context);
|
|
58
93
|
}
|
|
59
94
|
|
|
60
95
|
/**
|
|
61
|
-
* Run post-
|
|
96
|
+
* Run post-get hooks (after item is returned).
|
|
62
97
|
*/
|
|
63
|
-
async
|
|
98
|
+
async runPostGet(
|
|
64
99
|
itemId: string,
|
|
65
|
-
item: DismissibleItemDto
|
|
100
|
+
item: DismissibleItemDto,
|
|
66
101
|
userId: string,
|
|
67
102
|
context?: IRequestContext,
|
|
68
103
|
): Promise<void> {
|
|
69
|
-
await this.runPostHooks('
|
|
104
|
+
await this.runPostHooks('onAfterGet', itemId, item, userId, context);
|
|
70
105
|
}
|
|
71
106
|
|
|
107
|
+
// ─────────────────────────────────────────────────────────────────
|
|
108
|
+
// Create Hooks
|
|
109
|
+
// ─────────────────────────────────────────────────────────────────
|
|
110
|
+
|
|
72
111
|
/**
|
|
73
112
|
* Run pre-create hooks.
|
|
74
113
|
*/
|
|
@@ -85,13 +124,17 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
|
|
|
85
124
|
*/
|
|
86
125
|
async runPostCreate(
|
|
87
126
|
itemId: string,
|
|
88
|
-
item: DismissibleItemDto
|
|
127
|
+
item: DismissibleItemDto,
|
|
89
128
|
userId: string,
|
|
90
129
|
context?: IRequestContext,
|
|
91
130
|
): Promise<void> {
|
|
92
131
|
await this.runPostHooks('onAfterCreate', itemId, item, userId, context);
|
|
93
132
|
}
|
|
94
133
|
|
|
134
|
+
// ─────────────────────────────────────────────────────────────────
|
|
135
|
+
// Dismiss Hooks
|
|
136
|
+
// ─────────────────────────────────────────────────────────────────
|
|
137
|
+
|
|
95
138
|
/**
|
|
96
139
|
* Run pre-dismiss hooks.
|
|
97
140
|
*/
|
|
@@ -108,13 +151,17 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
|
|
|
108
151
|
*/
|
|
109
152
|
async runPostDismiss(
|
|
110
153
|
itemId: string,
|
|
111
|
-
item: DismissibleItemDto
|
|
154
|
+
item: DismissibleItemDto,
|
|
112
155
|
userId: string,
|
|
113
156
|
context?: IRequestContext,
|
|
114
157
|
): Promise<void> {
|
|
115
158
|
await this.runPostHooks('onAfterDismiss', itemId, item, userId, context);
|
|
116
159
|
}
|
|
117
160
|
|
|
161
|
+
// ─────────────────────────────────────────────────────────────────
|
|
162
|
+
// Restore Hooks
|
|
163
|
+
// ─────────────────────────────────────────────────────────────────
|
|
164
|
+
|
|
118
165
|
/**
|
|
119
166
|
* Run pre-restore hooks.
|
|
120
167
|
*/
|
|
@@ -131,7 +178,7 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
|
|
|
131
178
|
*/
|
|
132
179
|
async runPostRestore(
|
|
133
180
|
itemId: string,
|
|
134
|
-
item: DismissibleItemDto
|
|
181
|
+
item: DismissibleItemDto,
|
|
135
182
|
userId: string,
|
|
136
183
|
context?: IRequestContext,
|
|
137
184
|
): Promise<void> {
|
|
@@ -142,7 +189,7 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
|
|
|
142
189
|
* Internal method to run pre-hooks.
|
|
143
190
|
*/
|
|
144
191
|
private async runPreHooks(
|
|
145
|
-
hookName: keyof IDismissibleLifecycleHook
|
|
192
|
+
hookName: keyof IDismissibleLifecycleHook,
|
|
146
193
|
itemId: string,
|
|
147
194
|
userId: string,
|
|
148
195
|
context?: IRequestContext,
|
|
@@ -214,14 +261,93 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
|
|
|
214
261
|
};
|
|
215
262
|
}
|
|
216
263
|
|
|
264
|
+
/**
|
|
265
|
+
* Internal method to run pre-hooks that receive the item (e.g., onBeforeGet).
|
|
266
|
+
* Unlike standard pre-hooks, these receive the item for inspection/access control.
|
|
267
|
+
*/
|
|
268
|
+
private async runPreHooksWithItem(
|
|
269
|
+
hookName: keyof IDismissibleLifecycleHook,
|
|
270
|
+
itemId: string,
|
|
271
|
+
item: DismissibleItemDto,
|
|
272
|
+
userId: string,
|
|
273
|
+
context?: IRequestContext,
|
|
274
|
+
): Promise<IHookRunResult> {
|
|
275
|
+
let currentId = itemId;
|
|
276
|
+
let currentUserId = userId;
|
|
277
|
+
let currentContext = context ? { ...context } : undefined;
|
|
278
|
+
|
|
279
|
+
for (const hook of this.sortedHooks) {
|
|
280
|
+
const hookFn = hook[hookName] as
|
|
281
|
+
| ((
|
|
282
|
+
itemId: string,
|
|
283
|
+
item: DismissibleItemDto,
|
|
284
|
+
userId: string,
|
|
285
|
+
context?: IRequestContext,
|
|
286
|
+
) => Promise<IHookResult> | IHookResult)
|
|
287
|
+
| undefined;
|
|
288
|
+
|
|
289
|
+
if (hookFn) {
|
|
290
|
+
try {
|
|
291
|
+
const result = await hookFn.call(hook, currentId, item, currentUserId, currentContext);
|
|
292
|
+
|
|
293
|
+
if (!result.proceed) {
|
|
294
|
+
this.logger.debug(`Hook ${hook.constructor.name}.${hookName} blocked operation`, {
|
|
295
|
+
itemId: currentId,
|
|
296
|
+
userId: currentUserId,
|
|
297
|
+
reason: result.reason,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
proceed: false,
|
|
302
|
+
id: currentId,
|
|
303
|
+
userId: currentUserId,
|
|
304
|
+
context: currentContext,
|
|
305
|
+
reason: result.reason,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Apply mutations if present
|
|
310
|
+
if (result.mutations) {
|
|
311
|
+
if (result.mutations.id !== undefined) {
|
|
312
|
+
currentId = result.mutations.id;
|
|
313
|
+
}
|
|
314
|
+
if (result.mutations.userId !== undefined) {
|
|
315
|
+
currentUserId = result.mutations.userId;
|
|
316
|
+
}
|
|
317
|
+
if (result.mutations.context && currentContext) {
|
|
318
|
+
currentContext = { ...currentContext, ...result.mutations.context };
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
} catch (error) {
|
|
322
|
+
this.logger.error(
|
|
323
|
+
`Error in hook ${hook.constructor.name}.${hookName}`,
|
|
324
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
325
|
+
{
|
|
326
|
+
itemId: currentId,
|
|
327
|
+
userId: currentUserId,
|
|
328
|
+
},
|
|
329
|
+
);
|
|
330
|
+
throw error;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return {
|
|
336
|
+
proceed: true,
|
|
337
|
+
id: currentId,
|
|
338
|
+
userId: currentUserId,
|
|
339
|
+
context: currentContext,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
217
343
|
/**
|
|
218
344
|
* Internal method to run post-hooks.
|
|
219
345
|
* Post-hooks run in reverse priority order.
|
|
220
346
|
*/
|
|
221
347
|
private async runPostHooks(
|
|
222
|
-
hookName: keyof IDismissibleLifecycleHook
|
|
348
|
+
hookName: keyof IDismissibleLifecycleHook,
|
|
223
349
|
itemId: string,
|
|
224
|
-
item: DismissibleItemDto
|
|
350
|
+
item: DismissibleItemDto,
|
|
225
351
|
userId: string,
|
|
226
352
|
context?: IRequestContext,
|
|
227
353
|
): Promise<void> {
|
|
@@ -232,7 +358,7 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
|
|
|
232
358
|
const hookFn = hook[hookName] as
|
|
233
359
|
| ((
|
|
234
360
|
itemId: string,
|
|
235
|
-
item: DismissibleItemDto
|
|
361
|
+
item: DismissibleItemDto,
|
|
236
362
|
userId: string,
|
|
237
363
|
context?: IRequestContext,
|
|
238
364
|
) => Promise<void> | void)
|
package/src/core/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DismissibleItemDto } from '@dismissible/nestjs-dismissible-item';
|
|
2
2
|
import { IRequestContext } from '../request/request-context.interface';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -37,34 +37,72 @@ export interface IHookResult {
|
|
|
37
37
|
/**
|
|
38
38
|
* Interface for lifecycle hooks that can intercept dismissible operations.
|
|
39
39
|
*/
|
|
40
|
-
export interface IDismissibleLifecycleHook
|
|
40
|
+
export interface IDismissibleLifecycleHook {
|
|
41
41
|
/**
|
|
42
42
|
* Priority for hook execution (lower numbers run first).
|
|
43
43
|
* Default is 0.
|
|
44
44
|
*/
|
|
45
45
|
readonly priority?: number;
|
|
46
46
|
|
|
47
|
+
// ─────────────────────────────────────────────────────────────────
|
|
48
|
+
// Global Request Hooks (run on ALL operations)
|
|
49
|
+
// ─────────────────────────────────────────────────────────────────
|
|
50
|
+
|
|
47
51
|
/**
|
|
48
|
-
* Called
|
|
52
|
+
* Called at the start of any operation (getOrCreate, dismiss, restore).
|
|
53
|
+
* Use for global concerns like authentication, rate limiting, request validation.
|
|
49
54
|
*/
|
|
50
|
-
|
|
55
|
+
onBeforeRequest?(
|
|
51
56
|
itemId: string,
|
|
52
57
|
userId: string,
|
|
53
58
|
context?: IRequestContext,
|
|
54
59
|
): Promise<IHookResult> | IHookResult;
|
|
55
60
|
|
|
56
61
|
/**
|
|
57
|
-
* Called
|
|
62
|
+
* Called at the end of any operation (getOrCreate, dismiss, restore).
|
|
63
|
+
* Use for global concerns like audit logging, metrics, cleanup.
|
|
58
64
|
*/
|
|
59
|
-
|
|
65
|
+
onAfterRequest?(
|
|
60
66
|
itemId: string,
|
|
61
|
-
item: DismissibleItemDto
|
|
67
|
+
item: DismissibleItemDto,
|
|
62
68
|
userId: string,
|
|
63
69
|
context?: IRequestContext,
|
|
64
70
|
): Promise<void> | void;
|
|
65
71
|
|
|
72
|
+
// ─────────────────────────────────────────────────────────────────
|
|
73
|
+
// Get Hooks (when retrieving existing item)
|
|
74
|
+
// ─────────────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Called before returning an existing item.
|
|
78
|
+
* Only called when item exists in storage.
|
|
79
|
+
* Use for access control based on item state (e.g., block dismissed items).
|
|
80
|
+
*/
|
|
81
|
+
onBeforeGet?(
|
|
82
|
+
itemId: string,
|
|
83
|
+
item: DismissibleItemDto,
|
|
84
|
+
userId: string,
|
|
85
|
+
context?: IRequestContext,
|
|
86
|
+
): Promise<IHookResult> | IHookResult;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Called after returning an existing item.
|
|
90
|
+
* Only called when item exists in storage.
|
|
91
|
+
*/
|
|
92
|
+
onAfterGet?(
|
|
93
|
+
itemId: string,
|
|
94
|
+
item: DismissibleItemDto,
|
|
95
|
+
userId: string,
|
|
96
|
+
context?: IRequestContext,
|
|
97
|
+
): Promise<void> | void;
|
|
98
|
+
|
|
99
|
+
// ─────────────────────────────────────────────────────────────────
|
|
100
|
+
// Create Hooks (when creating new item)
|
|
101
|
+
// ─────────────────────────────────────────────────────────────────
|
|
102
|
+
|
|
66
103
|
/**
|
|
67
104
|
* Called before creating a new item.
|
|
105
|
+
* Use for plan limits, quota checks, etc.
|
|
68
106
|
*/
|
|
69
107
|
onBeforeCreate?(
|
|
70
108
|
itemId: string,
|
|
@@ -77,11 +115,15 @@ export interface IDismissibleLifecycleHook<TMetadata extends BaseMetadata = Base
|
|
|
77
115
|
*/
|
|
78
116
|
onAfterCreate?(
|
|
79
117
|
itemId: string,
|
|
80
|
-
item: DismissibleItemDto
|
|
118
|
+
item: DismissibleItemDto,
|
|
81
119
|
userId: string,
|
|
82
120
|
context?: IRequestContext,
|
|
83
121
|
): Promise<void> | void;
|
|
84
122
|
|
|
123
|
+
// ─────────────────────────────────────────────────────────────────
|
|
124
|
+
// Dismiss Hooks
|
|
125
|
+
// ─────────────────────────────────────────────────────────────────
|
|
126
|
+
|
|
85
127
|
/**
|
|
86
128
|
* Called before dismissing an item.
|
|
87
129
|
*/
|
|
@@ -96,11 +138,15 @@ export interface IDismissibleLifecycleHook<TMetadata extends BaseMetadata = Base
|
|
|
96
138
|
*/
|
|
97
139
|
onAfterDismiss?(
|
|
98
140
|
itemId: string,
|
|
99
|
-
item: DismissibleItemDto
|
|
141
|
+
item: DismissibleItemDto,
|
|
100
142
|
userId: string,
|
|
101
143
|
context?: IRequestContext,
|
|
102
144
|
): Promise<void> | void;
|
|
103
145
|
|
|
146
|
+
// ─────────────────────────────────────────────────────────────────
|
|
147
|
+
// Restore Hooks
|
|
148
|
+
// ─────────────────────────────────────────────────────────────────
|
|
149
|
+
|
|
104
150
|
/**
|
|
105
151
|
* Called before restoring an item.
|
|
106
152
|
*/
|
|
@@ -115,7 +161,7 @@ export interface IDismissibleLifecycleHook<TMetadata extends BaseMetadata = Base
|
|
|
115
161
|
*/
|
|
116
162
|
onAfterRestore?(
|
|
117
163
|
itemId: string,
|
|
118
|
-
item: DismissibleItemDto
|
|
164
|
+
item: DismissibleItemDto,
|
|
119
165
|
userId: string,
|
|
120
166
|
context?: IRequestContext,
|
|
121
167
|
): Promise<void> | void;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DismissibleItemDto } from '@dismissible/nestjs-dismissible-item';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Response from getOrCreate operation.
|
|
5
5
|
*/
|
|
6
|
-
export interface IGetOrCreateServiceResponse
|
|
6
|
+
export interface IGetOrCreateServiceResponse {
|
|
7
7
|
/** The item (either retrieved or created) */
|
|
8
|
-
item: DismissibleItemDto
|
|
8
|
+
item: DismissibleItemDto;
|
|
9
9
|
|
|
10
10
|
/** Whether the item was newly created */
|
|
11
11
|
created: boolean;
|
|
@@ -14,21 +14,21 @@ export interface IGetOrCreateServiceResponse<TMetadata extends BaseMetadata = Ba
|
|
|
14
14
|
/**
|
|
15
15
|
* Response from dismiss operation.
|
|
16
16
|
*/
|
|
17
|
-
export interface IDismissServiceResponse
|
|
17
|
+
export interface IDismissServiceResponse {
|
|
18
18
|
/** The dismissed item */
|
|
19
|
-
item: DismissibleItemDto
|
|
19
|
+
item: DismissibleItemDto;
|
|
20
20
|
|
|
21
21
|
/** The item state before dismissal */
|
|
22
|
-
previousItem: DismissibleItemDto
|
|
22
|
+
previousItem: DismissibleItemDto;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Response from restore operation.
|
|
27
27
|
*/
|
|
28
|
-
export interface IRestoreServiceResponse
|
|
28
|
+
export interface IRestoreServiceResponse {
|
|
29
29
|
/** The restored item */
|
|
30
|
-
item: DismissibleItemDto
|
|
30
|
+
item: DismissibleItemDto;
|
|
31
31
|
|
|
32
32
|
/** The item state before restoration */
|
|
33
|
-
previousItem: DismissibleItemDto
|
|
33
|
+
previousItem: DismissibleItemDto;
|
|
34
34
|
}
|