mirror of https://github.com/dapr/java-sdk.git
Query State Preview API implementation. (#697)
* Query State Preview API implementation. Signed-off-by: Mukundan Sundararajan <msundar.ms@outlook.com> * Use latest dapr ref and fix grpc query state api Signed-off-by: Mukundan Sundararajan <msundar.ms@outlook.com> * fix service invocation automatic unesacpe Signed-off-by: Mukundan Sundararajan <msundar.ms@outlook.com> * add more unit tests Signed-off-by: Mukundan Sundararajan <msundar.ms@outlook.com> * Add query state API docs Signed-off-by: Mukundan Sundararajan <msundar.ms@outlook.com> * Fix example to be user friendly Signed-off-by: Mukundan Sundararajan <msundar.ms@outlook.com> * Fix example in docs Signed-off-by: Mukundan Sundararajan <msundar.ms@outlook.com> * make pagination immutable Signed-off-by: Mukundan Sundararajan <msundar.ms@outlook.com> Co-authored-by: Artur Souza <artursouza.ms@outlook.com>
This commit is contained in:
parent
e3fa24f67b
commit
06d92dafca
|
@ -20,7 +20,7 @@ jobs:
|
||||||
matrix:
|
matrix:
|
||||||
java: [ 11, 13, 15, 16 ]
|
java: [ 11, 13, 15, 16 ]
|
||||||
env:
|
env:
|
||||||
GOVER: 1.15.0
|
GOVER: 1.17.7
|
||||||
GOOS: linux
|
GOOS: linux
|
||||||
GOARCH: amd64
|
GOARCH: amd64
|
||||||
GOPROXY: https://proxy.golang.org
|
GOPROXY: https://proxy.golang.org
|
||||||
|
@ -29,7 +29,7 @@ jobs:
|
||||||
DAPR_RUNTIME_VER: 1.6.0-rc.2
|
DAPR_RUNTIME_VER: 1.6.0-rc.2
|
||||||
DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/v1.6.0-rc.1/install/install.sh
|
DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/v1.6.0-rc.1/install/install.sh
|
||||||
DAPR_CLI_REF:
|
DAPR_CLI_REF:
|
||||||
DAPR_REF:
|
DAPR_REF: 5a307f3deaa1b322f7945179adad0403de80eb7e
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Set up OpenJDK ${{ env.JDK_VER }}
|
- name: Set up OpenJDK ${{ env.JDK_VER }}
|
||||||
|
@ -91,6 +91,10 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
docker-compose -f ./sdk-tests/deploy/local-test-vault.yml up -d
|
docker-compose -f ./sdk-tests/deploy/local-test-vault.yml up -d
|
||||||
docker ps
|
docker ps
|
||||||
|
- name: Install Local mongo database using docker-compose
|
||||||
|
run: |
|
||||||
|
docker-compose -f ./sdk-tests/deploy/local-test-mongo.yml up -d
|
||||||
|
docker ps
|
||||||
- name: Clean up files
|
- name: Clean up files
|
||||||
run: mvn clean
|
run: mvn clean
|
||||||
- name: Build sdk
|
- name: Build sdk
|
||||||
|
|
|
@ -31,7 +31,7 @@ jobs:
|
||||||
matrix:
|
matrix:
|
||||||
java: [ 11, 13, 15, 16 ]
|
java: [ 11, 13, 15, 16 ]
|
||||||
env:
|
env:
|
||||||
GOVER: 1.15.0
|
GOVER: 1.17.7
|
||||||
GOOS: linux
|
GOOS: linux
|
||||||
GOARCH: amd64
|
GOARCH: amd64
|
||||||
GOPROXY: https://proxy.golang.org
|
GOPROXY: https://proxy.golang.org
|
||||||
|
@ -40,7 +40,7 @@ jobs:
|
||||||
DAPR_RUNTIME_VER: 1.6.0-rc.2
|
DAPR_RUNTIME_VER: 1.6.0-rc.2
|
||||||
DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/v1.6.0-rc.1/install/install.sh
|
DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/v1.6.0-rc.1/install/install.sh
|
||||||
DAPR_CLI_REF:
|
DAPR_CLI_REF:
|
||||||
DAPR_REF:
|
DAPR_REF: 5a307f3deaa1b322f7945179adad0403de80eb7e
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Set up OpenJDK ${{ env.JDK_VER }}
|
- name: Set up OpenJDK ${{ env.JDK_VER }}
|
||||||
|
@ -108,6 +108,10 @@ jobs:
|
||||||
sudo apt-get install vault
|
sudo apt-get install vault
|
||||||
# Verify vault is installed
|
# Verify vault is installed
|
||||||
vault -h
|
vault -h
|
||||||
|
- name: Install Local mongo database using docker-compose
|
||||||
|
run: |
|
||||||
|
docker-compose -f ./sdk-tests/deploy/local-test-mongo.yml up -d
|
||||||
|
docker ps
|
||||||
- name: Clean up files
|
- name: Clean up files
|
||||||
run: mvn clean
|
run: mvn clean
|
||||||
- name: Build sdk
|
- name: Build sdk
|
||||||
|
@ -154,3 +158,7 @@ jobs:
|
||||||
working-directory: ./examples
|
working-directory: ./examples
|
||||||
run: |
|
run: |
|
||||||
mm.py ./src/main/java/io/dapr/examples/configuration/grpc/README.md
|
mm.py ./src/main/java/io/dapr/examples/configuration/grpc/README.md
|
||||||
|
- name: Validate query state HTTP example
|
||||||
|
working-directory: ./examples
|
||||||
|
run: |
|
||||||
|
mm.py ./src/main/java/io/dapr/examples/querystate/README.md
|
|
@ -241,6 +241,8 @@ public interface DemoActor {
|
||||||
|
|
||||||
### Get & Subscribe to application configurations
|
### Get & Subscribe to application configurations
|
||||||
|
|
||||||
|
> Note this is a preview API and thus will only be accessible via the DaprPreviewClient interface and not the normal DaprClient interface
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import io.dapr.client.DaprClientBuilder;
|
import io.dapr.client.DaprClientBuilder;
|
||||||
import io.dapr.client.DaprPreviewClient;
|
import io.dapr.client.DaprPreviewClient;
|
||||||
|
@ -267,5 +269,76 @@ try (DaprPreviewClient client = (new DaprClientBuilder()).buildPreviewClient())
|
||||||
- For a full list of configuration operations visit [How-To: Manage configuration from a store]({{< ref howto-manage-configuration.md >}}).
|
- For a full list of configuration operations visit [How-To: Manage configuration from a store]({{< ref howto-manage-configuration.md >}}).
|
||||||
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/configuration) for code samples and instructions to try out different configuration operations.
|
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/configuration) for code samples and instructions to try out different configuration operations.
|
||||||
|
|
||||||
|
### Query saved state
|
||||||
|
|
||||||
|
> Note this is a preview API and thus will only be accessible via the DaprPreviewClient interface and not the normal DaprClient interface
|
||||||
|
|
||||||
|
```java
|
||||||
|
import io.dapr.client.DaprClient;
|
||||||
|
import io.dapr.client.DaprClientBuilder;
|
||||||
|
import io.dapr.client.DaprPreviewClient;
|
||||||
|
import io.dapr.client.domain.QueryStateItem;
|
||||||
|
import io.dapr.client.domain.QueryStateRequest;
|
||||||
|
import io.dapr.client.domain.QueryStateResponse;
|
||||||
|
import io.dapr.client.domain.query.Query;
|
||||||
|
import io.dapr.client.domain.query.Sorting;
|
||||||
|
import io.dapr.client.domain.query.filters.EqFilter;
|
||||||
|
|
||||||
|
try (DaprClient client = builder.build(); DaprPreviewClient previewClient = builder.buildPreviewClient()) {
|
||||||
|
String searchVal = args.length == 0 ? "searchValue" : args[0];
|
||||||
|
|
||||||
|
// Create JSON data
|
||||||
|
Listing first = new Listing();
|
||||||
|
first.setPropertyType("apartment");
|
||||||
|
first.setId("1000");
|
||||||
|
...
|
||||||
|
Listing second = new Listing();
|
||||||
|
second.setPropertyType("row-house");
|
||||||
|
second.setId("1002");
|
||||||
|
...
|
||||||
|
Listing third = new Listing();
|
||||||
|
third.setPropertyType("apartment");
|
||||||
|
third.setId("1003");
|
||||||
|
...
|
||||||
|
Listing fourth = new Listing();
|
||||||
|
fourth.setPropertyType("apartment");
|
||||||
|
fourth.setId("1001");
|
||||||
|
...
|
||||||
|
Map<String, String> meta = new HashMap<>();
|
||||||
|
meta.put("contentType", "application/json");
|
||||||
|
|
||||||
|
// Save state
|
||||||
|
SaveStateRequest request = new SaveStateRequest(STATE_STORE_NAME).setStates(
|
||||||
|
new State<>("1", first, null, meta, null),
|
||||||
|
new State<>("2", second, null, meta, null),
|
||||||
|
new State<>("3", third, null, meta, null),
|
||||||
|
new State<>("4", fourth, null, meta, null)
|
||||||
|
);
|
||||||
|
client.saveBulkState(request).block();
|
||||||
|
|
||||||
|
|
||||||
|
// Create query and query state request
|
||||||
|
|
||||||
|
Query query = new Query()
|
||||||
|
.setFilter(new EqFilter<>("propertyType", "apartment"))
|
||||||
|
.setSort(Arrays.asList(new Sorting("id", Sorting.Order.DESC)));
|
||||||
|
QueryStateRequest request = new QueryStateRequest(STATE_STORE_NAME)
|
||||||
|
.setQuery(query);
|
||||||
|
|
||||||
|
// Use preview client to call query state API
|
||||||
|
QueryStateResponse<MyData> result = previewClient.queryState(request, MyData.class).block();
|
||||||
|
|
||||||
|
// View Query state response
|
||||||
|
System.out.println("Found " + result.getResults().size() + " items.");
|
||||||
|
for (QueryStateItem<Listing> item : result.getResults()) {
|
||||||
|
System.out.println("Key: " + item.getKey());
|
||||||
|
System.out.println("Data: " + item.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
- For a full list of configuration operations visit [How-To: Query state]({{< ref howto-state-query-api.md >}}).
|
||||||
|
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/querystate) for complete code sample.
|
||||||
|
|
||||||
## Related links
|
## Related links
|
||||||
- [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples)
|
- [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples)
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
apiVersion: dapr.io/v1alpha1
|
||||||
|
kind: Component
|
||||||
|
metadata:
|
||||||
|
name: mongo-statestore
|
||||||
|
spec:
|
||||||
|
type: state.mongodb
|
||||||
|
version: v1
|
||||||
|
metadata:
|
||||||
|
- name: host
|
||||||
|
value: localhost:27017
|
||||||
|
- name: databaseName
|
||||||
|
value: local
|
||||||
|
- name: collectionName
|
||||||
|
value: propertyCollection
|
|
@ -61,7 +61,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.os72</groupId>
|
<groupId>com.github.os72</groupId>
|
||||||
<artifactId>protoc-jar-maven-plugin</artifactId>
|
<artifactId>protoc-jar-maven-plugin</artifactId>
|
||||||
<version>3.10.1</version>
|
<version>3.11.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
@ -130,7 +130,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>com.github.os72</groupId>
|
<groupId>com.github.os72</groupId>
|
||||||
<artifactId>protoc-jar-maven-plugin</artifactId>
|
<artifactId>protoc-jar-maven-plugin</artifactId>
|
||||||
<version>3.10.1</version>
|
<version>3.11.4</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<phase>generate-sources</phase>
|
<phase>generate-sources</phase>
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Dapr Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.dapr.examples.querystate;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class Listing {
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String propertyType;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String city;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String state;
|
||||||
|
|
||||||
|
public Listing() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPropertyType() {
|
||||||
|
return propertyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPropertyType(String propertyType) {
|
||||||
|
this.propertyType = propertyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCity() {
|
||||||
|
return city;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCity(String city) {
|
||||||
|
this.city = city;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(String state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Listing{"
|
||||||
|
+ "propertyType='" + propertyType + '\''
|
||||||
|
+ ", id=" + id
|
||||||
|
+ ", city='" + city + '\''
|
||||||
|
+ ", state='" + state + '\''
|
||||||
|
+ '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Listing listing = (Listing) o;
|
||||||
|
return id == listing.id
|
||||||
|
&& propertyType.equals(listing.propertyType)
|
||||||
|
&& Objects.equals(city, listing.city)
|
||||||
|
&& Objects.equals(state, listing.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(propertyType, id, city, state);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Dapr Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.dapr.examples.querystate;
|
||||||
|
|
||||||
|
import io.dapr.client.DaprClient;
|
||||||
|
import io.dapr.client.DaprClientBuilder;
|
||||||
|
import io.dapr.client.DaprPreviewClient;
|
||||||
|
import io.dapr.client.domain.QueryStateItem;
|
||||||
|
import io.dapr.client.domain.QueryStateRequest;
|
||||||
|
import io.dapr.client.domain.QueryStateResponse;
|
||||||
|
import io.dapr.client.domain.SaveStateRequest;
|
||||||
|
import io.dapr.client.domain.State;
|
||||||
|
import io.dapr.client.domain.query.Query;
|
||||||
|
import io.dapr.client.domain.query.Sorting;
|
||||||
|
import io.dapr.client.domain.query.filters.EqFilter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Build and install jars:
|
||||||
|
* mvn clean install
|
||||||
|
* 2. cd [repo root]/examples
|
||||||
|
* 3. send a message to be saved as state:
|
||||||
|
* dapr run --components-path ./components/state -- \
|
||||||
|
* java -Ddapr.api.protocol=HTTP -jar target/dapr-java-sdk-examples-exec.jar \
|
||||||
|
* io.dapr.examples.querystate.QuerySavedState 'my message'
|
||||||
|
*/
|
||||||
|
public class QuerySavedState {
|
||||||
|
|
||||||
|
private static final String STATE_STORE_NAME = "mongo-statestore";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the sate actions.
|
||||||
|
* @param args messages to be sent as state value.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
DaprClientBuilder builder = new DaprClientBuilder();
|
||||||
|
try (DaprClient client = builder.build(); DaprPreviewClient previewClient = builder.buildPreviewClient()) {
|
||||||
|
System.out.println("Waiting for Dapr sidecar ...");
|
||||||
|
client.waitForSidecar(10000).block();
|
||||||
|
System.out.println("Dapr sidecar is ready.");
|
||||||
|
|
||||||
|
Listing first = new Listing();
|
||||||
|
first.setPropertyType("apartment");
|
||||||
|
first.setId("1000");
|
||||||
|
first.setCity("Seattle");
|
||||||
|
first.setState("WA");
|
||||||
|
Listing second = new Listing();
|
||||||
|
second.setPropertyType("row-house");
|
||||||
|
second.setId("1002");
|
||||||
|
second.setCity("Seattle");
|
||||||
|
second.setState("WA");
|
||||||
|
Listing third = new Listing();
|
||||||
|
third.setPropertyType("apartment");
|
||||||
|
third.setId("1003");
|
||||||
|
third.setCity("Portland");
|
||||||
|
third.setState("OR");
|
||||||
|
Listing fourth = new Listing();
|
||||||
|
fourth.setPropertyType("apartment");
|
||||||
|
fourth.setId("1001");
|
||||||
|
fourth.setCity("Portland");
|
||||||
|
fourth.setState("OR");
|
||||||
|
Map<String, String> meta = new HashMap<>();
|
||||||
|
meta.put("contentType", "application/json");
|
||||||
|
|
||||||
|
SaveStateRequest request = new SaveStateRequest(STATE_STORE_NAME).setStates(
|
||||||
|
new State<>("1", first, null, meta, null),
|
||||||
|
new State<>("2", second, null, meta, null),
|
||||||
|
new State<>("3", third, null, meta, null),
|
||||||
|
new State<>("4", fourth, null, meta, null)
|
||||||
|
);
|
||||||
|
client.saveBulkState(request).block();
|
||||||
|
|
||||||
|
System.out.println("Insert key: 1" + ", data: " + first);
|
||||||
|
System.out.println("Insert key: 2" + ", data: " + second);
|
||||||
|
System.out.println("Insert key: 3" + ", data: " + third);
|
||||||
|
System.out.println("Insert key: 4" + ", data: " + fourth);
|
||||||
|
|
||||||
|
|
||||||
|
Query query = new Query()
|
||||||
|
.setFilter(new EqFilter<>("propertyType", "apartment"))
|
||||||
|
.setSort(Arrays.asList(new Sorting("id", Sorting.Order.DESC)));
|
||||||
|
|
||||||
|
QueryStateRequest queryStateRequest = new QueryStateRequest(STATE_STORE_NAME)
|
||||||
|
.setQuery(query);
|
||||||
|
|
||||||
|
QueryStateResponse<Listing> result = previewClient.queryState(queryStateRequest, Listing.class).block();
|
||||||
|
|
||||||
|
System.out.println("Found " + result.getResults().size() + " items.");
|
||||||
|
for (QueryStateItem<Listing> item : result.getResults()) {
|
||||||
|
System.out.println("Key: " + item.getKey());
|
||||||
|
System.out.println("Data: " + item.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is an example, so for simplicity we are just exiting here.
|
||||||
|
// Normally a dapr app would be a web service and not exit main.
|
||||||
|
System.out.println("Done");
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,279 @@
|
||||||
|
## State management sample
|
||||||
|
|
||||||
|
This sample illustrates the capabilities provided by Dapr Java SDK for querying states. For further information about querying saved state please refer to [this link](https://docs.dapr.io/developing-applications/building-blocks/state-management/howto-state-query-api/)
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
|
||||||
|
* [Dapr and Dapr Cli](https://docs.dapr.io/getting-started/install-dapr/).
|
||||||
|
* Java JDK 11 (or greater): [Oracle JDK](https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK11) or [OpenJDK](https://jdk.java.net/13/).
|
||||||
|
* [Apache Maven](https://maven.apache.org/install.html) version 3.x.
|
||||||
|
|
||||||
|
### Checking out the code
|
||||||
|
|
||||||
|
Clone this repository:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/dapr/java-sdk.git
|
||||||
|
cd java-sdk
|
||||||
|
```
|
||||||
|
|
||||||
|
Then build the Maven project:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# make sure you are in the `java-sdk` directory.
|
||||||
|
mvn install
|
||||||
|
```
|
||||||
|
|
||||||
|
Then change into the `examples` directory:
|
||||||
|
```sh
|
||||||
|
cd examples
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running the StateClient
|
||||||
|
This example uses the Java SDK Dapr client in order to save bulk state and query state, in this case, an instance of a class. See the code snippets below:
|
||||||
|
|
||||||
|
The class saved and queried for is as below:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class Listing {
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String propertyType;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String city;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String state;
|
||||||
|
|
||||||
|
public Listing() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPropertyType() {
|
||||||
|
return propertyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPropertyType(String propertyType) {
|
||||||
|
this.propertyType = propertyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCity() {
|
||||||
|
return city;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCity(String city) {
|
||||||
|
this.city = city;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(String state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Listing{"
|
||||||
|
+ "propertyType='" + propertyType + '\''
|
||||||
|
+ ", id=" + id
|
||||||
|
+ ", city='" + city + '\''
|
||||||
|
+ ", state='" + state + '\''
|
||||||
|
+ '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Listing listing = (Listing) o;
|
||||||
|
return id == listing.id
|
||||||
|
&& propertyType.equals(listing.propertyType)
|
||||||
|
&& Objects.equals(city, listing.city)
|
||||||
|
&& Objects.equals(state, listing.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(propertyType, id, city, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The main application class for the example is as follows:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class QuerySavedState {
|
||||||
|
|
||||||
|
public static class MyData {
|
||||||
|
///...
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String STATE_STORE_NAME = "querystatestore";
|
||||||
|
|
||||||
|
private static final String FIRST_KEY_NAME = "key1";
|
||||||
|
|
||||||
|
private static final String SECOND_KEY_NAME = "key2";
|
||||||
|
|
||||||
|
private static final String THIRD_KEY_NAME = "key3";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the sate actions.
|
||||||
|
* @param args messages to be sent as state value.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
DaprClientBuilder builder = new DaprClientBuilder();
|
||||||
|
try (DaprClient client = builder.build(); DaprPreviewClient previewClient = builder.buildPreviewClient()) {
|
||||||
|
System.out.println("Waiting for Dapr sidecar ...");
|
||||||
|
client.waitForSidecar(10000).block();
|
||||||
|
System.out.println("Dapr sidecar is ready.");
|
||||||
|
|
||||||
|
Listing first = new Listing();
|
||||||
|
first.setPropertyType("apartment");
|
||||||
|
first.setId("1000");
|
||||||
|
first.setCity("Seattle");
|
||||||
|
first.setState("WA");
|
||||||
|
Listing second = new Listing();
|
||||||
|
second.setPropertyType("row-house");
|
||||||
|
second.setId("1002");
|
||||||
|
second.setCity("Seattle");
|
||||||
|
second.setState("WA");
|
||||||
|
Listing third = new Listing();
|
||||||
|
third.setPropertyType("apartment");
|
||||||
|
third.setId("1003");
|
||||||
|
third.setCity("Portland");
|
||||||
|
third.setState("OR");
|
||||||
|
Listing fourth = new Listing();
|
||||||
|
fourth.setPropertyType("apartment");
|
||||||
|
fourth.setId("1001");
|
||||||
|
fourth.setCity("Portland");
|
||||||
|
fourth.setState("OR");
|
||||||
|
Map<String, String> meta = new HashMap<>();
|
||||||
|
meta.put("contentType", "application/json");
|
||||||
|
|
||||||
|
SaveStateRequest request = new SaveStateRequest(STATE_STORE_NAME).setStates(
|
||||||
|
new State<>("1", first, null, meta, null),
|
||||||
|
new State<>("2", second, null, meta, null),
|
||||||
|
new State<>("3", third, null, meta, null),
|
||||||
|
new State<>("4", fourth, null, meta, null)
|
||||||
|
);
|
||||||
|
client.saveBulkState(request).block();
|
||||||
|
|
||||||
|
System.out.println("Insert key: 1" + ", data: " + first);
|
||||||
|
System.out.println("Insert key: 2" + ", data: " + second);
|
||||||
|
System.out.println("Insert key: 3" + ", data: " + third);
|
||||||
|
System.out.println("Insert key: 4" + ", data: " + fourth);
|
||||||
|
|
||||||
|
|
||||||
|
Query query = new Query()
|
||||||
|
.setFilter(new EqFilter<>("propertyType", "apartment"))
|
||||||
|
.setSort(Arrays.asList(new Sorting("id", Sorting.Order.DESC)));
|
||||||
|
|
||||||
|
QueryStateRequest queryStateRequest = new QueryStateRequest(STATE_STORE_NAME)
|
||||||
|
.setQuery(query);
|
||||||
|
|
||||||
|
QueryStateResponse<Listing> result = previewClient.queryState(queryStateRequest, Listing.class).block();
|
||||||
|
|
||||||
|
System.out.println("Found " + result.getResults().size() + " items.");
|
||||||
|
for (QueryStateItem<Listing> item : result.getResults()) {
|
||||||
|
System.out.println("Key: " + item.getKey());
|
||||||
|
System.out.println("Data: " + item.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is an example, so for simplicity we are just exiting here.
|
||||||
|
// Normally a dapr app would be a web service and not exit main.
|
||||||
|
System.out.println("Done");
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The code uses the `DaprClient` created by the `DaprClientBuilder` for waiting for sidecar to start as well as to save state. Notice that this builder uses default settings. Internally, it is using `DefaultObjectSerializer` for two properties: `objectSerializer` is for Dapr's sent and received objects, and `stateSerializer` is for objects to be persisted.
|
||||||
|
|
||||||
|
The code uses the `DaprPreviewClient` created by the `DaprClientBuilder` is used for the `queryState` preview API.
|
||||||
|
|
||||||
|
This example performs multiple operations:
|
||||||
|
* `client.waitForSidecar(...)` for waiting until Dapr sidecar is ready.
|
||||||
|
* `client.saveBulkState(...)` for persisting an instance of `Listing`.
|
||||||
|
* `client.query(...)` operation in order to query for persisted state.
|
||||||
|
|
||||||
|
The Dapr clients are also within a try-with-resource block to properly close the clients at the end.
|
||||||
|
|
||||||
|
### Running the example
|
||||||
|
<!-- STEP
|
||||||
|
name: Check state example
|
||||||
|
expected_stdout_lines:
|
||||||
|
- "== APP == Waiting for Dapr sidecar ..."
|
||||||
|
- "== APP == Dapr sidecar is ready."
|
||||||
|
- "== APP == Insert key: 1, data: Listing{propertyType='apartment', id=1000, city='Seattle', state='WA'}"
|
||||||
|
- "== APP == Insert key: 2, data: Listing{propertyType='row-house', id=1002, city='Seattle', state='WA'}"
|
||||||
|
- "== APP == Insert key: 3, data: Listing{propertyType='apartment', id=1003, city='Portland', state='OR'}"
|
||||||
|
- "== APP == Insert key: 4, data: Listing{propertyType='apartment', id=1001, city='Portland', state='OR'}"
|
||||||
|
- "== APP == Found 3 items."
|
||||||
|
- "== APP == Key: 3"
|
||||||
|
- "== APP == Data: Listing{propertyType='apartment', id=1003, city='Portland', state='OR'}"
|
||||||
|
- "== APP == Key: 4"
|
||||||
|
- "== APP == Data: Listing{propertyType='apartment', id=1001, city='Portland', state='OR'}"
|
||||||
|
- "== APP == Key: 1"
|
||||||
|
- "== APP == Data: Listing{propertyType='apartment', id=1000, city='Seattle', state='WA'}"
|
||||||
|
- "== APP == Done"
|
||||||
|
background: true
|
||||||
|
sleep: 10
|
||||||
|
-->
|
||||||
|
|
||||||
|
Run this example with the following command:
|
||||||
|
```bash
|
||||||
|
dapr run --components-path ./components/state --app-id query_state_example -H 3600 -- java -Ddapr.api.protocol=HTTP -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.querystate.QuerySavedState
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- END_STEP -->
|
||||||
|
|
||||||
|
Once running, the QuerySaveState example should print the output as follows:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
== APP == Waiting for Dapr sidecar ...
|
||||||
|
== APP == Dapr sidecar is ready.
|
||||||
|
== APP == Insert key: 1, data: Listing{propertyType='apartment', id=1000, city='Seattle', state='WA'}
|
||||||
|
== APP == Insert key: 2, data: Listing{propertyType='row-house', id=1002, city='Seattle', state='WA'}
|
||||||
|
== APP == Insert key: 3, data: Listing{propertyType='apartment', id=1003, city='Portland', state='OR'}
|
||||||
|
== APP == Insert key: 4, data: Listing{propertyType='apartment', id=1001, city='Portland', state='OR'}
|
||||||
|
== APP == Found 3 items.
|
||||||
|
== APP == Key: 3
|
||||||
|
== APP == Data: Listing{propertyType='apartment', id=1003, city='Portland', state='OR'}
|
||||||
|
== APP == Key: 4
|
||||||
|
== APP == Data: Listing{propertyType='apartment', id=1001, city='Portland', state='OR'}
|
||||||
|
== APP == Key: 1
|
||||||
|
== APP == Data: Listing{propertyType='apartment', id=1000, city='Seattle', state='WA'}
|
||||||
|
== APP == Done
|
||||||
|
```
|
||||||
|
Note that the output is got in the descending order of the field `id` and all the `propertyType` field values are the same `apartment`.
|
||||||
|
|
||||||
|
### Cleanup
|
||||||
|
|
||||||
|
To close the app either press `CTRL+C` or run
|
||||||
|
|
||||||
|
<!-- STEP
|
||||||
|
name: Cleanup
|
||||||
|
-->
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dapr stop --app-id query_state_example
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- END_STEP -->
|
|
@ -167,43 +167,26 @@ dapr run --components-path ./components/state --app-id state_example -- java -ja
|
||||||
|
|
||||||
<!-- END_STEP -->
|
<!-- END_STEP -->
|
||||||
|
|
||||||
Once running, the OutputBindingExample should print the output as follows:
|
Once running, the StateClient should print the output as follows:
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
== APP == Waiting for Dapr sidecar ...
|
== APP == Waiting for Dapr sidecar ...
|
||||||
|
|
||||||
== APP == Dapr sidecar is ready.
|
== APP == Dapr sidecar is ready.
|
||||||
|
|
||||||
== APP == Saving class with message: my message
|
== APP == Saving class with message: my message
|
||||||
|
|
||||||
== APP == Retrieved class message from state: my message
|
== APP == Retrieved class message from state: my message
|
||||||
|
|
||||||
== APP == Updating previous state and adding another state 'test state'...
|
== APP == Updating previous state and adding another state 'test state'...
|
||||||
|
|
||||||
== APP == Saving updated class with message: my message updated
|
== APP == Saving updated class with message: my message updated
|
||||||
|
|
||||||
== APP == Retrieved messages using bulk get:
|
== APP == Retrieved messages using bulk get:
|
||||||
|
|
||||||
== APP == StateKeyValue{key='myKey', value=my message updated, etag='2', metadata={'{}'}, error='null', options={'null'}}
|
== APP == StateKeyValue{key='myKey', value=my message updated, etag='2', metadata={'{}'}, error='null', options={'null'}}
|
||||||
|
|
||||||
== APP == StateKeyValue{key='myKey2', value=test message, etag='1', metadata={'{}'}, error='null', options={'null'}}
|
== APP == StateKeyValue{key='myKey2', value=test message, etag='1', metadata={'{}'}, error='null', options={'null'}}
|
||||||
|
|
||||||
== APP == Deleting states...
|
== APP == Deleting states...
|
||||||
|
|
||||||
== APP == Verify delete key request is aborted if an etag different from stored is passed.
|
== APP == Verify delete key request is aborted if an etag different from stored is passed.
|
||||||
|
|
||||||
== APP == Expected failure. ABORTED: failed deleting state with key myKey: possible etag mismatch. error from state store: ERR Error running script (call to f_9b5da7354cb61e2ca9faff50f6c43b81c73c0b94): @user_script:1: user_script:1: failed to delete Tailmad-Fang||myKey
|
== APP == Expected failure. ABORTED: failed deleting state with key myKey: possible etag mismatch. error from state store: ERR Error running script (call to f_9b5da7354cb61e2ca9faff50f6c43b81c73c0b94): @user_script:1: user_script:1: failed to delete Tailmad-Fang||myKey
|
||||||
|
|
||||||
== APP == Trying to delete again with correct etag.
|
== APP == Trying to delete again with correct etag.
|
||||||
|
|
||||||
== APP == Trying to retrieve deleted states:
|
== APP == Trying to retrieve deleted states:
|
||||||
|
|
||||||
== APP == StateKeyValue{key='myKey', value=null, etag='null', metadata={'{}'}, error='null', options={'null'}}
|
== APP == StateKeyValue{key='myKey', value=null, etag='null', metadata={'{}'}, error='null', options={'null'}}
|
||||||
|
|
||||||
== APP == StateKeyValue{key='myKey2', value=null, etag='null', metadata={'{}'}, error='null', options={'null'}}
|
== APP == StateKeyValue{key='myKey2', value=null, etag='null', metadata={'{}'}, error='null', options={'null'}}
|
||||||
|
|
||||||
== APP == Done
|
== APP == Done
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cleanup
|
### Cleanup
|
||||||
|
|
6
pom.xml
6
pom.xml
|
@ -14,9 +14,9 @@
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<grpc.version>1.39.0</grpc.version>
|
<grpc.version>1.42.1</grpc.version>
|
||||||
<protobuf.version>3.13.0</protobuf.version>
|
<protobuf.version>3.17.3</protobuf.version>
|
||||||
<dapr.proto.baseurl>https://raw.githubusercontent.com/dapr/dapr/v1.6.0-rc.3/dapr/proto</dapr.proto.baseurl>
|
<dapr.proto.baseurl>https://raw.githubusercontent.com/dapr/dapr/5a307f3deaa1b322f7945179adad0403de80eb7e/dapr/proto</dapr.proto.baseurl>
|
||||||
<os-maven-plugin.version>1.6.2</os-maven-plugin.version>
|
<os-maven-plugin.version>1.6.2</os-maven-plugin.version>
|
||||||
<maven-dependency-plugin.version>3.1.1</maven-dependency-plugin.version>
|
<maven-dependency-plugin.version>3.1.1</maven-dependency-plugin.version>
|
||||||
<maven-antrun-plugin.version>1.8</maven-antrun-plugin.version>
|
<maven-antrun-plugin.version>1.8</maven-antrun-plugin.version>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.deploy.skip>false</maven.deploy.skip>
|
<maven.deploy.skip>false</maven.deploy.skip>
|
||||||
<grpc.version>1.39.0</grpc.version>
|
<grpc.version>1.42.1</grpc.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<protobuf.output.directory>${project.build.directory}/generated-sources</protobuf.output.directory>
|
<protobuf.output.directory>${project.build.directory}/generated-sources</protobuf.output.directory>
|
||||||
<protobuf.input.directory>${project.build.directory}/proto</protobuf.input.directory>
|
<protobuf.input.directory>${project.build.directory}/proto</protobuf.input.directory>
|
||||||
<maven.deploy.skip>false</maven.deploy.skip>
|
<maven.deploy.skip>false</maven.deploy.skip>
|
||||||
<grpc.version>1.39.0</grpc.version>
|
<grpc.version>1.42.1</grpc.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
apiVersion: dapr.io/v1alpha1
|
||||||
|
kind: Component
|
||||||
|
metadata:
|
||||||
|
name: mongo-statestore
|
||||||
|
spec:
|
||||||
|
type: state.mongodb
|
||||||
|
version: v1
|
||||||
|
metadata:
|
||||||
|
- name: host
|
||||||
|
value: localhost:27017
|
||||||
|
- name: databaseName
|
||||||
|
value: local
|
||||||
|
- name: collectionName
|
||||||
|
value: testCollection
|
|
@ -0,0 +1,6 @@
|
||||||
|
version: '2'
|
||||||
|
services:
|
||||||
|
mongo:
|
||||||
|
image: mongo
|
||||||
|
ports:
|
||||||
|
- "27017:27017"
|
|
@ -17,8 +17,8 @@
|
||||||
<dapr.sdk.version>1.5.0-SNAPSHOT</dapr.sdk.version>
|
<dapr.sdk.version>1.5.0-SNAPSHOT</dapr.sdk.version>
|
||||||
<protobuf.output.directory>${project.build.directory}/generated-sources</protobuf.output.directory>
|
<protobuf.output.directory>${project.build.directory}/generated-sources</protobuf.output.directory>
|
||||||
<protobuf.input.directory>${project.basedir}/proto</protobuf.input.directory>
|
<protobuf.input.directory>${project.basedir}/proto</protobuf.input.directory>
|
||||||
<grpc.version>1.39.0</grpc.version>
|
<grpc.version>1.42.1</grpc.version>
|
||||||
<protobuf.version>3.13.0</protobuf.version>
|
<protobuf.version>3.17.3</protobuf.version>
|
||||||
<opentelemetry.version>0.14.0</opentelemetry.version>
|
<opentelemetry.version>0.14.0</opentelemetry.version>
|
||||||
<spring-boot.version>2.3.5.RELEASE</spring-boot.version>
|
<spring-boot.version>2.3.5.RELEASE</spring-boot.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.os72</groupId>
|
<groupId>com.github.os72</groupId>
|
||||||
<artifactId>protoc-jar-maven-plugin</artifactId>
|
<artifactId>protoc-jar-maven-plugin</artifactId>
|
||||||
<version>3.10.1</version>
|
<version>3.11.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.opentelemetry</groupId>
|
<groupId>io.opentelemetry</groupId>
|
||||||
|
@ -149,7 +149,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>com.github.os72</groupId>
|
<groupId>com.github.os72</groupId>
|
||||||
<artifactId>protoc-jar-maven-plugin</artifactId>
|
<artifactId>protoc-jar-maven-plugin</artifactId>
|
||||||
<version>3.10.1</version>
|
<version>3.11.4</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<phase>generate-sources</phase>
|
<phase>generate-sources</phase>
|
||||||
|
|
|
@ -30,6 +30,8 @@ public abstract class BaseIT {
|
||||||
|
|
||||||
protected static final String STATE_STORE_NAME = "statestore";
|
protected static final String STATE_STORE_NAME = "statestore";
|
||||||
|
|
||||||
|
protected static final String QUERY_STATE_STORE = "mongo-statestore";
|
||||||
|
|
||||||
private static final Map<String, DaprRun.Builder> DAPR_RUN_BUILDERS = new HashMap<>();
|
private static final Map<String, DaprRun.Builder> DAPR_RUN_BUILDERS = new HashMap<>();
|
||||||
|
|
||||||
private static final Queue<Stoppable> TO_BE_STOPPED = new LinkedList<>();
|
private static final Queue<Stoppable> TO_BE_STOPPED = new LinkedList<>();
|
||||||
|
|
|
@ -13,10 +13,19 @@ limitations under the License.
|
||||||
|
|
||||||
package io.dapr.it.state;
|
package io.dapr.it.state;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import io.dapr.client.DaprClient;
|
import io.dapr.client.DaprClient;
|
||||||
|
import io.dapr.client.DaprPreviewClient;
|
||||||
|
import io.dapr.client.domain.QueryStateItem;
|
||||||
|
import io.dapr.client.domain.QueryStateRequest;
|
||||||
|
import io.dapr.client.domain.QueryStateResponse;
|
||||||
|
import io.dapr.client.domain.SaveStateRequest;
|
||||||
import io.dapr.client.domain.State;
|
import io.dapr.client.domain.State;
|
||||||
import io.dapr.client.domain.StateOptions;
|
import io.dapr.client.domain.StateOptions;
|
||||||
import io.dapr.client.domain.TransactionalStateOperation;
|
import io.dapr.client.domain.TransactionalStateOperation;
|
||||||
|
import io.dapr.client.domain.query.Query;
|
||||||
|
import io.dapr.client.domain.query.Sorting;
|
||||||
|
import io.dapr.client.domain.query.filters.EqFilter;
|
||||||
import io.dapr.exceptions.DaprException;
|
import io.dapr.exceptions.DaprException;
|
||||||
import io.dapr.it.BaseIT;
|
import io.dapr.it.BaseIT;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -24,8 +33,11 @@ import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
@ -39,6 +51,7 @@ import static org.junit.Assert.assertTrue;
|
||||||
* Common test cases for Dapr client (GRPC and HTTP).
|
* Common test cases for Dapr client (GRPC and HTTP).
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractStateClientIT extends BaseIT {
|
public abstract class AbstractStateClientIT extends BaseIT {
|
||||||
|
private static final Logger logger = Logger.getLogger(AbstractStateClientIT.class.getName());
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void saveAndGetState() {
|
public void saveAndGetState() {
|
||||||
|
@ -126,6 +139,78 @@ public abstract class AbstractStateClientIT extends BaseIT {
|
||||||
assertNull(result.stream().skip(2).findFirst().get().getError());
|
assertNull(result.stream().skip(2).findFirst().get().getError());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void saveAndQueryAndDeleteState() throws JsonProcessingException {
|
||||||
|
final String stateKeyOne = UUID.randomUUID().toString();
|
||||||
|
final String stateKeyTwo = UUID.randomUUID().toString();
|
||||||
|
final String stateKeyThree = UUID.randomUUID().toString();
|
||||||
|
final String commonSearchValue = UUID.randomUUID().toString();
|
||||||
|
Map<String,String> meta = new HashMap<>();
|
||||||
|
meta.put("contentType", "application/json");
|
||||||
|
|
||||||
|
DaprClient daprClient = buildDaprClient();
|
||||||
|
DaprPreviewClient previewApiClient = (DaprPreviewClient) daprClient;
|
||||||
|
|
||||||
|
//saves the states.
|
||||||
|
MyData data = new MyData();
|
||||||
|
data.setPropertyA(commonSearchValue);
|
||||||
|
data.setPropertyB("query");
|
||||||
|
State<MyData> state = new State<>(stateKeyOne, data, null, meta, null );
|
||||||
|
SaveStateRequest request = new SaveStateRequest(QUERY_STATE_STORE).setStates(state);
|
||||||
|
daprClient.saveBulkState(request).block();
|
||||||
|
data = new MyData();
|
||||||
|
data.setPropertyA(commonSearchValue);
|
||||||
|
data.setPropertyB("query");
|
||||||
|
state = new State<>(stateKeyTwo, data, null, meta, null );
|
||||||
|
request = new SaveStateRequest(QUERY_STATE_STORE).setStates(state);
|
||||||
|
daprClient.saveBulkState(request).block();
|
||||||
|
data = new MyData();
|
||||||
|
data.setPropertyA("CA");
|
||||||
|
data.setPropertyB("no query");
|
||||||
|
state = new State<>(stateKeyThree, data, null, meta, null );
|
||||||
|
request = new SaveStateRequest(QUERY_STATE_STORE).setStates(state);
|
||||||
|
daprClient.saveBulkState(request).block();
|
||||||
|
|
||||||
|
|
||||||
|
QueryStateRequest queryStateRequest = new QueryStateRequest(QUERY_STATE_STORE);
|
||||||
|
Query query = new Query().setFilter(new EqFilter<>("propertyA", commonSearchValue))
|
||||||
|
.setSort(Arrays.asList(new Sorting("propertyB", Sorting.Order.ASC)));
|
||||||
|
queryStateRequest.setQuery(query).setMetadata(meta);
|
||||||
|
|
||||||
|
Mono<QueryStateResponse<MyData>> response = previewApiClient.queryState(queryStateRequest, MyData.class);
|
||||||
|
QueryStateResponse<MyData> result = response.block();
|
||||||
|
|
||||||
|
// Assert that the response is not null
|
||||||
|
assertNotNull(result);
|
||||||
|
List<QueryStateItem<MyData>> items = result.getResults();
|
||||||
|
assertNotNull(items);
|
||||||
|
|
||||||
|
QueryStateItem<MyData> item;
|
||||||
|
//Assert that the response is the correct one
|
||||||
|
assertEquals(2, items.size());
|
||||||
|
assertTrue(items.stream().anyMatch(f -> f.getKey().equals(stateKeyOne)));
|
||||||
|
item = items.stream().filter(f -> f.getKey().equals(stateKeyOne)).findFirst().get();
|
||||||
|
assertNotNull(item);
|
||||||
|
assertEquals(commonSearchValue, item.getValue().getPropertyA());
|
||||||
|
assertEquals("query", item.getValue().getPropertyB());
|
||||||
|
assertNull(item.getError());
|
||||||
|
|
||||||
|
assertTrue(items.stream().anyMatch(f -> f.getKey().equals(stateKeyTwo)));
|
||||||
|
item = items.stream().filter(f -> f.getKey().equals(stateKeyTwo)).findFirst().get();
|
||||||
|
assertEquals(commonSearchValue, item.getValue().getPropertyA());
|
||||||
|
assertEquals("query", item.getValue().getPropertyB());
|
||||||
|
assertNull(item.getError());
|
||||||
|
|
||||||
|
assertFalse(items.stream().anyMatch(f -> f.getKey().equals(stateKeyThree)));
|
||||||
|
|
||||||
|
assertEquals(2L, items.stream().filter(f -> f.getValue().getPropertyB().equals("query")).count());
|
||||||
|
|
||||||
|
//delete all states
|
||||||
|
daprClient.deleteState(QUERY_STATE_STORE, stateKeyOne).block();
|
||||||
|
daprClient.deleteState(QUERY_STATE_STORE, stateKeyTwo).block();
|
||||||
|
daprClient.deleteState(QUERY_STATE_STORE, stateKeyThree).block();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void saveUpdateAndGetState() {
|
public void saveUpdateAndGetState() {
|
||||||
|
|
||||||
|
@ -159,6 +244,7 @@ public abstract class AbstractStateClientIT extends BaseIT {
|
||||||
State<MyData> myDataResponse = response.block();
|
State<MyData> myDataResponse = response.block();
|
||||||
|
|
||||||
//review that the update was success action
|
//review that the update was success action
|
||||||
|
assertNotNull("expected non null response", myDataResponse);
|
||||||
assertEquals("data in property A", myDataResponse.getValue().getPropertyA());
|
assertEquals("data in property A", myDataResponse.getValue().getPropertyA());
|
||||||
assertEquals("data in property B2", myDataResponse.getValue().getPropertyB());
|
assertEquals("data in property B2", myDataResponse.getValue().getPropertyB());
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.deploy.skip>false</maven.deploy.skip>
|
<maven.deploy.skip>false</maven.deploy.skip>
|
||||||
<grpc.version>1.39.0</grpc.version>
|
<grpc.version>1.42.1</grpc.version>
|
||||||
<argLine>
|
<argLine>
|
||||||
--add-opens java.base/java.util=ALL-UNNAMED
|
--add-opens java.base/java.util=ALL-UNNAMED
|
||||||
</argLine>
|
</argLine>
|
||||||
|
|
|
@ -13,6 +13,7 @@ limitations under the License.
|
||||||
|
|
||||||
package io.dapr.client;
|
package io.dapr.client;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import io.dapr.client.domain.ConfigurationItem;
|
import io.dapr.client.domain.ConfigurationItem;
|
||||||
import io.dapr.client.domain.DeleteStateRequest;
|
import io.dapr.client.domain.DeleteStateRequest;
|
||||||
import io.dapr.client.domain.ExecuteStateTransactionRequest;
|
import io.dapr.client.domain.ExecuteStateTransactionRequest;
|
||||||
|
@ -25,11 +26,14 @@ import io.dapr.client.domain.HttpExtension;
|
||||||
import io.dapr.client.domain.InvokeBindingRequest;
|
import io.dapr.client.domain.InvokeBindingRequest;
|
||||||
import io.dapr.client.domain.InvokeMethodRequest;
|
import io.dapr.client.domain.InvokeMethodRequest;
|
||||||
import io.dapr.client.domain.PublishEventRequest;
|
import io.dapr.client.domain.PublishEventRequest;
|
||||||
|
import io.dapr.client.domain.QueryStateRequest;
|
||||||
|
import io.dapr.client.domain.QueryStateResponse;
|
||||||
import io.dapr.client.domain.SaveStateRequest;
|
import io.dapr.client.domain.SaveStateRequest;
|
||||||
import io.dapr.client.domain.State;
|
import io.dapr.client.domain.State;
|
||||||
import io.dapr.client.domain.StateOptions;
|
import io.dapr.client.domain.StateOptions;
|
||||||
import io.dapr.client.domain.SubscribeConfigurationRequest;
|
import io.dapr.client.domain.SubscribeConfigurationRequest;
|
||||||
import io.dapr.client.domain.TransactionalStateOperation;
|
import io.dapr.client.domain.TransactionalStateOperation;
|
||||||
|
import io.dapr.client.domain.query.Query;
|
||||||
import io.dapr.serializer.DaprObjectSerializer;
|
import io.dapr.serializer.DaprObjectSerializer;
|
||||||
import io.dapr.utils.TypeRef;
|
import io.dapr.utils.TypeRef;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
|
@ -50,6 +54,11 @@ import java.util.stream.Collectors;
|
||||||
*/
|
*/
|
||||||
abstract class AbstractDaprClient implements DaprClient, DaprPreviewClient {
|
abstract class AbstractDaprClient implements DaprClient, DaprPreviewClient {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mapper to serialize JSON request objects.
|
||||||
|
*/
|
||||||
|
protected static final ObjectMapper JSON_REQUEST_MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A utility class for serialize and deserialize the transient objects.
|
* A utility class for serialize and deserialize the transient objects.
|
||||||
*/
|
*/
|
||||||
|
@ -303,6 +312,82 @@ abstract class AbstractDaprClient implements DaprClient, DaprPreviewClient {
|
||||||
return this.getState(storeName, key, options, TypeRef.get(clazz));
|
return this.getState(storeName, key, options, TypeRef.get(clazz));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Mono<QueryStateResponse<T>> queryState(String storeName, String query, Map<String, String> metadata,
|
||||||
|
Class<T> clazz) {
|
||||||
|
return this.queryState(new QueryStateRequest(storeName).setQueryString(query).setMetadata(metadata), clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Mono<QueryStateResponse<T>> queryState(String storeName, String query, Map<String, String> metadata,
|
||||||
|
TypeRef<T> type) {
|
||||||
|
return this.queryState(new QueryStateRequest(storeName).setQueryString(query).setMetadata(metadata), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Mono<QueryStateResponse<T>> queryState(String storeName, String query, Class<T> clazz) {
|
||||||
|
return this.queryState(new QueryStateRequest(storeName).setQueryString(query), clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Mono<QueryStateResponse<T>> queryState(String storeName, String query, TypeRef<T> type) {
|
||||||
|
return this.queryState(new QueryStateRequest(storeName).setQueryString(query), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Mono<QueryStateResponse<T>> queryState(String storeName, Query query, Map<String, String> metadata,
|
||||||
|
Class<T> clazz) {
|
||||||
|
return this.queryState(new QueryStateRequest(storeName).setQuery(query).setMetadata(metadata), clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Mono<QueryStateResponse<T>> queryState(String storeName, Query query, Map<String, String> metadata,
|
||||||
|
TypeRef<T> type) {
|
||||||
|
return this.queryState(new QueryStateRequest(storeName).setQuery(query).setMetadata(metadata), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Mono<QueryStateResponse<T>> queryState(String storeName, Query query, Class<T> clazz) {
|
||||||
|
return this.queryState(new QueryStateRequest(storeName).setQuery(query), clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Mono<QueryStateResponse<T>> queryState(String storeName, Query query, TypeRef<T> type) {
|
||||||
|
return this.queryState(new QueryStateRequest(storeName).setQuery(query), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Mono<QueryStateResponse<T>> queryState(QueryStateRequest request, Class<T> clazz) {
|
||||||
|
return this.queryState(request, TypeRef.get(clazz));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -29,6 +29,9 @@ import io.dapr.client.domain.HttpExtension;
|
||||||
import io.dapr.client.domain.InvokeBindingRequest;
|
import io.dapr.client.domain.InvokeBindingRequest;
|
||||||
import io.dapr.client.domain.InvokeMethodRequest;
|
import io.dapr.client.domain.InvokeMethodRequest;
|
||||||
import io.dapr.client.domain.PublishEventRequest;
|
import io.dapr.client.domain.PublishEventRequest;
|
||||||
|
import io.dapr.client.domain.QueryStateItem;
|
||||||
|
import io.dapr.client.domain.QueryStateRequest;
|
||||||
|
import io.dapr.client.domain.QueryStateResponse;
|
||||||
import io.dapr.client.domain.SaveStateRequest;
|
import io.dapr.client.domain.SaveStateRequest;
|
||||||
import io.dapr.client.domain.State;
|
import io.dapr.client.domain.State;
|
||||||
import io.dapr.client.domain.StateOptions;
|
import io.dapr.client.domain.StateOptions;
|
||||||
|
@ -414,7 +417,7 @@ public class DaprClientGrpc extends AbstractDaprClient {
|
||||||
if (metadata != null) {
|
if (metadata != null) {
|
||||||
builder.putAllMetadata(metadata);
|
builder.putAllMetadata(metadata);
|
||||||
}
|
}
|
||||||
for (TransactionalStateOperation<?> operation: operations) {
|
for (TransactionalStateOperation<?> operation : operations) {
|
||||||
DaprProtos.TransactionalStateOperation.Builder operationBuilder = DaprProtos.TransactionalStateOperation
|
DaprProtos.TransactionalStateOperation.Builder operationBuilder = DaprProtos.TransactionalStateOperation
|
||||||
.newBuilder();
|
.newBuilder();
|
||||||
operationBuilder.setOperationType(operation.getOperation().toString().toLowerCase());
|
operationBuilder.setOperationType(operation.getOperation().toString().toLowerCase());
|
||||||
|
@ -656,10 +659,87 @@ public class DaprClientGrpc extends AbstractDaprClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Mono<QueryStateResponse<T>> queryState(QueryStateRequest request, TypeRef<T> type) {
|
||||||
|
try {
|
||||||
|
if (request == null) {
|
||||||
|
throw new IllegalArgumentException("Query state request cannot be null.");
|
||||||
|
}
|
||||||
|
final String storeName = request.getStoreName();
|
||||||
|
final Map<String, String> metadata = request.getMetadata();
|
||||||
|
if ((storeName == null) || (storeName.trim().isEmpty())) {
|
||||||
|
throw new IllegalArgumentException("State store name cannot be null or empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
String queryString;
|
||||||
|
if (request.getQuery() != null) {
|
||||||
|
queryString = JSON_REQUEST_MAPPER.writeValueAsString(request.getQuery());
|
||||||
|
} else if (request.getQueryString() != null) {
|
||||||
|
queryString = request.getQueryString();
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Both query and queryString fields are not set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
DaprProtos.QueryStateRequest.Builder builder = DaprProtos.QueryStateRequest.newBuilder()
|
||||||
|
.setStoreName(storeName)
|
||||||
|
.setQuery(queryString);
|
||||||
|
if (metadata != null) {
|
||||||
|
builder.putAllMetadata(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
DaprProtos.QueryStateRequest envelope = builder.build();
|
||||||
|
|
||||||
|
return Mono.subscriberContext().flatMap(
|
||||||
|
context -> this.<DaprProtos.QueryStateResponse>createMono(
|
||||||
|
it -> intercept(context, asyncStub).queryStateAlpha1(envelope, it)
|
||||||
|
)
|
||||||
|
).map(
|
||||||
|
it -> {
|
||||||
|
Map<String, String> resultMeta = it.getMetadataMap();
|
||||||
|
String token = it.getToken();
|
||||||
|
List<QueryStateItem<T>> res = it.getResultsList()
|
||||||
|
.stream()
|
||||||
|
.map(v -> {
|
||||||
|
try {
|
||||||
|
return buildQueryStateKeyValue(v, type);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw DaprException.propagate(e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return new QueryStateResponse<>(res, token).setMetadata(metadata);
|
||||||
|
});
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return DaprException.wrapMono(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> QueryStateItem<T> buildQueryStateKeyValue(
|
||||||
|
DaprProtos.QueryStateItem item,
|
||||||
|
TypeRef<T> type) throws IOException {
|
||||||
|
String key = item.getKey();
|
||||||
|
String error = item.getError();
|
||||||
|
if (!Strings.isNullOrEmpty(error)) {
|
||||||
|
return new QueryStateItem<>(key, null, error);
|
||||||
|
}
|
||||||
|
ByteString payload = item.getData();
|
||||||
|
byte[] data = payload == null ? null : payload.toByteArray();
|
||||||
|
T value = stateSerializer.deserialize(data, type);
|
||||||
|
String etag = item.getEtag();
|
||||||
|
if (etag.equals("")) {
|
||||||
|
etag = null;
|
||||||
|
}
|
||||||
|
return new QueryStateItem<>(key, value, etag);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the ManagedChannel for GRPC.
|
* Closes the ManagedChannel for GRPC.
|
||||||
* @see io.grpc.ManagedChannel#shutdown()
|
*
|
||||||
* @throws IOException on exception.
|
* @throws IOException on exception.
|
||||||
|
* @see io.grpc.ManagedChannel#shutdown()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void close() throws Exception {
|
public void close() throws Exception {
|
||||||
|
|
|
@ -27,6 +27,9 @@ import io.dapr.client.domain.HttpExtension;
|
||||||
import io.dapr.client.domain.InvokeBindingRequest;
|
import io.dapr.client.domain.InvokeBindingRequest;
|
||||||
import io.dapr.client.domain.InvokeMethodRequest;
|
import io.dapr.client.domain.InvokeMethodRequest;
|
||||||
import io.dapr.client.domain.PublishEventRequest;
|
import io.dapr.client.domain.PublishEventRequest;
|
||||||
|
import io.dapr.client.domain.QueryStateItem;
|
||||||
|
import io.dapr.client.domain.QueryStateRequest;
|
||||||
|
import io.dapr.client.domain.QueryStateResponse;
|
||||||
import io.dapr.client.domain.SaveStateRequest;
|
import io.dapr.client.domain.SaveStateRequest;
|
||||||
import io.dapr.client.domain.State;
|
import io.dapr.client.domain.State;
|
||||||
import io.dapr.client.domain.StateOptions;
|
import io.dapr.client.domain.StateOptions;
|
||||||
|
@ -44,6 +47,7 @@ import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -192,17 +196,21 @@ public class DaprClientHttp extends AbstractDaprClient {
|
||||||
if (method == null || method.trim().isEmpty()) {
|
if (method == null || method.trim().isEmpty()) {
|
||||||
throw new IllegalArgumentException("Method name cannot be null or empty.");
|
throw new IllegalArgumentException("Method name cannot be null or empty.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String[] methodSegments = method.split("/");
|
||||||
|
|
||||||
|
List<String> pathSegments = new ArrayList<>(Arrays.asList(DaprHttp.API_VERSION, "invoke", appId, "method"));
|
||||||
|
pathSegments.addAll(Arrays.asList(methodSegments));
|
||||||
|
|
||||||
byte[] serializedRequestBody = objectSerializer.serialize(request);
|
byte[] serializedRequestBody = objectSerializer.serialize(request);
|
||||||
|
|
||||||
String[] pathSegments = new String[]{ DaprHttp.API_VERSION, "invoke", appId, "method", method };
|
|
||||||
|
|
||||||
final Map<String, String> headers = new HashMap<>();
|
final Map<String, String> headers = new HashMap<>();
|
||||||
if (contentType != null && !contentType.isEmpty()) {
|
if (contentType != null && !contentType.isEmpty()) {
|
||||||
headers.put("content-type", contentType);
|
headers.put("content-type", contentType);
|
||||||
}
|
}
|
||||||
headers.putAll(httpExtension.getHeaders());
|
headers.putAll(httpExtension.getHeaders());
|
||||||
Mono<DaprHttp.Response> response = Mono.subscriberContext().flatMap(
|
Mono<DaprHttp.Response> response = Mono.subscriberContext().flatMap(
|
||||||
context -> this.client.invokeApi(httpMethod, pathSegments,
|
context -> this.client.invokeApi(httpMethod, pathSegments.toArray(new String[0]),
|
||||||
httpExtension.getQueryParams(), serializedRequestBody, headers, context)
|
httpExtension.getQueryParams(), serializedRequestBody, headers, context)
|
||||||
);
|
);
|
||||||
return response.flatMap(r -> getMono(type, r));
|
return response.flatMap(r -> getMono(type, r));
|
||||||
|
@ -310,7 +318,7 @@ public class DaprClientHttp extends AbstractDaprClient {
|
||||||
|
|
||||||
byte[] requestBody = INTERNAL_SERIALIZER.serialize(jsonMap);
|
byte[] requestBody = INTERNAL_SERIALIZER.serialize(jsonMap);
|
||||||
|
|
||||||
String[] pathSegments = new String[]{ DaprHttp.API_VERSION, "state", stateStoreName, "bulk"};
|
String[] pathSegments = new String[]{ DaprHttp.API_VERSION, "state", stateStoreName, "bulk" };
|
||||||
|
|
||||||
Map<String, List<String>> queryArgs = metadataToQueryArgs(metadata);
|
Map<String, List<String>> queryArgs = metadataToQueryArgs(metadata);
|
||||||
return Mono.subscriberContext().flatMap(
|
return Mono.subscriberContext().flatMap(
|
||||||
|
@ -356,7 +364,7 @@ public class DaprClientHttp extends AbstractDaprClient {
|
||||||
queryParams.putAll(optionsMap.entrySet().stream().collect(
|
queryParams.putAll(optionsMap.entrySet().stream().collect(
|
||||||
Collectors.toMap(kv -> kv.getKey(), kv -> Collections.singletonList(kv.getValue()))));
|
Collectors.toMap(kv -> kv.getKey(), kv -> Collections.singletonList(kv.getValue()))));
|
||||||
|
|
||||||
String[] pathSegments = new String[]{ DaprHttp.API_VERSION, "state", stateStoreName, key};
|
String[] pathSegments = new String[]{ DaprHttp.API_VERSION, "state", stateStoreName, key };
|
||||||
|
|
||||||
return Mono.subscriberContext().flatMap(
|
return Mono.subscriberContext().flatMap(
|
||||||
context -> this.client
|
context -> this.client
|
||||||
|
@ -414,7 +422,7 @@ public class DaprClientHttp extends AbstractDaprClient {
|
||||||
TransactionalStateRequest<Object> req = new TransactionalStateRequest<>(internalOperationObjects, metadata);
|
TransactionalStateRequest<Object> req = new TransactionalStateRequest<>(internalOperationObjects, metadata);
|
||||||
byte[] serializedOperationBody = INTERNAL_SERIALIZER.serialize(req);
|
byte[] serializedOperationBody = INTERNAL_SERIALIZER.serialize(req);
|
||||||
|
|
||||||
String[] pathSegments = new String[]{ DaprHttp.API_VERSION, "state", stateStoreName, "transaction"};
|
String[] pathSegments = new String[]{ DaprHttp.API_VERSION, "state", stateStoreName, "transaction" };
|
||||||
|
|
||||||
return Mono.subscriberContext().flatMap(
|
return Mono.subscriberContext().flatMap(
|
||||||
context -> this.client.invokeApi(
|
context -> this.client.invokeApi(
|
||||||
|
@ -462,7 +470,7 @@ public class DaprClientHttp extends AbstractDaprClient {
|
||||||
}
|
}
|
||||||
byte[] serializedStateBody = INTERNAL_SERIALIZER.serialize(internalStateObjects);
|
byte[] serializedStateBody = INTERNAL_SERIALIZER.serialize(internalStateObjects);
|
||||||
|
|
||||||
String[] pathSegments = new String[]{ DaprHttp.API_VERSION, "state", stateStoreName};
|
String[] pathSegments = new String[]{ DaprHttp.API_VERSION, "state", stateStoreName };
|
||||||
|
|
||||||
return Mono.subscriberContext().flatMap(
|
return Mono.subscriberContext().flatMap(
|
||||||
context -> this.client.invokeApi(
|
context -> this.client.invokeApi(
|
||||||
|
@ -505,7 +513,7 @@ public class DaprClientHttp extends AbstractDaprClient {
|
||||||
queryParams.putAll(optionsMap.entrySet().stream().collect(
|
queryParams.putAll(optionsMap.entrySet().stream().collect(
|
||||||
Collectors.toMap(kv -> kv.getKey(), kv -> Collections.singletonList(kv.getValue()))));
|
Collectors.toMap(kv -> kv.getKey(), kv -> Collections.singletonList(kv.getValue()))));
|
||||||
|
|
||||||
String[] pathSegments = new String[]{ DaprHttp.API_VERSION, "state", stateStoreName, key};
|
String[] pathSegments = new String[]{ DaprHttp.API_VERSION, "state", stateStoreName, key };
|
||||||
|
|
||||||
return Mono.subscriberContext().flatMap(
|
return Mono.subscriberContext().flatMap(
|
||||||
context -> this.client.invokeApi(
|
context -> this.client.invokeApi(
|
||||||
|
@ -593,11 +601,11 @@ public class DaprClientHttp extends AbstractDaprClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, List<String>> queryArgs = metadataToQueryArgs(metadata);
|
Map<String, List<String>> queryArgs = metadataToQueryArgs(metadata);
|
||||||
String[] pathSegments = new String[]{ DaprHttp.API_VERSION, "secrets", secretStoreName, key};
|
String[] pathSegments = new String[]{ DaprHttp.API_VERSION, "secrets", secretStoreName, key };
|
||||||
|
|
||||||
return Mono.subscriberContext().flatMap(
|
return Mono.subscriberContext().flatMap(
|
||||||
context -> this.client
|
context -> this.client
|
||||||
.invokeApi(DaprHttp.HttpMethods.GET.name(), pathSegments, queryArgs, (String)null, null, context)
|
.invokeApi(DaprHttp.HttpMethods.GET.name(), pathSegments, queryArgs, (String) null, null, context)
|
||||||
).flatMap(response -> {
|
).flatMap(response -> {
|
||||||
try {
|
try {
|
||||||
Map m = INTERNAL_SERIALIZER.deserialize(response.getBody(), Map.class);
|
Map m = INTERNAL_SERIALIZER.deserialize(response.getBody(), Map.class);
|
||||||
|
@ -610,7 +618,7 @@ public class DaprClientHttp extends AbstractDaprClient {
|
||||||
return DaprException.wrapMono(e);
|
return DaprException.wrapMono(e);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map(m -> (Map<String, String>)m);
|
.map(m -> (Map<String, String>) m);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -629,11 +637,11 @@ public class DaprClientHttp extends AbstractDaprClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, List<String>> queryArgs = metadataToQueryArgs(metadata);
|
Map<String, List<String>> queryArgs = metadataToQueryArgs(metadata);
|
||||||
String[] pathSegments = new String[]{ DaprHttp.API_VERSION, "secrets", secretStoreName, "bulk"};
|
String[] pathSegments = new String[]{ DaprHttp.API_VERSION, "secrets", secretStoreName, "bulk" };
|
||||||
|
|
||||||
return Mono.subscriberContext().flatMap(
|
return Mono.subscriberContext().flatMap(
|
||||||
context -> this.client
|
context -> this.client
|
||||||
.invokeApi(DaprHttp.HttpMethods.GET.name(), pathSegments, queryArgs, (String)null, null, context)
|
.invokeApi(DaprHttp.HttpMethods.GET.name(), pathSegments, queryArgs, (String) null, null, context)
|
||||||
).flatMap(response -> {
|
).flatMap(response -> {
|
||||||
try {
|
try {
|
||||||
Map m = INTERNAL_SERIALIZER.deserialize(response.getBody(), Map.class);
|
Map m = INTERNAL_SERIALIZER.deserialize(response.getBody(), Map.class);
|
||||||
|
@ -646,7 +654,47 @@ public class DaprClientHttp extends AbstractDaprClient {
|
||||||
return DaprException.wrapMono(e);
|
return DaprException.wrapMono(e);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map(m -> (Map<String, Map<String, String>>)m);
|
.map(m -> (Map<String, Map<String, String>>) m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Mono<QueryStateResponse<T>> queryState(QueryStateRequest request, TypeRef<T> type) {
|
||||||
|
try {
|
||||||
|
if (request == null) {
|
||||||
|
throw new IllegalArgumentException("Query state request cannot be null.");
|
||||||
|
}
|
||||||
|
String stateStoreName = request.getStoreName();
|
||||||
|
Map<String, String> metadata = request.getMetadata();
|
||||||
|
if ((stateStoreName == null) || (stateStoreName.trim().isEmpty())) {
|
||||||
|
throw new IllegalArgumentException("State store name cannot be null or empty.");
|
||||||
|
}
|
||||||
|
Map<String, List<String>> queryArgs = metadataToQueryArgs(metadata);
|
||||||
|
String[] pathSegments = new String[]{ DaprHttp.ALPHA_1_API_VERSION, "state", stateStoreName, "query" };
|
||||||
|
String serializedRequest;
|
||||||
|
if (request.getQuery() != null) {
|
||||||
|
serializedRequest = JSON_REQUEST_MAPPER.writeValueAsString(request.getQuery());
|
||||||
|
} else if (request.getQueryString() != null) {
|
||||||
|
serializedRequest = request.getQueryString();
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Both query and queryString fields are not set.");
|
||||||
|
}
|
||||||
|
return Mono.subscriberContext().flatMap(
|
||||||
|
context -> this.client
|
||||||
|
.invokeApi(DaprHttp.HttpMethods.POST.name(), pathSegments,
|
||||||
|
queryArgs, serializedRequest, null, context)
|
||||||
|
).flatMap(response -> {
|
||||||
|
try {
|
||||||
|
return Mono.justOrEmpty(buildQueryStateResponse(response, type));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return DaprException.wrapMono(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
return DaprException.wrapMono(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -669,6 +717,47 @@ public class DaprClientHttp extends AbstractDaprClient {
|
||||||
.then();
|
.then();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private <T> QueryStateResponse<T> buildQueryStateResponse(DaprHttp.Response response,
|
||||||
|
TypeRef<T> type) throws IOException {
|
||||||
|
JsonNode root = INTERNAL_SERIALIZER.parseNode(response.getBody());
|
||||||
|
if (!root.has("results")) {
|
||||||
|
return new QueryStateResponse<>(Collections.emptyList(), null);
|
||||||
|
}
|
||||||
|
String token = null;
|
||||||
|
if (root.has("token")) {
|
||||||
|
token = root.path("token").asText();
|
||||||
|
}
|
||||||
|
Map<String, String> metadata = new HashMap<>();
|
||||||
|
if (root.has("metadata")) {
|
||||||
|
for (Iterator<Map.Entry<String, JsonNode>> it = root.get("metadata").fields(); it.hasNext(); ) {
|
||||||
|
Map.Entry<String, JsonNode> entry = it.next();
|
||||||
|
metadata.put(entry.getKey(), entry.getValue().asText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<QueryStateItem<T>> result = new ArrayList<>();
|
||||||
|
for (Iterator<JsonNode> it = root.get("results").elements(); it.hasNext(); ) {
|
||||||
|
JsonNode node = it.next();
|
||||||
|
String key = node.path("key").asText();
|
||||||
|
String error = node.path("error").asText();
|
||||||
|
if (!Strings.isNullOrEmpty(error)) {
|
||||||
|
result.add(new QueryStateItem<>(key, null, error));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String etag = node.path("etag").asText();
|
||||||
|
if (etag.equals("")) {
|
||||||
|
etag = null;
|
||||||
|
}
|
||||||
|
// TODO(artursouza): JSON cannot differentiate if data returned is String or byte[], it is ambiguous.
|
||||||
|
// This is not a high priority since GRPC is the default (and recommended) client implementation.
|
||||||
|
byte[] data = node.path("data").toString().getBytes(Properties.STRING_CHARSET.get());
|
||||||
|
T value = stateSerializer.deserialize(data, type);
|
||||||
|
result.add(new QueryStateItem<>(key, value, etag));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new QueryStateResponse<>(result, token).setMetadata(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -687,6 +776,7 @@ public class DaprClientHttp extends AbstractDaprClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts metadata map into Query params.
|
* Converts metadata map into Query params.
|
||||||
|
*
|
||||||
* @param metadata metadata map
|
* @param metadata metadata map
|
||||||
* @return Query params
|
* @return Query params
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -23,6 +23,8 @@ import io.dapr.client.domain.HttpExtension;
|
||||||
import io.dapr.client.domain.InvokeBindingRequest;
|
import io.dapr.client.domain.InvokeBindingRequest;
|
||||||
import io.dapr.client.domain.InvokeMethodRequest;
|
import io.dapr.client.domain.InvokeMethodRequest;
|
||||||
import io.dapr.client.domain.PublishEventRequest;
|
import io.dapr.client.domain.PublishEventRequest;
|
||||||
|
import io.dapr.client.domain.QueryStateRequest;
|
||||||
|
import io.dapr.client.domain.QueryStateResponse;
|
||||||
import io.dapr.client.domain.SaveStateRequest;
|
import io.dapr.client.domain.SaveStateRequest;
|
||||||
import io.dapr.client.domain.State;
|
import io.dapr.client.domain.State;
|
||||||
import io.dapr.client.domain.StateOptions;
|
import io.dapr.client.domain.StateOptions;
|
||||||
|
|
|
@ -31,7 +31,6 @@ import reactor.core.publisher.Mono;
|
||||||
import reactor.util.context.Context;
|
import reactor.util.context.Context;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -51,6 +50,11 @@ public class DaprHttp implements AutoCloseable {
|
||||||
*/
|
*/
|
||||||
public static final String API_VERSION = "v1.0";
|
public static final String API_VERSION = "v1.0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dapr alpha API used in this client.
|
||||||
|
*/
|
||||||
|
public static final String ALPHA_1_API_VERSION = "v1.0-alpha1";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Header used for request id in Dapr.
|
* Header used for request id in Dapr.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -15,7 +15,11 @@ package io.dapr.client;
|
||||||
|
|
||||||
import io.dapr.client.domain.ConfigurationItem;
|
import io.dapr.client.domain.ConfigurationItem;
|
||||||
import io.dapr.client.domain.GetConfigurationRequest;
|
import io.dapr.client.domain.GetConfigurationRequest;
|
||||||
|
import io.dapr.client.domain.QueryStateRequest;
|
||||||
|
import io.dapr.client.domain.QueryStateResponse;
|
||||||
import io.dapr.client.domain.SubscribeConfigurationRequest;
|
import io.dapr.client.domain.SubscribeConfigurationRequest;
|
||||||
|
import io.dapr.client.domain.query.Query;
|
||||||
|
import io.dapr.utils.TypeRef;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
@ -103,4 +107,120 @@ public interface DaprPreviewClient extends AutoCloseable {
|
||||||
* @return Flux of List of configuration items
|
* @return Flux of List of configuration items
|
||||||
*/
|
*/
|
||||||
Flux<List<ConfigurationItem>> subscribeToConfiguration(SubscribeConfigurationRequest request);
|
Flux<List<ConfigurationItem>> subscribeToConfiguration(SubscribeConfigurationRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query for states using a query string.
|
||||||
|
*
|
||||||
|
* @param storeName Name of the state store to query.
|
||||||
|
* @param query String value of the query.
|
||||||
|
* @param metadata Optional metadata passed to the state store.
|
||||||
|
* @param clazz The type needed as return for the call.
|
||||||
|
* @param <T> The Type of the return, use byte[] to skip serialization.
|
||||||
|
* @return A Mono of QueryStateResponse of type T.
|
||||||
|
*/
|
||||||
|
<T> Mono<QueryStateResponse<T>> queryState(String storeName, String query,
|
||||||
|
Map<String, String> metadata, Class<T> clazz);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query for states using a query string.
|
||||||
|
*
|
||||||
|
* @param storeName Name of the state store to query.
|
||||||
|
* @param query String value of the query.
|
||||||
|
* @param metadata Optional metadata passed to the state store.
|
||||||
|
* @param type The type needed as return for the call.
|
||||||
|
* @param <T> The Type of the return, use byte[] to skip serialization.
|
||||||
|
* @return A Mono of QueryStateResponse of type T.
|
||||||
|
*/
|
||||||
|
<T> Mono<QueryStateResponse<T>> queryState(String storeName, String query,
|
||||||
|
Map<String, String> metadata, TypeRef<T> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query for states using a query string.
|
||||||
|
*
|
||||||
|
* @param storeName Name of the state store to query.
|
||||||
|
* @param query String value of the query.
|
||||||
|
* @param clazz The type needed as return for the call.
|
||||||
|
* @param <T> The Type of the return, use byte[] to skip serialization.
|
||||||
|
* @return A Mono of QueryStateResponse of type T.
|
||||||
|
*/
|
||||||
|
<T> Mono<QueryStateResponse<T>> queryState(String storeName, String query, Class<T> clazz);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query for states using a query string.
|
||||||
|
*
|
||||||
|
* @param storeName Name of the state store to query.
|
||||||
|
* @param query String value of the query.
|
||||||
|
* @param type The type needed as return for the call.
|
||||||
|
* @param <T> The Type of the return, use byte[] to skip serialization.
|
||||||
|
* @return A Mono of QueryStateResponse of type T.
|
||||||
|
*/
|
||||||
|
<T> Mono<QueryStateResponse<T>> queryState(String storeName, String query, TypeRef<T> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query for states using a query domain object.
|
||||||
|
*
|
||||||
|
* @param storeName Name of the state store to query.
|
||||||
|
* @param query Query value domain object.
|
||||||
|
* @param metadata Optional metadata passed to the state store.
|
||||||
|
* @param clazz The type needed as return for the call.
|
||||||
|
* @param <T> The Type of the return, use byte[] to skip serialization.
|
||||||
|
* @return A Mono of QueryStateResponse of type T.
|
||||||
|
*/
|
||||||
|
<T> Mono<QueryStateResponse<T>> queryState(String storeName, Query query,
|
||||||
|
Map<String, String> metadata, Class<T> clazz);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query for states using a query domain object.
|
||||||
|
*
|
||||||
|
* @param storeName Name of the state store to query.
|
||||||
|
* @param query Query value domain object.
|
||||||
|
* @param metadata Optional metadata passed to the state store.
|
||||||
|
* @param type The type needed as return for the call.
|
||||||
|
* @param <T> The Type of the return, use byte[] to skip serialization.
|
||||||
|
* @return A Mono of QueryStateResponse of type T.
|
||||||
|
*/
|
||||||
|
<T> Mono<QueryStateResponse<T>> queryState(String storeName, Query query,
|
||||||
|
Map<String, String> metadata, TypeRef<T> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query for states using a query domain object.
|
||||||
|
*
|
||||||
|
* @param storeName Name of the state store to query.
|
||||||
|
* @param query Query value domain object.
|
||||||
|
* @param clazz The type needed as return for the call.
|
||||||
|
* @param <T> The Type of the return, use byte[] to skip serialization.
|
||||||
|
* @return A Mono of QueryStateResponse of type T.
|
||||||
|
*/
|
||||||
|
<T> Mono<QueryStateResponse<T>> queryState(String storeName, Query query, Class<T> clazz);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query for states using a query domain object.
|
||||||
|
*
|
||||||
|
* @param storeName Name of the state store to query.
|
||||||
|
* @param query Query value domain object.
|
||||||
|
* @param type The type needed as return for the call.
|
||||||
|
* @param <T> The Type of the return, use byte[] to skip serialization.
|
||||||
|
* @return A Mono of QueryStateResponse of type T.
|
||||||
|
*/
|
||||||
|
<T> Mono<QueryStateResponse<T>> queryState(String storeName, Query query, TypeRef<T> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query for states using a query request.
|
||||||
|
*
|
||||||
|
* @param request Query request object.
|
||||||
|
* @param clazz The type needed as return for the call.
|
||||||
|
* @param <T> The Type of the return, use byte[] to skip serialization.
|
||||||
|
* @return A Mono of QueryStateResponse of type T.
|
||||||
|
*/
|
||||||
|
<T> Mono<QueryStateResponse<T>> queryState(QueryStateRequest request, Class<T> clazz);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query for states using a query request.
|
||||||
|
*
|
||||||
|
* @param request Query request object.
|
||||||
|
* @param type The type needed as return for the call.
|
||||||
|
* @param <T> The Type of the return, use byte[] to skip serialization.
|
||||||
|
* @return A Mono of QueryStateResponse of type T.
|
||||||
|
*/
|
||||||
|
<T> Mono<QueryStateResponse<T>> queryState(QueryStateRequest request, TypeRef<T> type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Dapr Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.dapr.client.domain;
|
||||||
|
|
||||||
|
|
||||||
|
public class QueryStateItem<T> {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value of the state.
|
||||||
|
*/
|
||||||
|
private final T value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The key of the state.
|
||||||
|
*/
|
||||||
|
private final String key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ETag to be used
|
||||||
|
* Keep in mind that for some state stores (like redis) only numbers are supported.
|
||||||
|
*/
|
||||||
|
private final String etag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The error in case the key could not be retrieved.
|
||||||
|
*/
|
||||||
|
private final String error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an immutable state reference to be retrieved or deleted.
|
||||||
|
* This Constructor CAN be used anytime you need to retrieve or delete a state.
|
||||||
|
*
|
||||||
|
* @param key - The key of the state
|
||||||
|
*/
|
||||||
|
public QueryStateItem(String key) {
|
||||||
|
this.key = key;
|
||||||
|
this.value = null;
|
||||||
|
this.etag = null;
|
||||||
|
this.error = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an immutable state reference to be retrieved or deleted.
|
||||||
|
* This Constructor CAN be used anytime you need to retrieve or delete a state.
|
||||||
|
*
|
||||||
|
* @param key - The key of the state
|
||||||
|
* @param etag - The etag of the state - Keep in mind that for some state stores (like redis) only numbers
|
||||||
|
* are supported.
|
||||||
|
* @param error - Error when fetching the state.
|
||||||
|
*/
|
||||||
|
public QueryStateItem(String key, String etag, String error) {
|
||||||
|
this.value = null;
|
||||||
|
this.key = key;
|
||||||
|
this.etag = etag;
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an immutable state.
|
||||||
|
* This Constructor CAN be used anytime you want the state to be saved.
|
||||||
|
*
|
||||||
|
* @param key - The key of the state.
|
||||||
|
* @param value - The value of the state.
|
||||||
|
* @param etag - The etag of the state - for some state stores (like redis) only numbers are supported.
|
||||||
|
*/
|
||||||
|
public QueryStateItem(String key, T value, String etag) {
|
||||||
|
this.value = value;
|
||||||
|
this.key = key;
|
||||||
|
this.etag = etag;
|
||||||
|
this.error = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the Value of the state.
|
||||||
|
*
|
||||||
|
* @return The value of the state
|
||||||
|
*/
|
||||||
|
public T getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the Key of the state.
|
||||||
|
*
|
||||||
|
* @return The key of the state
|
||||||
|
*/
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the ETag of this state.
|
||||||
|
*
|
||||||
|
* @return The etag of the state
|
||||||
|
*/
|
||||||
|
public String getEtag() {
|
||||||
|
return etag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the error for this state.
|
||||||
|
*
|
||||||
|
* @return The error for this state.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public String getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(o instanceof QueryStateItem)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryStateItem<?> that = (QueryStateItem<?>) o;
|
||||||
|
|
||||||
|
if (getValue() != null ? !getValue().equals(that.getValue()) : that.getValue() != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getKey() != null ? !getKey().equals(that.getKey()) : that.getKey() != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getEtag() != null ? !getEtag().equals(that.getEtag()) : that.getEtag() != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getError() != null ? getError().equals(that.getError()) : that.getError() == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = getValue() != null ? getValue().hashCode() : 0;
|
||||||
|
result = 31 * result + (getKey() != null ? getKey().hashCode() : 0);
|
||||||
|
result = 31 * result + (getEtag() != null ? getEtag().hashCode() : 0);
|
||||||
|
result = 31 * result + (getError() != null ? getError().hashCode() : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "QueryStateItem{"
|
||||||
|
+ "key='" + key + "'"
|
||||||
|
+ ", value=" + value
|
||||||
|
+ ", etag='" + etag + "'"
|
||||||
|
+ ", error='" + error + "'"
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Dapr Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.dapr.client.domain;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import io.dapr.client.domain.query.Query;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class QueryStateRequest {
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private final String storeName;
|
||||||
|
|
||||||
|
private Query query;
|
||||||
|
|
||||||
|
private String queryString;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private Map<String, String> metadata;
|
||||||
|
|
||||||
|
public QueryStateRequest(String storeName) {
|
||||||
|
this.storeName = storeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStoreName() {
|
||||||
|
return storeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Query getQuery() {
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate and set the query field. Mutually exclusive with the queryString field in this instance.
|
||||||
|
*
|
||||||
|
* @param query Valid Query domain object.
|
||||||
|
* @return This instance.
|
||||||
|
*/
|
||||||
|
public QueryStateRequest setQuery(Query query) {
|
||||||
|
if (this.queryString != null) {
|
||||||
|
throw new IllegalArgumentException("queryString filed is already set in the request. query field cannot be set.");
|
||||||
|
}
|
||||||
|
if (query == null || query.getFilter() == null) {
|
||||||
|
throw new IllegalArgumentException("query cannot be null or with null filter");
|
||||||
|
}
|
||||||
|
this.query = query;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getQueryString() {
|
||||||
|
return queryString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate and set the queryString field. Mutually exclusive with the query field in this instance.
|
||||||
|
*
|
||||||
|
* @param queryString String value of the query.
|
||||||
|
* @return This request object for fluent API.
|
||||||
|
*/
|
||||||
|
public QueryStateRequest setQueryString(String queryString) {
|
||||||
|
if (this.query != null) {
|
||||||
|
throw new IllegalArgumentException("query filed is already set in the request. queryString field cannot be set.");
|
||||||
|
}
|
||||||
|
if (queryString == null || queryString.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("queryString cannot be null or blank");
|
||||||
|
}
|
||||||
|
this.queryString = queryString;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getMetadata() {
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryStateRequest setMetadata(Map<String, String> metadata) {
|
||||||
|
this.metadata = metadata == null ? null : Collections.unmodifiableMap(metadata);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Dapr Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.dapr.client.domain;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class QueryStateResponse<T> {
|
||||||
|
|
||||||
|
private final List<QueryStateItem<T>> results;
|
||||||
|
|
||||||
|
private final String token;
|
||||||
|
|
||||||
|
private Map<String, String> metadata;
|
||||||
|
|
||||||
|
public QueryStateResponse(List<QueryStateItem<T>> results, String token) {
|
||||||
|
this.results = results == null ? null : Collections.unmodifiableList(results);
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<QueryStateItem<T>> getResults() {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getToken() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getMetadata() {
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryStateResponse<T> setMetadata(Map<String, String> metadata) {
|
||||||
|
this.metadata = metadata == null ? null : Collections.unmodifiableMap(metadata);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Dapr Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.dapr.client.domain.query;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
|
public class Pagination {
|
||||||
|
private int limit;
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
Pagination() {
|
||||||
|
// For JSON
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pagination(int limit, String token) {
|
||||||
|
this.limit = limit;
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLimit() {
|
||||||
|
return limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getToken() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Dapr Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.dapr.client.domain.query;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import io.dapr.client.domain.query.filters.Filter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Query {
|
||||||
|
private Filter<?> filter;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private Sorting[] sort = new Sorting[]{};
|
||||||
|
|
||||||
|
@JsonProperty("page")
|
||||||
|
private Pagination pagination = new Pagination();
|
||||||
|
|
||||||
|
public Filter<?> getFilter() {
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the filter field in the instance.
|
||||||
|
* @param filter Valid filter value.
|
||||||
|
* @return this instance.
|
||||||
|
*/
|
||||||
|
public Query setFilter(Filter<?> filter) {
|
||||||
|
if (!filter.isValid()) {
|
||||||
|
throw new IllegalArgumentException("the given filter is invalid configuration");
|
||||||
|
}
|
||||||
|
this.filter = filter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Sorting> getSort() {
|
||||||
|
return Collections.unmodifiableList(Arrays.asList(sort));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate and set sorting field.
|
||||||
|
*
|
||||||
|
* @param sort List of sorting objects.
|
||||||
|
* @return This instance.
|
||||||
|
*/
|
||||||
|
public Query setSort(List<Sorting> sort) {
|
||||||
|
if (sort == null || sort.size() == 0) {
|
||||||
|
throw new IllegalArgumentException("Sorting list is null or empty");
|
||||||
|
}
|
||||||
|
this.sort = sort.toArray(new Sorting[0]);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pagination getPagination() {
|
||||||
|
return pagination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Query setPagination(Pagination pagination) {
|
||||||
|
this.pagination = pagination;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Dapr Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.dapr.client.domain.query;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
|
public class Sorting {
|
||||||
|
private String key;
|
||||||
|
private Order order;
|
||||||
|
|
||||||
|
Sorting() {
|
||||||
|
// For JSON
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sorting(String key, Order order) {
|
||||||
|
this.key = key;
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Order getOrder() {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Order {
|
||||||
|
ASC("ASC"),
|
||||||
|
DESC("DESC");
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
Order(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonValue
|
||||||
|
public String getValue() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
public static Order fromValue(String value) {
|
||||||
|
return Order.valueOf(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Dapr Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.dapr.client.domain.query.filters;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class AndFilter extends Filter<Void> {
|
||||||
|
@JsonIgnore
|
||||||
|
private final List<Filter<?>> and;
|
||||||
|
|
||||||
|
public AndFilter() {
|
||||||
|
super("AND");
|
||||||
|
this.and = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
AndFilter(Filter<?>[] filters) {
|
||||||
|
super("AND");
|
||||||
|
this.and = Arrays.asList(filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <V extends Filter<?>> AndFilter addClause(V filter) {
|
||||||
|
this.and.add(filter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonValue
|
||||||
|
public Filter<?>[] getClauses() {
|
||||||
|
return this.and.toArray(new Filter[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@JsonIgnore
|
||||||
|
public String getRepresentation() {
|
||||||
|
return this.and.stream().map(Filter::getRepresentation).collect(Collectors.joining(" AND "));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean isValid() {
|
||||||
|
boolean validAnd = and != null && and.size() >= 2;
|
||||||
|
if (validAnd) {
|
||||||
|
for (Filter<?> filter : and) {
|
||||||
|
if (!filter.isValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return validAnd;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Dapr Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.dapr.client.domain.query.filters;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class EqFilter<T> extends Filter<T> {
|
||||||
|
|
||||||
|
@JsonValue
|
||||||
|
private Map.Entry<String, T> eq;
|
||||||
|
|
||||||
|
public EqFilter() {
|
||||||
|
super("EQ");
|
||||||
|
}
|
||||||
|
|
||||||
|
public EqFilter(String key, T value) {
|
||||||
|
super("EQ");
|
||||||
|
eq = new AbstractMap.SimpleImmutableEntry<>(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
EqFilter(Map.Entry<String, T> eq) {
|
||||||
|
super("EQ");
|
||||||
|
this.eq = eq;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public String getKey() {
|
||||||
|
return eq != null ? eq.getKey() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public T getValue() {
|
||||||
|
return eq != null ? eq.getValue() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRepresentation() {
|
||||||
|
return this.getKey() + " EQ " + this.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean isValid() {
|
||||||
|
return eq != null && eq.getKey() != null && !eq.getKey().isEmpty() && !eq.getKey().trim().isEmpty();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Dapr Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.dapr.client.domain.query.filters;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
|
||||||
|
@JsonSubTypes({
|
||||||
|
@JsonSubTypes.Type(value = AndFilter.class, name = "AND"),
|
||||||
|
@JsonSubTypes.Type(value = InFilter.class, name = "IN"),
|
||||||
|
@JsonSubTypes.Type(value = OrFilter.class, name = "OR"),
|
||||||
|
@JsonSubTypes.Type(value = EqFilter.class, name = "EQ")
|
||||||
|
})
|
||||||
|
public abstract class Filter<T> {
|
||||||
|
|
||||||
|
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
Filter() {
|
||||||
|
// For JSON Serialization
|
||||||
|
}
|
||||||
|
|
||||||
|
public Filter(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public abstract String getRepresentation();
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public abstract Boolean isValid();
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Dapr Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.dapr.client.domain.query.filters;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class InFilter<T> extends Filter<T> {
|
||||||
|
@JsonValue
|
||||||
|
private Map.Entry<String, List<T>> in;
|
||||||
|
|
||||||
|
public InFilter() {
|
||||||
|
super("IN");
|
||||||
|
}
|
||||||
|
|
||||||
|
public InFilter(String key, List<T> value) {
|
||||||
|
super("IN");
|
||||||
|
in = new AbstractMap.SimpleEntry<>(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
InFilter(Map.Entry<String, List<T>> in) {
|
||||||
|
super("IN");
|
||||||
|
this.in = in;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor for InFilter.
|
||||||
|
* @param key value of the key in the state store.
|
||||||
|
* @param values var args values list.
|
||||||
|
*/
|
||||||
|
public InFilter(String key, T... values) {
|
||||||
|
super("IN");
|
||||||
|
if (values == null || values.length == 0) {
|
||||||
|
throw new IllegalArgumentException("list of values must be at least 1");
|
||||||
|
}
|
||||||
|
in = new AbstractMap.SimpleImmutableEntry<>(key, Collections.unmodifiableList(Arrays.asList(values)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public String getKey() {
|
||||||
|
return in != null ? in.getKey() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public List<T> getValues() {
|
||||||
|
return in != null ? in.getValue() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRepresentation() {
|
||||||
|
return this.getKey() + " IN ["
|
||||||
|
+ this.getValues().stream().map(Object::toString).collect(Collectors.joining(","))
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean isValid() {
|
||||||
|
return in != null && in.getKey() != null && !in.getKey().isEmpty() && !in.getKey().trim().isEmpty()
|
||||||
|
&& in.getValue() != null && in.getValue().size() > 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Dapr Authors
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.dapr.client.domain.query.filters;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public class OrFilter extends Filter {
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private List<Filter<?>> or;
|
||||||
|
|
||||||
|
public OrFilter() {
|
||||||
|
super("OR");
|
||||||
|
this.or = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
OrFilter(Filter<?>[] filters) {
|
||||||
|
super("OR");
|
||||||
|
this.or = Arrays.asList(filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <V extends Filter> OrFilter addClause(V filter) {
|
||||||
|
this.or.add(filter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonValue
|
||||||
|
public Filter<?>[] getClauses() {
|
||||||
|
return this.or.toArray(new Filter[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@JsonIgnore
|
||||||
|
public String getRepresentation() {
|
||||||
|
return this.or.stream().map(Filter::getRepresentation).collect(Collectors.joining(" OR "));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean isValid() {
|
||||||
|
boolean validAnd = or != null && or.size() >= 2;
|
||||||
|
if (validAnd) {
|
||||||
|
for (Filter<?> filter : or) {
|
||||||
|
if (!filter.isValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return validAnd;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,9 +14,16 @@ limitations under the License.
|
||||||
package io.dapr.client;
|
package io.dapr.client;
|
||||||
|
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
import io.dapr.client.domain.ConfigurationItem;
|
import io.dapr.client.domain.ConfigurationItem;
|
||||||
import io.dapr.client.domain.GetConfigurationRequest;
|
import io.dapr.client.domain.GetConfigurationRequest;
|
||||||
|
import io.dapr.client.domain.QueryStateItem;
|
||||||
|
import io.dapr.client.domain.QueryStateRequest;
|
||||||
|
import io.dapr.client.domain.QueryStateResponse;
|
||||||
import io.dapr.client.domain.SubscribeConfigurationRequest;
|
import io.dapr.client.domain.SubscribeConfigurationRequest;
|
||||||
|
import io.dapr.client.domain.query.Query;
|
||||||
import io.dapr.serializer.DefaultObjectSerializer;
|
import io.dapr.serializer.DefaultObjectSerializer;
|
||||||
import io.dapr.v1.CommonProtos;
|
import io.dapr.v1.CommonProtos;
|
||||||
import io.dapr.v1.DaprGrpc;
|
import io.dapr.v1.DaprGrpc;
|
||||||
|
@ -29,6 +36,7 @@ import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -37,14 +45,24 @@ import java.util.Map;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
import static io.dapr.utils.TestUtils.assertThrowsDaprException;
|
import static io.dapr.utils.TestUtils.assertThrowsDaprException;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
import static org.mockito.Mockito.doNothing;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class DaprPreviewClientGrpcTest {
|
public class DaprPreviewClientGrpcTest {
|
||||||
|
|
||||||
|
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||||
private static final String CONFIG_STORE_NAME = "MyConfigStore";
|
private static final String CONFIG_STORE_NAME = "MyConfigStore";
|
||||||
|
private static final String QUERY_STORE_NAME = "testQueryStore";
|
||||||
|
|
||||||
private Closeable closeable;
|
private Closeable closeable;
|
||||||
private DaprGrpc.DaprStub daprStub;
|
private DaprGrpc.DaprStub daprStub;
|
||||||
|
@ -269,4 +287,105 @@ public class DaprPreviewClientGrpcTest {
|
||||||
.build();
|
.build();
|
||||||
return responseEnvelope;
|
return responseEnvelope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryStateExceptionsTest() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
previewClient.queryState("", "query", String.class).block();
|
||||||
|
});
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
previewClient.queryState("storeName", "", String.class).block();
|
||||||
|
});
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
previewClient.queryState("storeName", (Query) null, String.class).block();
|
||||||
|
});
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
previewClient.queryState("storeName", (String) null, String.class).block();
|
||||||
|
});
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
previewClient.queryState(new QueryStateRequest("storeName"), String.class).block();
|
||||||
|
});
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
previewClient.queryState(null, String.class).block();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryState() throws JsonProcessingException {
|
||||||
|
List<QueryStateItem<?>> resp = new ArrayList<>();
|
||||||
|
resp.add(new QueryStateItem<Object>("1", (Object)"testData", "6f54ad94-dfb9-46f0-a371-e42d550adb7d"));
|
||||||
|
DaprProtos.QueryStateResponse responseEnvelope = buildQueryStateResponse(resp, "");
|
||||||
|
doAnswer((Answer<Void>) invocation -> {
|
||||||
|
DaprProtos.QueryStateRequest req = invocation.getArgument(0);
|
||||||
|
assertEquals(QUERY_STORE_NAME, req.getStoreName());
|
||||||
|
assertEquals("query", req.getQuery());
|
||||||
|
assertEquals(0, req.getMetadataCount());
|
||||||
|
|
||||||
|
StreamObserver<DaprProtos.QueryStateResponse> observer = (StreamObserver<DaprProtos.QueryStateResponse>)
|
||||||
|
invocation.getArguments()[1];
|
||||||
|
observer.onNext(responseEnvelope);
|
||||||
|
observer.onCompleted();
|
||||||
|
return null;
|
||||||
|
}).when(daprStub).queryStateAlpha1(any(DaprProtos.QueryStateRequest.class), any());
|
||||||
|
|
||||||
|
QueryStateResponse<String> response = previewClient.queryState(QUERY_STORE_NAME, "query", String.class).block();
|
||||||
|
assertNotNull(response);
|
||||||
|
assertEquals("result size must be 1", 1, response.getResults().size());
|
||||||
|
assertEquals("result must be same", "1", response.getResults().get(0).getKey());
|
||||||
|
assertEquals("result must be same", "testData", response.getResults().get(0).getValue());
|
||||||
|
assertEquals("result must be same", "6f54ad94-dfb9-46f0-a371-e42d550adb7d", response.getResults().get(0).getEtag());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryStateMetadataError() throws JsonProcessingException {
|
||||||
|
List<QueryStateItem<?>> resp = new ArrayList<>();
|
||||||
|
resp.add(new QueryStateItem<Object>("1", null, "error data"));
|
||||||
|
DaprProtos.QueryStateResponse responseEnvelope = buildQueryStateResponse(resp, "");
|
||||||
|
doAnswer((Answer<Void>) invocation -> {
|
||||||
|
DaprProtos.QueryStateRequest req = invocation.getArgument(0);
|
||||||
|
assertEquals(QUERY_STORE_NAME, req.getStoreName());
|
||||||
|
assertEquals("query", req.getQuery());
|
||||||
|
assertEquals(1, req.getMetadataCount());
|
||||||
|
assertEquals(1, req.getMetadataCount());
|
||||||
|
|
||||||
|
StreamObserver<DaprProtos.QueryStateResponse> observer = (StreamObserver<DaprProtos.QueryStateResponse>)
|
||||||
|
invocation.getArguments()[1];
|
||||||
|
observer.onNext(responseEnvelope);
|
||||||
|
observer.onCompleted();
|
||||||
|
return null;
|
||||||
|
}).when(daprStub).queryStateAlpha1(any(DaprProtos.QueryStateRequest.class), any());
|
||||||
|
|
||||||
|
QueryStateResponse<String> response = previewClient.queryState(QUERY_STORE_NAME, "query",
|
||||||
|
new HashMap<String, String>(){{ put("key", "error"); }}, String.class).block();
|
||||||
|
assertNotNull(response);
|
||||||
|
assertEquals("result size must be 1", 1, response.getResults().size());
|
||||||
|
assertEquals("result must be same", "1", response.getResults().get(0).getKey());
|
||||||
|
assertEquals("result must be same", "error data", response.getResults().get(0).getError());
|
||||||
|
}
|
||||||
|
|
||||||
|
private DaprProtos.QueryStateResponse buildQueryStateResponse(List<QueryStateItem<?>> resp,String token)
|
||||||
|
throws JsonProcessingException {
|
||||||
|
List<DaprProtos.QueryStateItem> items = new ArrayList<>();
|
||||||
|
for (QueryStateItem<?> item: resp) {
|
||||||
|
items.add(buildQueryStateItem(item));
|
||||||
|
}
|
||||||
|
return DaprProtos.QueryStateResponse.newBuilder()
|
||||||
|
.addAllResults(items)
|
||||||
|
.setToken(token)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private DaprProtos.QueryStateItem buildQueryStateItem(QueryStateItem<?> item) throws JsonProcessingException {
|
||||||
|
DaprProtos.QueryStateItem.Builder it = DaprProtos.QueryStateItem.newBuilder().setKey(item.getKey());
|
||||||
|
if (item.getValue() != null) {
|
||||||
|
it.setData(ByteString.copyFrom(MAPPER.writeValueAsBytes(item.getValue())));
|
||||||
|
}
|
||||||
|
if (item.getEtag() != null) {
|
||||||
|
it.setEtag(item.getEtag());
|
||||||
|
}
|
||||||
|
if (item.getError() != null) {
|
||||||
|
it.setError(item.getError());
|
||||||
|
}
|
||||||
|
return it.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,32 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) Microsoft Corporation and Dapr Contributors.
|
* Copyright 2021 The Dapr Authors
|
||||||
* Licensed under the MIT License.
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
*/
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
package io.dapr.client;
|
package io.dapr.client;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import io.dapr.client.domain.QueryStateRequest;
|
||||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
import io.dapr.client.domain.QueryStateResponse;
|
||||||
|
import io.dapr.client.domain.query.Query;
|
||||||
import io.dapr.config.Properties;
|
import io.dapr.config.Properties;
|
||||||
import io.dapr.exceptions.DaprException;
|
import io.dapr.exceptions.DaprException;
|
||||||
import io.dapr.serializer.DaprObjectSerializer;
|
|
||||||
import io.dapr.utils.TypeRef;
|
import io.dapr.utils.TypeRef;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.mock.Behavior;
|
import okhttp3.mock.Behavior;
|
||||||
import okhttp3.mock.MockInterceptor;
|
import okhttp3.mock.MockInterceptor;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.Mockito;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import static org.junit.Assert.assertEquals;
|
||||||
import java.net.ServerSocket;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import java.net.Socket;
|
|
||||||
|
|
||||||
import static io.dapr.utils.TestUtils.findFreePort;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
public class DaprPreviewClientHttpTest {
|
public class DaprPreviewClientHttpTest {
|
||||||
|
@ -66,4 +68,56 @@ public class DaprPreviewClientHttpTest {
|
||||||
daprPreviewClientHttp.subscribeToConfiguration(CONFIG_STORE_NAME, "key1", "key2").blockFirst();
|
daprPreviewClientHttp.subscribeToConfiguration(CONFIG_STORE_NAME, "key1", "key2").blockFirst();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryStateExceptionsTest() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
daprPreviewClientHttp.queryState("", "query", TypeRef.BOOLEAN).block();
|
||||||
|
});
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
daprPreviewClientHttp.queryState("", "query", String.class).block();
|
||||||
|
});
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
daprPreviewClientHttp.queryState("storeName", "", TypeRef.BOOLEAN).block();
|
||||||
|
});
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
daprPreviewClientHttp.queryState("storeName", "", String.class).block();
|
||||||
|
});
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
daprPreviewClientHttp.queryState("storeName", (Query) null, TypeRef.BOOLEAN).block();
|
||||||
|
});
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
daprPreviewClientHttp.queryState("storeName", (Query) null, String.class).block();
|
||||||
|
});
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
daprPreviewClientHttp.queryState("storeName", (String) null, TypeRef.BOOLEAN).block();
|
||||||
|
});
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
daprPreviewClientHttp.queryState("storeName", (String) null, String.class).block();
|
||||||
|
});
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
daprPreviewClientHttp.queryState(null, TypeRef.BOOLEAN).block();
|
||||||
|
});
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
daprPreviewClientHttp.queryState(new QueryStateRequest("storeName"), TypeRef.BOOLEAN).block();
|
||||||
|
});
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
daprPreviewClientHttp.queryState(null, String.class).block();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryStateTest() {
|
||||||
|
mockInterceptor.addRule()
|
||||||
|
.post()
|
||||||
|
.path("/v1.0-alpha1/state/testStore/query")
|
||||||
|
.respond("{\"results\": [{\"key\": \"1\",\"data\": \"testData\","
|
||||||
|
+ "\"etag\": \"6f54ad94-dfb9-46f0-a371-e42d550adb7d\"}]}");
|
||||||
|
QueryStateResponse<String> response = daprPreviewClientHttp.queryState("testStore", "query", String.class).block();
|
||||||
|
assertNotNull(response);
|
||||||
|
assertEquals("result size must be 1", 1, response.getResults().size());
|
||||||
|
assertEquals("result must be same", "1", response.getResults().get(0).getKey());
|
||||||
|
assertEquals("result must be same", "testData", response.getResults().get(0).getValue());
|
||||||
|
assertEquals("result must be same", "6f54ad94-dfb9-46f0-a371-e42d550adb7d", response.getResults().get(0).getEtag());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
package io.dapr.client.domain;
|
||||||
|
|
||||||
|
import io.dapr.client.domain.query.Query;
|
||||||
|
import io.dapr.client.domain.query.filters.EqFilter;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
public class QueryStateRequestTest {
|
||||||
|
|
||||||
|
private String STORE_NAME = "STORE";
|
||||||
|
private String KEY = "KEY";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetMetadata() {
|
||||||
|
QueryStateRequest request = new QueryStateRequest(STORE_NAME);
|
||||||
|
// Null check
|
||||||
|
request.setMetadata(null);
|
||||||
|
assertNull(request.getMetadata());
|
||||||
|
// Modifiability check
|
||||||
|
Map<String, String> metadata = new HashMap<>();
|
||||||
|
metadata.put("test", "testval");
|
||||||
|
request.setMetadata(metadata);
|
||||||
|
Map<String, String> initial = request.getMetadata();
|
||||||
|
request.setMetadata(metadata);
|
||||||
|
Assert.assertNotSame("Should not be same map", request.getMetadata(), initial);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testSetNullQuery() {
|
||||||
|
QueryStateRequest request = new QueryStateRequest(STORE_NAME);
|
||||||
|
request.setQuery(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testSetNullFilterQuery() {
|
||||||
|
QueryStateRequest request = new QueryStateRequest(STORE_NAME);
|
||||||
|
Query query = new Query();
|
||||||
|
request.setQuery(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetQuery() {
|
||||||
|
QueryStateRequest request = new QueryStateRequest(STORE_NAME);
|
||||||
|
Query query = new Query();
|
||||||
|
query.setFilter(new EqFilter<>("key", "value"));
|
||||||
|
request.setQuery(query);
|
||||||
|
Assert.assertEquals(query, request.getQuery());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
package io.dapr.client.domain.query;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import io.dapr.client.domain.query.filters.*;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class QueryTest {
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
String json = "{\"filter\":{\"AND\":[{\"EQ\":{\"key\":\"value\"}},{\"IN\":{\"key2\":[\"v1\",\"v2\"]}}," +
|
||||||
|
"{\"OR\":[{\"EQ\":{\"v2\":true}},{\"IN\":{\"v3\":[1.3,1.5]}}]}]}," +
|
||||||
|
"\"sort\":[{\"key\":\"value.person.org\",\"order\":\"ASC\"},{\"key\":\"value.state\",\"order\":\"DESC\"}]," +
|
||||||
|
"\"page\":{\"limit\":10,\"token\":\"test-token\"}}";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testQuerySerialize() throws JsonProcessingException {
|
||||||
|
Query q = new Query();
|
||||||
|
|
||||||
|
AndFilter filter = new AndFilter();
|
||||||
|
filter.addClause(new EqFilter<>("key", "value"));
|
||||||
|
filter.addClause(new InFilter<>("key2", "v1", "v2"));
|
||||||
|
|
||||||
|
OrFilter orFilter = new OrFilter();
|
||||||
|
orFilter.addClause(new EqFilter<>("v2", true));
|
||||||
|
orFilter.addClause(new InFilter<>("v3", 1.3, 1.5));
|
||||||
|
|
||||||
|
filter.addClause(orFilter);
|
||||||
|
|
||||||
|
// Add Filter
|
||||||
|
q.setFilter(filter);
|
||||||
|
q.setPagination(new Pagination(10, "test-token"));
|
||||||
|
q.setSort(Arrays.asList(new Sorting("value.person.org", Sorting.Order.ASC),
|
||||||
|
new Sorting("value.state", Sorting.Order.DESC)));
|
||||||
|
Assert.assertEquals(json, mapper.writeValueAsString(q));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testQueryDeserialize() throws JsonProcessingException {
|
||||||
|
|
||||||
|
|
||||||
|
Query query = mapper.readValue(json, Query.class);
|
||||||
|
Assert.assertNotNull(query.getPagination());
|
||||||
|
Assert.assertNotNull(query.getFilter());
|
||||||
|
Assert.assertNotNull(query.getSort());
|
||||||
|
|
||||||
|
// Assert Pagination
|
||||||
|
Assert.assertEquals(10, query.getPagination().getLimit());
|
||||||
|
Assert.assertEquals("test-token", query.getPagination().getToken());
|
||||||
|
|
||||||
|
// Assert Sort
|
||||||
|
Assert.assertEquals(2, query.getSort().size());
|
||||||
|
Assert.assertEquals("value.person.org", query.getSort().get(0).getKey());
|
||||||
|
Assert.assertEquals(Sorting.Order.ASC, query.getSort().get(0).getOrder());
|
||||||
|
Assert.assertEquals("value.state", query.getSort().get(1).getKey());
|
||||||
|
Assert.assertEquals(Sorting.Order.DESC, query.getSort().get(1).getOrder());
|
||||||
|
|
||||||
|
// Assert Filter
|
||||||
|
// Top level AND filter
|
||||||
|
Assert.assertEquals("AND", query.getFilter().getName());
|
||||||
|
// Type cast to AND filter
|
||||||
|
AndFilter filter = (AndFilter) query.getFilter();
|
||||||
|
// Assert 3 AND clauses
|
||||||
|
Assert.assertEquals(3, filter.getClauses().length);
|
||||||
|
Filter<?>[] andClauses = filter.getClauses();
|
||||||
|
// First EQ
|
||||||
|
Assert.assertEquals("EQ", andClauses[0].getName());
|
||||||
|
Assert.assertSame(EqFilter.class, andClauses[0].getClass());
|
||||||
|
EqFilter<?> eq = (EqFilter<?>) andClauses[0];
|
||||||
|
Assert.assertEquals("key", eq.getKey());
|
||||||
|
Assert.assertEquals("value", eq.getValue());
|
||||||
|
// Second IN
|
||||||
|
Assert.assertEquals("IN", andClauses[1].getName());
|
||||||
|
Assert.assertSame(InFilter.class, andClauses[1].getClass());
|
||||||
|
InFilter<?> in = (InFilter<?>) andClauses[1];
|
||||||
|
Assert.assertEquals("key2", in.getKey());
|
||||||
|
Assert.assertArrayEquals(new String[]{ "v1", "v2" }, in.getValues().toArray());
|
||||||
|
// Third OR
|
||||||
|
Assert.assertEquals("OR", andClauses[2].getName());
|
||||||
|
Assert.assertSame(OrFilter.class, andClauses[2].getClass());
|
||||||
|
OrFilter orFilter = (OrFilter) andClauses[2];
|
||||||
|
Filter<?>[] orClauses = orFilter.getClauses();
|
||||||
|
// First EQ in OR
|
||||||
|
Assert.assertEquals("EQ", orClauses[0].getName());
|
||||||
|
Assert.assertSame(EqFilter.class, orClauses[0].getClass());
|
||||||
|
eq = (EqFilter<?>) orClauses[0];
|
||||||
|
Assert.assertEquals("v2", eq.getKey());
|
||||||
|
Assert.assertEquals(true, eq.getValue());
|
||||||
|
// Second IN in OR
|
||||||
|
Assert.assertEquals("IN", orClauses[1].getName());
|
||||||
|
Assert.assertSame(InFilter.class, orClauses[1].getClass());
|
||||||
|
in = (InFilter<?>) orClauses[1];
|
||||||
|
Assert.assertEquals("v3", in.getKey());
|
||||||
|
Assert.assertArrayEquals(new Double[]{ 1.3, 1.5 }, in.getValues().toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testQueryInValidFilter() throws JsonProcessingException {
|
||||||
|
Query q = new Query();
|
||||||
|
|
||||||
|
AndFilter filter = new AndFilter();
|
||||||
|
filter.addClause(new EqFilter<>("key", "value"));
|
||||||
|
filter.addClause(new InFilter<>("key2", "v1", "v2"));
|
||||||
|
|
||||||
|
OrFilter orFilter = new OrFilter();
|
||||||
|
orFilter.addClause(new EqFilter<>("v2", true));
|
||||||
|
// invalid OR filter
|
||||||
|
|
||||||
|
filter.addClause(orFilter);
|
||||||
|
|
||||||
|
// Add Filter
|
||||||
|
q.setFilter(filter);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package io.dapr.client.domain.query.filters;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class AndFilterTest {
|
||||||
|
|
||||||
|
private final static ObjectMapper MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
|
private String json = "{\"AND\":[{\"EQ\":{\"key\":\"value\"}},{\"IN\":{\"key2\":[\"v1\",\"v2\"]}}]}";
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
@Test
|
||||||
|
public void testSerialization() throws JsonProcessingException {
|
||||||
|
AndFilter filter = new AndFilter();
|
||||||
|
filter.addClause(new EqFilter<>("key", "value"));
|
||||||
|
filter.addClause(new InFilter<>("key2", "v1", "v2"));
|
||||||
|
|
||||||
|
Assert.assertEquals(json, MAPPER.writeValueAsString((Filter) filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeserialization() throws JsonProcessingException {
|
||||||
|
Filter<?> res = MAPPER.readValue(json, Filter.class);
|
||||||
|
|
||||||
|
// Check for AndFilter
|
||||||
|
Assert.assertEquals("AND", res.getName());
|
||||||
|
Assert.assertSame(AndFilter.class, res.getClass());
|
||||||
|
|
||||||
|
AndFilter filter = (AndFilter) res;
|
||||||
|
// Check 2 clauses
|
||||||
|
Assert.assertEquals(2, filter.getClauses().length);
|
||||||
|
// First EQ
|
||||||
|
Assert.assertSame(EqFilter.class, filter.getClauses()[0].getClass());
|
||||||
|
EqFilter<?> eq = (EqFilter<?>) filter.getClauses()[0];
|
||||||
|
Assert.assertEquals("key", eq.getKey());
|
||||||
|
Assert.assertEquals("value", eq.getValue());
|
||||||
|
// Second IN
|
||||||
|
Assert.assertSame(InFilter.class, filter.getClauses()[1].getClass());
|
||||||
|
InFilter<?> in = (InFilter<?>) filter.getClauses()[1];
|
||||||
|
Assert.assertEquals("key2", in.getKey());
|
||||||
|
Assert.assertArrayEquals(new String[]{ "v1", "v2" }, in.getValues().toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidation() {
|
||||||
|
AndFilter filter = new AndFilter();
|
||||||
|
Assert.assertFalse(filter.isValid());
|
||||||
|
|
||||||
|
filter.addClause(new EqFilter<>("key1", "v2"));
|
||||||
|
Assert.assertFalse(filter.isValid());
|
||||||
|
|
||||||
|
filter.addClause(new EqFilter<>("key2", "v3"));
|
||||||
|
Assert.assertTrue(filter.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package io.dapr.client.domain.query.filters;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class EqFilterTest {
|
||||||
|
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
|
String json = "{\"EQ\":{\"key\":1.5}}";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSerialization() throws JsonProcessingException {
|
||||||
|
EqFilter<?> filter = new EqFilter<>("key", 1.5);
|
||||||
|
|
||||||
|
Assert.assertEquals(json, MAPPER.writeValueAsString(filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeserialization() throws JsonProcessingException {
|
||||||
|
EqFilter<?> filter = MAPPER.readValue(json, EqFilter.class);
|
||||||
|
Assert.assertEquals("key", filter.getKey());
|
||||||
|
Assert.assertEquals(1.5, filter.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidation() {
|
||||||
|
EqFilter<?> filter = new EqFilter<>(null, "val");
|
||||||
|
Assert.assertFalse(filter.isValid());
|
||||||
|
|
||||||
|
|
||||||
|
filter = new EqFilter<>("", "");
|
||||||
|
Assert.assertFalse(filter.isValid());
|
||||||
|
|
||||||
|
filter = new EqFilter<>("", true);
|
||||||
|
Assert.assertFalse(filter.isValid());
|
||||||
|
|
||||||
|
filter = new EqFilter<>(" ", "valid");
|
||||||
|
Assert.assertFalse(filter.isValid());
|
||||||
|
|
||||||
|
filter = new EqFilter<>("valid", "");
|
||||||
|
Assert.assertTrue(filter.isValid());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package io.dapr.client.domain.query.filters;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class InFilterTest {
|
||||||
|
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
|
String json = "{\"IN\":{\"key\":[1.5,44.0]}}";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSerialization() throws JsonProcessingException {
|
||||||
|
InFilter<?> filter = new InFilter<>("key", 1.5, 44.0);
|
||||||
|
|
||||||
|
Assert.assertEquals(json, MAPPER.writeValueAsString(filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeserialization() throws JsonProcessingException {
|
||||||
|
InFilter<?> filter = MAPPER.readValue(json, InFilter.class);
|
||||||
|
Assert.assertEquals("key", filter.getKey());
|
||||||
|
Assert.assertArrayEquals(new Double[]{ 1.5, 44.0 }, filter.getValues().toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidation() {
|
||||||
|
InFilter<?> filter = new InFilter<>(null, "val");
|
||||||
|
Assert.assertFalse(filter.isValid());
|
||||||
|
|
||||||
|
|
||||||
|
filter = new InFilter<>("", "");
|
||||||
|
Assert.assertFalse(filter.isValid());
|
||||||
|
|
||||||
|
filter = new InFilter<>("", true);
|
||||||
|
Assert.assertFalse(filter.isValid());
|
||||||
|
|
||||||
|
filter = new InFilter<>(" ", "valid");
|
||||||
|
Assert.assertFalse(filter.isValid());
|
||||||
|
|
||||||
|
filter = new InFilter<>("valid", "");
|
||||||
|
Assert.assertTrue(filter.isValid());
|
||||||
|
|
||||||
|
filter = new InFilter<>("valid", "1.5", "2.5");
|
||||||
|
Assert.assertTrue(filter.isValid());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package io.dapr.client.domain.query.filters;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class OrFilterTest {
|
||||||
|
private final static ObjectMapper MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
|
private String json = "{\"OR\":[{\"EQ\":{\"key\":\"value\"}},{\"IN\":{\"key2\":[\"v1\",\"v2\"]}}]}";
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
@Test
|
||||||
|
public void testSerialization() throws JsonProcessingException {
|
||||||
|
OrFilter filter = new OrFilter();
|
||||||
|
filter.addClause(new EqFilter<>("key", "value"));
|
||||||
|
filter.addClause(new InFilter<>("key2", "v1", "v2"));
|
||||||
|
|
||||||
|
Assert.assertEquals(json, MAPPER.writeValueAsString((Filter) filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeserialization() throws JsonProcessingException {
|
||||||
|
Filter<?> res = MAPPER.readValue(json, Filter.class);
|
||||||
|
|
||||||
|
// Check for AndFilter
|
||||||
|
Assert.assertEquals("OR", res.getName());
|
||||||
|
Assert.assertSame(OrFilter.class, res.getClass());
|
||||||
|
|
||||||
|
OrFilter filter = (OrFilter) res;
|
||||||
|
// Check 2 clauses
|
||||||
|
Assert.assertEquals(2, filter.getClauses().length);
|
||||||
|
// First EQ
|
||||||
|
Assert.assertSame(EqFilter.class, filter.getClauses()[0].getClass());
|
||||||
|
EqFilter<?> eq = (EqFilter<?>) filter.getClauses()[0];
|
||||||
|
Assert.assertEquals("key", eq.getKey());
|
||||||
|
Assert.assertEquals("value", eq.getValue());
|
||||||
|
// Second IN
|
||||||
|
Assert.assertSame(InFilter.class, filter.getClauses()[1].getClass());
|
||||||
|
InFilter<?> in = (InFilter<?>) filter.getClauses()[1];
|
||||||
|
Assert.assertEquals("key2", in.getKey());
|
||||||
|
Assert.assertArrayEquals(new String[]{ "v1", "v2" }, in.getValues().toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidation() {
|
||||||
|
OrFilter filter = new OrFilter();
|
||||||
|
Assert.assertFalse(filter.isValid());
|
||||||
|
|
||||||
|
filter.addClause(new EqFilter<>("key1", "v2"));
|
||||||
|
Assert.assertFalse(filter.isValid());
|
||||||
|
|
||||||
|
filter.addClause(new EqFilter<>("key2", "v3"));
|
||||||
|
Assert.assertTrue(filter.isValid());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue