Skip to content

Commit 7a494f2

Browse files
authored
Allow passing metadata to client.state.save and return a custom response with error (#461)
* Use metadata in statestore save Signed-off-by: Shubham Sharma <[email protected]> * Prettify Signed-off-by: Shubham Sharma <[email protected]> * Add e2e tests and fix gRPC metadata Signed-off-by: Shubham Sharma <[email protected]> * Fix e2e tests Signed-off-by: Shubham Sharma <[email protected]> * Final Signed-off-by: Shubham Sharma <[email protected]> * Address comments Signed-off-by: Shubham Sharma <[email protected]> --------- Signed-off-by: Shubham Sharma <[email protected]>
1 parent f9b99b6 commit 7a494f2

File tree

13 files changed

+484
-464
lines changed

13 files changed

+484
-464
lines changed

daprdocs/content/en/js-sdk-docs/js-client/_index.md

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -184,16 +184,27 @@ async function start() {
184184
const serviceStoreName = "my-state-store-name";
185185

186186
// Save State
187-
const response = await client.state.save(serviceStoreName, [
188-
{
189-
key: "first-key-name",
190-
value: "hello",
191-
},
187+
const response = await client.state.save(
188+
serviceStoreName,
189+
[
190+
{
191+
key: "first-key-name",
192+
value: "hello",
193+
metadata: {
194+
foo: "bar",
195+
},
196+
},
197+
{
198+
key: "second-key-name",
199+
value: "world",
200+
},
201+
],
192202
{
193-
key: "second-key-name",
194-
value: "world",
203+
metadata: {
204+
ttlInSeconds: "3", // this should override the ttl in the state item
205+
},
195206
},
196-
]);
207+
);
197208

198209
// Get State
199210
const response = await client.state.get(serviceStoreName, "first-key-name");

src/implementation/Client/GRPCClient/state.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ import { StateQueryResponseType } from "../../../types/state/StateQueryResponse.
3636
import { StateGetBulkOptions } from "../../../types/state/StateGetBulkOptions.type";
3737
import { Settings } from "../../../utils/Settings.util";
3838
import { addMetadataToMap } from "../../../utils/Client.util";
39+
import { StateSaveResponseType } from "../../../types/state/StateSaveResponseType";
40+
import { StateSaveOptions } from "../../../types/state/StateSaveOptions.type";
3941

4042
// https://docs.dapr.io/reference/api/state_api/
4143
export default class GRPCClientState implements IClientState {
@@ -45,7 +47,11 @@ export default class GRPCClientState implements IClientState {
4547
this.client = client;
4648
}
4749

48-
async save(storeName: string, stateObjects: KeyValuePairType[]): Promise<void> {
50+
async save(
51+
storeName: string,
52+
stateObjects: KeyValuePairType[],
53+
options: StateSaveOptions = {},
54+
): Promise<StateSaveResponseType> {
4955
const stateList: StateItem[] = [];
5056

5157
for (const stateObject of stateObjects) {
@@ -57,6 +63,11 @@ export default class GRPCClientState implements IClientState {
5763
"utf-8",
5864
),
5965
);
66+
// Merge metadata from stateObject and options.
67+
// Note, metadata from options will override metadata from stateObject.
68+
// See https://github.com/dapr/dapr/blob/029ec8cb7a1c88ec5d222bc2b0d1d53541217f19/pkg/http/api.go#L1525-L1532
69+
addMetadataToMap(si.getMetadataMap(), stateObject.metadata);
70+
addMetadataToMap(si.getMetadataMap(), options.metadata);
6071
stateList.push(si);
6172
}
6273

@@ -69,11 +80,11 @@ export default class GRPCClientState implements IClientState {
6980
return new Promise((resolve, reject) => {
7081
client.saveState(msgService, (err, _res) => {
7182
if (err) {
72-
return reject(err);
83+
return reject({ error: err });
7384
}
7485

7586
// https://docs.dapr.io/reference/api/state_api/#response-body
76-
return resolve();
87+
return resolve({});
7788
});
7889
});
7990
}
@@ -107,7 +118,7 @@ export default class GRPCClientState implements IClientState {
107118
});
108119
}
109120

110-
async getBulk(storeName: string, keys: string[], options: StateGetBulkOptions): Promise<KeyValueType[]> {
121+
async getBulk(storeName: string, keys: string[], options: StateGetBulkOptions = {}): Promise<KeyValueType[]> {
111122
const msgService = new GetBulkStateRequest();
112123
msgService.setStoreName(storeName);
113124
msgService.setKeysList(keys);

src/implementation/Client/HTTPClient/state.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,28 +22,46 @@ import { StateQueryResponseType } from "../../../types/state/StateQueryResponse.
2222
import { StateGetBulkOptions } from "../../../types/state/StateGetBulkOptions.type";
2323
import { createHTTPMetadataQueryParam } from "../../../utils/Client.util";
2424
import { Settings } from "../../../utils/Settings.util";
25+
import { Logger } from "../../../logger/Logger";
26+
import { StateSaveResponseType } from "../../../types/state/StateSaveResponseType";
27+
import { StateSaveOptions } from "../../../types/state/StateSaveOptions.type";
2528

2629
// https://docs.dapr.io/reference/api/state_api/
2730
export default class HTTPClientState implements IClientState {
2831
client: HTTPClient;
32+
private readonly logger: Logger;
2933

3034
constructor(client: HTTPClient) {
3135
this.client = client;
36+
this.logger = new Logger("HTTPClient", "State", client.options.logger);
3237
}
3338

34-
async save(storeName: string, stateObjects: KeyValuePairType[]): Promise<void> {
35-
await this.client.execute(`/state/${storeName}`, {
36-
method: "POST",
37-
body: stateObjects,
38-
});
39+
async save(
40+
storeName: string,
41+
stateObjects: KeyValuePairType[],
42+
options: StateSaveOptions = {},
43+
): Promise<StateSaveResponseType> {
44+
const queryParams = createHTTPMetadataQueryParam(options.metadata);
45+
46+
try {
47+
await this.client.execute(`/state/${storeName}?${queryParams}`, {
48+
method: "POST",
49+
body: stateObjects,
50+
});
51+
} catch (e: any) {
52+
this.logger.error(`Error saving state to store ${storeName}, error: ${e}`);
53+
return { error: e };
54+
}
55+
56+
return {};
3957
}
4058

4159
async get(storeName: string, key: string): Promise<KeyValueType | string> {
4260
const result = await this.client.execute(`/state/${storeName}/${key}`);
4361
return result as KeyValueType;
4462
}
4563

46-
async getBulk(storeName: string, keys: string[], options: StateGetBulkOptions): Promise<KeyValueType[]> {
64+
async getBulk(storeName: string, keys: string[], options: StateGetBulkOptions = {}): Promise<KeyValueType[]> {
4765
const queryParams = createHTTPMetadataQueryParam(options.metadata);
4866

4967
const result = await this.client.execute(`/state/${storeName}/bulk?${queryParams}`, {

src/interfaces/Client/IClientState.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ import { KeyValueType } from "../../types/KeyValue.type";
1818
import { StateQueryType } from "../../types/state/StateQuery.type";
1919
import { StateQueryResponseType } from "../../types/state/StateQueryResponse.type";
2020
import { StateGetBulkOptions } from "../../types/state/StateGetBulkOptions.type";
21+
import { StateSaveResponseType } from "../../types/state/StateSaveResponseType";
22+
import { StateSaveOptions } from "../../types/state/StateSaveOptions.type";
2123

2224
export default interface IClientState {
23-
save(storeName: string, stateObjects: KeyValuePairType[]): Promise<void>;
25+
save(storeName: string, stateObjects: KeyValuePairType[], options?: StateSaveOptions): Promise<StateSaveResponseType>;
2426
get(storeName: string, key: string): Promise<KeyValueType | string>;
2527
getBulk(storeName: string, keys: string[], options?: StateGetBulkOptions): Promise<KeyValueType[]>;
2628
delete(storeName: string, key: string): Promise<void>;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
Copyright 2023 The Dapr Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
import { KeyValueType } from "../KeyValue.type";
15+
16+
export type StateSaveOptions = {
17+
/**
18+
* Metadata to be passed to get save operation.
19+
*/
20+
metadata?: KeyValueType;
21+
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
Copyright 2023 The Dapr Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
/**
15+
* StateSaveResponseType defines the response from a save request.
16+
*/
17+
export type StateSaveResponseType = {
18+
// error contains the error if the save request failed.
19+
error?: Error;
20+
};

src/utils/Client.util.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { PubSubBulkPublishEntry } from "../types/pubsub/PubSubBulkPublishEntry.t
2424
import { PubSubBulkPublishResponse } from "../types/pubsub/PubSubBulkPublishResponse.type";
2525
import { PubSubBulkPublishMessage } from "../types/pubsub/PubSubBulkPublishMessage.type";
2626
import { PubSubBulkPublishApiResponse } from "../types/pubsub/PubSubBulkPublishApiResponse.type";
27+
2728
/**
2829
* Adds metadata to a map.
2930
* @param map Input map
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#
2+
# Copyright 2023 The Dapr Authors
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS,
9+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
# See the License for the specific language governing permissions and
11+
# limitations under the License.
12+
#
13+
14+
apiVersion: dapr.io/v1alpha1
15+
kind: Component
16+
metadata:
17+
name: state-mongodb
18+
spec:
19+
type: state.mongodb
20+
version: v1
21+
metadata:
22+
- name: host
23+
value: localhost:27017
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#
2+
# Copyright 2023 The Dapr Authors
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS,
9+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
# See the License for the specific language governing permissions and
11+
# limitations under the License.
12+
#
13+
14+
# https://docs.dapr.io/reference/components-reference/supported-bindings/rabbitmq/
15+
apiVersion: dapr.io/v1alpha1
16+
kind: Component
17+
metadata:
18+
name: state-redis
19+
namespace: default
20+
spec:
21+
type: state.redis
22+
version: v1
23+
metadata:
24+
- name: redisHost
25+
value: localhost:6379
26+
- name: redisPassword
27+
value: ""
28+
- name: enableTLS
29+
value: "false"
30+
- name: failover
31+
value: "false"
32+
- name: actorStateStore
33+
value: "true"

0 commit comments

Comments
 (0)