Skip to content

Commit 1d2b90e

Browse files
ActorProxy implementation
Signed-off-by: Xavier Geerinck <[email protected]>
1 parent 82e37db commit 1d2b90e

18 files changed

+374
-376
lines changed

src/actors/ActorId.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import { v4 as uuidv4 } from 'uuid';
22

33
export default class ActorId {
4-
private readonly id: string;
4+
private readonly id: string;
55

6-
constructor(id: string) {
7-
this.id = id;
8-
}
6+
constructor(id: string) {
7+
this.id = id;
8+
}
99

10-
createRandomId(): ActorId {
11-
return new ActorId(uuidv4());
12-
}
13-
14-
getId() {
15-
return this.id;
16-
}
10+
static createRandomId(): ActorId {
11+
return new ActorId(uuidv4());
12+
}
13+
14+
getId() {
15+
return this.id;
16+
}
1717
}

src/actors/client/ActorProxy.ts

Lines changed: 0 additions & 38 deletions
This file was deleted.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { DaprClient } from "../..";
2+
import Class from "../../types/Class";
3+
import ActorClient from "./ActorClient/ActorClient";
4+
import ActorId from "../ActorId";
5+
6+
export default class ActorProxyBuilder<T> {
7+
actorClient: ActorClient;
8+
daprClient: DaprClient;
9+
actorTypeClass: Class<T>;
10+
11+
constructor(actorTypeClass: Class<T>, daprClient: DaprClient) {
12+
this.actorTypeClass = actorTypeClass;
13+
this.daprClient = daprClient;
14+
this.actorClient = new ActorClient(daprClient);
15+
}
16+
17+
build(actorId: ActorId): T {
18+
const self = this;
19+
20+
let handler = {
21+
get(target: any, propKey: any, receiver: any) {
22+
return async function (...args: any) {
23+
// console.log(`Invoking Actor "${self.actorTypeClass.name}" method "${propKey}"" with ${JSON.stringify(args)}`)
24+
25+
const method: "GET" | "PUT" = args.length > 0 ? "PUT" : "GET";
26+
const body = args.length > 0 ? args : null;
27+
const res = await self.actorClient.actor.invoke(method, self.actorTypeClass.name, actorId.getId(), propKey, body);
28+
29+
return res;
30+
};
31+
}
32+
};
33+
34+
35+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy
36+
// we implement a handler that will take a method and forward it to the actor client
37+
const proxy = new Proxy(this.actorTypeClass, handler);
38+
39+
// Return a NOT strongly typed API
40+
// @todo: this should return a strongly typed API as well, but requires reflection. How to do this in typescript?
41+
return proxy as unknown as T;
42+
}
43+
}

src/actors/client/ActorProxyFactory.interface.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/actors/client/ActorProxyFactory.ts

Lines changed: 0 additions & 17 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import Class from "../../types/Class";
2+
import ActorClient from "./ActorClient/ActorClient";
3+
import ActorId from "../ActorId";
4+
5+
export default class ActorProxyImpl<T> {
6+
actorClient: ActorClient;
7+
actorTypeClass: Class<T>;
8+
actorId: ActorId;
9+
10+
constructor(actorTypeClass: Class<T>, actorId: ActorId, actorClient: ActorClient) {
11+
this.actorTypeClass = actorTypeClass;
12+
this.actorClient = actorClient;
13+
this.actorId = actorId;
14+
}
15+
}

src/actors/runtime/AbstractActor.ts

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -73,26 +73,21 @@ export default abstract class AbstractActor {
7373
* @param <Type> Type of the state object
7474
* @return Async void response
7575
*/
76-
// async registerReminder<Type>(reminderName: string, state: Buffer, dueTime: string, period: string) {
77-
// const actorType = this.runtimeCtx.actorTypeInfo.name;
78-
// await this.runtimeCtx.daprClient.actor.reminderCreate(actorType, this.id.id, reminderName, {
79-
// period,
80-
// dueTime,
81-
// data: state
82-
// });
83-
// }
84-
85-
// async unregisterReminder(reminderName: string) {
86-
// const actorType = this.runtimeCtx.actorTypeInfo.name;
87-
// await this.runtimeCtx.daprClient.actor.reminderDelete(actorType, this.id.id, reminderName);
88-
// }
89-
90-
async registerTimer(timerName: string, callback: string, dueTime: Temporal.Duration, period: Temporal.Duration, state?: Buffer) {
91-
// // Make sure to activate the actor
92-
// const actor = await ActorRuntime.getInstance(this.client).getActorManager(this.actorType).getActiveActor(this.id);
76+
async registerReminder<Type>(reminderName: string, dueTime: Temporal.Duration, period: Temporal.Duration, state?: any) {
77+
await this.actorClient.actor.reminderCreate(this.actorType, this.id.getId(), reminderName, {
78+
period,
79+
dueTime,
80+
data: state
81+
});
82+
}
83+
84+
async unregisterReminder(reminderName: string) {
85+
await this.actorClient.actor.reminderDelete(this.actorType, this.id.getId(), reminderName);
86+
}
9387

88+
async registerTimer(timerName: string, callback: string, dueTime: Temporal.Duration, period: Temporal.Duration, state?: any) {
9489
// Register the timer in the sidecar
95-
console.log(`actorType: ${this.actorType}, actorId: ${this.id.getId()}, timerName: ${timerName}, callback: ${callback}, dueTime: ${dueTime.toString()}, period: ${period.toString()}`);
90+
// console.log(`actorType: ${this.actorType}, actorId: ${this.id.getId()}, timerName: ${timerName}, callback: ${callback}, dueTime: ${dueTime.toString()}, period: ${period.toString()}`);
9691
return await this.actorClient.actor.timerCreate(this.actorType, this.id.getId(), timerName, {
9792
period,
9893
dueTime,
@@ -101,6 +96,10 @@ export default abstract class AbstractActor {
10196
});
10297
}
10398

99+
async unregisterTimer(timerName: string) {
100+
await this.actorClient.actor.timerDelete(this.actorType, this.id.getId(), timerName);
101+
}
102+
104103
/**
105104
* Clears all state cache, calls the overridden onActivate and then saves the states
106105
* This method gets called when the actor is activated
@@ -207,7 +206,11 @@ export default abstract class AbstractActor {
207206
return this.stateManager;
208207
}
209208

210-
getId(): ActorId {
209+
getId(): string {
210+
return this.id.getId();
211+
}
212+
213+
getActorId(): ActorId {
211214
return this.id;
212215
}
213216

src/actors/runtime/ActorManager.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export default class ActorManager<T extends AbstractActor> {
115115
await this.callActorMethod(actorId, timerData.callback, timerData.state);
116116
}
117117

118-
async callActorMethod(actorId: ActorId, actorMethodName: string, ...args: any): Promise<Buffer> {
118+
async callActorMethod(actorId: ActorId, actorMethodName: string, args: any): Promise<Buffer> {
119119
const actorObject = await this.getActiveActor(actorId);
120120

121121
// Check if the actor method exists? Skip type-checking as it's the power of Javascript
@@ -131,8 +131,17 @@ export default class ActorManager<T extends AbstractActor> {
131131

132132
// Call the actor method, Skip type-checking as it's the power of Javascript
133133
await actorObject.onActorMethodPreInternal();
134-
// @ts-ignore
135-
const res = await actorObject[actorMethodName](...args);
134+
135+
let res;
136+
137+
if (typeof args === "object") {
138+
// @ts-ignore
139+
res = await actorObject[actorMethodName](...args);
140+
} else {
141+
// @ts-ignore
142+
res = await actorObject[actorMethodName](args);
143+
}
144+
136145
await actorObject.onActorMethodPostInternal();
137146

138147
return res;

src/actors/runtime/ActorStateManager.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export default class ActorStateManager<T> {
4040
return false;
4141
}
4242

43-
const didExist = await this.actor.getStateProvider().containsState(this.actor.getActorType(), this.actor.getId().getId(), stateName);
43+
const didExist = await this.actor.getStateProvider().containsState(this.actor.getActorType(), this.actor.getId(), stateName);
4444

4545
if (!didExist) {
4646
return false;
@@ -74,7 +74,7 @@ export default class ActorStateManager<T> {
7474
return [true, val !== undefined ? val : null];
7575
}
7676

77-
const [hasValue, value] = await this.actor.getStateProvider().tryLoadState(this.actor.getActorType(), this.actor.getId().getId(), stateName);
77+
const [hasValue, value] = await this.actor.getStateProvider().tryLoadState(this.actor.getActorType(), this.actor.getId(), stateName);
7878

7979

8080
if (hasValue) {
@@ -106,7 +106,7 @@ export default class ActorStateManager<T> {
106106
return;
107107
}
108108

109-
const didExist = await this.actor.getStateProvider().containsState(this.actor.getActorType(), this.actor.getId().getId(), stateName);
109+
const didExist = await this.actor.getStateProvider().containsState(this.actor.getActorType(), this.actor.getId(), stateName);
110110

111111
if (didExist) {
112112
stateChangeTracker.set(stateName, new StateMetadata(value, StateChangeKind.UPDATE));
@@ -141,7 +141,7 @@ export default class ActorStateManager<T> {
141141
return true;
142142
}
143143

144-
const didExist = await this.actor.getStateProvider().containsState(this.actor.getActorType(), this.actor.getId().getId(), stateName);
144+
const didExist = await this.actor.getStateProvider().containsState(this.actor.getActorType(), this.actor.getId(), stateName);
145145

146146
if (didExist) {
147147
stateChangeTracker.set(stateName, new StateMetadata(null as any, StateChangeKind.REMOVE));
@@ -159,7 +159,7 @@ export default class ActorStateManager<T> {
159159
return stateMetadata?.getChangeKind() !== StateChangeKind.REMOVE;
160160
}
161161

162-
const doesContainState = await this.actor.getStateProvider().containsState(this.actor.getActorType(), this.actor.getId().getId(), stateName);
162+
const doesContainState = await this.actor.getStateProvider().containsState(this.actor.getActorType(), this.actor.getId(), stateName);
163163
return doesContainState;
164164
}
165165

@@ -215,7 +215,7 @@ export default class ActorStateManager<T> {
215215
return newValue;
216216
}
217217

218-
const [hasValue, val] = await this.actor.getStateProvider().tryLoadState(this.actor.getActorType(), this.actor.getId().getId(), stateName);
218+
const [hasValue, val] = await this.actor.getStateProvider().tryLoadState(this.actor.getActorType(), this.actor.getId(), stateName);
219219

220220
if (hasValue) {
221221
const newValue = updateValueFactory(stateName, val);
@@ -273,7 +273,7 @@ export default class ActorStateManager<T> {
273273
});
274274

275275
if (stateChanges.length > 0) {
276-
await this.actor.getStateProvider().saveState(this.actor.getActorType(), this.actor.getId().getId(), stateChanges);
276+
await this.actor.getStateProvider().saveState(this.actor.getActorType(), this.actor.getId(), stateChanges);
277277
}
278278

279279
for (const stateName of statesToRemove) {

src/actors/runtime/BufferSerializer.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
export default class BufferSerializer {
2-
// constructor() {
3-
// }
4-
5-
deserialize(data: Buffer): string | object {
6-
let deserializedBody: string | object = data?.toString() || "";
2+
deserialize(data: Buffer): any {
3+
let deserializedBody: any = data?.toString() || "";
74

85
// Try to parse it to an object
96
// this way manager.invoke has string | object

0 commit comments

Comments
 (0)