@dismissible/nestjs-core 2.0.2-alpha.f50def9.0 → 2.0.2
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 +434 -232
- package/package.json +14 -14
- package/src/api/api.constants.d.ts +4 -0
- package/src/api/api.constants.js +8 -0
- package/src/api/api.constants.js.map +1 -0
- 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 +2 -0
- package/src/api/index.js +2 -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 +10 -8
- 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 +10 -8
- 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 +10 -8
- package/src/api/use-cases/restore/restore.controller.js.map +1 -1
- package/src/core/dismissible-core.service.d.ts +7 -6
- 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 +10 -8
- package/src/core/dismissible-core.service.js.map +1 -1
- package/src/core/dismissible-core.service.spec.js +1 -2
- package/src/core/dismissible-core.service.spec.js.map +1 -1
- package/src/core/dismissible.service.d.ts +6 -5
- 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 +6 -4
- package/src/core/dismissible.service.js.map +1 -1
- package/src/core/dismissible.service.spec.js +1 -2
- package/src/core/dismissible.service.spec.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.d.ts +2 -2
- 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,170 @@ 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
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
### `imports`
|
|
224
301
|
|
|
225
|
-
|
|
302
|
+
Adds additional modules to the DismissibleModule's imports. Useful for injecting dependencies that your hooks or providers need.
|
|
226
303
|
|
|
227
|
-
|
|
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, Inject } from '@nestjs/common';
|
|
371
|
+
import {
|
|
372
|
+
DismissibleModule,
|
|
373
|
+
IDismissibleService,
|
|
374
|
+
DISMISSIBLE_SERVICE,
|
|
375
|
+
} from '@dismissible/nestjs-core';
|
|
376
|
+
|
|
377
|
+
@Controller('custom')
|
|
378
|
+
class CustomController {
|
|
379
|
+
constructor(
|
|
380
|
+
@Inject(DISMISSIBLE_SERVICE)
|
|
381
|
+
private dismissibleService: IDismissibleService,
|
|
382
|
+
) {}
|
|
383
|
+
|
|
384
|
+
@Get(':userId/:itemId')
|
|
385
|
+
async getItem(@Param('userId') userId: string, @Param('itemId') itemId: string) {
|
|
386
|
+
return this.dismissibleService.getOrCreate(itemId, userId);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
286
389
|
|
|
287
390
|
@Module({
|
|
288
391
|
imports: [
|
|
289
392
|
DismissibleModule.forRoot({
|
|
290
|
-
|
|
393
|
+
controllers: [CustomController],
|
|
291
394
|
}),
|
|
292
395
|
],
|
|
293
396
|
})
|
|
294
397
|
export class AppModule {}
|
|
295
398
|
```
|
|
296
399
|
|
|
297
|
-
|
|
400
|
+
To disable the REST API entirely, pass an empty array:
|
|
298
401
|
|
|
299
|
-
|
|
402
|
+
```typescript
|
|
403
|
+
@Module({
|
|
404
|
+
imports: [
|
|
405
|
+
DismissibleModule.forRoot({
|
|
406
|
+
controllers: [],
|
|
407
|
+
}),
|
|
408
|
+
],
|
|
409
|
+
})
|
|
410
|
+
export class AppModule {}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## Using the Service Directly
|
|
416
|
+
|
|
417
|
+
Instead of using the built-in REST API, you can inject `DismissibleService` into your own controllers or services:
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
import { Controller, Get, Param, Delete, Post, Inject } from '@nestjs/common';
|
|
421
|
+
import { IDismissibleService, DISMISSIBLE_SERVICE } from '@dismissible/nestjs-core';
|
|
422
|
+
|
|
423
|
+
@Controller('features')
|
|
424
|
+
export class FeaturesController {
|
|
425
|
+
constructor(
|
|
426
|
+
@Inject(DISMISSIBLE_SERVICE)
|
|
427
|
+
private readonly dismissibleService: IDismissibleService,
|
|
428
|
+
) {}
|
|
429
|
+
|
|
430
|
+
@Get(':userId/items/:itemId')
|
|
431
|
+
async getOrCreateItem(@Param('userId') userId: string, @Param('itemId') itemId: string) {
|
|
432
|
+
const result = await this.dismissibleService.getOrCreate(itemId, userId);
|
|
433
|
+
return {
|
|
434
|
+
item: result.item,
|
|
435
|
+
wasCreated: result.created,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
@Delete(':userId/items/:itemId')
|
|
440
|
+
async dismissItem(@Param('userId') userId: string, @Param('itemId') itemId: string) {
|
|
441
|
+
const result = await this.dismissibleService.dismiss(itemId, userId);
|
|
442
|
+
return { item: result.item };
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
@Post(':userId/items/:itemId/restore')
|
|
446
|
+
async restoreItem(@Param('userId') userId: string, @Param('itemId') itemId: string) {
|
|
447
|
+
const result = await this.dismissibleService.restore(itemId, userId);
|
|
448
|
+
return { item: result.item };
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
## Events
|
|
454
|
+
|
|
455
|
+
The library emits events for all operations using NestJS's EventEmitter2:
|
|
300
456
|
|
|
301
457
|
```typescript
|
|
302
458
|
import { Injectable } from '@nestjs/common';
|
|
@@ -305,7 +461,6 @@ import {
|
|
|
305
461
|
ItemCreatedEvent,
|
|
306
462
|
ItemDismissedEvent,
|
|
307
463
|
ItemRestoredEvent,
|
|
308
|
-
ItemRetrievedEvent,
|
|
309
464
|
DismissibleEvents,
|
|
310
465
|
} from '@dismissible/nestjs-core';
|
|
311
466
|
|
|
@@ -313,188 +468,235 @@ import {
|
|
|
313
468
|
export class AnalyticsService {
|
|
314
469
|
@OnEvent(DismissibleEvents.ITEM_CREATED)
|
|
315
470
|
handleItemCreated(event: ItemCreatedEvent) {
|
|
316
|
-
|
|
317
|
-
console.log(`Analytics: Item ${event.id} created for user ${event.userId}`);
|
|
471
|
+
console.log(`Item ${event.id} created for user ${event.userId}`);
|
|
318
472
|
}
|
|
319
473
|
|
|
320
474
|
@OnEvent(DismissibleEvents.ITEM_DISMISSED)
|
|
321
475
|
handleItemDismissed(event: ItemDismissedEvent) {
|
|
322
|
-
|
|
323
|
-
console.log(`Analytics: Item ${event.id} dismissed by user ${event.userId}`);
|
|
476
|
+
console.log(`Item ${event.id} dismissed by user ${event.userId}`);
|
|
324
477
|
}
|
|
325
478
|
|
|
326
479
|
@OnEvent(DismissibleEvents.ITEM_RESTORED)
|
|
327
480
|
handleItemRestored(event: ItemRestoredEvent) {
|
|
328
|
-
|
|
329
|
-
console.log(`Analytics: Item ${event.id} restored by user ${event.userId}`);
|
|
481
|
+
console.log(`Item ${event.id} restored by user ${event.userId}`);
|
|
330
482
|
}
|
|
331
483
|
}
|
|
332
484
|
```
|
|
333
485
|
|
|
334
|
-
|
|
486
|
+
Available events:
|
|
487
|
+
|
|
488
|
+
- `DismissibleEvents.ITEM_CREATED` - New item created
|
|
489
|
+
- `DismissibleEvents.ITEM_RETRIEVED` - Existing item retrieved
|
|
490
|
+
- `DismissibleEvents.ITEM_DISMISSED` - Item dismissed
|
|
491
|
+
- `DismissibleEvents.ITEM_RESTORED` - Item restored
|
|
492
|
+
|
|
493
|
+
## Overriding Services
|
|
494
|
+
|
|
495
|
+
All core services can be overridden using symbol-based dependency injection tokens. This allows you to provide custom implementations while maintaining type safety.
|
|
335
496
|
|
|
336
|
-
|
|
497
|
+
### Available Service Tokens
|
|
498
|
+
|
|
499
|
+
| Token | Interface | Description |
|
|
500
|
+
| ------------------------------ | ------------------------- | ----------------------------------------------- |
|
|
501
|
+
| `DISMISSIBLE_SERVICE` | `IDismissibleService` | Main orchestration service |
|
|
502
|
+
| `DISMISSIBLE_CORE_SERVICE` | `IDismissibleCoreService` | Core business logic service |
|
|
503
|
+
| `DISMISSIBLE_HOOK_RUNNER` | `IHookRunner` | Lifecycle hook execution |
|
|
504
|
+
| `DISMISSIBLE_HELPER` | `IDismissibleHelper` | Helper utilities |
|
|
505
|
+
| `DISMISSIBLE_DATE_SERVICE` | `IDateService` | Date operations |
|
|
506
|
+
| `DISMISSIBLE_RESPONSE_SERVICE` | `IResponseService` | HTTP response formatting |
|
|
507
|
+
| `DISMISSIBLE_ITEM_MAPPER` | `IDismissibleItemMapper` | Domain to DTO mapping |
|
|
508
|
+
| `DISMISSIBLE_ITEM_FACTORY` | `IDismissibleItemFactory` | Item creation (from `@dismissible/nestjs-item`) |
|
|
509
|
+
|
|
510
|
+
### Example: Overriding the Date Service
|
|
511
|
+
|
|
512
|
+
Override the date service to control time in tests or add custom behavior:
|
|
337
513
|
|
|
338
514
|
```typescript
|
|
339
|
-
import { Injectable } from '@nestjs/common';
|
|
340
|
-
import {
|
|
341
|
-
|
|
515
|
+
import { Injectable, Module } from '@nestjs/common';
|
|
516
|
+
import {
|
|
517
|
+
DismissibleModule,
|
|
518
|
+
IDateService,
|
|
519
|
+
DISMISSIBLE_DATE_SERVICE,
|
|
520
|
+
} from '@dismissible/nestjs-core';
|
|
342
521
|
|
|
343
522
|
@Injectable()
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
//
|
|
347
|
-
|
|
523
|
+
class CustomDateService implements IDateService {
|
|
524
|
+
getNow(): Date {
|
|
525
|
+
// Custom implementation - e.g., use a fixed time for testing
|
|
526
|
+
return new Date('2024-01-01T00:00:00.000Z');
|
|
348
527
|
}
|
|
349
528
|
|
|
350
|
-
|
|
351
|
-
|
|
529
|
+
parseIso(isoString: string): Date {
|
|
530
|
+
return new Date(isoString);
|
|
352
531
|
}
|
|
353
532
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
error(message: string, context?: any) {
|
|
359
|
-
console.error(`[ERROR] ${message}`, context);
|
|
533
|
+
toIso(date: Date): string {
|
|
534
|
+
return date.toISOString();
|
|
360
535
|
}
|
|
361
536
|
}
|
|
362
537
|
|
|
363
538
|
@Module({
|
|
364
539
|
imports: [
|
|
365
540
|
DismissibleModule.forRoot({
|
|
366
|
-
|
|
541
|
+
providers: [
|
|
542
|
+
CustomDateService,
|
|
543
|
+
{ provide: DISMISSIBLE_DATE_SERVICE, useExisting: CustomDateService },
|
|
544
|
+
],
|
|
367
545
|
}),
|
|
368
546
|
],
|
|
369
547
|
})
|
|
370
548
|
export class AppModule {}
|
|
371
549
|
```
|
|
372
550
|
|
|
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
|
|
388
|
-
|
|
389
|
-
Returns: `Promise<IGetOrCreateServiceResponse>`
|
|
551
|
+
### Example: Overriding the Main Service
|
|
390
552
|
|
|
391
|
-
|
|
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
|
|
553
|
+
Override the main dismissible service for custom orchestration logic:
|
|
412
554
|
|
|
413
555
|
```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
|
|
556
|
+
import { Injectable, Inject, Module } from '@nestjs/common';
|
|
557
|
+
import {
|
|
558
|
+
DismissibleModule,
|
|
559
|
+
IDismissibleService,
|
|
560
|
+
IDismissibleCoreService,
|
|
561
|
+
DISMISSIBLE_SERVICE,
|
|
562
|
+
DISMISSIBLE_CORE_SERVICE,
|
|
563
|
+
IGetOrCreateServiceResponse,
|
|
564
|
+
IDismissServiceResponse,
|
|
565
|
+
IRestoreServiceResponse,
|
|
566
|
+
} from '@dismissible/nestjs-core';
|
|
567
|
+
import { IRequestContext } from '@dismissible/nestjs-request';
|
|
441
568
|
|
|
442
|
-
|
|
569
|
+
@Injectable()
|
|
570
|
+
class CustomDismissibleService implements IDismissibleService {
|
|
571
|
+
constructor(
|
|
572
|
+
@Inject(DISMISSIBLE_CORE_SERVICE)
|
|
573
|
+
private readonly coreService: IDismissibleCoreService,
|
|
574
|
+
) {}
|
|
443
575
|
|
|
444
|
-
|
|
576
|
+
async getOrCreate(
|
|
577
|
+
itemId: string,
|
|
578
|
+
userId: string,
|
|
579
|
+
context?: IRequestContext,
|
|
580
|
+
): Promise<IGetOrCreateServiceResponse> {
|
|
581
|
+
// Add custom logic before/after core operation
|
|
582
|
+
console.log('Custom getOrCreate called');
|
|
583
|
+
return this.coreService.getOrCreate(itemId, userId);
|
|
584
|
+
}
|
|
445
585
|
|
|
446
|
-
|
|
586
|
+
async dismiss(
|
|
587
|
+
itemId: string,
|
|
588
|
+
userId: string,
|
|
589
|
+
context?: IRequestContext,
|
|
590
|
+
): Promise<IDismissServiceResponse> {
|
|
591
|
+
return this.coreService.dismiss(itemId, userId);
|
|
592
|
+
}
|
|
447
593
|
|
|
448
|
-
|
|
594
|
+
async restore(
|
|
595
|
+
itemId: string,
|
|
596
|
+
userId: string,
|
|
597
|
+
context?: IRequestContext,
|
|
598
|
+
): Promise<IRestoreServiceResponse> {
|
|
599
|
+
return this.coreService.restore(itemId, userId);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
449
602
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
603
|
+
@Module({
|
|
604
|
+
imports: [
|
|
605
|
+
DismissibleModule.forRoot({
|
|
606
|
+
providers: [
|
|
607
|
+
CustomDismissibleService,
|
|
608
|
+
{ provide: DISMISSIBLE_SERVICE, useExisting: CustomDismissibleService },
|
|
609
|
+
],
|
|
610
|
+
}),
|
|
611
|
+
],
|
|
612
|
+
})
|
|
613
|
+
export class AppModule {}
|
|
614
|
+
```
|
|
458
615
|
|
|
459
|
-
|
|
616
|
+
### Example: Overriding the Item Factory
|
|
460
617
|
|
|
461
|
-
|
|
462
|
-
- **Mutate parameters** by returning `{ proceed: true, mutations: { id?, userId?, context? } }`
|
|
463
|
-
- **Perform side effects** in post-hooks (no return value needed)
|
|
618
|
+
Override the item factory to customize how items are created:
|
|
464
619
|
|
|
465
|
-
|
|
620
|
+
```typescript
|
|
621
|
+
import { Injectable, Module } from '@nestjs/common';
|
|
622
|
+
import { DismissibleModule } from '@dismissible/nestjs-core';
|
|
623
|
+
import {
|
|
624
|
+
DismissibleItemDto,
|
|
625
|
+
IDismissibleItemFactory,
|
|
626
|
+
ICreateDismissibleItemOptions,
|
|
627
|
+
DISMISSIBLE_ITEM_FACTORY,
|
|
628
|
+
} from '@dismissible/nestjs-item';
|
|
466
629
|
|
|
467
|
-
|
|
630
|
+
@Injectable()
|
|
631
|
+
class CustomItemFactory implements IDismissibleItemFactory {
|
|
632
|
+
create(options: ICreateDismissibleItemOptions): DismissibleItemDto {
|
|
633
|
+
const item = new DismissibleItemDto();
|
|
634
|
+
item.id = options.id;
|
|
635
|
+
item.userId = options.userId;
|
|
636
|
+
item.createdAt = options.createdAt;
|
|
637
|
+
item.dismissedAt = options.dismissedAt;
|
|
638
|
+
return item;
|
|
639
|
+
}
|
|
468
640
|
|
|
469
|
-
|
|
641
|
+
clone(item: DismissibleItemDto): DismissibleItemDto {
|
|
642
|
+
return this.create({
|
|
643
|
+
id: item.id,
|
|
644
|
+
createdAt: item.createdAt,
|
|
645
|
+
userId: item.userId,
|
|
646
|
+
dismissedAt: item.dismissedAt,
|
|
647
|
+
});
|
|
648
|
+
}
|
|
470
649
|
|
|
471
|
-
|
|
650
|
+
createDismissed(item: DismissibleItemDto, dismissedAt: Date): DismissibleItemDto {
|
|
651
|
+
return this.create({
|
|
652
|
+
id: item.id,
|
|
653
|
+
createdAt: item.createdAt,
|
|
654
|
+
userId: item.userId,
|
|
655
|
+
dismissedAt,
|
|
656
|
+
});
|
|
657
|
+
}
|
|
472
658
|
|
|
473
|
-
|
|
659
|
+
createRestored(item: DismissibleItemDto): DismissibleItemDto {
|
|
660
|
+
return this.create({
|
|
661
|
+
id: item.id,
|
|
662
|
+
createdAt: item.createdAt,
|
|
663
|
+
userId: item.userId,
|
|
664
|
+
dismissedAt: undefined,
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
}
|
|
474
668
|
|
|
475
|
-
|
|
669
|
+
@Module({
|
|
670
|
+
imports: [
|
|
671
|
+
DismissibleModule.forRoot({
|
|
672
|
+
providers: [
|
|
673
|
+
CustomItemFactory,
|
|
674
|
+
{ provide: DISMISSIBLE_ITEM_FACTORY, useExisting: CustomItemFactory },
|
|
675
|
+
],
|
|
676
|
+
}),
|
|
677
|
+
],
|
|
678
|
+
})
|
|
679
|
+
export class AppModule {}
|
|
680
|
+
```
|
|
476
681
|
|
|
477
|
-
###
|
|
682
|
+
### Injecting Overridable Services
|
|
478
683
|
|
|
479
|
-
|
|
684
|
+
When injecting these services in your own code, use the symbol tokens for maximum flexibility:
|
|
480
685
|
|
|
481
686
|
```typescript
|
|
482
|
-
import { Injectable } from '@nestjs/common';
|
|
483
|
-
import {
|
|
484
|
-
import { DismissibleItemDto } from '@dismissible/nestjs-item';
|
|
687
|
+
import { Injectable, Inject } from '@nestjs/common';
|
|
688
|
+
import { IDismissibleService, DISMISSIBLE_SERVICE } from '@dismissible/nestjs-core';
|
|
485
689
|
|
|
486
690
|
@Injectable()
|
|
487
|
-
export class
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
async update(userId: string, item: DismissibleItemDto): Promise<void> {
|
|
497
|
-
// Your implementation
|
|
691
|
+
export class MyService {
|
|
692
|
+
constructor(
|
|
693
|
+
@Inject(DISMISSIBLE_SERVICE)
|
|
694
|
+
private readonly dismissibleService: IDismissibleService,
|
|
695
|
+
) {}
|
|
696
|
+
|
|
697
|
+
async myMethod(userId: string, itemId: string) {
|
|
698
|
+
// Your custom implementation will be injected if you've overridden it
|
|
699
|
+
return this.dismissibleService.getOrCreate(itemId, userId);
|
|
498
700
|
}
|
|
499
701
|
}
|
|
500
702
|
```
|