Skip to content

Commit

Permalink
Merge pull request #59 from joeg/odataescaping
Browse files Browse the repository at this point in the history
HotFix Issue #58 Add URL Encoding for Keys in Table Batch Operations
  • Loading branch information
jcookems committed May 1, 2012
2 parents fad32f6 + 74aaf66 commit c4d5b88
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -577,10 +577,15 @@ else if (this.getOperationType() == TableOperationType.RETRIEVE) {
* @param entryName
* The entry name to use as the request identity if the <code>isSingleIndexEntry</code> parameter is
* <code>true</code>.
* @param encodeKeys
* Pass <code>true</code> to url encode the partition & row keys
* @return
* A <code>String</code> 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);
}
Expand All @@ -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 <code>String</code> containing the name of the table.
* @return
* A <code>String</code> 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));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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");
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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();
Expand All @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit c4d5b88

Please sign in to comment.