Skip to content

Commit 46c83d9

Browse files
authored
Add Actor ID validation (#539)
* Add Actor ID validation Signed-off-by: heunghingwan <[email protected]> * Validatie Actor ID cannot be empty Signed-off-by: heunghingwan <[email protected]> * Encode URI instead of checking Signed-off-by: heunghingwan <[email protected]> * Make empty check more general Signed-off-by: heunghingwan <[email protected]> * Update ActorId.ts Signed-off-by: heunghingwan <[email protected]> * only use encoded actor id in http protocol Signed-off-by: heunghingwan <[email protected]> * fix prettier error Signed-off-by: heunghingwan <[email protected]> --------- Signed-off-by: heunghingwan <[email protected]>
1 parent f661574 commit 46c83d9

File tree

3 files changed

+37
-10
lines changed

3 files changed

+37
-10
lines changed

src/actors/ActorId.ts

+7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ export default class ActorId {
1717
private readonly id: string;
1818

1919
constructor(id: string) {
20+
if (!id) {
21+
throw new Error("ActorId cannot be empty");
22+
}
2023
this.id = id;
2124
}
2225

@@ -28,6 +31,10 @@ export default class ActorId {
2831
return this.id;
2932
}
3033

34+
getURLSafeId() {
35+
return encodeURIComponent(this.id);
36+
}
37+
3138
toString() {
3239
return this.id;
3340
}

src/actors/client/ActorClient/ActorClientHTTP.ts

+10-10
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export default class ActorClientHTTP implements IClientActor {
2828
}
2929

3030
async invoke(actorType: string, actorId: ActorId, methodName: string, body?: any): Promise<object> {
31-
const result = await this.client.execute(`/actors/${actorType}/${actorId.getId()}/method/${methodName}`, {
31+
const result = await this.client.execute(`/actors/${actorType}/${actorId.getURLSafeId()}/method/${methodName}`, {
3232
method: "POST", // we always use POST calls for Invoking (ref: https://github.com/dapr/js-sdk/pull/137#discussion_r772636068)
3333
body,
3434
});
@@ -37,7 +37,7 @@ export default class ActorClientHTTP implements IClientActor {
3737
}
3838

3939
async stateTransaction(actorType: string, actorId: ActorId, operations: OperationType[]): Promise<void> {
40-
await this.client.execute(`/actors/${actorType}/${actorId.getId()}/state`, {
40+
await this.client.execute(`/actors/${actorType}/${actorId.getURLSafeId()}/state`, {
4141
method: "POST",
4242
headers: {
4343
"Content-Type": "application/json",
@@ -47,7 +47,7 @@ export default class ActorClientHTTP implements IClientActor {
4747
}
4848

4949
async stateGet(actorType: string, actorId: ActorId, key: string): Promise<KeyValueType | string> {
50-
const result = await this.client.execute(`/actors/${actorType}/${actorId.getId()}/state/${key}`);
50+
const result = await this.client.execute(`/actors/${actorType}/${actorId.getURLSafeId()}/state/${key}`);
5151
return result as any;
5252
}
5353

@@ -57,7 +57,7 @@ export default class ActorClientHTTP implements IClientActor {
5757
name: string,
5858
reminder: ActorReminderType,
5959
): Promise<void> {
60-
await this.client.execute(`/actors/${actorType}/${actorId.getId()}/reminders/${name}`, {
60+
await this.client.execute(`/actors/${actorType}/${actorId.getURLSafeId()}/reminders/${name}`, {
6161
method: "POST",
6262
headers: {
6363
"Content-Type": "application/json",
@@ -72,18 +72,18 @@ export default class ActorClientHTTP implements IClientActor {
7272
}
7373

7474
async reminderGet(actorType: string, actorId: ActorId, name: string): Promise<object> {
75-
const result = await this.client.execute(`/actors/${actorType}/${actorId.getId()}/reminders/${name}`);
75+
const result = await this.client.execute(`/actors/${actorType}/${actorId.getURLSafeId()}/reminders/${name}`);
7676
return result as object;
7777
}
7878

7979
async unregisterActorReminder(actorType: string, actorId: ActorId, name: string): Promise<void> {
80-
await this.client.execute(`/actors/${actorType}/${actorId.getId()}/reminders/${name}`, {
80+
await this.client.execute(`/actors/${actorType}/${actorId.getURLSafeId()}/reminders/${name}`, {
8181
method: "DELETE",
8282
});
8383
}
8484

8585
async registerActorTimer(actorType: string, actorId: ActorId, name: string, timer: ActorTimerType): Promise<void> {
86-
await this.client.execute(`/actors/${actorType}/${actorId.getId()}/timers/${name}`, {
86+
await this.client.execute(`/actors/${actorType}/${actorId.getURLSafeId()}/timers/${name}`, {
8787
method: "POST",
8888
headers: {
8989
"Content-Type": "application/json",
@@ -99,13 +99,13 @@ export default class ActorClientHTTP implements IClientActor {
9999
}
100100

101101
async unregisterActorTimer(actorType: string, actorId: ActorId, name: string): Promise<void> {
102-
await this.client.execute(`/actors/${actorType}/${actorId.getId()}/timers/${name}`, {
102+
await this.client.execute(`/actors/${actorType}/${actorId.getURLSafeId()}/timers/${name}`, {
103103
method: "DELETE",
104104
});
105105
}
106106

107-
async deactivate(actorType: string, actorId: string): Promise<void> {
108-
await this.client.execute(`/actors/${actorType}/${actorId}`, {
107+
async deactivate(actorType: string, actorId: ActorId): Promise<void> {
108+
await this.client.execute(`/actors/${actorType}/${actorId.getURLSafeId()}`, {
109109
method: "DELETE",
110110
});
111111
}

test/e2e/http/actors.test.ts

+20
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,26 @@ describe("http/actors", () => {
123123
});
124124
});
125125

126+
describe("actorId", () => {
127+
it("should be able to create an actorId", () => {
128+
const actorId = ActorId.createRandomId();
129+
expect(actorId.getId()).toBeDefined();
130+
expect(actorId.getURLSafeId()).toBeDefined();
131+
expect(actorId.toString()).toBeDefined();
132+
});
133+
134+
it("should not be able to create an actorId with an empty string", () => {
135+
expect(() => new ActorId("")).toThrowError("ActorId cannot be empty");
136+
});
137+
138+
it("should be able to create an actorId with url unsafe characters like '/'", () => {
139+
const actorId = new ActorId("test/actor");
140+
expect(actorId.getURLSafeId()).toEqual("test%2Factor");
141+
expect(actorId.getId()).toEqual("test/actor");
142+
expect(actorId.toString()).toEqual("test/actor");
143+
});
144+
});
145+
126146
describe("actorProxy", () => {
127147
it("should be able to create an actor object through the proxy", async () => {
128148
const builder = new ActorProxyBuilder<DemoActorCounterInterface>(DemoActorCounterImpl, client);

0 commit comments

Comments
 (0)