Skip to content

Commit 39bb8c1

Browse files
Add Actor abstraction on top that allows us to use the Proxy Builder through client.actor.create
Signed-off-by: Xavier Geerinck <[email protected]>
1 parent 6beae03 commit 39bb8c1

File tree

7 files changed

+75
-325
lines changed

7 files changed

+75
-325
lines changed

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,29 @@
22

33
## 2.x release
44

5+
### v2.1.0
6+
7+
#### Actors
8+
9+
To make development easier, Actors can now be called in 2 ways:
10+
11+
1. By creating an actor through a helper method in the `DaprClient` class, removing the need of needing to know how a builder works.
12+
13+
```javascript
14+
const actor = await client.actor.create<DemoActorSayImpl>(DemoActorSayImpl);
15+
const res = await actor.sayMulti(123, "123", { hello: "world 123" }, [1, 2, 3]);
16+
```
17+
18+
2. By utilizing the Actor builder, where we create a Proxy object that does this for us.
19+
20+
```javascript
21+
const builder = new ActorProxyBuilder<DemoActorSayImpl>(DemoActorSayImpl, client);
22+
const actor = builder.build(ActorId.createRandomId());
23+
const res = await actor.sayMulti(123, "123", { hello: "world 123" }, [1, 2, 3]);
24+
```
25+
26+
Behind the hoods, method #1 will utilize method #2
27+
528
### v2.0.0
629

730
Version 2.0.0 brings a lot of changes to the Dapr JS SDK that were long due. Below an overview of the major contributions can be found, with a more detailed overview of the **Breaking Changes** under it.

src/actors/client/ActorProxyBuilder.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
1-
import { DaprClient } from "../..";
1+
import { CommunicationProtocolEnum, DaprClient } from "../..";
22
import Class from "../../types/Class";
33
import ActorClient from "./ActorClient/ActorClient";
44
import ActorId from "../ActorId";
5+
import { DaprClientOptions } from "../../types/DaprClientOptions";
56

67
export default class ActorProxyBuilder<T> {
78
actorClient: ActorClient;
8-
daprClient: DaprClient;
99
actorTypeClass: Class<T>;
1010

11-
constructor(actorTypeClass: Class<T>, daprClient: DaprClient) {
11+
constructor(actorTypeClass: Class<T>, daprClient: DaprClient);
12+
constructor(actorTypeClass: Class<T>, host: string, port: string, communicationProtocol: CommunicationProtocolEnum, clientOptions: DaprClientOptions);
13+
constructor(actorTypeClass: Class<T>, ...args: any[]) {
1214
this.actorTypeClass = actorTypeClass;
13-
this.daprClient = daprClient;
14-
this.actorClient = new ActorClient(daprClient.getDaprHost(), daprClient.getDaprPort(), daprClient.getCommunicationProtocol(), daprClient.getOptions());
15+
16+
if (args.length == 1) {
17+
const [daprClient] = args;
18+
this.actorClient = new ActorClient(daprClient.getDaprHost(), daprClient.getDaprPort(), daprClient.getCommunicationProtocol(), daprClient.getOptions());
19+
} else {
20+
const [host, port, communicationProtocol, clientOptions] = args;
21+
this.actorClient = new ActorClient(host, port, communicationProtocol, clientOptions);
22+
}
1523
}
1624

1725
build(actorId: ActorId): T {

src/implementation/Client/DaprClient.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import IClientSecret from '../../interfaces/Client/IClientSecret';
66
import IClientHealth from '../../interfaces/Client/IClientHealth';
77
import IClientMetadata from '../../interfaces/Client/IClientMetadata';
88
import IClientSidecar from '../../interfaces/Client/IClientSidecar';
9+
import IClientActorBuilder from '../../interfaces/Client/IClientActorBuilder';
910
import IClient from '../../interfaces/Client/IClient';
1011

1112
import GRPCClientBinding from './GRPCClient/binding';
@@ -16,6 +17,7 @@ import GRPCClientSecret from './GRPCClient/secret';
1617
import GRPCClientHealth from './GRPCClient/health';
1718
import GRPCClientMetadata from './GRPCClient/metadata';
1819
import GRPCClientSidecar from './GRPCClient/sidecar';
20+
import GRPCClientActor from './GRPCClient/actor';
1921
import GRPCClient from './GRPCClient/GRPCClient';
2022

2123
import HTTPClientBinding from './HTTPClient/binding';
@@ -26,6 +28,7 @@ import HTTPClientSecret from './HTTPClient/secret';
2628
import HTTPClientHealth from './HTTPClient/health';
2729
import HTTPClientMetadata from './HTTPClient/metadata';
2830
import HTTPClientSidecar from './HTTPClient/sidecar';
31+
import HTTPClientActor from './HTTPClient/actor';
2932
import HTTPClient from './HTTPClient/HTTPClient';
3033

3134
import CommunicationProtocolEnum from '../../enum/CommunicationProtocol.enum';
@@ -46,6 +49,7 @@ export default class DaprClient {
4649
readonly health: IClientHealth;
4750
readonly metadata: IClientMetadata;
4851
readonly sidecar: IClientSidecar;
52+
readonly actor: IClientActorBuilder;
4953

5054
constructor(
5155
daprHost: string
@@ -79,6 +83,7 @@ export default class DaprClient {
7983
this.health = new GRPCClientHealth(client);
8084
this.metadata = new GRPCClientMetadata(client);
8185
this.sidecar = new GRPCClientSidecar(client);
86+
this.actor = new GRPCClientActor(client); // we use a abstractor here since we interface through a builder with the Actor Runtime
8287
break;
8388
}
8489
case CommunicationProtocolEnum.HTTP:
@@ -94,6 +99,7 @@ export default class DaprClient {
9499
this.health = new HTTPClientHealth(client);
95100
this.metadata = new HTTPClientMetadata(client);
96101
this.sidecar = new HTTPClientSidecar(client);
102+
this.actor = new HTTPClientActor(client); // we use a abstractor here since we interface through a builder with the Actor Runtime
97103
break;
98104
}
99105
}
Lines changed: 11 additions & 235 deletions
Original file line numberDiff line numberDiff line change
@@ -1,246 +1,22 @@
1-
import { Empty } from "google-protobuf/google/protobuf/empty_pb";
2-
import { Any } from "google-protobuf/google/protobuf/any_pb";
3-
import { ExecuteActorStateTransactionRequest, GetActorStateRequest, GetActorStateResponse, GetMetadataResponse, InvokeActorRequest, InvokeActorResponse, RegisterActorReminderRequest, RegisterActorTimerRequest, TransactionalActorStateOperation, UnregisterActorReminderRequest, UnregisterActorTimerRequest } from '../../../proto/dapr/proto/runtime/v1/dapr_pb';
41
import GRPCClient from './GRPCClient';
5-
import { OperationType } from '../../../types/Operation.type';
6-
import { ActorReminderType } from '../../../types/ActorReminder.type';
7-
import { ActorTimerType } from '../../../types/ActorTimer.type';
8-
import IClientActor from '../../../interfaces/Client/IClientActor';
9-
import { KeyValueType } from '../../../types/KeyValue.type';
2+
import IClientActorBuilder from "../../../interfaces/Client/IClientActorBuilder";
3+
import ActorProxyBuilder from "../../../actors/client/ActorProxyBuilder";
4+
import Class from "../../../types/Class";
5+
import ActorId from "../../../actors/ActorId";
106

117
// https://docs.dapr.io/reference/api/actors_api/
12-
export default class GRPCClientActor implements IClientActor {
8+
export default class GRPCClientActor implements IClientActorBuilder {
139
client: GRPCClient;
1410

1511
constructor(client: GRPCClient) {
1612
this.client = client;
1713
}
1814

19-
async invoke(method: "GET" | "POST" | "PUT" | "DELETE", actorType: string, actorId: string, methodName: string, body?: any): Promise<object> {
20-
const msgService = new InvokeActorRequest();
21-
msgService.setActorId(actorId)
22-
msgService.setActorType(actorType);
23-
msgService.setMethod(methodName);
24-
25-
if (body) {
26-
// @todo: if body is any, do we have to figure out how to serialize in JS? (e.g. if object -> JSON.stringify?)
27-
msgService.setData(body);
28-
}
29-
30-
return new Promise((resolve, reject) => {
31-
const client = this.client.getClient();
32-
client.invokeActor(msgService, (err, res: InvokeActorResponse) => {
33-
if (err) {
34-
return reject(err);
35-
}
36-
37-
// https://docs.dapr.io/reference/api/secrets_api/#response-body
38-
const resData = Buffer.from(res.getData()).toString();
39-
40-
try {
41-
return resolve(JSON.parse(resData));
42-
} catch (e) {
43-
return resolve(resData as any);
44-
}
45-
});
46-
});
47-
}
48-
49-
async stateTransaction(actorType: string, actorId: string, operations: OperationType[]): Promise<void> {
50-
const transactionItems: TransactionalActorStateOperation[] = [];
51-
52-
for (const o of operations) {
53-
const transactionItem = new TransactionalActorStateOperation();
54-
transactionItem.setKey(o.request.key);
55-
transactionItem.setOperationtype(o.operation);
56-
57-
const msgSerialized = new Any();
58-
msgSerialized.setValue(Buffer.from(`${o.request.value}`, "utf-8"));
59-
transactionItem.setValue(msgSerialized);
60-
61-
transactionItems.push(transactionItem);
62-
}
63-
64-
const msgService = new ExecuteActorStateTransactionRequest();
65-
msgService.setActorType(actorType);
66-
msgService.setActorId(actorId);
67-
msgService.setOperationsList(transactionItems);
68-
69-
return new Promise((resolve, reject) => {
70-
const client = this.client.getClient();
71-
client.executeActorStateTransaction(msgService, (err, res) => {
72-
if (err) {
73-
return reject(err);
74-
}
75-
76-
// https://docs.dapr.io/reference/api/state_api/#request-body-1
77-
return resolve();
78-
});
79-
});
80-
}
81-
82-
async stateGet(actorType: string, actorId: string, key: string): Promise<KeyValueType | string> {
83-
const msgService = new GetActorStateRequest();
84-
msgService.setActorType(actorType);
85-
msgService.setActorId(actorId)
86-
msgService.setKey(key);
87-
88-
return new Promise((resolve, reject) => {
89-
const client = this.client.getClient();
90-
client.getActorState(msgService, (err, res: GetActorStateResponse) => {
91-
if (err) {
92-
return reject(err);
93-
}
94-
95-
// https://docs.dapr.io/reference/api/actors_api/#http-response-codes-2
96-
const resData = Buffer.from(res.getData()).toString();
97-
98-
try {
99-
const json = JSON.parse(resData);
100-
return resolve(json);
101-
} catch (e) {
102-
return resolve(resData);
103-
}
104-
});
105-
});
106-
}
107-
108-
async registerActorReminder(actorType: string, actorId: string, name: string, reminder: ActorReminderType): Promise<void> {
109-
const msgService = new RegisterActorReminderRequest();
110-
msgService.setActorType(actorType);
111-
msgService.setActorId(actorId);
112-
msgService.setName(name);
113-
114-
if (reminder.data) {
115-
msgService.setData(Buffer.from(reminder?.data.toString(), "utf-8"))
116-
}
117-
118-
if (reminder.period) {
119-
msgService.setPeriod(reminder.period.toString());
120-
}
121-
122-
if (reminder.dueTime) {
123-
msgService.setDueTime(reminder.dueTime.toString());
124-
}
125-
126-
return new Promise((resolve, reject) => {
127-
const client = this.client.getClient();
128-
client.registerActorReminder(msgService, (err, res) => {
129-
if (err) {
130-
return reject(err);
131-
}
132-
133-
// https://docs.dapr.io/reference/api/actors_api/#http-response-codes-3
134-
return resolve();
135-
});
136-
});
137-
}
138-
139-
async unregisterActorReminder(actorType: string, actorId: string, name: string): Promise<void> {
140-
const msgService = new UnregisterActorReminderRequest();
141-
msgService.setActorType(actorType);
142-
msgService.setActorId(actorId);
143-
msgService.setName(name);
144-
145-
return new Promise((resolve, reject) => {
146-
const client = this.client.getClient();
147-
client.unregisterActorReminder(msgService, (err, res) => {
148-
if (err) {
149-
return reject(err);
150-
}
151-
152-
// https://docs.dapr.io/reference/api/actors_api/#delete-actor-reminder
153-
return resolve();
154-
});
155-
});
156-
}
157-
158-
async registerActorTimer(actorType: string, actorId: string, name: string, timer: ActorTimerType): Promise<void> {
159-
const msgService = new RegisterActorTimerRequest();
160-
msgService.setActorType(actorType);
161-
msgService.setActorId(actorId);
162-
msgService.setName(name);
163-
164-
if (timer.callback) {
165-
msgService.setCallback(timer.callback);
166-
}
167-
168-
if (timer.data) {
169-
msgService.setData(Buffer.from(timer.data, "utf-8"))
170-
}
171-
172-
if (timer.period) {
173-
msgService.setPeriod(timer.period.toString());
174-
}
175-
176-
if (timer.dueTime) {
177-
msgService.setDueTime(timer.dueTime.toString());
178-
}
179-
180-
return new Promise((resolve, reject) => {
181-
const client = this.client.getClient();
182-
client.registerActorTimer(msgService, (err, res) => {
183-
if (err) {
184-
return reject(err);
185-
}
186-
187-
// https://docs.dapr.io/reference/api/actors_api/#http-response-codes-3
188-
return resolve();
189-
});
190-
});
191-
}
192-
193-
async unregisterActorTimer(actorType: string, actorId: string, name: string): Promise<void> {
194-
const msgService = new UnregisterActorTimerRequest();
195-
msgService.setActorType(actorType);
196-
msgService.setActorId(actorId);
197-
msgService.setName(name);
198-
199-
return new Promise((resolve, reject) => {
200-
const client = this.client.getClient();
201-
client.unregisterActorTimer(msgService, (err, res) => {
202-
if (err) {
203-
return reject(err);
204-
}
205-
206-
// https://docs.dapr.io/reference/api/actors_api/#delete-actor-timer
207-
return resolve();
208-
});
209-
});
210-
}
211-
212-
// @todo: cannot find this one
213-
// async deactivate(actorType: string, actorId: string): Promise<ResActorDeactivateDto> {
214-
// const msgService = new UnregisterActorTimerRequest();
215-
// msgService.setActorType(actorType);
216-
// msgService.setActorId(actorId);
217-
// msgService.setName(name);
218-
219-
// return new Promise(async (resolve, reject) => {
220-
// const client = await GRPCClientSingleton.getClient();
221-
// client.unregisterActorTimer(msgService, (err, res) => {
222-
// if (err) {
223-
// return reject(err);
224-
// }
225-
226-
// // https://docs.dapr.io/reference/api/actors_api/#delete-actor-timer
227-
// return resolve();
228-
// });
229-
// });
230-
// }
231-
232-
async getActors(): Promise<object> {
233-
return new Promise((resolve, reject) => {
234-
const client = this.client.getClient();
235-
236-
client.getMetadata(new Empty(), (err, res: GetMetadataResponse) => {
237-
if (err) {
238-
return reject(err);
239-
}
240-
241-
// https://docs.dapr.io/reference/api/actors_api/#http-response-codes-2
242-
return resolve(res.getActiveActorsCountList());
243-
});
244-
});
15+
// Note: actorTypeClass is required since Javascript compiled has type information erased
16+
// this means that we can't use T to new up an object (sadly enough) so we have to pass it
17+
create<T>(actorTypeClass: Class<T>): T {
18+
const builder = new ActorProxyBuilder<T>(actorTypeClass, this.client.getClientHost(), this.client.getClientPort(), this.client.getClientCommunicationProtocol(), this.client.getOptions());
19+
const actor = builder.build(ActorId.createRandomId());
20+
return actor;
24521
}
24622
}

0 commit comments

Comments
 (0)