Skip to content

Commit c5bae84

Browse files
committed
Merge pull request #578 from ajkannan/fix-cursor
Remove check for valid utf-8 cursor
2 parents 8c70155 + 03ca1bf commit c5bae84

File tree

3 files changed

+20
-22
lines changed

3 files changed

+20
-22
lines changed

gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Cursor.java

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,14 @@
1717
package com.google.gcloud.datastore;
1818

1919
import static com.google.common.base.Preconditions.checkNotNull;
20-
import static java.nio.charset.StandardCharsets.UTF_8;
2120

2221
import com.google.api.services.datastore.DatastoreV1;
2322
import com.google.api.services.datastore.DatastoreV1.Value;
2423
import com.google.common.base.MoreObjects;
2524
import com.google.common.base.MoreObjects.ToStringHelper;
26-
import com.google.common.base.Preconditions;
25+
import com.google.common.io.BaseEncoding;
2726
import com.google.protobuf.ByteString;
2827
import com.google.protobuf.InvalidProtocolBufferException;
29-
import com.google.protobuf.TextFormat;
30-
import com.google.protobuf.TextFormat.ParseException;
31-
32-
import java.io.UnsupportedEncodingException;
33-
import java.net.URLDecoder;
34-
import java.net.URLEncoder;
3528

3629
/**
3730
* A Google Cloud Datastore cursor.
@@ -44,7 +37,6 @@ public final class Cursor extends Serializable<DatastoreV1.Value> {
4437
private final transient ByteString byteString;
4538

4639
Cursor(ByteString byteString) {
47-
Preconditions.checkArgument(byteString.isValidUtf8(), "content is not a valid UTF-8");
4840
this.byteString = byteString;
4941
}
5042

@@ -76,23 +68,16 @@ ByteString byteString() {
7668
* Returns the cursor in an encoded form that can be used as part of a URL.
7769
*/
7870
public String toUrlSafe() {
79-
try {
80-
return URLEncoder.encode(TextFormat.printToString(toPb()), UTF_8.name());
81-
} catch (UnsupportedEncodingException e) {
82-
throw new IllegalStateException("Unexpected encoding exception", e);
83-
}
71+
return BaseEncoding.base64Url().encode(byteString.toByteArray());
8472
}
8573

8674
/**
8775
* Create a {@code Cursor} given its URL safe encoded form.
8876
*/
8977
public static Cursor fromUrlSafe(String urlSafe) {
9078
try {
91-
String utf8Str = URLDecoder.decode(urlSafe, UTF_8.name());
92-
DatastoreV1.Value.Builder builder = DatastoreV1.Value.newBuilder();
93-
TextFormat.merge(utf8Str, builder);
94-
return fromPb(builder.build());
95-
} catch (UnsupportedEncodingException | ParseException e) {
79+
return Cursor.copyFrom(BaseEncoding.base64Url().decode(urlSafe));
80+
} catch (IllegalArgumentException e) {
9681
throw new IllegalStateException("Unexpected decoding exception", e);
9782
}
9883
}

gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/StructuredQuery.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ public StructuredQuery<V> build() {
782782
}
783783
}
784784

785-
static final class Builder<V> extends BaseBuilder<V, Builder<V>> {
785+
public static final class Builder<V> extends BaseBuilder<V, Builder<V>> {
786786

787787
Builder(ResultType<V> resultType) {
788788
super(resultType);

gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreTest.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import com.google.gcloud.datastore.testing.LocalGcdHelper;
4040
import com.google.gcloud.spi.DatastoreRpc;
4141
import com.google.gcloud.spi.DatastoreRpcFactory;
42+
import com.google.protobuf.ByteString;
4243

4344
import org.easymock.EasyMock;
4445
import org.junit.AfterClass;
@@ -496,7 +497,7 @@ public void testQueryPaginationWithLimit() throws DatastoreException {
496497
}
497498
query = query.toBuilder().startCursor(results.cursorAfter()).build();
498499
}
499-
assertEquals(totalCount, 5);
500+
assertEquals(5, totalCount);
500501
EasyMock.verify(rpcFactoryMock, rpcMock);
501502
}
502503

@@ -524,7 +525,8 @@ private List<RunQueryResponse> buildResponsesForQueryPaginationWithLimit() {
524525
.setMoreResults(QueryResultBatch.MoreResultsType.MORE_RESULTS_AFTER_LIMIT)
525526
.clearEntityResult()
526527
.addAllEntityResult(queryResultBatchPb.getEntityResultList().subList(1, 2))
527-
.setEndCursor(queryResultBatchPb.getEntityResultList().get(1).getCursor())
528+
.setEndCursor(
529+
ByteString.copyFrom(new byte[] {(byte) 0x80})) // test invalid UTF-8 string
528530
.build();
529531
responses.add(RunQueryResponse.newBuilder().setBatch(queryResultBatchPb2).build());
530532
QueryResultBatch queryResultBatchPb3 = QueryResultBatch.newBuilder()
@@ -546,6 +548,17 @@ private List<RunQueryResponse> buildResponsesForQueryPaginationWithLimit() {
546548
return responses;
547549
}
548550

551+
@Test
552+
public void testToUrlSafe() {
553+
byte[][] invalidUtf8 =
554+
new byte[][] {{(byte) 0xfe}, {(byte) 0xc1, (byte) 0xbf}, {(byte) 0xc0}, {(byte) 0x80}};
555+
for (byte[] bytes : invalidUtf8) {
556+
assertFalse(ByteString.copyFrom(bytes).isValidUtf8());
557+
Cursor cursor = new Cursor(ByteString.copyFrom(bytes));
558+
assertEquals(cursor, Cursor.fromUrlSafe(cursor.toUrlSafe()));
559+
}
560+
}
561+
549562
@Test
550563
public void testAllocateId() {
551564
KeyFactory keyFactory = datastore.newKeyFactory().kind(KIND1);

0 commit comments

Comments
 (0)