@dismissible/nestjs-core 2.0.2-alpha.99ffc23.0 → 2.0.2-canary.2913ba8.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 +424 -232
- package/package.json +7 -7
- package/src/api/dismissible-item.mapper.d.ts +2 -1
- package/src/api/dismissible-item.mapper.interface.d.ts +17 -0
- package/src/api/dismissible-item.mapper.interface.js +8 -0
- package/src/api/dismissible-item.mapper.interface.js.map +1 -0
- package/src/api/dismissible-item.mapper.js.map +1 -1
- package/src/api/index.d.ts +1 -0
- package/src/api/index.js +1 -0
- package/src/api/index.js.map +1 -1
- package/src/api/use-cases/dismiss/dismiss.controller.d.ts +4 -4
- package/src/api/use-cases/dismiss/dismiss.controller.js +7 -6
- package/src/api/use-cases/dismiss/dismiss.controller.js.map +1 -1
- package/src/api/use-cases/get-or-create/get-or-create.controller.d.ts +4 -4
- package/src/api/use-cases/get-or-create/get-or-create.controller.js +7 -6
- package/src/api/use-cases/get-or-create/get-or-create.controller.js.map +1 -1
- package/src/api/use-cases/restore/restore.controller.d.ts +4 -4
- package/src/api/use-cases/restore/restore.controller.js +7 -6
- package/src/api/use-cases/restore/restore.controller.js.map +1 -1
- package/src/core/dismissible-core.service.d.ts +6 -5
- package/src/core/dismissible-core.service.interface.d.ts +48 -0
- package/src/core/dismissible-core.service.interface.js +8 -0
- package/src/core/dismissible-core.service.interface.js.map +1 -0
- package/src/core/dismissible-core.service.js +6 -5
- package/src/core/dismissible-core.service.js.map +1 -1
- package/src/core/dismissible.service.d.ts +5 -4
- package/src/core/dismissible.service.interface.d.ts +33 -0
- package/src/core/dismissible.service.interface.js +8 -0
- package/src/core/dismissible.service.interface.js.map +1 -0
- package/src/core/dismissible.service.js +5 -4
- package/src/core/dismissible.service.js.map +1 -1
- package/src/core/hook-runner.interface.d.ts +56 -0
- package/src/core/hook-runner.interface.js +8 -0
- package/src/core/hook-runner.interface.js.map +1 -0
- package/src/core/hook-runner.service.d.ts +2 -1
- package/src/core/hook-runner.service.js.map +1 -1
- package/src/core/index.d.ts +3 -0
- package/src/core/index.js +3 -0
- package/src/core/index.js.map +1 -1
- package/src/dismissible.module.js +31 -1
- package/src/dismissible.module.js.map +1 -1
- package/src/response/index.d.ts +1 -0
- package/src/response/index.js +1 -0
- package/src/response/index.js.map +1 -1
- package/src/response/response.service.d.ts +2 -1
- package/src/response/response.service.interface.d.ts +19 -0
- package/src/response/response.service.interface.js +8 -0
- package/src/response/response.service.interface.js.map +1 -0
- package/src/response/response.service.js.map +1 -1
- package/src/utils/date/date.service.d.ts +2 -1
- package/src/utils/date/date.service.interface.d.ts +21 -0
- package/src/utils/date/date.service.interface.js +8 -0
- package/src/utils/date/date.service.interface.js.map +1 -0
- package/src/utils/date/date.service.js.map +1 -1
- package/src/utils/date/index.d.ts +1 -0
- package/src/utils/date/index.js +1 -0
- package/src/utils/date/index.js.map +1 -1
- package/src/utils/dismissible.helper.d.ts +2 -1
- package/src/utils/dismissible.helper.interface.d.ts +14 -0
- package/src/utils/dismissible.helper.interface.js +8 -0
- package/src/utils/dismissible.helper.interface.js.map +1 -0
- package/src/utils/dismissible.helper.js.map +1 -1
- package/src/utils/index.d.ts +1 -0
- package/src/utils/index.js +1 -0
- package/src/utils/index.js.map +1 -1
package/README.md
CHANGED
|
@@ -14,10 +14,28 @@
|
|
|
14
14
|
|
|
15
15
|
# @dismissible/nestjs-core
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
The core NestJS library for managing dismissible state in your applications. This is the central package that ties together all the main Dismissible libraries and contains the domain logic for dismissible items.
|
|
18
|
+
|
|
19
|
+
Perfect for guided tours, user preferences, onboarding flows, and any scenario where you need to track whether a user has dismissed or interacted with specific items.
|
|
18
20
|
|
|
19
21
|
> **Part of the Dismissible API** - This library is part of the [Dismissible API](https://dismissible.io) ecosystem. Visit [dismissible.io](https://dismissible.io) for more information and documentation.
|
|
20
22
|
|
|
23
|
+
## Related Packages
|
|
24
|
+
|
|
25
|
+
This core library integrates with several lower-level packages:
|
|
26
|
+
|
|
27
|
+
| Package | Description |
|
|
28
|
+
| ------------------------------------------------------------------------------------------------------------ | -------------------------------------------- |
|
|
29
|
+
| [@dismissible/nestjs-storage](https://www.npmjs.com/package/@dismissible/nestjs-storage) | Storage interfaces and base module |
|
|
30
|
+
| [@dismissible/nestjs-postgres-storage](https://www.npmjs.com/package/@dismissible/nestjs-postgres-storage) | PostgreSQL storage adapter |
|
|
31
|
+
| [@dismissible/nestjs-dynamodb-storage](https://www.npmjs.com/package/@dismissible/nestjs-dynamodb-storage) | DynamoDB storage adapter |
|
|
32
|
+
| [@dismissible/nestjs-hooks](https://www.npmjs.com/package/@dismissible/nestjs-hooks) | Lifecycle hook interfaces |
|
|
33
|
+
| [@dismissible/nestjs-jwt-auth-hook](https://www.npmjs.com/package/@dismissible/nestjs-jwt-auth-hook) | JWT authentication hook |
|
|
34
|
+
| [@dismissible/nestjs-rate-limiter-hook](https://www.npmjs.com/package/@dismissible/nestjs-rate-limiter-hook) | Rate limiting hook |
|
|
35
|
+
| [@dismissible/nestjs-logger](https://www.npmjs.com/package/@dismissible/nestjs-logger) | Logger interfaces and default implementation |
|
|
36
|
+
| [@dismissible/nestjs-item](https://www.npmjs.com/package/@dismissible/nestjs-item) | Dismissible item DTOs and types |
|
|
37
|
+
| [@dismissible/react-client](https://www.npmjs.com/package/@dismissible/react-client) | React client for frontend integration |
|
|
38
|
+
|
|
21
39
|
## Features
|
|
22
40
|
|
|
23
41
|
- **Simple API** - Easy-to-use service methods for get-or-create, dismiss, and restore operations
|
|
@@ -48,7 +66,7 @@ import { Module } from '@nestjs/common';
|
|
|
48
66
|
import { DismissibleModule } from '@dismissible/nestjs-core';
|
|
49
67
|
|
|
50
68
|
@Module({
|
|
51
|
-
imports: [DismissibleModule.forRoot(
|
|
69
|
+
imports: [DismissibleModule.forRoot()],
|
|
52
70
|
})
|
|
53
71
|
export class AppModule {}
|
|
54
72
|
```
|
|
@@ -113,11 +131,39 @@ function WelcomeBanner() {
|
|
|
113
131
|
|
|
114
132
|
The React client automatically uses the built-in REST API endpoints, so no additional configuration is needed on the backend.
|
|
115
133
|
|
|
116
|
-
##
|
|
134
|
+
## Configuration Options
|
|
135
|
+
|
|
136
|
+
All configuration is done through `DismissibleModule.forRoot()`. The following options are available:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
interface IDismissibleModuleOptions {
|
|
140
|
+
// Custom storage module (defaults to memory storage)
|
|
141
|
+
storage?: Type<any> | DynamicModule;
|
|
142
|
+
|
|
143
|
+
// Custom logger implementation
|
|
144
|
+
logger?: Type<IDismissibleLogger>;
|
|
145
|
+
|
|
146
|
+
// Lifecycle hooks to register
|
|
147
|
+
hooks?: Type<IDismissibleLifecycleHook>[];
|
|
148
|
+
|
|
149
|
+
// Additional modules to import
|
|
150
|
+
imports?: DynamicModule[];
|
|
151
|
+
|
|
152
|
+
// Additional providers to register
|
|
153
|
+
providers?: Provider[];
|
|
154
|
+
|
|
155
|
+
// Custom controllers (overrides default REST API controllers)
|
|
156
|
+
controllers?: Type<any>[];
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
117
161
|
|
|
118
|
-
###
|
|
162
|
+
### `storage`
|
|
119
163
|
|
|
120
|
-
|
|
164
|
+
Specifies a custom storage module for persisting dismissible items. Defaults to in-memory storage.
|
|
165
|
+
|
|
166
|
+
**Default:** In-memory storage (data lost on restart)
|
|
121
167
|
|
|
122
168
|
```typescript
|
|
123
169
|
import { Module } from '@nestjs/common';
|
|
@@ -127,77 +173,100 @@ import { PostgresStorageModule } from '@dismissible/nestjs-postgres-storage';
|
|
|
127
173
|
@Module({
|
|
128
174
|
imports: [
|
|
129
175
|
DismissibleModule.forRoot({
|
|
130
|
-
storage: PostgresStorageModule
|
|
176
|
+
storage: PostgresStorageModule.forRoot({
|
|
177
|
+
connectionString: 'postgresql://user:password@localhost:5432/dismissible',
|
|
178
|
+
}),
|
|
131
179
|
}),
|
|
132
180
|
],
|
|
133
181
|
})
|
|
134
182
|
export class AppModule {}
|
|
135
183
|
```
|
|
136
184
|
|
|
137
|
-
**
|
|
138
|
-
|
|
139
|
-
1. Install the PostgreSQL storage package:
|
|
185
|
+
**Related packages:**
|
|
140
186
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
187
|
+
- [@dismissible/nestjs-storage](https://www.npmjs.com/package/@dismissible/nestjs-storage) - Storage interfaces and base module for implementing custom adapters
|
|
188
|
+
- [@dismissible/nestjs-postgres-storage](https://www.npmjs.com/package/@dismissible/nestjs-postgres-storage) - PostgreSQL storage adapter
|
|
189
|
+
- [@dismissible/nestjs-dynamodb-storage](https://www.npmjs.com/package/@dismissible/nestjs-dynamodb-storage) - DynamoDB storage adapter
|
|
144
190
|
|
|
145
|
-
|
|
191
|
+
---
|
|
146
192
|
|
|
147
|
-
|
|
148
|
-
DISMISSIBLE_STORAGE_POSTGRES_CONNECTION_STRING=postgresql://user:password@localhost:5432/dismissible
|
|
149
|
-
```
|
|
193
|
+
### `logger`
|
|
150
194
|
|
|
151
|
-
|
|
152
|
-
```bash
|
|
153
|
-
npx prisma migrate dev
|
|
154
|
-
```
|
|
195
|
+
Provides a custom logger implementation. The logger must implement the `IDismissibleLogger` interface.
|
|
155
196
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
### Using the Service
|
|
159
|
-
|
|
160
|
-
Instead of using the built-in REST API endpoints, you can inject `DismissibleService` directly into your controllers or other services for more control:
|
|
197
|
+
**Default:** Built-in console logger
|
|
161
198
|
|
|
162
199
|
```typescript
|
|
163
|
-
import {
|
|
164
|
-
import {
|
|
165
|
-
|
|
166
|
-
@Controller('features')
|
|
167
|
-
export class FeaturesController {
|
|
168
|
-
constructor(private readonly dismissibleService: DismissibleService) {}
|
|
200
|
+
import { Injectable, Module } from '@nestjs/common';
|
|
201
|
+
import { DismissibleModule } from '@dismissible/nestjs-core';
|
|
202
|
+
import { IDismissibleLogger } from '@dismissible/nestjs-logger';
|
|
169
203
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
undefined, // optional request context
|
|
176
|
-
);
|
|
204
|
+
@Injectable()
|
|
205
|
+
class CustomLogger implements IDismissibleLogger {
|
|
206
|
+
debug(message: string, context?: any) {
|
|
207
|
+
console.log(`[DEBUG] ${message}`, context);
|
|
208
|
+
}
|
|
177
209
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
wasCreated: result.created,
|
|
181
|
-
};
|
|
210
|
+
info(message: string, context?: any) {
|
|
211
|
+
console.log(`[INFO] ${message}`, context);
|
|
182
212
|
}
|
|
183
213
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const result = await this.dismissibleService.dismiss(itemId, userId);
|
|
187
|
-
return { item: result.item };
|
|
214
|
+
warn(message: string, context?: any) {
|
|
215
|
+
console.warn(`[WARN] ${message}`, context);
|
|
188
216
|
}
|
|
189
217
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const result = await this.dismissibleService.restore(itemId, userId);
|
|
193
|
-
return { item: result.item };
|
|
218
|
+
error(message: string, context?: any) {
|
|
219
|
+
console.error(`[ERROR] ${message}`, context);
|
|
194
220
|
}
|
|
195
221
|
}
|
|
222
|
+
|
|
223
|
+
@Module({
|
|
224
|
+
imports: [
|
|
225
|
+
DismissibleModule.forRoot({
|
|
226
|
+
logger: CustomLogger,
|
|
227
|
+
}),
|
|
228
|
+
],
|
|
229
|
+
})
|
|
230
|
+
export class AppModule {}
|
|
196
231
|
```
|
|
197
232
|
|
|
198
|
-
|
|
233
|
+
**Related packages:**
|
|
234
|
+
|
|
235
|
+
- [@dismissible/nestjs-logger](https://www.npmjs.com/package/@dismissible/nestjs-logger) - Logger interfaces and default implementation
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
### `hooks`
|
|
240
|
+
|
|
241
|
+
Registers lifecycle hooks that intercept operations. Hooks can block operations, mutate parameters, or perform side effects.
|
|
242
|
+
|
|
243
|
+
**Default:** No hooks
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import { Injectable, Module } from '@nestjs/common';
|
|
247
|
+
import { DismissibleModule } from '@dismissible/nestjs-core';
|
|
248
|
+
import { IDismissibleLifecycleHook, IHookResult } from '@dismissible/nestjs-hooks';
|
|
249
|
+
|
|
250
|
+
@Injectable()
|
|
251
|
+
class AuditHook implements IDismissibleLifecycleHook {
|
|
252
|
+
readonly priority = 10; // Lower runs first
|
|
253
|
+
|
|
254
|
+
async onAfterDismiss(itemId: string, userId: string): Promise<void> {
|
|
255
|
+
console.log(`User ${userId} dismissed ${itemId}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
@Module({
|
|
260
|
+
imports: [
|
|
261
|
+
DismissibleModule.forRoot({
|
|
262
|
+
hooks: [AuditHook],
|
|
263
|
+
}),
|
|
264
|
+
],
|
|
265
|
+
})
|
|
266
|
+
export class AppModule {}
|
|
267
|
+
```
|
|
199
268
|
|
|
200
|
-
|
|
269
|
+
For JWT authentication, use the [@dismissible/nestjs-jwt-auth-hook](https://www.npmjs.com/package/@dismissible/nestjs-jwt-auth-hook) package:
|
|
201
270
|
|
|
202
271
|
```typescript
|
|
203
272
|
import { Module } from '@nestjs/common';
|
|
@@ -220,83 +289,160 @@ import { JwtAuthHookModule, JwtAuthHook } from '@dismissible/nestjs-jwt-auth-hoo
|
|
|
220
289
|
export class AppModule {}
|
|
221
290
|
```
|
|
222
291
|
|
|
223
|
-
|
|
292
|
+
**Related packages:**
|
|
293
|
+
|
|
294
|
+
- [@dismissible/nestjs-hooks](https://www.npmjs.com/package/@dismissible/nestjs-hooks) - Hook interfaces and types for implementing custom lifecycle hooks
|
|
295
|
+
- [@dismissible/nestjs-jwt-auth-hook](https://www.npmjs.com/package/@dismissible/nestjs-jwt-auth-hook) - JWT authentication hook for OIDC providers
|
|
296
|
+
- [@dismissible/nestjs-rate-limiter-hook](https://www.npmjs.com/package/@dismissible/nestjs-rate-limiter-hook) - Rate limiting hook
|
|
224
297
|
|
|
225
|
-
|
|
298
|
+
---
|
|
226
299
|
|
|
227
|
-
|
|
300
|
+
### `imports`
|
|
301
|
+
|
|
302
|
+
Adds additional modules to the DismissibleModule's imports. Useful for injecting dependencies that your hooks or providers need.
|
|
303
|
+
|
|
304
|
+
**Default:** None
|
|
228
305
|
|
|
229
306
|
```typescript
|
|
230
|
-
import {
|
|
231
|
-
import {
|
|
232
|
-
@
|
|
233
|
-
export class AuditHook implements IDismissibleLifecycleHook {
|
|
234
|
-
// Lower priority runs first (default is 0)
|
|
235
|
-
readonly priority = 10;
|
|
307
|
+
import { Module } from '@nestjs/common';
|
|
308
|
+
import { DismissibleModule } from '@dismissible/nestjs-core';
|
|
309
|
+
import { HttpModule } from '@nestjs/axios';
|
|
236
310
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Allow the operation to proceed
|
|
251
|
-
return { proceed: true };
|
|
252
|
-
}
|
|
311
|
+
@Module({
|
|
312
|
+
imports: [
|
|
313
|
+
DismissibleModule.forRoot({
|
|
314
|
+
imports: [HttpModule],
|
|
315
|
+
hooks: [WebhookHook], // Hook that uses HttpService
|
|
316
|
+
}),
|
|
317
|
+
],
|
|
318
|
+
})
|
|
319
|
+
export class AppModule {}
|
|
320
|
+
```
|
|
253
321
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
### `providers`
|
|
325
|
+
|
|
326
|
+
Registers additional providers within the DismissibleModule. Useful for services that your hooks depend on.
|
|
327
|
+
|
|
328
|
+
**Default:** None
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
import { Injectable, Module } from '@nestjs/common';
|
|
332
|
+
import { DismissibleModule, IDismissibleLifecycleHook } from '@dismissible/nestjs-core';
|
|
333
|
+
|
|
334
|
+
@Injectable()
|
|
335
|
+
class AnalyticsService {
|
|
336
|
+
track(event: string, data: any) {
|
|
337
|
+
// Send to analytics
|
|
262
338
|
}
|
|
339
|
+
}
|
|
263
340
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
// Normalize item IDs (e.g., lowercase)
|
|
271
|
-
return {
|
|
272
|
-
proceed: true,
|
|
273
|
-
mutations: {
|
|
274
|
-
id: itemId.toLowerCase(),
|
|
275
|
-
},
|
|
276
|
-
};
|
|
341
|
+
@Injectable()
|
|
342
|
+
class AnalyticsHook implements IDismissibleLifecycleHook {
|
|
343
|
+
constructor(private analytics: AnalyticsService) {}
|
|
344
|
+
|
|
345
|
+
async onAfterDismiss(itemId: string, userId: string): Promise<void> {
|
|
346
|
+
this.analytics.track('item_dismissed', { itemId, userId });
|
|
277
347
|
}
|
|
278
348
|
}
|
|
349
|
+
|
|
350
|
+
@Module({
|
|
351
|
+
imports: [
|
|
352
|
+
DismissibleModule.forRoot({
|
|
353
|
+
providers: [AnalyticsService],
|
|
354
|
+
hooks: [AnalyticsHook],
|
|
355
|
+
}),
|
|
356
|
+
],
|
|
357
|
+
})
|
|
358
|
+
export class AppModule {}
|
|
279
359
|
```
|
|
280
360
|
|
|
281
|
-
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
### `controllers`
|
|
364
|
+
|
|
365
|
+
Overrides the default REST API controllers. Use this when you want complete control over the API endpoints.
|
|
366
|
+
|
|
367
|
+
**Default:** Built-in controllers for get-or-create, dismiss, and restore
|
|
282
368
|
|
|
283
369
|
```typescript
|
|
284
|
-
import {
|
|
285
|
-
import {
|
|
370
|
+
import { Controller, Get, Param, Module } from '@nestjs/common';
|
|
371
|
+
import { DismissibleModule, DismissibleService } from '@dismissible/nestjs-core';
|
|
372
|
+
|
|
373
|
+
@Controller('custom')
|
|
374
|
+
class CustomController {
|
|
375
|
+
constructor(private dismissibleService: DismissibleService) {}
|
|
376
|
+
|
|
377
|
+
@Get(':userId/:itemId')
|
|
378
|
+
async getItem(@Param('userId') userId: string, @Param('itemId') itemId: string) {
|
|
379
|
+
return this.dismissibleService.getOrCreate(itemId, userId);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
286
382
|
|
|
287
383
|
@Module({
|
|
288
384
|
imports: [
|
|
289
385
|
DismissibleModule.forRoot({
|
|
290
|
-
|
|
386
|
+
controllers: [CustomController],
|
|
291
387
|
}),
|
|
292
388
|
],
|
|
293
389
|
})
|
|
294
390
|
export class AppModule {}
|
|
295
391
|
```
|
|
296
392
|
|
|
297
|
-
|
|
393
|
+
To disable the REST API entirely, pass an empty array:
|
|
298
394
|
|
|
299
|
-
|
|
395
|
+
```typescript
|
|
396
|
+
@Module({
|
|
397
|
+
imports: [
|
|
398
|
+
DismissibleModule.forRoot({
|
|
399
|
+
controllers: [],
|
|
400
|
+
}),
|
|
401
|
+
],
|
|
402
|
+
})
|
|
403
|
+
export class AppModule {}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## Using the Service Directly
|
|
409
|
+
|
|
410
|
+
Instead of using the built-in REST API, you can inject `DismissibleService` into your own controllers or services:
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
import { Controller, Get, Param, Delete, Post } from '@nestjs/common';
|
|
414
|
+
import { DismissibleService } from '@dismissible/nestjs-core';
|
|
415
|
+
|
|
416
|
+
@Controller('features')
|
|
417
|
+
export class FeaturesController {
|
|
418
|
+
constructor(private readonly dismissibleService: DismissibleService) {}
|
|
419
|
+
|
|
420
|
+
@Get(':userId/items/:itemId')
|
|
421
|
+
async getOrCreateItem(@Param('userId') userId: string, @Param('itemId') itemId: string) {
|
|
422
|
+
const result = await this.dismissibleService.getOrCreate(itemId, userId);
|
|
423
|
+
return {
|
|
424
|
+
item: result.item,
|
|
425
|
+
wasCreated: result.created,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
@Delete(':userId/items/:itemId')
|
|
430
|
+
async dismissItem(@Param('userId') userId: string, @Param('itemId') itemId: string) {
|
|
431
|
+
const result = await this.dismissibleService.dismiss(itemId, userId);
|
|
432
|
+
return { item: result.item };
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
@Post(':userId/items/:itemId/restore')
|
|
436
|
+
async restoreItem(@Param('userId') userId: string, @Param('itemId') itemId: string) {
|
|
437
|
+
const result = await this.dismissibleService.restore(itemId, userId);
|
|
438
|
+
return { item: result.item };
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
## Events
|
|
444
|
+
|
|
445
|
+
The library emits events for all operations using NestJS's EventEmitter2:
|
|
300
446
|
|
|
301
447
|
```typescript
|
|
302
448
|
import { Injectable } from '@nestjs/common';
|
|
@@ -305,7 +451,6 @@ import {
|
|
|
305
451
|
ItemCreatedEvent,
|
|
306
452
|
ItemDismissedEvent,
|
|
307
453
|
ItemRestoredEvent,
|
|
308
|
-
ItemRetrievedEvent,
|
|
309
454
|
DismissibleEvents,
|
|
310
455
|
} from '@dismissible/nestjs-core';
|
|
311
456
|
|
|
@@ -313,188 +458,235 @@ import {
|
|
|
313
458
|
export class AnalyticsService {
|
|
314
459
|
@OnEvent(DismissibleEvents.ITEM_CREATED)
|
|
315
460
|
handleItemCreated(event: ItemCreatedEvent) {
|
|
316
|
-
|
|
317
|
-
console.log(`Analytics: Item ${event.id} created for user ${event.userId}`);
|
|
461
|
+
console.log(`Item ${event.id} created for user ${event.userId}`);
|
|
318
462
|
}
|
|
319
463
|
|
|
320
464
|
@OnEvent(DismissibleEvents.ITEM_DISMISSED)
|
|
321
465
|
handleItemDismissed(event: ItemDismissedEvent) {
|
|
322
|
-
|
|
323
|
-
console.log(`Analytics: Item ${event.id} dismissed by user ${event.userId}`);
|
|
466
|
+
console.log(`Item ${event.id} dismissed by user ${event.userId}`);
|
|
324
467
|
}
|
|
325
468
|
|
|
326
469
|
@OnEvent(DismissibleEvents.ITEM_RESTORED)
|
|
327
470
|
handleItemRestored(event: ItemRestoredEvent) {
|
|
328
|
-
|
|
329
|
-
console.log(`Analytics: Item ${event.id} restored by user ${event.userId}`);
|
|
471
|
+
console.log(`Item ${event.id} restored by user ${event.userId}`);
|
|
330
472
|
}
|
|
331
473
|
}
|
|
332
474
|
```
|
|
333
475
|
|
|
334
|
-
|
|
476
|
+
Available events:
|
|
477
|
+
|
|
478
|
+
- `DismissibleEvents.ITEM_CREATED` - New item created
|
|
479
|
+
- `DismissibleEvents.ITEM_RETRIEVED` - Existing item retrieved
|
|
480
|
+
- `DismissibleEvents.ITEM_DISMISSED` - Item dismissed
|
|
481
|
+
- `DismissibleEvents.ITEM_RESTORED` - Item restored
|
|
335
482
|
|
|
336
|
-
|
|
483
|
+
## Overriding Services
|
|
484
|
+
|
|
485
|
+
All core services can be overridden using symbol-based dependency injection tokens. This allows you to provide custom implementations while maintaining type safety.
|
|
486
|
+
|
|
487
|
+
### Available Service Tokens
|
|
488
|
+
|
|
489
|
+
| Token | Interface | Description |
|
|
490
|
+
| ------------------------------ | ------------------------- | ----------------------------------------------- |
|
|
491
|
+
| `DISMISSIBLE_SERVICE` | `IDismissibleService` | Main orchestration service |
|
|
492
|
+
| `DISMISSIBLE_CORE_SERVICE` | `IDismissibleCoreService` | Core business logic service |
|
|
493
|
+
| `DISMISSIBLE_HOOK_RUNNER` | `IHookRunner` | Lifecycle hook execution |
|
|
494
|
+
| `DISMISSIBLE_HELPER` | `IDismissibleHelper` | Helper utilities |
|
|
495
|
+
| `DISMISSIBLE_DATE_SERVICE` | `IDateService` | Date operations |
|
|
496
|
+
| `DISMISSIBLE_RESPONSE_SERVICE` | `IResponseService` | HTTP response formatting |
|
|
497
|
+
| `DISMISSIBLE_ITEM_MAPPER` | `IDismissibleItemMapper` | Domain to DTO mapping |
|
|
498
|
+
| `DISMISSIBLE_ITEM_FACTORY` | `IDismissibleItemFactory` | Item creation (from `@dismissible/nestjs-item`) |
|
|
499
|
+
|
|
500
|
+
### Example: Overriding the Date Service
|
|
501
|
+
|
|
502
|
+
Override the date service to control time in tests or add custom behavior:
|
|
337
503
|
|
|
338
504
|
```typescript
|
|
339
|
-
import { Injectable } from '@nestjs/common';
|
|
340
|
-
import {
|
|
341
|
-
|
|
505
|
+
import { Injectable, Module } from '@nestjs/common';
|
|
506
|
+
import {
|
|
507
|
+
DismissibleModule,
|
|
508
|
+
IDateService,
|
|
509
|
+
DISMISSIBLE_DATE_SERVICE,
|
|
510
|
+
} from '@dismissible/nestjs-core';
|
|
342
511
|
|
|
343
512
|
@Injectable()
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
//
|
|
347
|
-
|
|
513
|
+
class CustomDateService implements IDateService {
|
|
514
|
+
getNow(): Date {
|
|
515
|
+
// Custom implementation - e.g., use a fixed time for testing
|
|
516
|
+
return new Date('2024-01-01T00:00:00.000Z');
|
|
348
517
|
}
|
|
349
518
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
warn(message: string, context?: any) {
|
|
355
|
-
console.warn(`[WARN] ${message}`, context);
|
|
519
|
+
parseIso(isoString: string): Date {
|
|
520
|
+
return new Date(isoString);
|
|
356
521
|
}
|
|
357
522
|
|
|
358
|
-
|
|
359
|
-
|
|
523
|
+
toIso(date: Date): string {
|
|
524
|
+
return date.toISOString();
|
|
360
525
|
}
|
|
361
526
|
}
|
|
362
527
|
|
|
363
528
|
@Module({
|
|
364
529
|
imports: [
|
|
365
530
|
DismissibleModule.forRoot({
|
|
366
|
-
|
|
531
|
+
providers: [
|
|
532
|
+
CustomDateService,
|
|
533
|
+
{ provide: DISMISSIBLE_DATE_SERVICE, useExisting: CustomDateService },
|
|
534
|
+
],
|
|
367
535
|
}),
|
|
368
536
|
],
|
|
369
537
|
})
|
|
370
538
|
export class AppModule {}
|
|
371
539
|
```
|
|
372
540
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
### DismissibleService
|
|
376
|
-
|
|
377
|
-
The main service for interacting with dismissible items.
|
|
378
|
-
|
|
379
|
-
#### Methods
|
|
380
|
-
|
|
381
|
-
**`getOrCreate(itemId, userId, context?)`**
|
|
382
|
-
|
|
383
|
-
Retrieves an existing item or creates a new one if it doesn't exist.
|
|
384
|
-
|
|
385
|
-
- `itemId: string` - Unique identifier for the item
|
|
386
|
-
- `userId: string` - User identifier (required)
|
|
387
|
-
- `context?: IRequestContext` - Optional request context for tracing
|
|
541
|
+
### Example: Overriding the Main Service
|
|
388
542
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
**`dismiss(itemId, userId, context?)`**
|
|
392
|
-
|
|
393
|
-
Marks an item as dismissed.
|
|
394
|
-
|
|
395
|
-
- `itemId: string` - Item identifier
|
|
396
|
-
- `userId: string` - User identifier
|
|
397
|
-
- `context?: IRequestContext` - Optional request context
|
|
398
|
-
|
|
399
|
-
Returns: `Promise<IDismissServiceResponse>`
|
|
400
|
-
|
|
401
|
-
**`restore(itemId, userId, context?)`**
|
|
402
|
-
|
|
403
|
-
Restores a previously dismissed item.
|
|
404
|
-
|
|
405
|
-
- `itemId: string` - Item identifier
|
|
406
|
-
- `userId: string` - User identifier
|
|
407
|
-
- `context?: IRequestContext` - Optional request context
|
|
408
|
-
|
|
409
|
-
Returns: `Promise<IRestoreServiceResponse>`
|
|
410
|
-
|
|
411
|
-
### Module Configuration
|
|
543
|
+
Override the main dismissible service for custom orchestration logic:
|
|
412
544
|
|
|
413
545
|
```typescript
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
## Events
|
|
427
|
-
|
|
428
|
-
The library emits the following events:
|
|
429
|
-
|
|
430
|
-
- `DismissibleEvents.ITEM_CREATED` - Emitted when a new item is created
|
|
431
|
-
- `DismissibleEvents.ITEM_RETRIEVED` - Emitted when an existing item is retrieved
|
|
432
|
-
- `DismissibleEvents.ITEM_DISMISSED` - Emitted when an item is dismissed
|
|
433
|
-
- `DismissibleEvents.ITEM_RESTORED` - Emitted when an item is restored
|
|
434
|
-
|
|
435
|
-
All events include:
|
|
436
|
-
|
|
437
|
-
- `id: string` - The item identifier
|
|
438
|
-
- `item: DismissibleItemDto` - The current item state
|
|
439
|
-
- `userId: string` - The user identifier
|
|
440
|
-
- `context?: IRequestContext` - Optional request context
|
|
546
|
+
import { Injectable, Inject, Module } from '@nestjs/common';
|
|
547
|
+
import {
|
|
548
|
+
DismissibleModule,
|
|
549
|
+
IDismissibleService,
|
|
550
|
+
IDismissibleCoreService,
|
|
551
|
+
DISMISSIBLE_SERVICE,
|
|
552
|
+
DISMISSIBLE_CORE_SERVICE,
|
|
553
|
+
IGetOrCreateServiceResponse,
|
|
554
|
+
IDismissServiceResponse,
|
|
555
|
+
IRestoreServiceResponse,
|
|
556
|
+
} from '@dismissible/nestjs-core';
|
|
557
|
+
import { IRequestContext } from '@dismissible/nestjs-request';
|
|
441
558
|
|
|
442
|
-
|
|
559
|
+
@Injectable()
|
|
560
|
+
class CustomDismissibleService implements IDismissibleService {
|
|
561
|
+
constructor(
|
|
562
|
+
@Inject(DISMISSIBLE_CORE_SERVICE)
|
|
563
|
+
private readonly coreService: IDismissibleCoreService,
|
|
564
|
+
) {}
|
|
443
565
|
|
|
444
|
-
|
|
566
|
+
async getOrCreate(
|
|
567
|
+
itemId: string,
|
|
568
|
+
userId: string,
|
|
569
|
+
context?: IRequestContext,
|
|
570
|
+
): Promise<IGetOrCreateServiceResponse> {
|
|
571
|
+
// Add custom logic before/after core operation
|
|
572
|
+
console.log('Custom getOrCreate called');
|
|
573
|
+
return this.coreService.getOrCreate(itemId, userId);
|
|
574
|
+
}
|
|
445
575
|
|
|
446
|
-
|
|
576
|
+
async dismiss(
|
|
577
|
+
itemId: string,
|
|
578
|
+
userId: string,
|
|
579
|
+
context?: IRequestContext,
|
|
580
|
+
): Promise<IDismissServiceResponse> {
|
|
581
|
+
return this.coreService.dismiss(itemId, userId);
|
|
582
|
+
}
|
|
447
583
|
|
|
448
|
-
|
|
584
|
+
async restore(
|
|
585
|
+
itemId: string,
|
|
586
|
+
userId: string,
|
|
587
|
+
context?: IRequestContext,
|
|
588
|
+
): Promise<IRestoreServiceResponse> {
|
|
589
|
+
return this.coreService.restore(itemId, userId);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
449
592
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
593
|
+
@Module({
|
|
594
|
+
imports: [
|
|
595
|
+
DismissibleModule.forRoot({
|
|
596
|
+
providers: [
|
|
597
|
+
CustomDismissibleService,
|
|
598
|
+
{ provide: DISMISSIBLE_SERVICE, useExisting: CustomDismissibleService },
|
|
599
|
+
],
|
|
600
|
+
}),
|
|
601
|
+
],
|
|
602
|
+
})
|
|
603
|
+
export class AppModule {}
|
|
604
|
+
```
|
|
458
605
|
|
|
459
|
-
|
|
606
|
+
### Example: Overriding the Item Factory
|
|
460
607
|
|
|
461
|
-
|
|
462
|
-
- **Mutate parameters** by returning `{ proceed: true, mutations: { id?, userId?, context? } }`
|
|
463
|
-
- **Perform side effects** in post-hooks (no return value needed)
|
|
608
|
+
Override the item factory to customize how items are created:
|
|
464
609
|
|
|
465
|
-
|
|
610
|
+
```typescript
|
|
611
|
+
import { Injectable, Module } from '@nestjs/common';
|
|
612
|
+
import { DismissibleModule } from '@dismissible/nestjs-core';
|
|
613
|
+
import {
|
|
614
|
+
DismissibleItemDto,
|
|
615
|
+
IDismissibleItemFactory,
|
|
616
|
+
ICreateDismissibleItemOptions,
|
|
617
|
+
DISMISSIBLE_ITEM_FACTORY,
|
|
618
|
+
} from '@dismissible/nestjs-item';
|
|
466
619
|
|
|
467
|
-
|
|
620
|
+
@Injectable()
|
|
621
|
+
class CustomItemFactory implements IDismissibleItemFactory {
|
|
622
|
+
create(options: ICreateDismissibleItemOptions): DismissibleItemDto {
|
|
623
|
+
const item = new DismissibleItemDto();
|
|
624
|
+
item.id = options.id;
|
|
625
|
+
item.userId = options.userId;
|
|
626
|
+
item.createdAt = options.createdAt;
|
|
627
|
+
item.dismissedAt = options.dismissedAt;
|
|
628
|
+
return item;
|
|
629
|
+
}
|
|
468
630
|
|
|
469
|
-
|
|
631
|
+
clone(item: DismissibleItemDto): DismissibleItemDto {
|
|
632
|
+
return this.create({
|
|
633
|
+
id: item.id,
|
|
634
|
+
createdAt: item.createdAt,
|
|
635
|
+
userId: item.userId,
|
|
636
|
+
dismissedAt: item.dismissedAt,
|
|
637
|
+
});
|
|
638
|
+
}
|
|
470
639
|
|
|
471
|
-
|
|
640
|
+
createDismissed(item: DismissibleItemDto, dismissedAt: Date): DismissibleItemDto {
|
|
641
|
+
return this.create({
|
|
642
|
+
id: item.id,
|
|
643
|
+
createdAt: item.createdAt,
|
|
644
|
+
userId: item.userId,
|
|
645
|
+
dismissedAt,
|
|
646
|
+
});
|
|
647
|
+
}
|
|
472
648
|
|
|
473
|
-
|
|
649
|
+
createRestored(item: DismissibleItemDto): DismissibleItemDto {
|
|
650
|
+
return this.create({
|
|
651
|
+
id: item.id,
|
|
652
|
+
createdAt: item.createdAt,
|
|
653
|
+
userId: item.userId,
|
|
654
|
+
dismissedAt: undefined,
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
}
|
|
474
658
|
|
|
475
|
-
|
|
659
|
+
@Module({
|
|
660
|
+
imports: [
|
|
661
|
+
DismissibleModule.forRoot({
|
|
662
|
+
providers: [
|
|
663
|
+
CustomItemFactory,
|
|
664
|
+
{ provide: DISMISSIBLE_ITEM_FACTORY, useExisting: CustomItemFactory },
|
|
665
|
+
],
|
|
666
|
+
}),
|
|
667
|
+
],
|
|
668
|
+
})
|
|
669
|
+
export class AppModule {}
|
|
670
|
+
```
|
|
476
671
|
|
|
477
|
-
###
|
|
672
|
+
### Injecting Overridable Services
|
|
478
673
|
|
|
479
|
-
|
|
674
|
+
When injecting these services in your own code, use the symbol tokens for maximum flexibility:
|
|
480
675
|
|
|
481
676
|
```typescript
|
|
482
|
-
import { Injectable } from '@nestjs/common';
|
|
483
|
-
import {
|
|
484
|
-
import { DismissibleItemDto } from '@dismissible/nestjs-item';
|
|
677
|
+
import { Injectable, Inject } from '@nestjs/common';
|
|
678
|
+
import { IDismissibleService, DISMISSIBLE_SERVICE } from '@dismissible/nestjs-core';
|
|
485
679
|
|
|
486
680
|
@Injectable()
|
|
487
|
-
export class
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
async update(userId: string, item: DismissibleItemDto): Promise<void> {
|
|
497
|
-
// Your implementation
|
|
681
|
+
export class MyService {
|
|
682
|
+
constructor(
|
|
683
|
+
@Inject(DISMISSIBLE_SERVICE)
|
|
684
|
+
private readonly dismissibleService: IDismissibleService,
|
|
685
|
+
) {}
|
|
686
|
+
|
|
687
|
+
async myMethod(userId: string, itemId: string) {
|
|
688
|
+
// Your custom implementation will be injected if you've overridden it
|
|
689
|
+
return this.dismissibleService.getOrCreate(itemId, userId);
|
|
498
690
|
}
|
|
499
691
|
}
|
|
500
692
|
```
|