diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/QueryTableOperation.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/QueryTableOperation.java index 52dbf5ecdccb..2f02f4bb50e2 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/QueryTableOperation.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/QueryTableOperation.java @@ -183,7 +183,7 @@ public TableResult execute(final CloudTableClient client, final QueryTableOperat final OperationContext opContext) throws Exception { final HttpURLConnection request = TableRequest.query(client.getEndpoint(), tableName, - generateRequestIdentity(isTableEntry, operation.getPartitionKey()), + generateRequestIdentity(isTableEntry, operation.getPartitionKey(), false), options.getTimeoutIntervalInMs(), null/* Query Builder */, null/* Continuation Token */, options, opContext); diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableOperation.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableOperation.java index c9c72e0856dd..8b8ec3e422bc 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableOperation.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableOperation.java @@ -257,7 +257,7 @@ public TableResult execute(final CloudTableClient client, final TableOperation o final OperationContext opContext) throws Exception { final HttpURLConnection request = TableRequest.delete(client.getEndpoint(), tableName, - generateRequestIdentity(isTableEntry, tableIdentity), operation.getEntity().getEtag(), + generateRequestIdentity(isTableEntry, tableIdentity, false), operation.getEntity().getEtag(), options.getTimeoutIntervalInMs(), null, options, opContext); client.getCredentials().signRequestLite(request, -1L, opContext); @@ -324,7 +324,7 @@ private TableResult performInsert(final CloudTableClient client, final String ta public TableResult execute(final CloudTableClient client, final TableOperation operation, final OperationContext opContext) throws Exception { final HttpURLConnection request = TableRequest.insert(client.getEndpoint(), tableName, - generateRequestIdentity(isTableEntry, tableIdentity), + generateRequestIdentity(isTableEntry, tableIdentity, false), operation.opType != TableOperationType.INSERT ? operation.getEntity().getEtag() : null, operation.opType.getUpdateType(), options.getTimeoutIntervalInMs(), null, options, opContext); @@ -411,7 +411,7 @@ public TableResult execute(final CloudTableClient client, final TableOperation o final OperationContext opContext) throws Exception { final HttpURLConnection request = TableRequest.merge(client.getEndpoint(), tableName, - generateRequestIdentity(false, null), operation.getEntity().getEtag(), + generateRequestIdentity(false, null, false), operation.getEntity().getEtag(), options.getTimeoutIntervalInMs(), null, options, opContext); client.getCredentials().signRequestLite(request, -1L, opContext); @@ -477,7 +477,7 @@ public TableResult execute(final CloudTableClient client, final TableOperation o final OperationContext opContext) throws Exception { final HttpURLConnection request = TableRequest.update(client.getEndpoint(), tableName, - generateRequestIdentity(false, null), operation.getEntity().getEtag(), + generateRequestIdentity(false, null, false), operation.getEntity().getEtag(), options.getTimeoutIntervalInMs(), null, options, opContext); client.getCredentials().signRequestLite(request, -1L, opContext); @@ -577,10 +577,15 @@ else if (this.getOperationType() == TableOperationType.RETRIEVE) { * @param entryName * The entry name to use as the request identity if the isSingleIndexEntry parameter is * true. + * @param encodeKeys + * Pass true to url encode the partition & row keys * @return * A String containing the formatted request identity string. + * @throws StorageException + * If a storage service error occurred. */ - protected String generateRequestIdentity(boolean isSingleIndexEntry, final String entryName) { + protected String generateRequestIdentity(boolean isSingleIndexEntry, final String entryName, boolean encodeKeys) + throws StorageException { if (isSingleIndexEntry) { return String.format("'%s'", entryName); } @@ -602,22 +607,24 @@ protected String generateRequestIdentity(boolean isSingleIndexEntry, final Strin rk = this.getEntity().getRowKey(); } - return String.format("%s='%s',%s='%s'", TableConstants.PARTITION_KEY, pk, TableConstants.ROW_KEY, rk); + return String.format("%s='%s',%s='%s'", TableConstants.PARTITION_KEY, encodeKeys ? Utility.safeEncode(pk) + : pk, TableConstants.ROW_KEY, encodeKeys ? Utility.safeEncode(rk) : rk); } } /** * Reserved for internal use. Generates the request identity string for the specified table. The request identity * string combines the table name with the PartitionKey and RowKey from the operation to identify specific table - * entities. + * entities. This request identity is already UrlEncoded. * * @param tableName * A String containing the name of the table. * @return * A String containing the formatted request identity string for the specified table. + * @throws StorageException */ - protected String generateRequestIdentityWithTable(final String tableName) { - return String.format("/%s(%s)", tableName, generateRequestIdentity(false, null)); + protected String generateRequestIdentityWithTable(final String tableName) throws StorageException { + return String.format("/%s(%s)", tableName, generateRequestIdentity(false, null, true)); } /** diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableEscapingTests.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableEscapingTests.java index ce90b73f1af4..3926fd49aea6 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableEscapingTests.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableEscapingTests.java @@ -28,12 +28,12 @@ public class TableEscapingTests extends TableTestBase { @Test public void emptyString() throws StorageException { - doEscapeTest("", false); + doEscapeTest("", false, true); } @Test public void emptyStringBatch() throws StorageException { - doEscapeTest("", true); + doEscapeTest("", true, true); } @Test @@ -46,6 +46,18 @@ public void randomCharsBatch() throws StorageException { doEscapeTest("!$'\"()*+,;=", true); } + @Test + @Ignore + public void percent25() throws StorageException { + // Disabled Until Double Percent decoding issue is fixed for single entity operations + // doEscapeTest("foo%25", false, true); + } + + @Test + public void percent25Batch() throws StorageException { + doEscapeTest("foo%25", true, true); + } + @Test public void regularPKInQuery() throws StorageException { doQueryEscapeTest("data"); @@ -63,18 +75,18 @@ public void specialCharsBatch() throws StorageException { @Test public void unicode() throws StorageException { - doEscapeTest("\u00A9\u770b\u5168\u90e8", false); - doEscapeTest("char中文test", false); - doEscapeTest("char中文test", false); - doEscapeTest("世界你好", false); + doEscapeTest("\u00A9\u770b\u5168\u90e8", false, true); + doEscapeTest("char中文test", false, true); + doEscapeTest("char中文test", false, true); + doEscapeTest("世界你好", false, true); } @Test public void unicodeBatch() throws StorageException { - doEscapeTest("\u00A9\u770b\u5168\u90e8", true); - doEscapeTest("char中文test", true); - doEscapeTest("char中文test", true); - doEscapeTest("世界你好", true); + doEscapeTest("\u00A9\u770b\u5168\u90e8", true, true); + doEscapeTest("char中文test", true, true); + doEscapeTest("char中文test", true, true); + doEscapeTest("世界你好", true, true); } @Test @@ -87,12 +99,12 @@ public void unicodeInQuery() throws StorageException { @Test public void whiteSpaceOnly() throws StorageException { - doEscapeTest(" ", false); + doEscapeTest(" ", false, true); } @Test public void whiteSpaceOnlyBatch() throws StorageException { - doEscapeTest(" ", true); + doEscapeTest(" ", true, true); } @Test @@ -119,10 +131,14 @@ public void xmlTestBatch() throws StorageException { } private void doEscapeTest(String data, boolean useBatch) throws StorageException { + doEscapeTest(data, useBatch, false); + } + + private void doEscapeTest(String data, boolean useBatch, boolean includeInKey) throws StorageException { class1 ref = new class1(); ref.setA(data); - ref.setPartitionKey("temp"); + ref.setPartitionKey(includeInKey ? "temp" + data : "temp"); ref.setRowKey(UUID.randomUUID().toString()); if (useBatch) { TableBatchOperation batch = new TableBatchOperation(); @@ -147,6 +163,7 @@ private void doEscapeTest(String data, boolean useBatch) throws StorageException class1 retObj = res.getResultAsType(); Assert.assertEquals(ref.getA(), retObj.getA()); + Assert.assertEquals(ref.getPartitionKey(), retObj.getPartitionKey()); ref.setEtag(retObj.getEtag()); ref.setB(data); @@ -202,6 +219,15 @@ private void doEscapeTest(String data, boolean useBatch) throws StorageException Assert.assertEquals(ref.getA(), retObj.getA()); Assert.assertEquals(ref.getB(), retObj.getB()); Assert.assertEquals(ref.getC(), retObj.getC()); + + if (useBatch) { + TableBatchOperation batch = new TableBatchOperation(); + batch.delete(retObj); + res = tClient.execute(testSuiteTableName, batch).get(0); + } + else { + res = tClient.execute(testSuiteTableName, TableOperation.delete(retObj)); + } } private void doQueryEscapeTest(String data) throws StorageException {