mirror of https://github.com/dapr/java-sdk.git
				
				
				
			Bulk get state (#335)
* unfinished work * Working version of Bulk get state API. Co-authored-by: Haishi2016 <hbai@microsoft.com>
This commit is contained in:
		
							parent
							
								
									e2ddc9f96d
								
							
						
					
					
						commit
						baab109202
					
				|  | @ -25,7 +25,7 @@ jobs: | |||
|       DAPR_RUNTIME_VER: 0.10.0-rc.0 | ||||
|       DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/e7c9a643dfefbcfff0c2c26c12029259e6e81180/install/install.sh | ||||
|       DAPR_CLI_REF: e7c9a643dfefbcfff0c2c26c12029259e6e81180 | ||||
|       DAPR_REF: | ||||
|       DAPR_REF: 1e3b7aa3202e268759bce6cf9b30b6d95471ab8c | ||||
|       OSSRH_USER_TOKEN: ${{ secrets.OSSRH_USER_TOKEN }} | ||||
|       OSSRH_PWD_TOKEN: ${{ secrets.OSSRH_PWD_TOKEN }} | ||||
|       GPG_KEY: ${{ secrets.GPG_KEY }} | ||||
|  |  | |||
							
								
								
									
										2
									
								
								pom.xml
								
								
								
								
							
							
						
						
									
										2
									
								
								pom.xml
								
								
								
								
							|  | @ -17,7 +17,7 @@ | |||
|     <grpc.version>1.25.0</grpc.version> | ||||
|     <protobuf.version>3.11.0</protobuf.version> | ||||
|     <protoc.version>3.10.0</protoc.version> | ||||
|     <dapr.proto.baseurl>https://raw.githubusercontent.com/dapr/dapr/2f32759fb9798d79b6d3fa92b1d972b07a62b6e8/dapr/proto</dapr.proto.baseurl> | ||||
|     <dapr.proto.baseurl>https://raw.githubusercontent.com/dapr/dapr/1660773c3da6740377f4440cae89dea93479c9f5/dapr/proto</dapr.proto.baseurl> | ||||
|     <os-maven-plugin.version>1.6.2</os-maven-plugin.version> | ||||
|     <maven-dependency-plugin.version>3.1.1</maven-dependency-plugin.version> | ||||
|     <maven-antrun-plugin.version>1.8</maven-antrun-plugin.version> | ||||
|  |  | |||
|  | @ -78,6 +78,12 @@ | |||
|       <version>5.1.0</version> | ||||
|       <scope>test</scope> | ||||
|     </dependency> | ||||
|       <dependency> | ||||
|           <groupId>io.projectreactor</groupId> | ||||
|           <artifactId>reactor-core</artifactId> | ||||
|           <version>3.3.1.RELEASE</version> | ||||
|           <scope>test</scope> | ||||
|       </dependency> | ||||
|   </dependencies> | ||||
| 
 | ||||
|   <build> | ||||
|  |  | |||
|  | @ -42,7 +42,7 @@ public class DaprRun { | |||
|                   Class serviceClass, | ||||
|                   int maxWaitMilliseconds) { | ||||
|     // The app name needs to be deterministic since we depend on it to kill previous runs. | ||||
|     this.appName = String.format("%s_%s", testName, serviceClass == null ? "" : serviceClass.getSimpleName()); | ||||
|     this.appName = serviceClass == null ? testName : String.format("%s_%s", testName, serviceClass.getSimpleName()); | ||||
|     this.startCommand = | ||||
|         new Command(successMessage, buildDaprCommand(this.appName, serviceClass, ports)); | ||||
|     this.listCommand = new Command( | ||||
|  |  | |||
|  | @ -0,0 +1,490 @@ | |||
| /* | ||||
|  * Copyright (c) Microsoft Corporation. | ||||
|  * Licensed under the MIT License. | ||||
|  */ | ||||
| 
 | ||||
| package io.dapr.it.state; | ||||
| 
 | ||||
| import io.dapr.client.DaprClient; | ||||
| import io.dapr.client.DaprClientBuilder; | ||||
| import io.dapr.client.domain.State; | ||||
| import io.dapr.client.domain.StateOptions; | ||||
| import io.dapr.it.BaseIT; | ||||
| import io.dapr.it.DaprRun; | ||||
| import org.junit.Assert; | ||||
| import org.junit.BeforeClass; | ||||
| import org.junit.Test; | ||||
| import reactor.core.publisher.Mono; | ||||
| 
 | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
| import java.util.UUID; | ||||
| 
 | ||||
| import static org.junit.Assert.assertEquals; | ||||
| import static org.junit.Assert.assertNull; | ||||
| 
 | ||||
| /** | ||||
|  * Common test cases for Dapr client (GRPC and HTTP). | ||||
|  */ | ||||
| public abstract class AbstractStateClientIT extends BaseIT { | ||||
| 
 | ||||
|   @Test | ||||
|   public void saveAndGetState() { | ||||
| 
 | ||||
|     //The key use to store the state | ||||
|     final String stateKey = "myKey"; | ||||
| 
 | ||||
|     //create the http client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
| 
 | ||||
|     //creation of a dummy data | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //create of the deferred call to DAPR to store the state | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //create of the deferred call to DAPR to get the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State(stateKey), MyData.class); | ||||
| 
 | ||||
|     //retrieve the state | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     //Assert that the response is the correct one | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void saveAndGetBulkStates() { | ||||
|     final String stateKeyOne = UUID.randomUUID().toString(); | ||||
|     final String stateKeyTwo = UUID.randomUUID().toString(); | ||||
|     final String stateKeyThree = "NotFound"; | ||||
| 
 | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
| 
 | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //saves the states. | ||||
|     daprClient.saveState(STATE_STORE_NAME, stateKeyOne, "1", data, null).block(); | ||||
|     daprClient.saveState(STATE_STORE_NAME, stateKeyTwo, null, null, null).block(); | ||||
| 
 | ||||
|     //retrieves states in bulk. | ||||
|     Mono<List<State<MyData>>> response = | ||||
|         daprClient.getStates(STATE_STORE_NAME, Arrays.asList(stateKeyOne, stateKeyTwo, stateKeyThree), MyData.class); | ||||
|     List<State<MyData>> result = response.block(); | ||||
| 
 | ||||
|     //Assert that the response is the correct one | ||||
|     assertEquals(3, result.size()); | ||||
|     assertEquals(stateKeyOne, result.stream().findFirst().get().getKey()); | ||||
|     assertEquals(data, result.stream().findFirst().get().getValue()); | ||||
|     assertEquals("1", result.stream().findFirst().get().getEtag()); | ||||
|     assertNull(result.stream().findFirst().get().getError()); | ||||
| 
 | ||||
|     assertEquals(stateKeyTwo, result.stream().skip(1).findFirst().get().getKey()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getValue()); | ||||
|     assertEquals("1", result.stream().skip(1).findFirst().get().getEtag()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getError()); | ||||
| 
 | ||||
|     assertEquals(stateKeyThree, result.stream().skip(2).findFirst().get().getKey()); | ||||
|     assertNull(result.stream().skip(2).findFirst().get().getValue()); | ||||
|     assertEquals("", result.stream().skip(2).findFirst().get().getEtag()); | ||||
|     assertNull("not found", result.stream().skip(2).findFirst().get().getError()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void saveUpdateAndGetState() { | ||||
| 
 | ||||
|     //The key use to store the state and be updated | ||||
|     final String stateKey = "keyToBeUpdated"; | ||||
| 
 | ||||
|     //create http DAPR client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute save action to DAPR | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //change data properties | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //create deferred action to update the sate without any etag or options | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the update action to DAPR | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to retrieve the action | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the retrieve of the state | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that the update was success action | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B2", myDataResponse.getValue().getPropertyB()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void saveAndDeleteState() { | ||||
|     //The key use to store the state and be deleted | ||||
|     final String stateKey = "myeKeyToBeDeleted"; | ||||
| 
 | ||||
|     //create DAPR client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
| 
 | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save state action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to retrieve the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the retrieve of the state | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that the state was saved correctly | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     //create deferred action to delete the state | ||||
|     Mono<Void> deleteResponse = daprClient.deleteState(STATE_STORE_NAME, stateKey, null, null); | ||||
|     //execute the delete action | ||||
|     deleteResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to retrieve the state | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), MyData.class); | ||||
|     //execute the retrieve of the state | ||||
|     myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that the action does not return any value, because the state was deleted | ||||
|     Assert.assertNull(myDataResponse.getValue()); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   @Test | ||||
|   public void saveUpdateAndGetStateWithEtag() { | ||||
|     //The key use to store the state and be updated using etags | ||||
|     final String stateKey = "keyToBeUpdatedWithEtag"; | ||||
|     //create DAPR client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save state action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to retrieve the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the action for retrieve the state and the etag | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that the etag is not empty | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     String firstETag = myDataResponse.getEtag(); | ||||
| 
 | ||||
|     //change the data in order to update the state | ||||
|     data.setPropertyA("data in property A2"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //Create deferred action to update the data using the correct etag | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), data, null); | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), MyData.class); | ||||
|     //retrive the data wihout any etag | ||||
|     myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that state value changes | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     //review that the etag changes after an update | ||||
|     Assert.assertNotEquals(firstETag, myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A2", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B2", myDataResponse.getValue().getPropertyB()); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   @Test(expected = RuntimeException.class) | ||||
|   public void saveUpdateAndGetStateWithWrongEtag() { | ||||
|     final String stateKey = "keyToBeUpdatedWithWrongEtag"; | ||||
| 
 | ||||
|     //create DAPR client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save state action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to retrieve the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the action for retrieve the state and the etag | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that the etag is not empty | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     String firstETag = myDataResponse.getEtag(); | ||||
| 
 | ||||
|     //change the data in order to update the state | ||||
|     data.setPropertyA("data in property A2"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //Create deferred action to update the data using the incorrect etag | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, "99999999999999", data, null); | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), MyData.class); | ||||
|     //retrive the data wihout any etag | ||||
|     myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that state value changes | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     //review that the etag changes after an update | ||||
|     Assert.assertNotEquals(firstETag, myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A2", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B2", myDataResponse.getValue().getPropertyB()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void saveAndDeleteStateWithEtag() { | ||||
|     final String stateKey = "myeKeyToBeDeletedWithEtag"; | ||||
|     //create DAPR client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save state action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to get the state with the etag | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the get state | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     //Create deferred action to delete an state sending the etag | ||||
|     Mono<Void> deleteResponse = daprClient.deleteState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), null); | ||||
|     //execute the delete of the state | ||||
|     deleteResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to get the sate without an etag | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State(stateKey), MyData.class); | ||||
|     myDataResponse = response.block(); | ||||
| 
 | ||||
|     //Review that the response is null, because the state was deleted | ||||
|     Assert.assertNull(myDataResponse.getValue()); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   @Test(expected = RuntimeException.class) | ||||
|   public void saveAndDeleteStateWithWrongEtag() { | ||||
|     final String stateKey = "myeKeyToBeDeletedWithWrongEtag"; | ||||
| 
 | ||||
|     //create DAPR client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save state action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to get the state with the etag | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the get state | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     //Create deferred action to delete an state sending the incorrect etag | ||||
|     Mono<Void> deleteResponse = daprClient.deleteState(STATE_STORE_NAME, stateKey, "99999999999", null); | ||||
|     //execute the delete of the state, this should trhow an exception | ||||
|     deleteResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to get the sate without an etag | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State(stateKey), MyData.class); | ||||
|     myDataResponse = response.block(); | ||||
| 
 | ||||
|     //Review that the response is null, because the state was deleted | ||||
|     Assert.assertNull(myDataResponse.getValue()); | ||||
|   } | ||||
| 
 | ||||
|   @Test(expected = RuntimeException.class) | ||||
|   public void saveUpdateAndGetStateWithEtagAndStateOptionsFirstWrite() { | ||||
|     final String stateKey = "keyToBeUpdatedWithEtagAndOptions"; | ||||
| 
 | ||||
|     //create option with concurrency with first writte and consistency of strong | ||||
|     StateOptions stateOptions = new StateOptions(StateOptions.Consistency.STRONG, | ||||
|         StateOptions.Concurrency.FIRST_WRITE); | ||||
| 
 | ||||
|     //create dapr client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
|     //create Dummy data | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //create state using stateOptions | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, stateOptions); | ||||
|     //execute the save state | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     //crate deferred action to retrieve the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, stateOptions), | ||||
|         MyData.class); | ||||
|     //execute the retrieve of the state using options | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     //change data to be udpated | ||||
|     data.setPropertyA("data in property A2"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //create deferred action to update the action with options | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), data, stateOptions); | ||||
|     //update the state | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     data.setPropertyA("last write"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //create deferred action to update the action with the same etag | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), data, stateOptions); | ||||
|     //throws an exception, the state was already udpated | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, stateOptions), MyData.class); | ||||
|     State<MyData> myLastDataResponse = response.block(); | ||||
| 
 | ||||
|     Assert.assertNotNull(myLastDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myLastDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myLastDataResponse.getValue()); | ||||
|     Assert.assertNotNull(myDataResponse.getEtag(), myLastDataResponse.getEtag()); | ||||
|     Assert.assertEquals("data in property A2", myLastDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B2", myLastDataResponse.getValue().getPropertyB()); | ||||
|   } | ||||
| 
 | ||||
|   @Test() | ||||
|   public void saveUpdateAndGetStateWithEtagAndStateOptionsLastWrite() { | ||||
|     final String stateKey = "keyToBeUpdatedWithEtagAndOptions"; | ||||
| 
 | ||||
|     //create option with concurrency with first writte and consistency of strong | ||||
|     StateOptions stateOptions = new StateOptions(StateOptions.Consistency.STRONG, StateOptions.Concurrency.LAST_WRITE); | ||||
| 
 | ||||
|     //create dapr client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
|     //create Dummy data | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //create state using stateOptions | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, stateOptions); | ||||
|     //execute the save state | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     //crate deferred action to retrieve the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, stateOptions), | ||||
|         MyData.class); | ||||
|     //execute the retrieve of the state using options | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     //change data to be udpated | ||||
|     data.setPropertyA("data in property A2"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //create deferred action to update the action with options | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), data, stateOptions); | ||||
|     //update the state | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     data.setPropertyA("last write"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //create deferred action to update the action with the same etag | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), data, stateOptions); | ||||
|     //update the state without an error | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, stateOptions), MyData.class); | ||||
|     State<MyData> myLastDataResponse = response.block(); | ||||
| 
 | ||||
|     Assert.assertNotNull(myLastDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myLastDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myLastDataResponse.getValue()); | ||||
|     Assert.assertNotNull(myDataResponse.getEtag(), myLastDataResponse.getEtag()); | ||||
|     Assert.assertEquals("last write", myLastDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B2", myLastDataResponse.getValue().getPropertyB()); | ||||
|   } | ||||
| 
 | ||||
|   protected abstract DaprClient buildDaprClient(); | ||||
| 
 | ||||
| } | ||||
|  | @ -8,28 +8,18 @@ package io.dapr.it.state; | |||
| import io.dapr.client.DaprClient; | ||||
| import io.dapr.client.DaprClientBuilder; | ||||
| import io.dapr.client.DaprClientGrpc; | ||||
| import io.dapr.client.domain.State; | ||||
| import io.dapr.client.domain.StateOptions; | ||||
| import io.dapr.it.BaseIT; | ||||
| import io.dapr.it.DaprRun; | ||||
| import org.junit.AfterClass; | ||||
| import org.junit.BeforeClass; | ||||
| import org.junit.Ignore; | ||||
| import org.junit.Test; | ||||
| import reactor.core.publisher.Mono; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| import static org.junit.Assert.assertEquals; | ||||
| import static org.junit.Assert.assertNotEquals; | ||||
| import static org.junit.Assert.assertNotNull; | ||||
| import static org.junit.Assert.assertNull; | ||||
| import static org.junit.Assert.assertTrue; | ||||
| 
 | ||||
| /** | ||||
|  * Test State GRPC DAPR capabilities using a DAPR instance with an empty service running | ||||
|  */ | ||||
| public class GRPCStateClientIT extends BaseIT { | ||||
| public class GRPCStateClientIT extends AbstractStateClientIT { | ||||
| 
 | ||||
|   private static DaprRun daprRun; | ||||
| 
 | ||||
|  | @ -49,419 +39,9 @@ public class GRPCStateClientIT extends BaseIT { | |||
|     daprClient.close(); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   @Test | ||||
|   public void saveAndGetState() { | ||||
| 
 | ||||
|     //The key use to store the state | ||||
|     final String stateKey = "myKey"; | ||||
| 
 | ||||
|     //create the http client | ||||
| 
 | ||||
| 
 | ||||
|     //creation of a dummy data | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //create of the deferred call to DAPR to store the state | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //create of the deferred call to DAPR to get the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, null), MyData.class); | ||||
| 
 | ||||
|     //retrieve the state | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     //Assert that the response is the correct one | ||||
|     assertNotNull(myDataResponse.getEtag()); | ||||
|     assertNotNull(myDataResponse.getKey()); | ||||
|     assertNotNull(myDataResponse.getValue()); | ||||
|     assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void saveUpdateAndGetState() { | ||||
| 
 | ||||
|     //The key use to store the state and be updated | ||||
|     final String stateKey = "keyToBeUpdated"; | ||||
| 
 | ||||
|     //create http DAPR client | ||||
| 
 | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute save action to DAPR | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //change data properties | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //create deferred action to update the sate without any etag or options | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the update action to DAPR | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to retrieve the action | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the retrieve of the state | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that the update was success action | ||||
|     assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     assertEquals("data in property B2", myDataResponse.getValue().getPropertyB()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void saveAndDeleteState() { | ||||
|     //The key use to store the state and be deleted | ||||
|     final String stateKey = "myeKeyToBeDeleted"; | ||||
| 
 | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save state action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to retrieve the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the retrieve of the state | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that the state was saved correctly | ||||
|     assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     //create deferred action to delete the state | ||||
|     Mono<Void> deleteResponse = daprClient.deleteState(STATE_STORE_NAME, stateKey, null, null); | ||||
|     //execute the delete action | ||||
|     deleteResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to retrieve the state | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), MyData.class); | ||||
|     //execute the retrieve of the state | ||||
|     myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that the action does not return any value, because the state was deleted | ||||
|     assertNull(myDataResponse.getValue()); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   @Test | ||||
|   public void saveUpdateAndGetStateWithEtag() { | ||||
|     //The key use to store the state and be updated using etags | ||||
|     final String stateKey = "keyToBeUpdatedWithEtag"; | ||||
| 
 | ||||
| 
 | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save state action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to retrieve the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the action for retrieve the state and the etag | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that the etag is not empty | ||||
|     assertNotNull(myDataResponse.getEtag()); | ||||
|     assertNotNull(myDataResponse.getKey()); | ||||
|     assertNotNull(myDataResponse.getValue()); | ||||
|     assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     String firstETag = myDataResponse.getEtag(); | ||||
| 
 | ||||
|     //change the data in order to update the state | ||||
|     data.setPropertyA("data in property A2"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //Create deferred action to update the data using the correct etag | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), data, null); | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), MyData.class); | ||||
|     //retrive the data wihout any etag | ||||
|     myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that state value changes | ||||
|     assertNotNull(myDataResponse.getEtag()); | ||||
|     //review that the etag changes after an update | ||||
|     assertNotEquals(firstETag, myDataResponse.getEtag()); | ||||
|     assertNotNull(myDataResponse.getKey()); | ||||
|     assertNotNull(myDataResponse.getValue()); | ||||
|     assertEquals("data in property A2", myDataResponse.getValue().getPropertyA()); | ||||
|     assertEquals("data in property B2", myDataResponse.getValue().getPropertyB()); | ||||
|   } | ||||
| 
 | ||||
|   @Ignore("This test case is ignored because DAPR  ignore the ETag is wrong when is sent from GRPC protocol, the " + | ||||
|       "execution continues and the state is updated.") | ||||
|   @Test(expected = RuntimeException.class) | ||||
|   public void saveUpdateAndGetStateWithWrongEtag() { | ||||
|     final String stateKey = "keyToBeUpdatedWithWrongEtag"; | ||||
| 
 | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save state action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to retrieve the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the action for retrieve the state and the etag | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that the etag is not empty | ||||
|     assertNotNull(myDataResponse.getEtag()); | ||||
|     assertNotNull(myDataResponse.getKey()); | ||||
|     assertNotNull(myDataResponse.getValue()); | ||||
|     assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     String firstETag = myDataResponse.getEtag(); | ||||
| 
 | ||||
|     //change the data in order to update the state | ||||
|     data.setPropertyA("data in property A2"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //Create deferred action to update the data using the incorrect etag | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, "99999999999999", data, null); | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), MyData.class); | ||||
|     //retrive the data wihout any etag | ||||
|     myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that state value changes | ||||
|     assertNotNull(myDataResponse.getEtag()); | ||||
|     //review that the etag changes after an update | ||||
|     assertNotEquals(firstETag, myDataResponse.getEtag()); | ||||
|     assertNotNull(myDataResponse.getKey()); | ||||
|     assertNotNull(myDataResponse.getValue()); | ||||
|     assertEquals("data in property A2", myDataResponse.getValue().getPropertyA()); | ||||
|     assertEquals("data in property B2", myDataResponse.getValue().getPropertyB()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void saveAndDeleteStateWithEtag() { | ||||
|     final String stateKey = "myeKeyToBeDeletedWithEtag"; | ||||
| 
 | ||||
| 
 | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save state action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to get the state with the etag | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the get state | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     assertNotNull(myDataResponse.getEtag()); | ||||
|     assertNotNull(myDataResponse.getKey()); | ||||
|     assertNotNull(myDataResponse.getValue()); | ||||
|     assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     //Create deferred action to delete an state sending the etag | ||||
|     Mono<Void> deleteResponse = daprClient.deleteState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), null); | ||||
|     //execute the delete of the state | ||||
|     deleteResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to get the sate without an etag | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, null), MyData.class); | ||||
|     myDataResponse = response.block(); | ||||
| 
 | ||||
|     //Review that the response is null, because the state was deleted | ||||
|     assertNull(myDataResponse.getValue()); | ||||
|   } | ||||
| 
 | ||||
|   @Ignore("This test case is ignored because DAPR  ignore if the ETag is wrong when is sent from GRPC protocol, the " + | ||||
|       "execution continues and the state is deleted.") | ||||
|   @Test(expected = RuntimeException.class) | ||||
|   public void saveAndDeleteStateWithWrongEtag() { | ||||
|     final String stateKey = "myeKeyToBeDeletedWithWrongEtag"; | ||||
| 
 | ||||
| 
 | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save state action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to get the state with the etag | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the get state | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     assertNotNull(myDataResponse.getEtag()); | ||||
|     assertNotNull(myDataResponse.getKey()); | ||||
|     assertNotNull(myDataResponse.getValue()); | ||||
|     assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     //Create deferred action to delete an state sending the incorrect etag | ||||
|     Mono<Void> deleteResponse = daprClient.deleteState(STATE_STORE_NAME, stateKey, "99999999999", null); | ||||
|     //execute the delete of the state, this should trhow an exception | ||||
|     deleteResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to get the sate without an etag | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, null), MyData.class); | ||||
|     myDataResponse = response.block(); | ||||
| 
 | ||||
|     //Review that the response is null, because the state was deleted | ||||
|     assertNull(myDataResponse.getValue()); | ||||
|   } | ||||
| 
 | ||||
|   @Ignore("This test case is  ignored because it seems that DAPR using GRPC is ignoring the state options for " + | ||||
|       "consistency and concurrency.") | ||||
|   @Test(expected = RuntimeException.class) | ||||
|   public void saveUpdateAndGetStateWithEtagAndStateOptionsFirstWrite() { | ||||
|     final String stateKey = "keyToBeUpdatedWithEtagAndOptions"; | ||||
| 
 | ||||
|     //create option with concurrency with first writte and consistency of strong | ||||
|     StateOptions stateOptions = new StateOptions(StateOptions.Consistency.STRONG, | ||||
|         StateOptions.Concurrency.FIRST_WRITE); | ||||
| 
 | ||||
| 
 | ||||
|     //create Dummy data | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //create state using stateOptions | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, stateOptions); | ||||
|     //execute the save state | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     //crate deferred action to retrieve the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, stateOptions), | ||||
|         MyData.class); | ||||
|     //execute the retrieve of the state using options | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     assertNotNull(myDataResponse.getEtag()); | ||||
|     assertNotNull(myDataResponse.getKey()); | ||||
|     assertNotNull(myDataResponse.getValue()); | ||||
|     assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     //change data to be udpated | ||||
|     data.setPropertyA("data in property A2"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //create deferred action to update the action with options | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), data, stateOptions); | ||||
|     //update the state | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     data.setPropertyA("last write"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //create deferred action to update the action with the same etag | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), data, stateOptions); | ||||
|     //throws an exception, the state was already udpated | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, stateOptions), MyData.class); | ||||
|     State<MyData> myLastDataResponse = response.block(); | ||||
| 
 | ||||
|     assertNotNull(myLastDataResponse.getEtag()); | ||||
|     assertNotNull(myLastDataResponse.getKey()); | ||||
|     assertNotNull(myLastDataResponse.getValue()); | ||||
|     assertNotNull(myDataResponse.getEtag(), myLastDataResponse.getEtag()); | ||||
|     assertEquals("data in property A2", myLastDataResponse.getValue().getPropertyA()); | ||||
|     assertEquals("data in property B2", myLastDataResponse.getValue().getPropertyB()); | ||||
|   } | ||||
| 
 | ||||
|   @Test() | ||||
|   public void saveUpdateAndGetStateWithEtagAndStateOptionsLastWrite() { | ||||
|     final String stateKey = "keyToBeUpdatedWithEtagAndOptions"; | ||||
| 
 | ||||
|     //create option with concurrency with first writte and consistency of strong | ||||
|     StateOptions stateOptions = new StateOptions(StateOptions.Consistency.STRONG, StateOptions.Concurrency.LAST_WRITE); | ||||
| 
 | ||||
| 
 | ||||
|     //create Dummy data | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //create state using stateOptions | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, stateOptions); | ||||
|     //execute the save state | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     //crate deferred action to retrieve the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, stateOptions), | ||||
|         MyData.class); | ||||
|     //execute the retrieve of the state using options | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     assertNotNull(myDataResponse.getEtag()); | ||||
|     assertNotNull(myDataResponse.getKey()); | ||||
|     assertNotNull(myDataResponse.getValue()); | ||||
|     assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     //change data to be udpated | ||||
|     data.setPropertyA("data in property A2"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //create deferred action to update the action with options | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), data, stateOptions); | ||||
|     //update the state | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     data.setPropertyA("last write"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //create deferred action to update the action with the same etag | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), data, stateOptions); | ||||
|     //update the state without an error | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, stateOptions), MyData.class); | ||||
|     State<MyData> myLastDataResponse = response.block(); | ||||
| 
 | ||||
|     assertNotNull(myLastDataResponse.getEtag()); | ||||
|     assertNotNull(myLastDataResponse.getKey()); | ||||
|     assertNotNull(myLastDataResponse.getValue()); | ||||
|     assertNotNull(myDataResponse.getEtag(), myLastDataResponse.getEtag()); | ||||
|     assertEquals("last write", myLastDataResponse.getValue().getPropertyA()); | ||||
|     assertEquals("data in property B2", myLastDataResponse.getValue().getPropertyB()); | ||||
|   @Override | ||||
|   protected DaprClient buildDaprClient() { | ||||
|     return daprClient; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -7,448 +7,41 @@ package io.dapr.it.state; | |||
| 
 | ||||
| import io.dapr.client.DaprClient; | ||||
| import io.dapr.client.DaprClientBuilder; | ||||
| import io.dapr.client.domain.State; | ||||
| import io.dapr.client.domain.StateOptions; | ||||
| import io.dapr.it.BaseIT; | ||||
| import io.dapr.client.DaprClientHttp; | ||||
| import io.dapr.it.DaprRun; | ||||
| import org.junit.Assert; | ||||
| import org.junit.AfterClass; | ||||
| import org.junit.BeforeClass; | ||||
| import org.junit.Test; | ||||
| import reactor.core.publisher.Mono; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| import static org.junit.Assert.assertTrue; | ||||
| 
 | ||||
| /** | ||||
|  * Test State HTTP DAPR capabilities using a DAPR instance with an empty service running | ||||
|  */ | ||||
| public class HttpStateClientIT extends BaseIT { | ||||
| public class HttpStateClientIT extends AbstractStateClientIT { | ||||
| 
 | ||||
|   private static DaprRun daprRun; | ||||
| 
 | ||||
|   private static DaprClient daprClient; | ||||
| 
 | ||||
|   @BeforeClass | ||||
|   public static void init() throws Exception { | ||||
|     daprRun = startDaprApp(HttpStateClientIT.class.getSimpleName(), 1000); | ||||
|     daprRun = startDaprApp(HttpStateClientIT.class.getSimpleName(), 5000); | ||||
|     daprRun.switchToHTTP(); | ||||
|     daprClient = new DaprClientBuilder().build(); | ||||
| 
 | ||||
|     assertTrue(daprClient instanceof DaprClientHttp); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void saveAndGetState() { | ||||
| 
 | ||||
|     //The key use to store the state | ||||
|     final String stateKey = "myKey"; | ||||
| 
 | ||||
|     //create the http client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
| 
 | ||||
|     //creation of a dummy data | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //create of the deferred call to DAPR to store the state | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //create of the deferred call to DAPR to get the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, null), MyData.class); | ||||
| 
 | ||||
|     //retrieve the state | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     //Assert that the response is the correct one | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
|   @AfterClass | ||||
|   public static void tearDown() throws IOException { | ||||
|     daprClient.close(); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void saveUpdateAndGetState() { | ||||
| 
 | ||||
|     //The key use to store the state and be updated | ||||
|     final String stateKey = "keyToBeUpdated"; | ||||
| 
 | ||||
|     //create http DAPR client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute save action to DAPR | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //change data properties | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //create deferred action to update the sate without any etag or options | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the update action to DAPR | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to retrieve the action | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the retrieve of the state | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that the update was success action | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B2", myDataResponse.getValue().getPropertyB()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void saveAndDeleteState() { | ||||
|     //The key use to store the state and be deleted | ||||
|     final String stateKey = "myeKeyToBeDeleted"; | ||||
| 
 | ||||
|     //create DAPR client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
| 
 | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save state action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to retrieve the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the retrieve of the state | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that the state was saved correctly | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     //create deferred action to delete the state | ||||
|     Mono<Void> deleteResponse = daprClient.deleteState(STATE_STORE_NAME, stateKey, null, null); | ||||
|     //execute the delete action | ||||
|     deleteResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to retrieve the state | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), MyData.class); | ||||
|     //execute the retrieve of the state | ||||
|     myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that the action does not return any value, because the state was deleted | ||||
|     Assert.assertNull(myDataResponse.getValue()); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   @Test | ||||
|   public void saveUpdateAndGetStateWithEtag() { | ||||
|     //The key use to store the state and be updated using etags | ||||
|     final String stateKey = "keyToBeUpdatedWithEtag"; | ||||
|     //create DAPR client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save state action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to retrieve the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the action for retrieve the state and the etag | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that the etag is not empty | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     String firstETag = myDataResponse.getEtag(); | ||||
| 
 | ||||
|     //change the data in order to update the state | ||||
|     data.setPropertyA("data in property A2"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //Create deferred action to update the data using the correct etag | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), data, null); | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), MyData.class); | ||||
|     //retrive the data wihout any etag | ||||
|     myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that state value changes | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     //review that the etag changes after an update | ||||
|     Assert.assertNotEquals(firstETag, myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A2", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B2", myDataResponse.getValue().getPropertyB()); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   @Test(expected = RuntimeException.class) | ||||
|   public void saveUpdateAndGetStateWithWrongEtag() { | ||||
|     final String stateKey = "keyToBeUpdatedWithWrongEtag"; | ||||
| 
 | ||||
|     //create DAPR client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save state action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to retrieve the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the action for retrieve the state and the etag | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that the etag is not empty | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     String firstETag = myDataResponse.getEtag(); | ||||
| 
 | ||||
|     //change the data in order to update the state | ||||
|     data.setPropertyA("data in property A2"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //Create deferred action to update the data using the incorrect etag | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, "99999999999999", data, null); | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), MyData.class); | ||||
|     //retrive the data wihout any etag | ||||
|     myDataResponse = response.block(); | ||||
| 
 | ||||
|     //review that state value changes | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     //review that the etag changes after an update | ||||
|     Assert.assertNotEquals(firstETag, myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A2", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B2", myDataResponse.getValue().getPropertyB()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void saveAndDeleteStateWithEtag() { | ||||
|     final String stateKey = "myeKeyToBeDeletedWithEtag"; | ||||
|     //create DAPR client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save state action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to get the state with the etag | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the get state | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     //Create deferred action to delete an state sending the etag | ||||
|     Mono<Void> deleteResponse = daprClient.deleteState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), null); | ||||
|     //execute the delete of the state | ||||
|     deleteResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to get the sate without an etag | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, null), MyData.class); | ||||
|     myDataResponse = response.block(); | ||||
| 
 | ||||
|     //Review that the response is null, because the state was deleted | ||||
|     Assert.assertNull(myDataResponse.getValue()); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   @Test(expected = RuntimeException.class) | ||||
|   public void saveAndDeleteStateWithWrongEtag() { | ||||
|     final String stateKey = "myeKeyToBeDeletedWithWrongEtag"; | ||||
| 
 | ||||
|     //create DAPR client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
|     //Create dummy data to be store | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
|     //Create deferred action to save the sate | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, null); | ||||
|     //execute the save state action | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to get the state with the etag | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State<MyData>(stateKey, null, null), | ||||
|         MyData.class); | ||||
|     //execute the get state | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     //Create deferred action to delete an state sending the incorrect etag | ||||
|     Mono<Void> deleteResponse = daprClient.deleteState(STATE_STORE_NAME, stateKey, "99999999999", null); | ||||
|     //execute the delete of the state, this should trhow an exception | ||||
|     deleteResponse.block(); | ||||
| 
 | ||||
|     //Create deferred action to get the sate without an etag | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, null), MyData.class); | ||||
|     myDataResponse = response.block(); | ||||
| 
 | ||||
|     //Review that the response is null, because the state was deleted | ||||
|     Assert.assertNull(myDataResponse.getValue()); | ||||
|   } | ||||
| 
 | ||||
|   @Test(expected = RuntimeException.class) | ||||
|   public void saveUpdateAndGetStateWithEtagAndStateOptionsFirstWrite() { | ||||
|     final String stateKey = "keyToBeUpdatedWithEtagAndOptions"; | ||||
| 
 | ||||
|     //create option with concurrency with first writte and consistency of strong | ||||
|     StateOptions stateOptions = new StateOptions(StateOptions.Consistency.STRONG, | ||||
|         StateOptions.Concurrency.FIRST_WRITE); | ||||
| 
 | ||||
|     //create dapr client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
|     //create Dummy data | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //create state using stateOptions | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, stateOptions); | ||||
|     //execute the save state | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     //crate deferred action to retrieve the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, stateOptions), | ||||
|         MyData.class); | ||||
|     //execute the retrieve of the state using options | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     //change data to be udpated | ||||
|     data.setPropertyA("data in property A2"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //create deferred action to update the action with options | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), data, stateOptions); | ||||
|     //update the state | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     data.setPropertyA("last write"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //create deferred action to update the action with the same etag | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), data, stateOptions); | ||||
|     //throws an exception, the state was already udpated | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, stateOptions), MyData.class); | ||||
|     State<MyData> myLastDataResponse = response.block(); | ||||
| 
 | ||||
|     Assert.assertNotNull(myLastDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myLastDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myLastDataResponse.getValue()); | ||||
|     Assert.assertNotNull(myDataResponse.getEtag(), myLastDataResponse.getEtag()); | ||||
|     Assert.assertEquals("data in property A2", myLastDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B2", myLastDataResponse.getValue().getPropertyB()); | ||||
|   } | ||||
| 
 | ||||
|   @Test() | ||||
|   public void saveUpdateAndGetStateWithEtagAndStateOptionsLastWrite() { | ||||
|     final String stateKey = "keyToBeUpdatedWithEtagAndOptions"; | ||||
| 
 | ||||
|     //create option with concurrency with first writte and consistency of strong | ||||
|     StateOptions stateOptions = new StateOptions(StateOptions.Consistency.STRONG, StateOptions.Concurrency.LAST_WRITE); | ||||
| 
 | ||||
|     //create dapr client | ||||
|     DaprClient daprClient = buildDaprClient(); | ||||
|     //create Dummy data | ||||
|     MyData data = new MyData(); | ||||
|     data.setPropertyA("data in property A"); | ||||
|     data.setPropertyB("data in property B"); | ||||
| 
 | ||||
|     //create state using stateOptions | ||||
|     Mono<Void> saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, null, data, stateOptions); | ||||
|     //execute the save state | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     //crate deferred action to retrieve the state | ||||
|     Mono<State<MyData>> response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, stateOptions), | ||||
|         MyData.class); | ||||
|     //execute the retrieve of the state using options | ||||
|     State<MyData> myDataResponse = response.block(); | ||||
| 
 | ||||
|     Assert.assertNotNull(myDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myDataResponse.getValue()); | ||||
|     Assert.assertEquals("data in property A", myDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); | ||||
| 
 | ||||
|     //change data to be udpated | ||||
|     data.setPropertyA("data in property A2"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //create deferred action to update the action with options | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), data, stateOptions); | ||||
|     //update the state | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
| 
 | ||||
|     data.setPropertyA("last write"); | ||||
|     data.setPropertyB("data in property B2"); | ||||
|     //create deferred action to update the action with the same etag | ||||
|     saveResponse = daprClient.saveState(STATE_STORE_NAME, stateKey, myDataResponse.getEtag(), data, stateOptions); | ||||
|     //update the state without an error | ||||
|     saveResponse.block(); | ||||
| 
 | ||||
|     response = daprClient.getState(STATE_STORE_NAME, new State(stateKey, null, stateOptions), MyData.class); | ||||
|     State<MyData> myLastDataResponse = response.block(); | ||||
| 
 | ||||
|     Assert.assertNotNull(myLastDataResponse.getEtag()); | ||||
|     Assert.assertNotNull(myLastDataResponse.getKey()); | ||||
|     Assert.assertNotNull(myLastDataResponse.getValue()); | ||||
|     Assert.assertNotNull(myDataResponse.getEtag(), myLastDataResponse.getEtag()); | ||||
|     Assert.assertEquals("last write", myLastDataResponse.getValue().getPropertyA()); | ||||
|     Assert.assertEquals("data in property B2", myLastDataResponse.getValue().getPropertyB()); | ||||
|   } | ||||
| 
 | ||||
|   private static DaprClient buildDaprClient() { | ||||
|     return new DaprClientBuilder().build(); | ||||
|   @Override | ||||
|   protected DaprClient buildDaprClient() { | ||||
|     return daprClient; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -5,6 +5,8 @@ | |||
| 
 | ||||
| package io.dapr.it.state; | ||||
| 
 | ||||
| import java.util.Objects; | ||||
| 
 | ||||
| public class MyData { | ||||
| 
 | ||||
|   /// Gets or sets the value for PropertyA. | ||||
|  | @ -13,8 +15,6 @@ public class MyData { | |||
|   /// Gets or sets the value for PropertyB. | ||||
|   private String propertyB; | ||||
| 
 | ||||
|   private MyData myData; | ||||
| 
 | ||||
|   public String getPropertyB() { | ||||
|     return propertyB; | ||||
|   } | ||||
|  | @ -39,11 +39,17 @@ public class MyData { | |||
|       '}'; | ||||
|   } | ||||
| 
 | ||||
|   public MyData getMyData() { | ||||
|     return myData; | ||||
|   @Override | ||||
|   public boolean equals(Object o) { | ||||
|     if (this == o) return true; | ||||
|     if (o == null || getClass() != o.getClass()) return false; | ||||
|     MyData myData = (MyData) o; | ||||
|     return Objects.equals(propertyA, myData.propertyA) && | ||||
|         Objects.equals(propertyB, myData.propertyB); | ||||
|   } | ||||
| 
 | ||||
|   public void setMyData(MyData myData) { | ||||
|     this.myData = myData; | ||||
|   @Override | ||||
|   public int hashCode() { | ||||
|     return Objects.hash(propertyA, propertyB); | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ import io.dapr.client.domain.GetSecretRequest; | |||
| import io.dapr.client.domain.GetSecretRequestBuilder; | ||||
| import io.dapr.client.domain.GetStateRequest; | ||||
| import io.dapr.client.domain.GetStateRequestBuilder; | ||||
| import io.dapr.client.domain.GetStatesRequestBuilder; | ||||
| import io.dapr.client.domain.HttpExtension; | ||||
| import io.dapr.client.domain.InvokeBindingRequest; | ||||
| import io.dapr.client.domain.InvokeBindingRequestBuilder; | ||||
|  | @ -302,17 +303,20 @@ abstract class AbstractDaprClient implements DaprClient { | |||
|     return this.getState(stateStoreName, key, etag, options, TypeRef.get(clazz)); | ||||
|   } | ||||
| 
 | ||||
|   private <T> State<T> buildStateKeyValue( | ||||
|       DaprProtos.GetStateResponse response, | ||||
|       String requestedKey, | ||||
|       StateOptions stateOptions, | ||||
|       TypeRef<T> type) throws IOException { | ||||
|     ByteString payload = response.getData(); | ||||
|     byte[] data = payload == null ? null : payload.toByteArray(); | ||||
|     T value = stateSerializer.deserialize(data, type); | ||||
|     String etag = response.getEtag(); | ||||
|     String key = requestedKey; | ||||
|     return new State<>(value, key, etag, stateOptions); | ||||
|   /** | ||||
|    * {@inheritDoc} | ||||
|    */ | ||||
|   @Override | ||||
|   public <T> Mono<List<State<T>>> getStates(String stateStoreName, List<String> keys, TypeRef<T> type) { | ||||
|     return this.getStates(new GetStatesRequestBuilder(stateStoreName, keys).build(), type).map(r -> r.getObject()); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritDoc} | ||||
|    */ | ||||
|   @Override | ||||
|   public <T> Mono<List<State<T>>> getStates(String stateStoreName, List<String> keys, Class<T> clazz) { | ||||
|     return this.getStates(stateStoreName, keys, TypeRef.get(clazz)); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ package io.dapr.client; | |||
| import io.dapr.client.domain.DeleteStateRequest; | ||||
| import io.dapr.client.domain.GetSecretRequest; | ||||
| import io.dapr.client.domain.GetStateRequest; | ||||
| import io.dapr.client.domain.GetStatesRequest; | ||||
| import io.dapr.client.domain.HttpExtension; | ||||
| import io.dapr.client.domain.InvokeBindingRequest; | ||||
| import io.dapr.client.domain.InvokeServiceRequest; | ||||
|  | @ -349,6 +350,19 @@ public interface DaprClient extends Closeable { | |||
|    */ | ||||
|   <T> Mono<State<T>> getState(String stateStoreName, String key, String etag, StateOptions options, TypeRef<T> type); | ||||
| 
 | ||||
|   /** | ||||
|    * Retrieve a State based on their key. | ||||
|    * | ||||
|    * @param stateStoreName The name of the state store. | ||||
|    * @param key            The key of the State to be retrieved. | ||||
|    * @param etag           Optional etag for conditional get | ||||
|    * @param options        Optional settings for retrieve operation. | ||||
|    * @param clazz          The Type of State needed as return. | ||||
|    * @param <T>            The Type of the return. | ||||
|    * @return A Mono Plan for the requested State. | ||||
|    */ | ||||
|   <T> Mono<State<T>> getState(String stateStoreName, String key, String etag, StateOptions options, Class<T> clazz); | ||||
| 
 | ||||
|   /** | ||||
|    * Retrieve a State based on their key. | ||||
|    * | ||||
|  | @ -360,17 +374,36 @@ public interface DaprClient extends Closeable { | |||
|   <T> Mono<Response<State<T>>> getState(GetStateRequest request, TypeRef<T> type); | ||||
| 
 | ||||
|   /** | ||||
|    * Retrieve a State based on their key. | ||||
|    * Retrieve bulk States based on their keys. | ||||
|    * | ||||
|    * @param stateStoreName The name of the state store. | ||||
|    * @param key            The key of the State to be retrieved. | ||||
|    * @param etag           Optional etag for conditional get | ||||
|    * @param options        Optional settings for retrieve operation. | ||||
|    * @param keys           The keys of the State to be retrieved. | ||||
|    * @param type           The type of State needed as return. | ||||
|    * @param <T>            The type of the return. | ||||
|    * @return A Mono Plan for the requested State. | ||||
|    */ | ||||
|   <T> Mono<List<State<T>>> getStates(String stateStoreName, List<String> keys, TypeRef<T> type); | ||||
| 
 | ||||
|   /** | ||||
|    * Retrieve bulk States based on their keys. | ||||
|    * | ||||
|    * @param stateStoreName The name of the state store. | ||||
|    * @param keys           The keys of the State to be retrieved. | ||||
|    * @param clazz          The type of State needed as return. | ||||
|    * @param <T>            The type of the return. | ||||
|    * @return A Mono Plan for the requested State. | ||||
|    */ | ||||
|   <T> Mono<State<T>> getState(String stateStoreName, String key, String etag, StateOptions options, Class<T> clazz); | ||||
|   <T> Mono<List<State<T>>> getStates(String stateStoreName, List<String> keys, Class<T> clazz); | ||||
| 
 | ||||
|   /** | ||||
|    * Retrieve bulk States based on their keys. | ||||
|    * | ||||
|    * @param request The request to get state. | ||||
|    * @param type    The Type of State needed as return. | ||||
|    * @param <T>     The Type of the return. | ||||
|    * @return A Mono Plan for the requested State. | ||||
|    */ | ||||
|   <T> Mono<Response<List<State<T>>>> getStates(GetStatesRequest request, TypeRef<T> type); | ||||
| 
 | ||||
|   /** | ||||
|    * Save/Update a list of states. | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| 
 | ||||
| package io.dapr.client; | ||||
| 
 | ||||
| import com.google.common.base.Strings; | ||||
| import com.google.common.util.concurrent.ListenableFuture; | ||||
| import com.google.protobuf.Any; | ||||
| import com.google.protobuf.ByteString; | ||||
|  | @ -12,6 +13,7 @@ import com.google.protobuf.Empty; | |||
| import io.dapr.client.domain.DeleteStateRequest; | ||||
| import io.dapr.client.domain.GetSecretRequest; | ||||
| import io.dapr.client.domain.GetStateRequest; | ||||
| import io.dapr.client.domain.GetStatesRequest; | ||||
| import io.dapr.client.domain.HttpExtension; | ||||
| import io.dapr.client.domain.InvokeBindingRequest; | ||||
| import io.dapr.client.domain.InvokeServiceRequest; | ||||
|  | @ -52,6 +54,7 @@ import java.util.HashMap; | |||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.Callable; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| /** | ||||
|  * An adapter for the GRPC Client. | ||||
|  | @ -253,6 +256,74 @@ public class DaprClientGrpc extends AbstractDaprClient { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritDoc} | ||||
|    */ | ||||
|   @Override | ||||
|   public <T> Mono<Response<List<State<T>>>> getStates(GetStatesRequest request, TypeRef<T> type) { | ||||
|     try { | ||||
|       final String stateStoreName = request.getStateStoreName(); | ||||
|       final List<String> keys = request.getKeys(); | ||||
|       final int parallelism = request.getParallelism(); | ||||
|       final Context context = request.getContext(); | ||||
|       if ((stateStoreName == null) || (stateStoreName.trim().isEmpty())) { | ||||
|         throw new IllegalArgumentException("State store name cannot be null or empty."); | ||||
|       } | ||||
|       if (keys == null || keys.isEmpty()) { | ||||
|         throw new IllegalArgumentException("Key cannot be null or empty."); | ||||
|       } | ||||
| 
 | ||||
|       if (parallelism < 0) { | ||||
|         throw new IllegalArgumentException("Parallelism cannot be negative."); | ||||
|       } | ||||
|       DaprProtos.GetBulkStateRequest.Builder builder = DaprProtos.GetBulkStateRequest.newBuilder() | ||||
|           .setStoreName(stateStoreName) | ||||
|           .addAllKeys(keys) | ||||
|           .setParallelism(parallelism); | ||||
| 
 | ||||
|       DaprProtos.GetBulkStateRequest envelope = builder.build(); | ||||
|       return Mono.fromCallable(wrap(context, () -> { | ||||
|         ListenableFuture<DaprProtos.GetBulkStateResponse> futureResponse = client.getBulkState(envelope); | ||||
|         DaprProtos.GetBulkStateResponse response = null; | ||||
|         try { | ||||
|           response = futureResponse.get(); | ||||
|         } catch (NullPointerException npe) { | ||||
|           return null; | ||||
|         } | ||||
| 
 | ||||
|         return response | ||||
|             .getItemsList() | ||||
|             .stream() | ||||
|             .map(b -> { | ||||
|               try { | ||||
|                 return buildStateKeyValue(b, type); | ||||
|               } catch (IOException e) { | ||||
|                 throw new RuntimeException(e); | ||||
|               } | ||||
|             }) | ||||
|             .collect(Collectors.toList()); | ||||
|       })).map(s -> new Response(context, s)); | ||||
|     } catch (Exception ex) { | ||||
|       return Mono.error(ex); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private <T> State<T> buildStateKeyValue( | ||||
|       DaprProtos.BulkStateItem item, | ||||
|       TypeRef<T> type) throws IOException { | ||||
|     String key = item.getKey(); | ||||
|     String error = item.getError(); | ||||
|     if (!Strings.isNullOrEmpty(error)) { | ||||
|       return new State<>(key, error); | ||||
|     } | ||||
| 
 | ||||
|     ByteString payload = item.getData(); | ||||
|     byte[] data = payload == null ? null : payload.toByteArray(); | ||||
|     T value = stateSerializer.deserialize(data, type); | ||||
|     String etag = item.getEtag(); | ||||
|     return new State<>(value, key, etag); | ||||
|   } | ||||
| 
 | ||||
|   private <T> State<T> buildStateKeyValue( | ||||
|       DaprProtos.GetStateResponse response, | ||||
|       String requestedKey, | ||||
|  | @ -300,13 +371,13 @@ public class DaprClientGrpc extends AbstractDaprClient { | |||
| 
 | ||||
|   private <T> CommonProtos.StateItem.Builder buildStateRequest(State<T> state) throws IOException { | ||||
|     byte[] bytes = stateSerializer.serialize(state.getValue()); | ||||
|     ByteString data = ByteString.copyFrom(bytes); | ||||
| 
 | ||||
|     CommonProtos.StateItem.Builder stateBuilder = CommonProtos.StateItem.newBuilder(); | ||||
|     if (state.getEtag() != null) { | ||||
|       stateBuilder.setEtag(state.getEtag()); | ||||
|     } | ||||
|     if (data != null) { | ||||
|       stateBuilder.setValue(data); | ||||
|     if (bytes != null) { | ||||
|       stateBuilder.setValue(ByteString.copyFrom(bytes)); | ||||
|     } | ||||
|     stateBuilder.setKey(state.getKey()); | ||||
|     CommonProtos.StateOptions.Builder optionBuilder = null; | ||||
|  | @ -347,8 +418,6 @@ public class DaprClientGrpc extends AbstractDaprClient { | |||
| 
 | ||||
|       CommonProtos.StateOptions.Builder optionBuilder = null; | ||||
|       if (options != null) { | ||||
|         optionBuilder = CommonProtos.StateOptions.newBuilder(); | ||||
| 
 | ||||
|         optionBuilder = CommonProtos.StateOptions.newBuilder(); | ||||
|         if (options.getConcurrency() != null) { | ||||
|           optionBuilder.setConcurrency(getGrpcStateConcurrency(options)); | ||||
|  |  | |||
|  | @ -5,9 +5,12 @@ | |||
| 
 | ||||
| package io.dapr.client; | ||||
| 
 | ||||
| import com.fasterxml.jackson.databind.JsonNode; | ||||
| import com.google.common.base.Strings; | ||||
| import io.dapr.client.domain.DeleteStateRequest; | ||||
| import io.dapr.client.domain.GetSecretRequest; | ||||
| import io.dapr.client.domain.GetStateRequest; | ||||
| import io.dapr.client.domain.GetStatesRequest; | ||||
| import io.dapr.client.domain.HttpExtension; | ||||
| import io.dapr.client.domain.InvokeBindingRequest; | ||||
| import io.dapr.client.domain.InvokeServiceRequest; | ||||
|  | @ -16,6 +19,7 @@ import io.dapr.client.domain.Response; | |||
| import io.dapr.client.domain.SaveStateRequest; | ||||
| import io.dapr.client.domain.State; | ||||
| import io.dapr.client.domain.StateOptions; | ||||
| import io.dapr.config.Properties; | ||||
| import io.dapr.serializer.DaprObjectSerializer; | ||||
| import io.dapr.serializer.DefaultObjectSerializer; | ||||
| import io.dapr.utils.TypeRef; | ||||
|  | @ -26,6 +30,7 @@ import java.io.IOException; | |||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.HashMap; | ||||
| import java.util.Iterator; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Optional; | ||||
|  | @ -74,6 +79,11 @@ public class DaprClientHttp extends AbstractDaprClient { | |||
|    */ | ||||
|   public static final String SECRETS_PATH = DaprHttp.API_VERSION + "/secrets"; | ||||
| 
 | ||||
|   /** | ||||
|    * State Path format for bulk state API. | ||||
|    */ | ||||
|   public static final String STATE_BULK_PATH_FORMAT = STATE_PATH + "/%s/bulk"; | ||||
| 
 | ||||
|   /** | ||||
|    * The HTTP client to be used. | ||||
|    * | ||||
|  | @ -256,6 +266,50 @@ public class DaprClientHttp extends AbstractDaprClient { | |||
|       return Mono.error(ex); | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   /** | ||||
|    * {@inheritDoc} | ||||
|    */ | ||||
|   @Override | ||||
|   public <T> Mono<Response<List<State<T>>>> getStates(GetStatesRequest request, TypeRef<T> type) { | ||||
|     try { | ||||
|       final String stateStoreName = request.getStateStoreName(); | ||||
|       final List<String> keys = request.getKeys(); | ||||
|       final int parallelism = request.getParallelism(); | ||||
|       final Context context = request.getContext(); | ||||
|       if ((stateStoreName == null) || (stateStoreName.trim().isEmpty())) { | ||||
|         throw new IllegalArgumentException("State store name cannot be null or empty."); | ||||
|       } | ||||
|       if (keys == null || keys.isEmpty()) { | ||||
|         throw new IllegalArgumentException("Key cannot be null or empty."); | ||||
|       } | ||||
| 
 | ||||
|       if (parallelism < 0) { | ||||
|         throw new IllegalArgumentException("Parallelism cannot be negative."); | ||||
|       } | ||||
| 
 | ||||
|       String url = String.format(STATE_BULK_PATH_FORMAT, stateStoreName); | ||||
|       Map<String, Object> jsonMap = new HashMap<>(); | ||||
|       jsonMap.put("keys", keys); | ||||
|       jsonMap.put("parallelism", parallelism); | ||||
| 
 | ||||
|       byte[] requestBody = INTERNAL_SERIALIZER.serialize(jsonMap); | ||||
|       return this.client | ||||
|           .invokeApi(DaprHttp.HttpMethods.POST.name(), url, null, requestBody, null, context) | ||||
|           .flatMap(s -> { | ||||
|             try { | ||||
|               return Mono.just(buildStates(s, type)); | ||||
|             } catch (Exception ex) { | ||||
|               return Mono.error(ex); | ||||
|             } | ||||
|           }) | ||||
|           .map(r -> new Response<>(context, r)); | ||||
| 
 | ||||
|     } catch (Exception ex) { | ||||
|       return Mono.error(ex); | ||||
|     } | ||||
|   } | ||||
|    | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritDoc} | ||||
|  | @ -295,7 +349,7 @@ public class DaprClientHttp extends AbstractDaprClient { | |||
|           .invokeApi(DaprHttp.HttpMethods.GET.name(), url.toString(), urlParameters, headers, context) | ||||
|           .flatMap(s -> { | ||||
|             try { | ||||
|               return Mono.just(buildStateKeyValue(s, key, options, type)); | ||||
|               return Mono.just(buildState(s, key, options, type)); | ||||
|             } catch (Exception ex) { | ||||
|               return Mono.error(ex); | ||||
|             } | ||||
|  | @ -399,10 +453,10 @@ public class DaprClientHttp extends AbstractDaprClient { | |||
|    * @param requestedKey The Key Requested. | ||||
|    * @param type        The Class of the Value of the state | ||||
|    * @param <T>          The Type of the Value of the state | ||||
|    * @return A StateKeyValue instance | ||||
|    * @throws IOException If there's a issue deserialzing the response. | ||||
|    * @return A State instance | ||||
|    * @throws IOException If there's a issue deserializing the response. | ||||
|    */ | ||||
|   private <T> State<T> buildStateKeyValue( | ||||
|   private <T> State<T> buildState( | ||||
|       DaprHttp.Response response, String requestedKey, StateOptions stateOptions, TypeRef<T> type) throws IOException { | ||||
|     // The state is in the body directly, so we use the state serializer here. | ||||
|     T value = stateSerializer.deserialize(response.getBody(), type); | ||||
|  | @ -414,6 +468,39 @@ public class DaprClientHttp extends AbstractDaprClient { | |||
|     return new State<>(value, key, etag, stateOptions); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Builds a State object based on the Response. | ||||
|    * | ||||
|    * @param response     The response of the HTTP Call | ||||
|    * @param type        The Class of the Value of the state | ||||
|    * @param <T>          The Type of the Value of the state | ||||
|    * @return A list of states. | ||||
|    * @throws IOException If there's a issue deserializing the response. | ||||
|    */ | ||||
|   private <T> List<State<T>> buildStates( | ||||
|       DaprHttp.Response response, TypeRef<T> type) throws IOException { | ||||
|     JsonNode root = INTERNAL_SERIALIZER.parseNode(response.getBody()); | ||||
|     List<State<T>> result = new ArrayList<>(); | ||||
|     for (Iterator<JsonNode> it = root.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 State<>(key, error)); | ||||
|         continue; | ||||
|       } | ||||
| 
 | ||||
|       String etag = node.path("etag").asText(); | ||||
|       // 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 State<>(value, key, etag)); | ||||
|     } | ||||
| 
 | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritDoc} | ||||
|    */ | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ package io.dapr.client; | |||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||||
| import com.fasterxml.jackson.databind.DeserializationFeature; | ||||
| import com.fasterxml.jackson.databind.JavaType; | ||||
| import com.fasterxml.jackson.databind.JsonNode; | ||||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||||
| import io.dapr.client.domain.CloudEvent; | ||||
| import io.dapr.utils.TypeRef; | ||||
|  | @ -112,6 +113,17 @@ public class ObjectSerializer { | |||
|     return OBJECT_MAPPER.readValue(content, javaType); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Parses the JSON content into a node for fine-grained processing. | ||||
|    * | ||||
|    * @param content JSON content. | ||||
|    * @return JsonNode. | ||||
|    * @throws IOException In case content cannot be parsed. | ||||
|    */ | ||||
|   public JsonNode parseNode(byte[] content) throws IOException { | ||||
|     return  OBJECT_MAPPER.readTree(content); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Parses a given String to the corresponding object defined by class. | ||||
|    * | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ import java.util.Collections; | |||
| import java.util.Map; | ||||
| 
 | ||||
| /** | ||||
|  * Builds a request to publish an event. | ||||
|  * Builds a request to request state. | ||||
|  */ | ||||
| public class GetStateRequestBuilder { | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,56 @@ | |||
| /* | ||||
|  * Copyright (c) Microsoft Corporation. | ||||
|  * Licensed under the MIT License. | ||||
|  */ | ||||
| 
 | ||||
| package io.dapr.client.domain; | ||||
| 
 | ||||
| import io.grpc.Context; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| /** | ||||
|  * A request to get bulk state by keys. | ||||
|  */ | ||||
| public class GetStatesRequest { | ||||
| 
 | ||||
|   private String stateStoreName; | ||||
| 
 | ||||
|   private List<String> keys; | ||||
| 
 | ||||
|   private int parallelism; | ||||
| 
 | ||||
|   private Context context; | ||||
| 
 | ||||
|   public String getStateStoreName() { | ||||
|     return stateStoreName; | ||||
|   } | ||||
| 
 | ||||
|   void setStateStoreName(String stateStoreName) { | ||||
|     this.stateStoreName = stateStoreName; | ||||
|   } | ||||
| 
 | ||||
|   public List<String> getKeys() { | ||||
|     return keys; | ||||
|   } | ||||
| 
 | ||||
|   void setKeys(List<String> keys) { | ||||
|     this.keys = keys; | ||||
|   } | ||||
| 
 | ||||
|   public int getParallelism() { | ||||
|     return parallelism; | ||||
|   } | ||||
| 
 | ||||
|   void setParallelism(int parallelism) { | ||||
|     this.parallelism = parallelism; | ||||
|   } | ||||
| 
 | ||||
|   public Context getContext() { | ||||
|     return context; | ||||
|   } | ||||
| 
 | ||||
|   void setContext(Context context) { | ||||
|     this.context = context; | ||||
|   } | ||||
| } | ||||
|  | @ -0,0 +1,60 @@ | |||
| /* | ||||
|  * Copyright (c) Microsoft Corporation. | ||||
|  * Licensed under the MIT License. | ||||
|  */ | ||||
| 
 | ||||
| package io.dapr.client.domain; | ||||
| 
 | ||||
| import io.grpc.Context; | ||||
| 
 | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| 
 | ||||
| /** | ||||
|  * Builds a request to request states. | ||||
|  */ | ||||
| public class GetStatesRequestBuilder { | ||||
| 
 | ||||
|   private final String stateStoreName; | ||||
| 
 | ||||
|   private final List<String> keys; | ||||
| 
 | ||||
|   private int parallelism = 1; | ||||
| 
 | ||||
|   private Context context; | ||||
| 
 | ||||
|   public GetStatesRequestBuilder(String stateStoreName, List<String> keys) { | ||||
|     this.stateStoreName = stateStoreName; | ||||
|     this.keys = Collections.unmodifiableList(keys); | ||||
|   } | ||||
| 
 | ||||
|   public GetStatesRequestBuilder(String stateStoreName, String... keys) { | ||||
|     this.stateStoreName = stateStoreName; | ||||
|     this.keys = Collections.unmodifiableList(Arrays.asList(keys)); | ||||
|   } | ||||
| 
 | ||||
|   public GetStatesRequestBuilder withParallelism(int parallelism) { | ||||
|     this.parallelism = parallelism; | ||||
|     return this; | ||||
|   } | ||||
| 
 | ||||
|   public GetStatesRequestBuilder withContext(Context context) { | ||||
|     this.context = context; | ||||
|     return this; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Builds a request object. | ||||
|    * @return Request object. | ||||
|    */ | ||||
|   public GetStatesRequest build() { | ||||
|     GetStatesRequest request = new GetStatesRequest(); | ||||
|     request.setStateStoreName(this.stateStoreName); | ||||
|     request.setKeys(this.keys); | ||||
|     request.setParallelism(this.parallelism); | ||||
|     request.setContext(this.context); | ||||
|     return request; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  | @ -11,28 +11,50 @@ package io.dapr.client.domain; | |||
|  * @param <T> The type of the value of the sate | ||||
|  */ | ||||
| public class State<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 reids) only numbers are supported. | ||||
|    */ | ||||
|   private final String etag; | ||||
| 
 | ||||
|   /** | ||||
|    * The error in case the key could not be retrieved. | ||||
|    */ | ||||
|   private final String error; | ||||
| 
 | ||||
|   /** | ||||
|    * The options used for saving the state. | ||||
|    */ | ||||
|   private final StateOptions options; | ||||
| 
 | ||||
|   /** | ||||
|    * Create an inmutable state | ||||
|    * This Constructor MUST be used anytime you need to retrieve or delete a State. | ||||
|    * 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 State(String key) { | ||||
|     this.key = key; | ||||
|     this.value = null; | ||||
|     this.etag = null; | ||||
|     this.options = 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 | ||||
|  | @ -44,16 +66,16 @@ public class State<T> { | |||
|     this.key = key; | ||||
|     this.etag = etag; | ||||
|     this.options = options; | ||||
|     this.error = null; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Create an inmutable state. | ||||
|    * This Constructor MUST be used anytime you want the state to be send for a Save operation. | ||||
|    * Create an immutable state. | ||||
|    * This Constructor CAN be used anytime you want the state to be saved. | ||||
|    * | ||||
|    * @param value   - The value of the 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 etag    - The etag of the state - for some state stores (like redis) only numbers are supported. | ||||
|    * @param options - REQUIRED when saving a state. | ||||
|    */ | ||||
|   public State(T value, String key, String etag, StateOptions options) { | ||||
|  | @ -61,6 +83,38 @@ public class State<T> { | |||
|     this.key = key; | ||||
|     this.etag = etag; | ||||
|     this.options = options; | ||||
|     this.error = null; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Create an immutable state. | ||||
|    * This Constructor CAN be used anytime you want the state to be saved. | ||||
|    * | ||||
|    * @param value   - The value of the state. | ||||
|    * @param key     - The key of the state. | ||||
|    * @param etag    - The etag of the state - some state stores (like redis) only numbers are supported. | ||||
|    */ | ||||
|   public State(T value, String key, String etag) { | ||||
|     this.value = value; | ||||
|     this.key = key; | ||||
|     this.etag = etag; | ||||
|     this.options = null; | ||||
|     this.error = null; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Create an immutable state. | ||||
|    * This Constructor MUST be used anytime the key could not be retrieved and contains an error. | ||||
|    * | ||||
|    * @param key     - The key of the state. | ||||
|    * @param error   - Error when fetching the state. | ||||
|    */ | ||||
|   public State(String key, String error) { | ||||
|     this.value = null; | ||||
|     this.key = key; | ||||
|     this.etag = null; | ||||
|     this.options = null; | ||||
|     this.error = error; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|  | @ -90,6 +144,16 @@ public class State<T> { | |||
|     return etag; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Retrieve the error for this state. | ||||
|    * | ||||
|    * @return The error for this state. | ||||
|    */ | ||||
| 
 | ||||
|   public String getError() { | ||||
|     return error; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Retrieve the Options used for saving the state. | ||||
|    * | ||||
|  | @ -123,6 +187,10 @@ public class State<T> { | |||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     if (getError() != null ? !getEtag().equals(that.getError()) : that.getError() != null) { | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     if (getOptions() != null ? !getOptions().equals(that.getOptions()) : that.getOptions() != null) { | ||||
|       return false; | ||||
|     } | ||||
|  | @ -135,6 +203,7 @@ public class State<T> { | |||
|     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); | ||||
|     result = 31 * result + (getOptions() != null ? options.hashCode() : 0); | ||||
|     return result; | ||||
|   } | ||||
|  | @ -145,6 +214,7 @@ public class State<T> { | |||
|         + "value=" + value | ||||
|         + ", key='" + key + "'" | ||||
|         + ", etag='" + etag + "'" | ||||
|         + ", etag='" + error + "'" | ||||
|         + ", options={'" + options.toString() + "}" | ||||
|         + "}"; | ||||
|   } | ||||
|  |  | |||
|  | @ -34,12 +34,15 @@ import reactor.core.publisher.Mono; | |||
| import java.io.Closeable; | ||||
| import java.io.IOException; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import static com.google.common.util.concurrent.Futures.addCallback; | ||||
| import static com.google.common.util.concurrent.MoreExecutors.directExecutor; | ||||
| import static org.junit.Assert.assertArrayEquals; | ||||
| import static org.junit.Assert.assertEquals; | ||||
| import static org.junit.Assert.assertFalse; | ||||
| import static org.junit.Assert.assertNull; | ||||
|  | @ -753,6 +756,183 @@ public class DaprClientGrpcTest { | |||
|     assertEquals(expectedState, result.block()); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   @Test | ||||
|   public void getStatesString() throws IOException { | ||||
|     DaprProtos.GetBulkStateResponse responseEnvelope = DaprProtos.GetBulkStateResponse.newBuilder() | ||||
|         .addItems(DaprProtos.BulkStateItem.newBuilder() | ||||
|             .setData(serialize("hello world")) | ||||
|             .setKey("100") | ||||
|             .setEtag("1") | ||||
|             .build()) | ||||
|         .addItems(DaprProtos.BulkStateItem.newBuilder() | ||||
|             .setKey("200") | ||||
|             .setError("not found") | ||||
|             .build()) | ||||
|         .build(); | ||||
|     SettableFuture<DaprProtos.GetBulkStateResponse> settableFuture = SettableFuture.create(); | ||||
|     MockCallback<DaprProtos.GetBulkStateResponse> callback = new MockCallback<>(responseEnvelope); | ||||
|     addCallback(settableFuture, callback, directExecutor()); | ||||
|     when(client.getBulkState(any(DaprProtos.GetBulkStateRequest.class))) | ||||
|         .thenAnswer(c -> { | ||||
|           settableFuture.set(responseEnvelope); | ||||
|           return settableFuture; | ||||
|         }); | ||||
|     List<State<String>> result = adapter.getStates(STATE_STORE_NAME, Arrays.asList("100", "200"), String.class).block(); | ||||
|     assertTrue(callback.wasCalled); | ||||
| 
 | ||||
|     assertEquals(2, result.size()); | ||||
|     assertEquals("100", result.stream().findFirst().get().getKey()); | ||||
|     assertEquals("hello world", result.stream().findFirst().get().getValue()); | ||||
|     assertEquals("1", result.stream().findFirst().get().getEtag()); | ||||
|     assertNull(result.stream().findFirst().get().getError()); | ||||
|     assertEquals("200", result.stream().skip(1).findFirst().get().getKey()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getValue()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getEtag()); | ||||
|     assertEquals("not found", result.stream().skip(1).findFirst().get().getError()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void getStatesInteger() throws IOException { | ||||
|     DaprProtos.GetBulkStateResponse responseEnvelope = DaprProtos.GetBulkStateResponse.newBuilder() | ||||
|         .addItems(DaprProtos.BulkStateItem.newBuilder() | ||||
|             .setData(serialize(1234)) | ||||
|             .setKey("100") | ||||
|             .setEtag("1") | ||||
|             .build()) | ||||
|         .addItems(DaprProtos.BulkStateItem.newBuilder() | ||||
|             .setKey("200") | ||||
|             .setError("not found") | ||||
|             .build()) | ||||
|         .build(); | ||||
|     SettableFuture<DaprProtos.GetBulkStateResponse> settableFuture = SettableFuture.create(); | ||||
|     MockCallback<DaprProtos.GetBulkStateResponse> callback = new MockCallback<>(responseEnvelope); | ||||
|     addCallback(settableFuture, callback, directExecutor()); | ||||
|     when(client.getBulkState(any(DaprProtos.GetBulkStateRequest.class))) | ||||
|         .thenAnswer(c -> { | ||||
|           settableFuture.set(responseEnvelope); | ||||
|           return settableFuture; | ||||
|         }); | ||||
|     List<State<Integer>> result = adapter.getStates(STATE_STORE_NAME, Arrays.asList("100", "200"), int.class).block(); | ||||
|     assertTrue(callback.wasCalled); | ||||
| 
 | ||||
|     assertEquals(2, result.size()); | ||||
|     assertEquals("100", result.stream().findFirst().get().getKey()); | ||||
|     assertEquals(1234, (int)result.stream().findFirst().get().getValue()); | ||||
|     assertEquals("1", result.stream().findFirst().get().getEtag()); | ||||
|     assertNull(result.stream().findFirst().get().getError()); | ||||
|     assertEquals("200", result.stream().skip(1).findFirst().get().getKey()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getValue()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getEtag()); | ||||
|     assertEquals("not found", result.stream().skip(1).findFirst().get().getError()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void getStatesBoolean() throws IOException { | ||||
|     DaprProtos.GetBulkStateResponse responseEnvelope = DaprProtos.GetBulkStateResponse.newBuilder() | ||||
|         .addItems(DaprProtos.BulkStateItem.newBuilder() | ||||
|             .setData(serialize(true)) | ||||
|             .setKey("100") | ||||
|             .setEtag("1") | ||||
|             .build()) | ||||
|         .addItems(DaprProtos.BulkStateItem.newBuilder() | ||||
|             .setKey("200") | ||||
|             .setError("not found") | ||||
|             .build()) | ||||
|         .build(); | ||||
|     SettableFuture<DaprProtos.GetBulkStateResponse> settableFuture = SettableFuture.create(); | ||||
|     MockCallback<DaprProtos.GetBulkStateResponse> callback = new MockCallback<>(responseEnvelope); | ||||
|     addCallback(settableFuture, callback, directExecutor()); | ||||
|     when(client.getBulkState(any(DaprProtos.GetBulkStateRequest.class))) | ||||
|         .thenAnswer(c -> { | ||||
|           settableFuture.set(responseEnvelope); | ||||
|           return settableFuture; | ||||
|         }); | ||||
|     List<State<Boolean>> result = adapter.getStates(STATE_STORE_NAME, Arrays.asList("100", "200"), boolean.class).block(); | ||||
|     assertTrue(callback.wasCalled); | ||||
| 
 | ||||
|     assertEquals(2, result.size()); | ||||
|     assertEquals("100", result.stream().findFirst().get().getKey()); | ||||
|     assertEquals(true, result.stream().findFirst().get().getValue()); | ||||
|     assertEquals("1", result.stream().findFirst().get().getEtag()); | ||||
|     assertNull(result.stream().findFirst().get().getError()); | ||||
|     assertEquals("200", result.stream().skip(1).findFirst().get().getKey()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getValue()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getEtag()); | ||||
|     assertEquals("not found", result.stream().skip(1).findFirst().get().getError()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void getStatesByteArray() throws IOException { | ||||
|     DaprProtos.GetBulkStateResponse responseEnvelope = DaprProtos.GetBulkStateResponse.newBuilder() | ||||
|         .addItems(DaprProtos.BulkStateItem.newBuilder() | ||||
|             .setData(serialize(new byte[]{1, 2, 3})) | ||||
|             .setKey("100") | ||||
|             .setEtag("1") | ||||
|             .build()) | ||||
|         .addItems(DaprProtos.BulkStateItem.newBuilder() | ||||
|             .setKey("200") | ||||
|             .setError("not found") | ||||
|             .build()) | ||||
|         .build(); | ||||
|     SettableFuture<DaprProtos.GetBulkStateResponse> settableFuture = SettableFuture.create(); | ||||
|     MockCallback<DaprProtos.GetBulkStateResponse> callback = new MockCallback<>(responseEnvelope); | ||||
|     addCallback(settableFuture, callback, directExecutor()); | ||||
|     when(client.getBulkState(any(DaprProtos.GetBulkStateRequest.class))) | ||||
|         .thenAnswer(c -> { | ||||
|           settableFuture.set(responseEnvelope); | ||||
|           return settableFuture; | ||||
|         }); | ||||
|     List<State<byte[]>> result = adapter.getStates(STATE_STORE_NAME, Arrays.asList("100", "200"), byte[].class).block(); | ||||
|     assertTrue(callback.wasCalled); | ||||
| 
 | ||||
|     assertEquals(2, result.size()); | ||||
|     assertEquals("100", result.stream().findFirst().get().getKey()); | ||||
|     assertArrayEquals(new byte[]{1, 2, 3}, result.stream().findFirst().get().getValue()); | ||||
|     assertEquals("1", result.stream().findFirst().get().getEtag()); | ||||
|     assertNull(result.stream().findFirst().get().getError()); | ||||
|     assertEquals("200", result.stream().skip(1).findFirst().get().getKey()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getValue()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getEtag()); | ||||
|     assertEquals("not found", result.stream().skip(1).findFirst().get().getError()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void getStatesObject() throws IOException { | ||||
|     MyObject object = new MyObject(1, "Event"); | ||||
|     DaprProtos.GetBulkStateResponse responseEnvelope = DaprProtos.GetBulkStateResponse.newBuilder() | ||||
|         .addItems(DaprProtos.BulkStateItem.newBuilder() | ||||
|             .setData(serialize(object)) | ||||
|             .setKey("100") | ||||
|             .setEtag("1") | ||||
|             .build()) | ||||
|         .addItems(DaprProtos.BulkStateItem.newBuilder() | ||||
|             .setKey("200") | ||||
|             .setError("not found") | ||||
|             .build()) | ||||
|         .build(); | ||||
|     SettableFuture<DaprProtos.GetBulkStateResponse> settableFuture = SettableFuture.create(); | ||||
|     MockCallback<DaprProtos.GetBulkStateResponse> callback = new MockCallback<>(responseEnvelope); | ||||
|     addCallback(settableFuture, callback, directExecutor()); | ||||
|     when(client.getBulkState(any(DaprProtos.GetBulkStateRequest.class))) | ||||
|         .thenAnswer(c -> { | ||||
|           settableFuture.set(responseEnvelope); | ||||
|           return settableFuture; | ||||
|         }); | ||||
|     List<State<MyObject>> result = adapter.getStates(STATE_STORE_NAME, Arrays.asList("100", "200"), MyObject.class).block(); | ||||
|     assertTrue(callback.wasCalled); | ||||
| 
 | ||||
|     assertEquals(2, result.size()); | ||||
|     assertEquals("100", result.stream().findFirst().get().getKey()); | ||||
|     assertEquals(object, result.stream().findFirst().get().getValue()); | ||||
|     assertEquals("1", result.stream().findFirst().get().getEtag()); | ||||
|     assertNull(result.stream().findFirst().get().getError()); | ||||
|     assertEquals("200", result.stream().skip(1).findFirst().get().getKey()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getValue()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getEtag()); | ||||
|     assertEquals("not found", result.stream().skip(1).findFirst().get().getError()); | ||||
|   } | ||||
| 
 | ||||
|   @Test(expected = RuntimeException.class) | ||||
|   public void deleteStateExceptionThrowTest() { | ||||
|     when(client.deleteState(any(io.dapr.v1.DaprProtos.DeleteStateRequest.class))).thenThrow(RuntimeException.class); | ||||
|  |  | |||
|  | @ -21,11 +21,13 @@ import reactor.core.publisher.Mono; | |||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.Base64; | ||||
| import java.util.Collections; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import static org.junit.Assert.assertArrayEquals; | ||||
| import static org.junit.Assert.assertEquals; | ||||
| import static org.junit.Assert.assertNull; | ||||
| import static org.junit.Assert.assertTrue; | ||||
|  | @ -38,6 +40,9 @@ public class DaprClientHttpTest { | |||
| 
 | ||||
|   private static final String SECRET_STORE_NAME = "MySecretStore"; | ||||
| 
 | ||||
|   private final String EXPECTED_RESULT = | ||||
|       "{\"data\":\"ewoJCSJwcm9wZXJ0eUEiOiAidmFsdWVBIiwKCQkicHJvcGVydHlCIjogInZhbHVlQiIKCX0=\"}"; | ||||
| 
 | ||||
|   private DaprClientHttp daprClientHttp; | ||||
| 
 | ||||
|   private DaprHttp daprHttp; | ||||
|  | @ -46,9 +51,6 @@ public class DaprClientHttpTest { | |||
| 
 | ||||
|   private MockInterceptor mockInterceptor; | ||||
| 
 | ||||
|   private final String EXPECTED_RESULT = | ||||
|       "{\"data\":\"ewoJCSJwcm9wZXJ0eUEiOiAidmFsdWVBIiwKCQkicHJvcGVydHlCIjogInZhbHVlQiIKCX0=\"}"; | ||||
| 
 | ||||
|   @Before | ||||
|   public void setUp() throws Exception { | ||||
|     mockInterceptor = new MockInterceptor(Behavior.UNORDERED); | ||||
|  | @ -382,7 +384,120 @@ public class DaprClientHttpTest { | |||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void getStates() { | ||||
|   public void getStatesString() { | ||||
|     mockInterceptor.addRule() | ||||
|         .post("http://127.0.0.1:3000/v1.0/state/MyStateStore/bulk") | ||||
|         .respond("[{\"key\": \"100\", \"data\": \"hello world\", \"etag\": \"1\"}," + | ||||
|             "{\"key\": \"200\", \"error\": \"not found\"}]"); | ||||
|     daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), 3000, okHttpClient); | ||||
|     daprClientHttp = new DaprClientHttp(daprHttp); | ||||
|     List<State<String>> result = | ||||
|         daprClientHttp.getStates(STATE_STORE_NAME, Arrays.asList("100", "200"), String.class).block(); | ||||
|     assertEquals(2, result.size()); | ||||
|     assertEquals("100", result.stream().findFirst().get().getKey()); | ||||
|     assertEquals("hello world", result.stream().findFirst().get().getValue()); | ||||
|     assertEquals("1", result.stream().findFirst().get().getEtag()); | ||||
|     assertNull(result.stream().findFirst().get().getError()); | ||||
|     assertEquals("200", result.stream().skip(1).findFirst().get().getKey()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getValue()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getEtag()); | ||||
|     assertEquals("not found", result.stream().skip(1).findFirst().get().getError()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void getStatesInteger() { | ||||
|     mockInterceptor.addRule() | ||||
|         .post("http://127.0.0.1:3000/v1.0/state/MyStateStore/bulk") | ||||
|         .respond("[{\"key\": \"100\", \"data\": 1234, \"etag\": \"1\"}," + | ||||
|             "{\"key\": \"200\", \"error\": \"not found\"}]"); | ||||
|     daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), 3000, okHttpClient); | ||||
|     daprClientHttp = new DaprClientHttp(daprHttp); | ||||
|     List<State<Integer>> result = | ||||
|         daprClientHttp.getStates(STATE_STORE_NAME, Arrays.asList("100", "200"), int.class).block(); | ||||
|     assertEquals(2, result.size()); | ||||
|     assertEquals("100", result.stream().findFirst().get().getKey()); | ||||
|     assertEquals(1234, (int)result.stream().findFirst().get().getValue()); | ||||
|     assertEquals("1", result.stream().findFirst().get().getEtag()); | ||||
|     assertNull(result.stream().findFirst().get().getError()); | ||||
|     assertEquals("200", result.stream().skip(1).findFirst().get().getKey()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getValue()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getEtag()); | ||||
|     assertEquals("not found", result.stream().skip(1).findFirst().get().getError()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void getStatesBoolean() { | ||||
|     mockInterceptor.addRule() | ||||
|         .post("http://127.0.0.1:3000/v1.0/state/MyStateStore/bulk") | ||||
|         .respond("[{\"key\": \"100\", \"data\": true, \"etag\": \"1\"}," + | ||||
|             "{\"key\": \"200\", \"error\": \"not found\"}]"); | ||||
|     daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), 3000, okHttpClient); | ||||
|     daprClientHttp = new DaprClientHttp(daprHttp); | ||||
|     List<State<Boolean>> result = | ||||
|         daprClientHttp.getStates(STATE_STORE_NAME, Arrays.asList("100", "200"), boolean.class).block(); | ||||
|     assertEquals(2, result.size()); | ||||
|     assertEquals("100", result.stream().findFirst().get().getKey()); | ||||
|     assertEquals(true, (boolean)result.stream().findFirst().get().getValue()); | ||||
|     assertEquals("1", result.stream().findFirst().get().getEtag()); | ||||
|     assertNull(result.stream().findFirst().get().getError()); | ||||
|     assertEquals("200", result.stream().skip(1).findFirst().get().getKey()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getValue()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getEtag()); | ||||
|     assertEquals("not found", result.stream().skip(1).findFirst().get().getError()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void getStatesByteArray() { | ||||
|     byte[] value = new byte[]{1, 2, 3}; | ||||
|     String base64Value = Base64.getEncoder().encodeToString(value); | ||||
|     mockInterceptor.addRule() | ||||
|         .post("http://127.0.0.1:3000/v1.0/state/MyStateStore/bulk") | ||||
|         .respond("[{\"key\": \"100\", \"data\": \"" + base64Value + "\", \"etag\": \"1\"}," + | ||||
|             "{\"key\": \"200\", \"error\": \"not found\"}]"); | ||||
|     daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), 3000, okHttpClient); | ||||
|     daprClientHttp = new DaprClientHttp(daprHttp); | ||||
|     // JSON cannot differentiate if data returned is String or byte[], it is ambiguous. So we get base64 encoded back. | ||||
|     // So, users should use String instead of byte[]. | ||||
|     List<State<String>> result = | ||||
|         daprClientHttp.getStates(STATE_STORE_NAME, Arrays.asList("100", "200"), String.class).block(); | ||||
|     assertEquals(2, result.size()); | ||||
|     assertEquals("100", result.stream().findFirst().get().getKey()); | ||||
|     assertEquals(base64Value, result.stream().findFirst().get().getValue()); | ||||
|     assertEquals("1", result.stream().findFirst().get().getEtag()); | ||||
|     assertNull(result.stream().findFirst().get().getError()); | ||||
|     assertEquals("200", result.stream().skip(1).findFirst().get().getKey()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getValue()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getEtag()); | ||||
|     assertEquals("not found", result.stream().skip(1).findFirst().get().getError()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void getStatesObject() { | ||||
|     MyObject object = new MyObject(1, "Event"); | ||||
|     mockInterceptor.addRule() | ||||
|         .post("http://127.0.0.1:3000/v1.0/state/MyStateStore/bulk") | ||||
|         .respond("[{\"key\": \"100\", \"data\": " + | ||||
|             "{ \"id\": \"" + object.id + "\", \"value\": \"" + object.value + "\"}, \"etag\": \"1\"}," + | ||||
|             "{\"key\": \"200\", \"error\": \"not found\"}]"); | ||||
|     daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), 3000, okHttpClient); | ||||
|     daprClientHttp = new DaprClientHttp(daprHttp); | ||||
|     // JSON cannot differentiate if data returned is String or byte[], it is ambiguous. So we get base64 encoded back. | ||||
|     // So, users should use String instead of byte[]. | ||||
|     List<State<MyObject>> result = | ||||
|         daprClientHttp.getStates(STATE_STORE_NAME, Arrays.asList("100", "200"), MyObject.class).block(); | ||||
|     assertEquals(2, result.size()); | ||||
|     assertEquals("100", result.stream().findFirst().get().getKey()); | ||||
|     assertEquals(object, result.stream().findFirst().get().getValue()); | ||||
|     assertEquals("1", result.stream().findFirst().get().getEtag()); | ||||
|     assertNull(result.stream().findFirst().get().getError()); | ||||
|     assertEquals("200", result.stream().skip(1).findFirst().get().getKey()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getValue()); | ||||
|     assertNull(result.stream().skip(1).findFirst().get().getEtag()); | ||||
|     assertEquals("not found", result.stream().skip(1).findFirst().get().getError()); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void getState() { | ||||
|     StateOptions stateOptions = mock(StateOptions.class); | ||||
|     State<String> stateKeyValue = new State("value", "key", "etag", stateOptions); | ||||
|     State<String> stateKeyNull = new State("value", null, "etag", stateOptions); | ||||
|  | @ -715,4 +830,53 @@ public class DaprClientHttpTest { | |||
|       assertEquals("mysecretvalue2", secret.get("mysecretkey2")); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   public static class MyObject { | ||||
|     private Integer id; | ||||
|     private String value; | ||||
| 
 | ||||
|     public MyObject() { | ||||
|     } | ||||
| 
 | ||||
|     public MyObject(Integer id, String value) { | ||||
|       this.id = id; | ||||
|       this.value = value; | ||||
|     } | ||||
| 
 | ||||
|     public Integer getId() { | ||||
|       return id; | ||||
|     } | ||||
| 
 | ||||
|     public void setId(Integer id) { | ||||
|       this.id = id; | ||||
|     } | ||||
| 
 | ||||
|     public String getValue() { | ||||
|       return value; | ||||
|     } | ||||
| 
 | ||||
|     public void setValue(String value) { | ||||
|       this.value = value; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean equals(Object o) { | ||||
|       if (this == o) return true; | ||||
|       if (!(o instanceof MyObject)) return false; | ||||
| 
 | ||||
|       MyObject myObject = (MyObject) o; | ||||
| 
 | ||||
|       if (!getId().equals(myObject.getId())) return false; | ||||
|       if (getValue() != null ? !getValue().equals(myObject.getValue()) : myObject.getValue() != null) return false; | ||||
| 
 | ||||
|       return true; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int hashCode() { | ||||
|       int result = getId().hashCode(); | ||||
|       result = 31 * result + (getValue() != null ? getValue().hashCode() : 0); | ||||
|       return result; | ||||
|     } | ||||
|   } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue