diff --git a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosItemIdEncodingTest.java b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosItemIdEncodingTest.java index 3411ec85fe18..c121aa821f49 100644 --- a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosItemIdEncodingTest.java +++ b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosItemIdEncodingTest.java @@ -12,7 +12,13 @@ import com.azure.cosmos.models.CosmosContainerProperties; import com.azure.cosmos.models.CosmosItemRequestOptions; import com.azure.cosmos.models.CosmosItemResponse; +import com.azure.cosmos.models.CosmosQueryRequestOptions; +import com.azure.cosmos.models.CosmosTriggerProperties; +import com.azure.cosmos.models.CosmosTriggerResponse; +import com.azure.cosmos.models.FeedResponse; import com.azure.cosmos.models.PartitionKey; +import com.azure.cosmos.models.TriggerOperation; +import com.azure.cosmos.models.TriggerType; import com.azure.cosmos.rx.TestSuiteBase; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonProcessingException; @@ -26,6 +32,10 @@ import org.testng.annotations.Test; import reactor.core.Exceptions; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; @@ -93,18 +103,27 @@ public void plainVanillaId() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT)); this.executeTestCase(scenario); @@ -120,18 +139,27 @@ public void containerIdWithUnicodeCharacter() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT)); this.executeTestCase(scenario); @@ -147,18 +175,27 @@ public void idWithWhitespaces() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT)); this.executeTestCase(scenario); @@ -174,18 +211,27 @@ public void idStartingWithWhitespace() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT)); this.executeTestCase(scenario); @@ -201,18 +247,27 @@ public void idStartingWithWhitespaces() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT)); this.executeTestCase(scenario); @@ -228,18 +283,27 @@ public void idEndingWithWhitespace() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.UNAUTHORIZED, HttpConstants.StatusCodes.UNAUTHORIZED, - HttpConstants.StatusCodes.UNAUTHORIZED), + HttpConstants.StatusCodes.UNAUTHORIZED, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, + HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT)); this.executeTestCase(scenario); @@ -255,18 +319,27 @@ public void idEndingWithWhitespaces() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.UNAUTHORIZED, HttpConstants.StatusCodes.UNAUTHORIZED, - HttpConstants.StatusCodes.UNAUTHORIZED), + HttpConstants.StatusCodes.UNAUTHORIZED, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, + HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT)); this.executeTestCase(scenario); @@ -282,18 +355,27 @@ public void idWithUnicodeCharacters() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT)); this.executeTestCase(scenario); @@ -309,18 +391,27 @@ public void idWithAllowedSpecialCharacters() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT)); this.executeTestCase(scenario); @@ -339,18 +430,27 @@ public void idWithBase64EncodedIdCharacters() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT)); this.executeTestCase(scenario); @@ -366,19 +466,28 @@ public void idEndingWithPercentEncodedWhitespace() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.UNAUTHORIZED, HttpConstants.StatusCodes.UNAUTHORIZED, + HttpConstants.StatusCodes.UNAUTHORIZED, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.UNAUTHORIZED), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, - HttpConstants.StatusCodes.NO_CONTENT), + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, + HttpConstants.StatusCodes.UNAUTHORIZED), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, - HttpConstants.StatusCodes.NO_CONTENT)); + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, + HttpConstants.StatusCodes.UNAUTHORIZED)); this.executeTestCase(scenario); } @@ -393,19 +502,28 @@ public void idWithPercentEncodedSpecialChar() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.UNAUTHORIZED, HttpConstants.StatusCodes.UNAUTHORIZED, + HttpConstants.StatusCodes.UNAUTHORIZED, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.UNAUTHORIZED), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, - HttpConstants.StatusCodes.NO_CONTENT), + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, + HttpConstants.StatusCodes.UNAUTHORIZED), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, - HttpConstants.StatusCodes.NO_CONTENT)); + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, + HttpConstants.StatusCodes.UNAUTHORIZED)); this.executeTestCase(scenario); } @@ -420,19 +538,28 @@ public void idWithDisallowedCharQuestionMark() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, - HttpConstants.StatusCodes.NO_CONTENT), + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + -1, + HttpConstants.StatusCodes.NOTFOUND), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, - HttpConstants.StatusCodes.NO_CONTENT), + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + -1, + HttpConstants.StatusCodes.NOTFOUND), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, - HttpConstants.StatusCodes.NO_CONTENT)); + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + -1, + HttpConstants.StatusCodes.NOTFOUND)); this.executeTestCase(scenario); } @@ -447,18 +574,27 @@ public void idWithDisallowedCharForwardSlash() { HttpConstants.StatusCodes.CREATED, -1 , // Non-CosmosException - in this case IllegalArgumentException -1, + -1, + HttpConstants.StatusCodes.OK, + -1, -1), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, -1 , // Non-CosmosException - in this case IllegalArgumentException -1, + -1, + HttpConstants.StatusCodes.OK, + -1, -1), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, -1 , // Non-CosmosException - in this case IllegalArgumentException -1, + -1, + HttpConstants.StatusCodes.OK, + -1, -1)); this.executeTestCase(scenario); @@ -475,18 +611,27 @@ public void idWithDisallowedCharForwardSlashButIdValidationEnabled() { HttpConstants.StatusCodes.BADREQUEST, -1 , // Non-CosmosException - in this case IllegalArgumentException -1, + -1, + HttpConstants.StatusCodes.OK, + -1, -1), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.BADREQUEST, -1 , // Non-CosmosException - in this case IllegalArgumentException -1, + -1, + HttpConstants.StatusCodes.OK, + -1, -1), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.BADREQUEST, -1 , // Non-CosmosException - in this case IllegalArgumentException -1, + -1, + HttpConstants.StatusCodes.OK, + -1, -1)); this.executeTestCase(scenario); @@ -502,19 +647,28 @@ public void idWithDisallowedCharBackSlash() { HttpConstants.StatusCodes.BADREQUEST, HttpConstants.StatusCodes.BADREQUEST, HttpConstants.StatusCodes.BADREQUEST, - HttpConstants.StatusCodes.BADREQUEST), + HttpConstants.StatusCodes.BADREQUEST, + HttpConstants.StatusCodes.OK, + -1, + HttpConstants.StatusCodes.UNAUTHORIZED), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.BADREQUEST, HttpConstants.StatusCodes.BADREQUEST, HttpConstants.StatusCodes.BADREQUEST, - HttpConstants.StatusCodes.BADREQUEST), + HttpConstants.StatusCodes.BADREQUEST, + HttpConstants.StatusCodes.OK, + -1, + HttpConstants.StatusCodes.UNAUTHORIZED), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.BADREQUEST, HttpConstants.StatusCodes.BADREQUEST, HttpConstants.StatusCodes.BADREQUEST, - HttpConstants.StatusCodes.BADREQUEST)); + HttpConstants.StatusCodes.BADREQUEST, + HttpConstants.StatusCodes.OK, + -1, + HttpConstants.StatusCodes.UNAUTHORIZED)); this.executeTestCase(scenario); } @@ -529,19 +683,28 @@ public void idWithDisallowedCharPoundSign() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.UNAUTHORIZED, HttpConstants.StatusCodes.UNAUTHORIZED, + HttpConstants.StatusCodes.UNAUTHORIZED, + HttpConstants.StatusCodes.OK, + -1, HttpConstants.StatusCodes.UNAUTHORIZED), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, - HttpConstants.StatusCodes.NO_CONTENT), + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + -1, + HttpConstants.StatusCodes.UNAUTHORIZED), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, - HttpConstants.StatusCodes.NO_CONTENT)); + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + -1, + HttpConstants.StatusCodes.UNAUTHORIZED)); this.executeTestCase(scenario); } @@ -556,19 +719,28 @@ public void idWithCarriageReturn() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.BADREQUEST, HttpConstants.StatusCodes.BADREQUEST, + HttpConstants.StatusCodes.BADREQUEST, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.BADREQUEST), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, - HttpConstants.StatusCodes.NO_CONTENT), + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, + HttpConstants.StatusCodes.BADREQUEST), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, - HttpConstants.StatusCodes.NO_CONTENT)); + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, + HttpConstants.StatusCodes.BADREQUEST)); this.executeTestCase(scenario); } @@ -583,19 +755,28 @@ public void idWithTab() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.BADREQUEST, HttpConstants.StatusCodes.BADREQUEST, + HttpConstants.StatusCodes.BADREQUEST, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.BADREQUEST), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, - HttpConstants.StatusCodes.NO_CONTENT), + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, + HttpConstants.StatusCodes.BADREQUEST), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, - HttpConstants.StatusCodes.NO_CONTENT)); + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, + HttpConstants.StatusCodes.BADREQUEST)); this.executeTestCase(scenario); } @@ -610,18 +791,63 @@ public void idWithLineFeed() { HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.BADREQUEST, HttpConstants.StatusCodes.BADREQUEST, + HttpConstants.StatusCodes.BADREQUEST, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.BADREQUEST), new TestScenarioExpectations( "COMPUTE_GATEWAY", HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, + HttpConstants.StatusCodes.BADREQUEST), + new TestScenarioExpectations( + ConnectionMode.DIRECT.toString(), + HttpConstants.StatusCodes.CREATED, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, + HttpConstants.StatusCodes.BADREQUEST)); + + this.executeTestCase(scenario); + } + + @Test(groups = { "emulator" }, timeOut = TIMEOUT) + public void idWithNonAsciiCharacter() { + TestScenario scenario = new TestScenario( + "idWithNonAsciiCharacter", + "táá" + UUID.randomUUID(), + new TestScenarioExpectations( + ConnectionMode.GATEWAY.toString(), + HttpConstants.StatusCodes.CREATED, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, + HttpConstants.StatusCodes.NO_CONTENT), + new TestScenarioExpectations( + "COMPUTE_GATEWAY", + HttpConstants.StatusCodes.CREATED, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT), new TestScenarioExpectations( ConnectionMode.DIRECT.toString(), HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.OK, HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.NO_CONTENT, + HttpConstants.StatusCodes.OK, + HttpConstants.StatusCodes.CREATED, HttpConstants.StatusCodes.NO_CONTENT)); this.executeTestCase(scenario); @@ -640,11 +866,23 @@ private void executeTestCase(TestScenario scenario) { } try { + // create pre-trigger and post-trigger + String preTriggerId = scenario.id + "pre-" + UUID.randomUUID(); + String postTriggerId = scenario.id + "post" + UUID.randomUUID(); + createTrigger(preTriggerId, TriggerType.PRE, expected.ExpectedTriggerCreateStatusCode); + createTrigger(postTriggerId, TriggerType.POST, expected.ExpectedTriggerCreateStatusCode); + try { + CosmosItemRequestOptions itemRequestOptions = new CosmosItemRequestOptions(); + if (expected.ExpectedTriggerCreateStatusCode == HttpConstants.StatusCodes.OK) { + itemRequestOptions.setPreTriggerInclude(Arrays.asList(preTriggerId)); + itemRequestOptions.setPostTriggerInclude(Arrays.asList(postTriggerId)); + } + CosmosItemResponse response = this.container.createItem( getDocumentDefinition(scenario.id), new PartitionKey(scenario.id), - null); + itemRequestOptions); deserializeAndValidatePayload(response, scenario.id, expected.ExpectedCreateStatusCode); } catch (Throwable throwable) { @@ -714,6 +952,8 @@ private void executeTestCase(TestScenario scenario) { assertThat(cosmosError.getStatusCode()).isEqualTo(expected.ExpectedReplaceStatusCode); } + validateQueryOperation(scenario.id, scenario.id, expected.ExpectedQueryStatusCode); + try { CosmosItemResponse response = this.container.deleteItem( scenario.id, @@ -730,11 +970,103 @@ private void executeTestCase(TestScenario scenario) { } assertThat(cosmosError.getStatusCode()).isEqualTo(expected.ExpectedDeleteStatusCode); } + + deleteTrigger(preTriggerId, expected.ExpectedTriggerDeleteStatusCode); + deleteTrigger(postTriggerId, expected.ExpectedTriggerDeleteStatusCode); } finally { System.clearProperty(Configs.PREVENT_INVALID_ID_CHARS); } } + private void deleteTrigger(String triggerId, int expectedStatusCode) { + try { + CosmosTriggerResponse response = this.container.getScripts().getTrigger(triggerId).delete(); + assertThat(response.getStatusCode()).isEqualTo(HttpConstants.StatusCodes.NO_CONTENT); + } catch (Throwable throwable) { + CosmosException cosmosError = Utils.as(Exceptions.unwrap(throwable), CosmosException.class); + if (cosmosError == null) { + if (expectedStatusCode == -1) { + return; + } + + Fail.fail( + "Unexpected exception type " + Exceptions.unwrap(throwable).getClass().getName(), + throwable); + } + if (cosmosError.getStatusCode() == 0 && + cosmosError.getCause() instanceof IllegalArgumentException && + cosmosError.getCause().getCause() instanceof JsonParseException && + (cosmosError.getCause().toString().contains("Bad Request") || + cosmosError.getCause().getCause().toString().contains("Bad Request"))) { + + logger.info("HTML BAD REQUEST", cosmosError); + assertThat(expectedStatusCode).isEqualTo(400); + } else { + logger.info("BAD REQUEST", cosmosError); + assertThat(cosmosError.getStatusCode()).isEqualTo(expectedStatusCode); + } + } + } + + private void createTrigger(String triggerId, TriggerType triggerType, int expectedStatusCode) { + try { + CosmosTriggerProperties preTrigger = new CosmosTriggerProperties( + triggerId, + "function() {var x = 10;}" + ); + preTrigger.setTriggerOperation(TriggerOperation.CREATE); + preTrigger.setTriggerType(triggerType); + CosmosTriggerResponse triggerResponse = this.container.getScripts().createTrigger(preTrigger); + assertThat(triggerResponse.getStatusCode()).isEqualTo(HttpConstants.StatusCodes.CREATED); + + if (expectedStatusCode == -1) { + fail("Create trigger should have failed with IllegalArgumentException with trigger id " + triggerId); + } + } catch (Throwable throwable) { + CosmosException cosmosError = Utils.as(Exceptions.unwrap(throwable), CosmosException.class); + if (cosmosError == null) { + if (expectedStatusCode == -1) { + return; + } + + Fail.fail( + "Unexpected exception type " + Exceptions.unwrap(throwable).getClass().getName(), + throwable); + } + assertThat(cosmosError.getStatusCode()).isEqualTo(expectedStatusCode); + } + } + + private void validateQueryOperation(String id, String pkValue, int expectedStatusCode) { + try { + + String query = "select TOP 1 * from c"; // using a query type which will force query plan to happen + CosmosQueryRequestOptions queryRequestOptions = new CosmosQueryRequestOptions(); + queryRequestOptions.setPartitionKey(new PartitionKey(pkValue)); + Iterator> results = this.container.queryItems(query, queryRequestOptions, ObjectNode.class) + .iterableByPage().iterator(); + List items = new ArrayList<>(); + while (results.hasNext()) { + items.addAll(results.next().getResults()); + } + + assertThat(items.size()).isEqualTo(1); + assertThat(items.get(0).get("id").asText()).isEqualTo(id); + assertThat(items.get(0).get("mypk").asText()).isEqualTo(id); + } catch (Throwable throwable) { + CosmosException cosmosError = Utils.as(Exceptions.unwrap(throwable), CosmosException.class); + if (cosmosError == null) { + Fail.fail( + "Unexpected exception type " + Exceptions.unwrap(throwable).getClass().getName(), + throwable); + } + + logger.error(cosmosError.toString()); + + assertThat(cosmosError.getStatusCode()).isEqualTo(expectedStatusCode); + } + } + private void deserializeAndValidatePayload( CosmosItemResponse response, String expectedId, @@ -768,13 +1100,19 @@ public TestScenarioExpectations( int expectedCreateStatusCode, int expectedReadStatusCode, int expectedReplaceStatusCode, - int expectedDeleteStatusCode + int expectedDeleteStatusCode, + int expectedQueryStatusCode, + int expectedTriggerCreateStatusCode, + int expectedTriggerDeleteStatusCode ) { this.ConnectionMode = connectionMode; this.ExpectedCreateStatusCode = expectedCreateStatusCode; this.ExpectedReadStatusCode = expectedReadStatusCode; this.ExpectedReplaceStatusCode = expectedReplaceStatusCode; this.ExpectedDeleteStatusCode = expectedDeleteStatusCode; + this.ExpectedQueryStatusCode = expectedQueryStatusCode; + this.ExpectedTriggerCreateStatusCode = expectedTriggerCreateStatusCode; + this.ExpectedTriggerDeleteStatusCode = expectedTriggerDeleteStatusCode; } public String ConnectionMode; @@ -786,6 +1124,12 @@ public TestScenarioExpectations( public int ExpectedReplaceStatusCode; public int ExpectedDeleteStatusCode; + + public int ExpectedQueryStatusCode; + + public int ExpectedTriggerCreateStatusCode; + + public int ExpectedTriggerDeleteStatusCode; } private static class TestScenario { diff --git a/sdk/cosmos/azure-cosmos/CHANGELOG.md b/sdk/cosmos/azure-cosmos/CHANGELOG.md index 24ab767f24ed..6b87265e4a7d 100644 --- a/sdk/cosmos/azure-cosmos/CHANGELOG.md +++ b/sdk/cosmos/azure-cosmos/CHANGELOG.md @@ -7,6 +7,8 @@ #### Breaking Changes #### Bugs Fixed +* Fixed an issue where `query plan` failed with `400` or query return empty result when `CosmosQueryRequestOptions` has partition key filter and partition key value contains non-ascii character. See [PR 47881](https://github.com/Azure/azure-sdk-for-java/pull/47881) +* Fixed an issue where operation failed with `400` when configured with pre-trigger or post-trigger with non-ascii character. Only impact for gateway mode. See [PR 47881](https://github.com/Azure/azure-sdk-for-java/pull/47881) #### Other Changes * Added `x-ms-hub-region-processing-only` header to allow hub-region stickiness when 404 `READ SESSION NOT AVAIALBLE` is hit for Single-Writer accounts. - [PR 47631](https://github.com/Azure/azure-sdk-for-java/pull/47631) diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java index 4c9643a3eafd..8ff886bd7046 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java @@ -510,7 +510,7 @@ private RxDocumentClientImpl(URI serviceEndpoint, if (value == null) { return 1; } - + return value + 1; }); @@ -2136,7 +2136,7 @@ private void addPartitionKeyInformation(RxDocumentServiceRequest request, request.setPartitionKeyInternal(partitionKeyInternal); request.setPartitionKeyDefinition(partitionKeyDefinition); - request.getHeaders().put(HttpConstants.HttpHeaders.PARTITION_KEY, Utils.escapeNonAscii(partitionKeyInternal.toJson())); + request.getHeaders().put(HttpConstants.HttpHeaders.PARTITION_KEY, partitionKeyInternal.toJson()); } private Mono>> getCreateDocumentRequest(DocumentClientRetryPolicy requestRetryPolicy, @@ -2345,7 +2345,7 @@ private RxDocumentServiceRequest addBatchHeaders(RxDocumentServiceRequest reques } request.setPartitionKeyInternal(partitionKeyInternal); - request.getHeaders().put(HttpConstants.HttpHeaders.PARTITION_KEY, Utils.escapeNonAscii(partitionKeyInternal.toJson())); + request.getHeaders().put(HttpConstants.HttpHeaders.PARTITION_KEY, partitionKeyInternal.toJson()); } else if(serverBatchRequest instanceof PartitionKeyRangeServerBatchRequest) { request.setPartitionKeyRangeIdentity(new PartitionKeyRangeIdentity(((PartitionKeyRangeServerBatchRequest) serverBatchRequest).getPartitionKeyRangeId())); } else { diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java index c7b7e0b3aa7f..a85e48cb2cce 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java @@ -47,6 +47,7 @@ import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.time.Instant; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -67,6 +68,13 @@ public class RxGatewayStoreModel implements RxStoreModel, HttpTransportSerialize private static final boolean leakDetectionDebuggingEnabled = ResourceLeakDetector.getLevel().ordinal() >= ResourceLeakDetector.Level.ADVANCED.ordinal(); private static final boolean HTTP_CONNECTION_WITHOUT_TLS_ALLOWED = Configs.isHttpConnectionWithoutTLSAllowed(); + private static final List headersNeedToBeEscaped = Arrays.asList( + HttpConstants.HttpHeaders.PARTITION_KEY, + HttpConstants.HttpHeaders.POST_TRIGGER_EXCLUDE, + HttpConstants.HttpHeaders.POST_TRIGGER_INCLUDE, + HttpConstants.HttpHeaders.PRE_TRIGGER_EXCLUDE, + HttpConstants.HttpHeaders.PRE_TRIGGER_INCLUDE + ); private final DiagnosticsClientContext clientContext; private final Logger logger = LoggerFactory.getLogger(RxGatewayStoreModel.class); @@ -335,7 +343,11 @@ private HttpHeaders getHttpRequestHeaders(Map headers) { for (Entry entry : this.defaultHeaders.entrySet()) { if (!headers.containsKey(entry.getKey())) { // populate default header only if there is no overwrite by the request header - httpHeaders.set(entry.getKey(), entry.getValue()); + if (headersNeedToBeEscaped.contains(entry.getKey())) { + httpHeaders.set(entry.getKey(), Utils.escapeNonAscii(entry.getValue())); + } else { + httpHeaders.set(entry.getKey(), entry.getValue()); + } } } @@ -346,7 +358,11 @@ private HttpHeaders getHttpRequestHeaders(Map headers) { // netty doesn't allow setting null value in header httpHeaders.set(entry.getKey(), ""); } else { - httpHeaders.set(entry.getKey(), entry.getValue()); + if (headersNeedToBeEscaped.contains(entry.getKey())) { + httpHeaders.set(entry.getKey(), Utils.escapeNonAscii(entry.getValue())); + } else { + httpHeaders.set(entry.getKey(), entry.getValue()); + } } } } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/Utils.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/Utils.java index 87750de890e4..b04cb11324e3 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/Utils.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/Utils.java @@ -742,27 +742,31 @@ public static CosmosChangeFeedRequestOptions getEffectiveCosmosChangeFeedRequest cosmosChangeFeedRequestRequestOptions, pagedFluxOptions); } - static String escapeNonAscii(String partitionKeyJson) { + public static String escapeNonAscii(String value) { + if (StringUtils.isEmpty(value)) { + return value; + } + // if all are ascii original string will be returned, and avoids copying data. StringBuilder sb = null; - for (int i = 0; i < partitionKeyJson.length(); i++) { - int val = partitionKeyJson.charAt(i); + for (int i = 0; i < value.length(); i++) { + int val = value.charAt(i); if (val > 127) { if (sb == null) { - sb = new StringBuilder(partitionKeyJson.length()); - sb.append(partitionKeyJson, 0, i); + sb = new StringBuilder(value.length()); + sb.append(value, 0, i); } sb.append("\\u").append(String.format("%04X", val)); } else { if (sb != null) { - sb.append(partitionKeyJson.charAt(i)); + sb.append(value.charAt(i)); } } } if (sb == null) { // all are ascii character - return partitionKeyJson; + return value; } else { return sb.toString(); }