@rayondigital/nest-dapr 0.9.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/LICENSE +20 -0
- package/README.md +529 -0
- package/dist/actors/dapr-actor-client.service.d.ts +14 -0
- package/dist/actors/dapr-actor-client.service.js +64 -0
- package/dist/actors/nest-actor-manager.d.ts +8 -0
- package/dist/actors/nest-actor-manager.js +72 -0
- package/dist/actors/stateful-actor-of.d.ts +6 -0
- package/dist/actors/stateful-actor-of.js +27 -0
- package/dist/actors/stateful.actor.d.ts +7 -0
- package/dist/actors/stateful.actor.js +49 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +6 -0
- package/dist/dapr-actor.decorator.d.ts +6 -0
- package/dist/dapr-actor.decorator.js +16 -0
- package/dist/dapr-binding.decorator.d.ts +4 -0
- package/dist/dapr-binding.decorator.js +7 -0
- package/dist/dapr-metadata.accessor.d.ts +12 -0
- package/dist/dapr-metadata.accessor.js +34 -0
- package/dist/dapr-pubsub.decorator.d.ts +6 -0
- package/dist/dapr-pubsub.decorator.js +11 -0
- package/dist/dapr.loader.d.ts +23 -0
- package/dist/dapr.loader.js +157 -0
- package/dist/dapr.module.d.ts +28 -0
- package/dist/dapr.module.js +133 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +24 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
Copyright (c) 2023 Deep Blue Company
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
# NestJS + Dapr
|
|
2
|
+
Develop NestJs microservices using [Dapr](https://dapr.io/) pubsub, actors and bindings.
|
|
3
|
+
|
|
4
|
+
# Description
|
|
5
|
+
|
|
6
|
+
Dapr Module for [Nest](https://github.com/nestjs/nest) built on top of the [Dapr JS SDK](https://github.com/dapr/js-sdk).
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Supported features
|
|
10
|
+
- [x] [Actors](https://docs.dapr.io/developing-applications/building-blocks/actors/actors-overview/)
|
|
11
|
+
- [x] [PubSub](https://docs.dapr.io/developing-applications/building-blocks/pubsub/pubsub-overview/)
|
|
12
|
+
- [x] [Bindings](https://docs.dapr.io/developing-applications/building-blocks/bindings/bindings-overview/)
|
|
13
|
+
- [ ] [Distributed Lock](https://docs.dapr.io/developing-applications/building-blocks/distributed-lock/)
|
|
14
|
+
- [ ] [State](https://docs.dapr.io/developing-applications/building-blocks/state-management/state-management-overview/)
|
|
15
|
+
- [ ] [Service Invocation](https://docs.dapr.io/developing-applications/building-blocks/service-invocation/service-invocation-overview/)
|
|
16
|
+
- [ ] [Workflows](https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm i --save @rayondigital/nest-dapr
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
# Requirements
|
|
26
|
+
|
|
27
|
+
Install [Dapr](https://dapr.io/) as per getting started [guide](https://docs.dapr.io/getting-started/). Ensure Dapr is running with
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
dapr --version
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Output:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
CLI version: 1.12.0
|
|
37
|
+
Runtime version: 1.12.2
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
# Quick start
|
|
41
|
+
|
|
42
|
+
The following scaffolds a [Nest](https://github.com/nestjs/nest) project with the [nest-dapr](https://www.npmjs.com/package/@rayondigital/nest-dapr) package and demonstrates using Nest with Dapr using actors and [RabbitMQ](https://www.rabbitmq.com/) pubsub bindings.
|
|
43
|
+
|
|
44
|
+
Install Nest [CLI](https://docs.nestjs.com/cli/overview)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install -g @nestjs/cli
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Scaffold Nest project
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
nest new nest-dapr
|
|
54
|
+
cd nest-dapr/
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Install [nest-dapr](https://www.npmjs.com/package/@rayondigital/nest-dapr) package
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm i --save @rayondigital/nest-dapr
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Import `DaprModule` in `AppModule` class
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
@Module({
|
|
67
|
+
imports: [DaprModule.register()],
|
|
68
|
+
controllers: [AppController],
|
|
69
|
+
providers: [AppService],
|
|
70
|
+
})
|
|
71
|
+
export class AppModule {}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Import `DaprClient` from `@dapr/dapr` package and add dependency to `AppController` class
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { DaprClient } from '@dapr/dapr';
|
|
78
|
+
import { Controller, Get } from '@nestjs/common';
|
|
79
|
+
import { AppService } from './app.service';
|
|
80
|
+
|
|
81
|
+
@Controller()
|
|
82
|
+
export class AppController {
|
|
83
|
+
constructor(
|
|
84
|
+
private readonly appService: AppService,
|
|
85
|
+
private readonly daprClient: DaprClient,
|
|
86
|
+
) {}
|
|
87
|
+
|
|
88
|
+
@Get()
|
|
89
|
+
getHello(): string {
|
|
90
|
+
return this.appService.getHello();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Actors
|
|
96
|
+
|
|
97
|
+
Create actors and connect them to your NestJS application using the `@DaprActor` decorator.
|
|
98
|
+
This decorator takes in the interface of the actor, and marks the Actor as transient inside the NestJS
|
|
99
|
+
dependency injection container.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// You must expose your actors interface as an abstract class because Typescript interfaces are not available at runtime (erasure).
|
|
103
|
+
// Having the interface as an abstract class allows us to call the actor by only knowing the interface type.
|
|
104
|
+
export abstract class CounterActorInterface {
|
|
105
|
+
abstract increment(): Promise<number>;
|
|
106
|
+
abstract getCounter(): Promise<number>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@DaprActor({
|
|
110
|
+
interfaceType: CounterActorInterface,
|
|
111
|
+
})
|
|
112
|
+
export class CounterActor
|
|
113
|
+
extends StatefulActor
|
|
114
|
+
implements CounterActorInterface
|
|
115
|
+
{
|
|
116
|
+
// You can inject other NestJS services into your actor.
|
|
117
|
+
// Only Singleton services are supported at this time.
|
|
118
|
+
@Inject(CacheService)
|
|
119
|
+
private readonly cacheService: CacheService;
|
|
120
|
+
|
|
121
|
+
counter: number;
|
|
122
|
+
|
|
123
|
+
async onActivate(): Promise<void> {
|
|
124
|
+
this.counter = await this.getState('counter', 0);
|
|
125
|
+
return super.onActivate();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async increment(): Promise<number> {
|
|
129
|
+
this.counter++;
|
|
130
|
+
// Use a NestJS service as an example.
|
|
131
|
+
// Share in memory state between actors on this node.
|
|
132
|
+
// You probably will never want to do this, but we're just demonstrating a singleton service.
|
|
133
|
+
await this.cacheService.increment('total');
|
|
134
|
+
|
|
135
|
+
await this.setState('counter', this.counter);
|
|
136
|
+
await this.saveState();
|
|
137
|
+
return this.counter;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async getCounter(): Promise<number> {
|
|
141
|
+
return this.counter;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Actor Client
|
|
147
|
+
|
|
148
|
+
This module provides the `DaprActorClient` which is a NestJS service.
|
|
149
|
+
It can be injected into controllers, services, handlers and other actors.
|
|
150
|
+
It acts as a proxy service to the actors, and allows you to call methods on the actors - similar to the Orleans GrainFactory.
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
@Controller()
|
|
154
|
+
export class CounterController {
|
|
155
|
+
constructor(
|
|
156
|
+
private readonly actorClient: DaprActorClient,
|
|
157
|
+
) {}
|
|
158
|
+
|
|
159
|
+
@Get(":id")
|
|
160
|
+
async increment(@Param("id") id: string): Promise<string> {
|
|
161
|
+
const value = await this.actorClient
|
|
162
|
+
.getActor(CounterActorInterface, id)
|
|
163
|
+
.increment();
|
|
164
|
+
return `Counter incremented to ${value}`;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## PubSub
|
|
170
|
+
|
|
171
|
+
Create pubsub & topic names used for pubsub operations and message interface
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
const pubSubName = 'my-pubsub';
|
|
175
|
+
const topicName = 'my-topic';
|
|
176
|
+
|
|
177
|
+
interface Message {
|
|
178
|
+
hello: string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@Controller()
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Create endpoint to publish topic message
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
@Post('pubsub')
|
|
188
|
+
async pubsub(): Promise<boolean> {
|
|
189
|
+
const message: Message = { hello: 'world' };
|
|
190
|
+
|
|
191
|
+
return this.daprClient.pubsub.publish(pubSubName, topicName, message);
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Create pubsub handler which will subscribe to the topic and log the received message
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
@DaprPubSub(pubSubName, topicName)
|
|
199
|
+
pubSubHandler(message: Message): void {
|
|
200
|
+
console.log(`Received topic:${topicName} message:`, message);
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Create Dapr [pubsub](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) component in `components` folder
|
|
205
|
+
|
|
206
|
+
```yaml
|
|
207
|
+
apiVersion: dapr.io/v1alpha1
|
|
208
|
+
kind: Component
|
|
209
|
+
metadata:
|
|
210
|
+
name: my-pubsub
|
|
211
|
+
namespace: default
|
|
212
|
+
spec:
|
|
213
|
+
type: pubsub.rabbitmq
|
|
214
|
+
version: v1
|
|
215
|
+
metadata:
|
|
216
|
+
- name: host
|
|
217
|
+
value: amqp://guest:guest@localhost:5674
|
|
218
|
+
```
|
|
219
|
+
Save file as `components/rabbitmq-pubsub.yaml`
|
|
220
|
+
|
|
221
|
+
Create `docker-compose.yml` in the project root used to run [RabbitMQ](https://www.rabbitmq.com/)
|
|
222
|
+
|
|
223
|
+
```yaml
|
|
224
|
+
version: '3.9'
|
|
225
|
+
services:
|
|
226
|
+
pubsub:
|
|
227
|
+
image: rabbitmq:3-management-alpine
|
|
228
|
+
ports:
|
|
229
|
+
- 5674:5672
|
|
230
|
+
- 15674:15672
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Start RabbitMQ
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
docker-compose up
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Create script to bootstrap your Nest project using Dapr sidecar. Update `package.json` and add script
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
"scripts": {
|
|
243
|
+
..
|
|
244
|
+
"start:dapr": "dapr run --app-id nest-dapr --app-protocol http --app-port 50001 --dapr-http-port 50000 --components-path ./components npm run start"
|
|
245
|
+
},
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Start Nest app with Dapr
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
npm run start:dapr
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Invoke endpoint to publish the message
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
curl -X POST localhost:3000/pubsub
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
This should publish a message to RabbitMQ which should be consumed by the handler and written to the console:
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
== APP == Received topic:my-topic message: { hello: 'world' }
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Full example
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
import { DaprClient } from '@dapr/dapr';
|
|
270
|
+
import { DaprPubSub } from '@rayondigital/nest-dapr';
|
|
271
|
+
import { Controller, Get, Post } from '@nestjs/common';
|
|
272
|
+
import { AppService } from './app.service';
|
|
273
|
+
|
|
274
|
+
const pubSubName = 'my-pubsub';
|
|
275
|
+
const topicName = 'my-topic';
|
|
276
|
+
|
|
277
|
+
interface Message {
|
|
278
|
+
hello: string;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
@Controller()
|
|
282
|
+
export class AppController {
|
|
283
|
+
constructor(
|
|
284
|
+
private readonly appService: AppService,
|
|
285
|
+
private readonly daprClient: DaprClient,
|
|
286
|
+
) {}
|
|
287
|
+
|
|
288
|
+
@Get()
|
|
289
|
+
getHello(): string {
|
|
290
|
+
return this.appService.getHello();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
@Post('pubsub')
|
|
294
|
+
async pubsub(): Promise<boolean> {
|
|
295
|
+
const message: Message = { hello: 'world' };
|
|
296
|
+
|
|
297
|
+
return this.daprClient.pubsub.publish(pubSubName, topicName, message);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
@DaprPubSub(pubSubName, topicName)
|
|
301
|
+
pubSubHandler(message: Message): void {
|
|
302
|
+
console.log(`Received topic:${topicName} message:`, message);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
# DaprModule
|
|
308
|
+
|
|
309
|
+
`DaprModule` is a global Nest [Module](https://docs.nestjs.com/modules) used to register `DaprServer` & `DaprClient` as [providers](https://docs.nestjs.com/providers) within your project. It also registers all your handlers which listen to Dapr pubsub and input bindings so that when messages are received by Dapr, they are forwarded to the handler. Handler registration occurs during the `onApplicationBootstrap` lifecycle hook.
|
|
310
|
+
|
|
311
|
+
To use `nest-dapr`, import the `DaprModule` into the root `AppModule` and run the `register()` static method.
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
@Module({
|
|
315
|
+
imports: [DaprModule.register()],
|
|
316
|
+
controllers: [AppController],
|
|
317
|
+
providers: [AppService],
|
|
318
|
+
})
|
|
319
|
+
export class AppModule {}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
`register()` takes an optional `DaprModuleOptions` object which allows passing arguments to `DaprServer` instance.
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
export interface DaprModuleOptions {
|
|
326
|
+
serverHost?: string;
|
|
327
|
+
serverPort?: string;
|
|
328
|
+
daprHost?: string;
|
|
329
|
+
daprPort?: string;
|
|
330
|
+
communicationProtocol?: CommunicationProtocolEnum;
|
|
331
|
+
clientOptions?: DaprClientOptions;
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
See Dapr JS [docs](https://docs.dapr.io/developing-applications/sdks/js/js-server/) for more information about these arguments.
|
|
336
|
+
|
|
337
|
+
## Async configuration
|
|
338
|
+
|
|
339
|
+
You can pass your options asynchronously instead of statically. In this case, use the `registerAsync()` method, which provides several ways to deal with async configuration. One of which is to use a factory function:
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
DaprModule.registerAsync({
|
|
343
|
+
imports: [ConfigModule],
|
|
344
|
+
useFactory: (configService: ConfigService) => ({
|
|
345
|
+
serverHost: configService.get('DAPR_SERVER_HOST'),
|
|
346
|
+
serverPort: configService.get('DAPR_SERVER_PORT'),
|
|
347
|
+
daprHost: configService.get('DAPR_HOST'),
|
|
348
|
+
daprPort: configService.get('DAPR_PORT'),
|
|
349
|
+
communicationProtocol: CommunicationProtocolEnum.GRPC,
|
|
350
|
+
clientOptions: {
|
|
351
|
+
logger: {
|
|
352
|
+
level: LogLevel.Verbose,
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
}),
|
|
356
|
+
inject: [ConfigService],
|
|
357
|
+
})
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
# DaprServer & DaprClient providers
|
|
361
|
+
|
|
362
|
+
`DaprModule` registers [DaprServer](https://docs.dapr.io/developing-applications/sdks/js/js-server/) and [DaprClient](https://docs.dapr.io/developing-applications/sdks/js/js-client/) as Nest [providers](https://docs.nestjs.com/providers). These can be injected into your controllers and services like any other provider.
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
import { DaprClient } from '@dapr/dapr';
|
|
366
|
+
import { Controller, Post } from '@nestjs/common';
|
|
367
|
+
|
|
368
|
+
@Controller()
|
|
369
|
+
export class AppController {
|
|
370
|
+
constructor(readonly daprClient: DaprClient) {}
|
|
371
|
+
|
|
372
|
+
@Post()
|
|
373
|
+
async pubsub(): Promise<boolean> {
|
|
374
|
+
return this.daprClient.pubsub.publish('my-pub-sub', 'my-topic', {
|
|
375
|
+
hello: 'world',
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
# Dapr decorators
|
|
382
|
+
|
|
383
|
+
`nest-dapr` provides two TypeScript [decorators](https://www.typescriptlang.org/docs/handbook/decorators.html#decorators) which are used to declaratively configure subscriptions and bindings. These are used by `DaprModule` in conjunction with the handler method to define the handler implementations.
|
|
384
|
+
|
|
385
|
+
## DaprPubSub decorator
|
|
386
|
+
|
|
387
|
+
`DaprPubSub` decorator is used to set-up a handler for receiving pubsub topic messages. The handler has 3 arguments (`name`, `topicName` & `route`). `name` specifies the pubsub component `name` as defined in the Dapr component `metadata` section. `topicName` is the name of the pubsub topic. Route is an optional argument and defines possible [routing](https://docs.dapr.io/developing-applications/building-blocks/pubsub/howto-route-messages/) values.
|
|
388
|
+
|
|
389
|
+
Example:
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
@DaprPubSub('my-pubsub', 'my-topic')
|
|
393
|
+
pubSubHandler(message: any): void {
|
|
394
|
+
console.log('Received message:', message);
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
RabbitMQ pubsub Component:
|
|
399
|
+
|
|
400
|
+
```yaml
|
|
401
|
+
apiVersion: dapr.io/v1alpha1
|
|
402
|
+
kind: Component
|
|
403
|
+
metadata:
|
|
404
|
+
name: my-pubsub
|
|
405
|
+
namespace: default
|
|
406
|
+
spec:
|
|
407
|
+
type: pubsub.rabbitmq
|
|
408
|
+
version: v1
|
|
409
|
+
metadata:
|
|
410
|
+
- name: host
|
|
411
|
+
value: amqp://guest:guest@localhost:5674
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
Publish message:
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
await this.daprClient.pubsub.publish('my-pubsub', 'my-topic', { hello: 'world' });
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
In this example the handler `pubSubHandler` method will receive messages from the `my-topic` topic through the `my-pubsub` component which in this case is RabbitMQ.
|
|
421
|
+
|
|
422
|
+
## DaprBinding decorator
|
|
423
|
+
|
|
424
|
+
`DaprBinding` decorator is used to set-up a handler for receiving input binding data. The handler has one argument `name` which specifies the binding component `name` as defined in the Dapr component `metadata` section.
|
|
425
|
+
|
|
426
|
+
Example:
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
@DaprBinding('my-queue-binding')
|
|
430
|
+
bindingHandler(message: any): void {
|
|
431
|
+
coneole.log('Received message:', message);
|
|
432
|
+
}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
RabbitMQ binding component:
|
|
436
|
+
|
|
437
|
+
```yaml
|
|
438
|
+
apiVersion: dapr.io/v1alpha1
|
|
439
|
+
kind: Component
|
|
440
|
+
metadata:
|
|
441
|
+
name: my-queue-binding
|
|
442
|
+
namespace: default
|
|
443
|
+
spec:
|
|
444
|
+
type: bindings.rabbitmq
|
|
445
|
+
version: v1
|
|
446
|
+
metadata:
|
|
447
|
+
- name: queueName
|
|
448
|
+
value: queue1
|
|
449
|
+
- name: host
|
|
450
|
+
value: amqp://guest:guest@localhost:5674
|
|
451
|
+
- name: durable
|
|
452
|
+
value: true
|
|
453
|
+
- name: deleteWhenUnused
|
|
454
|
+
value: false
|
|
455
|
+
- name: ttlInSeconds
|
|
456
|
+
value: 60
|
|
457
|
+
- name: prefetchCount
|
|
458
|
+
value: 0
|
|
459
|
+
- name: exclusive
|
|
460
|
+
value: false
|
|
461
|
+
- name: maxPriority
|
|
462
|
+
value: 5
|
|
463
|
+
- name: contentType
|
|
464
|
+
value: "text/plain"
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Send message:
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
await this.daprClient.binding.send('my-queue-binding', 'create', { hello: 'world' });
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
In this example the handler `bindingHandler` method will receive messages from the `queue1` queue defined in the `my-queue-binding` component which in this case is RabbitMQ.
|
|
474
|
+
|
|
475
|
+
## Writing handlers
|
|
476
|
+
|
|
477
|
+
`DaprModule` uses reflection to register all handlers found either in [Controller](https://docs.nestjs.com/controllers) or [Provider](https://docs.nestjs.com/providers) classes. These classes must be registered in a Nest [module](https://docs.nestjs.com/modules). Providers must be decorated with the `@Injectable()` decorator at the class level. Once this is done and your provider is added to your module's [providers] array then `nest-dapr` will use Nest dependency injection container to resolve the provider instance and call your handler when the message is received.
|
|
478
|
+
|
|
479
|
+
Here's an example of a [Provider](https://docs.nestjs.com/providers) containing a Dapr handler.
|
|
480
|
+
|
|
481
|
+
```typescript
|
|
482
|
+
import { DaprPubSub } from '@rayondigital/nest-dapr';
|
|
483
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
484
|
+
|
|
485
|
+
@Injectable()
|
|
486
|
+
export class AppService {
|
|
487
|
+
private readonly logger = new Logger(AppService.name);
|
|
488
|
+
|
|
489
|
+
@DaprPubSub('my-pubsub', 'my-topic')
|
|
490
|
+
pubSubHandler(message: any): void {
|
|
491
|
+
this.logger.log(`Received topic message:`, message);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
# Examples
|
|
497
|
+
|
|
498
|
+
| Example | Description |
|
|
499
|
+
|---|-------------------------------------------------------------------------|
|
|
500
|
+
| [Basics](examples/basics/README.md) | Demonstrates a very basic actors, pubsub & input binding using RabbitMQ |
|
|
501
|
+
|
|
502
|
+
# Troubleshooting
|
|
503
|
+
|
|
504
|
+
[Dapr](https://dapr.io/) is a complex set of tools and services and must be set-up and deployed carefully to ensure your system operates correctly.
|
|
505
|
+
This library is merely integration using the existing Dapr [js-sdk](https://github.com/dapr/js-sdk).
|
|
506
|
+
If things are not working out for you please review:
|
|
507
|
+
- Your configuration
|
|
508
|
+
- Your Dapr local environment
|
|
509
|
+
- Your port numbers and hostnames
|
|
510
|
+
- Dapr & SDK documentation
|
|
511
|
+
- The tests and examples in this project
|
|
512
|
+
|
|
513
|
+
If you find that both Dapr and the Javascript SDK is both working fine but `nest-dapr` is not working in some way,
|
|
514
|
+
please file an issue and state clearly the problem and provide a reproducible code example.
|
|
515
|
+
Filing an issue with something like: "It doesn't work" is likely to be ignored or removed.
|
|
516
|
+
|
|
517
|
+
# Credits/Contributions :heart:
|
|
518
|
+
|
|
519
|
+
Thanks to:
|
|
520
|
+
|
|
521
|
+
- [@dbc-tech/nest-dapr](https://github.com/nad-au/nest-dapr) - We forked from this repository
|
|
522
|
+
- [nad-au](https://github.com/nad-au) - Worked on pubsub and initial integration
|
|
523
|
+
- [dapr-nestjs-pubsub](https://github.com/avifatal/dapr-nestjs-pubsub) - The original library
|
|
524
|
+
- [@dapr/dapr](https://github.com/dapr/js-sdk) - Development team
|
|
525
|
+
- [Nest](https://github.com/nestjs/nest) - Development team
|
|
526
|
+
|
|
527
|
+
# Licence
|
|
528
|
+
|
|
529
|
+
Released under the [MIT](LICENSE) license. No warranty expressed or implied.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DaprClient } from '@dapr/dapr';
|
|
2
|
+
import { Type } from '@nestjs/common';
|
|
3
|
+
export declare class DaprActorClient {
|
|
4
|
+
private readonly daprClient;
|
|
5
|
+
private actorClients;
|
|
6
|
+
constructor(daprClient: DaprClient);
|
|
7
|
+
register<T>(actorTypeName: string, actorType: Type<T> | Function, daprClient?: DaprClient): void;
|
|
8
|
+
registerInterface<T>(actorType: Type<T> | Function, interfaceType: Type<T> | Function, daprClient?: DaprClient): void;
|
|
9
|
+
getActor<TActorInterface>(actorType: Type<TActorInterface> | Function, actorId: string): TActorInterface;
|
|
10
|
+
getActorByTypeName<TActorInterface>(actorTypeName: string, actorId: string): TActorInterface;
|
|
11
|
+
contains(actorTypeName: string): boolean;
|
|
12
|
+
private formatActorTypeName;
|
|
13
|
+
private getActorClient;
|
|
14
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
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.DaprActorClient = void 0;
|
|
13
|
+
const dapr_1 = require("@dapr/dapr");
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
let DaprActorClient = class DaprActorClient {
|
|
16
|
+
constructor(daprClient) {
|
|
17
|
+
this.daprClient = daprClient;
|
|
18
|
+
this.actorClients = new Map();
|
|
19
|
+
}
|
|
20
|
+
register(actorTypeName, actorType, daprClient) {
|
|
21
|
+
this.actorClients.set(this.formatActorTypeName(actorTypeName), new dapr_1.ActorProxyBuilder(actorType, daprClient !== null && daprClient !== void 0 ? daprClient : this.daprClient));
|
|
22
|
+
}
|
|
23
|
+
registerInterface(actorType, interfaceType, daprClient) {
|
|
24
|
+
var _a;
|
|
25
|
+
const interfaceTypeName = (_a = interfaceType.name) !== null && _a !== void 0 ? _a : interfaceType.constructor.name;
|
|
26
|
+
this.actorClients.set(this.formatActorTypeName(interfaceTypeName), new dapr_1.ActorProxyBuilder(actorType, daprClient !== null && daprClient !== void 0 ? daprClient : this.daprClient));
|
|
27
|
+
}
|
|
28
|
+
getActor(actorType, actorId) {
|
|
29
|
+
var _a;
|
|
30
|
+
const actorTypeName = (_a = actorType.name) !== null && _a !== void 0 ? _a : actorType.constructor.name;
|
|
31
|
+
if (!actorTypeName) {
|
|
32
|
+
throw new Error(`Actor type name must be provided`);
|
|
33
|
+
}
|
|
34
|
+
if (!this.contains(actorTypeName)) {
|
|
35
|
+
throw new Error(`Actor ${actorTypeName} not found`);
|
|
36
|
+
}
|
|
37
|
+
const actorClient = this.getActorClient(actorTypeName);
|
|
38
|
+
return actorClient.build(new dapr_1.ActorId(actorId));
|
|
39
|
+
}
|
|
40
|
+
getActorByTypeName(actorTypeName, actorId) {
|
|
41
|
+
if (!actorTypeName) {
|
|
42
|
+
throw new Error(`Actor type name must be provided`);
|
|
43
|
+
}
|
|
44
|
+
if (!this.contains(actorTypeName)) {
|
|
45
|
+
throw new Error(`Actor ${actorTypeName} not found`);
|
|
46
|
+
}
|
|
47
|
+
const actorClient = this.getActorClient(actorTypeName);
|
|
48
|
+
return actorClient.build(new dapr_1.ActorId(actorId));
|
|
49
|
+
}
|
|
50
|
+
contains(actorTypeName) {
|
|
51
|
+
return this.actorClients.has(this.formatActorTypeName(actorTypeName));
|
|
52
|
+
}
|
|
53
|
+
formatActorTypeName(actorTypeName) {
|
|
54
|
+
return actorTypeName.toLowerCase();
|
|
55
|
+
}
|
|
56
|
+
getActorClient(actorTypeName) {
|
|
57
|
+
return this.actorClients.get(this.formatActorTypeName(actorTypeName));
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
DaprActorClient = __decorate([
|
|
61
|
+
(0, common_1.Injectable)(),
|
|
62
|
+
__metadata("design:paramtypes", [dapr_1.DaprClient])
|
|
63
|
+
], DaprActorClient);
|
|
64
|
+
exports.DaprActorClient = DaprActorClient;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ActorId } from '@dapr/dapr';
|
|
2
|
+
import { ModuleRef } from '@nestjs/core';
|
|
3
|
+
export declare function patchActorManagerForNest(moduleRef: ModuleRef, invokeWrapperFn?: (actorId: ActorId, methodName: string, data: any, method: (actorId: ActorId, methodName: string, data: any) => Promise<any>) => Promise<any>): void;
|
|
4
|
+
export interface ActorMethodInvocation {
|
|
5
|
+
actorId: ActorId;
|
|
6
|
+
method: string;
|
|
7
|
+
data: any;
|
|
8
|
+
}
|