Skip to content

Commit 557749e

Browse files
authored
Migrate Firestore to the v1 protocol (#194)
* Remove Firestore Admin protos * Remove Firestore v1beta1 protos * Add Firestore v1 protos * Update local protos to reference v1 * s/v1beta1/v1/g
1 parent 4a02ae3 commit 557749e

File tree

20 files changed

+301
-659
lines changed

20 files changed

+301
-659
lines changed

firebase-firestore/src/main/java/com/google/firebase/firestore/local/LocalSerializer.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,12 @@ MaybeDocument decodeMaybeDocument(com.google.firebase.firestore.proto.MaybeDocum
9090
}
9191

9292
/**
93-
* Encodes a Document for local storage. This differs from the v1beta1 RPC serializer for
94-
* Documents in that it preserves the updateTime, which is considered an output only value by the
95-
* server.
93+
* Encodes a Document for local storage. This differs from the v1 RPC serializer for Documents in
94+
* that it preserves the updateTime, which is considered an output only value by the server.
9695
*/
97-
private com.google.firestore.v1beta1.Document encodeDocument(Document document) {
98-
com.google.firestore.v1beta1.Document.Builder builder =
99-
com.google.firestore.v1beta1.Document.newBuilder();
96+
private com.google.firestore.v1.Document encodeDocument(Document document) {
97+
com.google.firestore.v1.Document.Builder builder =
98+
com.google.firestore.v1.Document.newBuilder();
10099
builder.setName(rpcSerializer.encodeKey(document.getKey()));
101100

102101
ObjectValue value = document.getData();
@@ -111,7 +110,7 @@ private com.google.firestore.v1beta1.Document encodeDocument(Document document)
111110

112111
/** Decodes a Document proto to the equivalent model. */
113112
private Document decodeDocument(
114-
com.google.firestore.v1beta1.Document document, boolean hasCommittedMutations) {
113+
com.google.firestore.v1.Document document, boolean hasCommittedMutations) {
115114
DocumentKey key = rpcSerializer.decodeKey(document.getName());
116115
ObjectValue value = rpcSerializer.decodeFields(document.getFieldsMap());
117116
SnapshotVersion version = rpcSerializer.decodeVersion(document.getUpdateTime());

firebase-firestore/src/main/java/com/google/firebase/firestore/model/Document.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ public static Comparator<Document> keyComparator() {
5656
* Memoized serialized form of the document for optimization purposes (avoids repeated
5757
* serialization). Might be null.
5858
*/
59-
private final com.google.firestore.v1beta1.Document proto;
59+
private final com.google.firestore.v1.Document proto;
6060

61-
public @Nullable com.google.firestore.v1beta1.Document getProto() {
61+
public @Nullable com.google.firestore.v1.Document getProto() {
6262
return proto;
6363
}
6464

@@ -75,7 +75,7 @@ public Document(
7575
SnapshotVersion version,
7676
ObjectValue data,
7777
DocumentState documentState,
78-
com.google.firestore.v1beta1.Document proto) {
78+
com.google.firestore.v1.Document proto) {
7979
super(key, version);
8080
this.data = data;
8181
this.documentState = documentState;

firebase-firestore/src/main/java/com/google/firebase/firestore/remote/Datastore.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@
2828
import com.google.firebase.firestore.util.AsyncQueue;
2929
import com.google.firebase.firestore.util.FirestoreChannel;
3030
import com.google.firebase.firestore.util.Supplier;
31-
import com.google.firestore.v1beta1.BatchGetDocumentsRequest;
32-
import com.google.firestore.v1beta1.BatchGetDocumentsResponse;
33-
import com.google.firestore.v1beta1.CommitRequest;
34-
import com.google.firestore.v1beta1.CommitResponse;
35-
import com.google.firestore.v1beta1.FirestoreGrpc;
31+
import com.google.firestore.v1.BatchGetDocumentsRequest;
32+
import com.google.firestore.v1.BatchGetDocumentsResponse;
33+
import com.google.firestore.v1.CommitRequest;
34+
import com.google.firestore.v1.CommitResponse;
35+
import com.google.firestore.v1.FirestoreGrpc;
3636
import io.grpc.ManagedChannelBuilder;
3737
import io.grpc.Status;
3838
import io.grpc.android.AndroidChannelBuilder;
@@ -179,7 +179,7 @@ public Task<List<MutationResult>> commit(List<Mutation> mutations) {
179179
int count = response.getWriteResultsCount();
180180
ArrayList<MutationResult> results = new ArrayList<>(count);
181181
for (int i = 0; i < count; i++) {
182-
com.google.firestore.v1beta1.WriteResult result = response.getWriteResults(i);
182+
com.google.firestore.v1.WriteResult result = response.getWriteResults(i);
183183
results.add(serializer.decodeMutationResult(result, commitVersion));
184184
}
185185
return results;

firebase-firestore/src/main/java/com/google/firebase/firestore/remote/RemoteSerializer.java

Lines changed: 50 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -67,29 +67,29 @@
6767
import com.google.firebase.firestore.remote.WatchChange.WatchTargetChange;
6868
import com.google.firebase.firestore.remote.WatchChange.WatchTargetChangeType;
6969
import com.google.firebase.firestore.util.Assert;
70-
import com.google.firestore.v1beta1.BatchGetDocumentsResponse;
71-
import com.google.firestore.v1beta1.BatchGetDocumentsResponse.ResultCase;
72-
import com.google.firestore.v1beta1.Cursor;
73-
import com.google.firestore.v1beta1.DocumentChange;
74-
import com.google.firestore.v1beta1.DocumentDelete;
75-
import com.google.firestore.v1beta1.DocumentMask;
76-
import com.google.firestore.v1beta1.DocumentRemove;
77-
import com.google.firestore.v1beta1.DocumentTransform;
78-
import com.google.firestore.v1beta1.ListenResponse;
79-
import com.google.firestore.v1beta1.ListenResponse.ResponseTypeCase;
80-
import com.google.firestore.v1beta1.MapValue;
81-
import com.google.firestore.v1beta1.StructuredQuery;
82-
import com.google.firestore.v1beta1.StructuredQuery.CollectionSelector;
83-
import com.google.firestore.v1beta1.StructuredQuery.CompositeFilter;
84-
import com.google.firestore.v1beta1.StructuredQuery.FieldFilter;
85-
import com.google.firestore.v1beta1.StructuredQuery.FieldReference;
86-
import com.google.firestore.v1beta1.StructuredQuery.Filter.FilterTypeCase;
87-
import com.google.firestore.v1beta1.StructuredQuery.Order;
88-
import com.google.firestore.v1beta1.StructuredQuery.UnaryFilter;
89-
import com.google.firestore.v1beta1.Target;
90-
import com.google.firestore.v1beta1.Target.DocumentsTarget;
91-
import com.google.firestore.v1beta1.Target.QueryTarget;
92-
import com.google.firestore.v1beta1.Value;
70+
import com.google.firestore.v1.BatchGetDocumentsResponse;
71+
import com.google.firestore.v1.BatchGetDocumentsResponse.ResultCase;
72+
import com.google.firestore.v1.Cursor;
73+
import com.google.firestore.v1.DocumentChange;
74+
import com.google.firestore.v1.DocumentDelete;
75+
import com.google.firestore.v1.DocumentMask;
76+
import com.google.firestore.v1.DocumentRemove;
77+
import com.google.firestore.v1.DocumentTransform;
78+
import com.google.firestore.v1.ListenResponse;
79+
import com.google.firestore.v1.ListenResponse.ResponseTypeCase;
80+
import com.google.firestore.v1.MapValue;
81+
import com.google.firestore.v1.StructuredQuery;
82+
import com.google.firestore.v1.StructuredQuery.CollectionSelector;
83+
import com.google.firestore.v1.StructuredQuery.CompositeFilter;
84+
import com.google.firestore.v1.StructuredQuery.FieldFilter;
85+
import com.google.firestore.v1.StructuredQuery.FieldReference;
86+
import com.google.firestore.v1.StructuredQuery.Filter.FilterTypeCase;
87+
import com.google.firestore.v1.StructuredQuery.Order;
88+
import com.google.firestore.v1.StructuredQuery.UnaryFilter;
89+
import com.google.firestore.v1.Target;
90+
import com.google.firestore.v1.Target.DocumentsTarget;
91+
import com.google.firestore.v1.Target.QueryTarget;
92+
import com.google.firestore.v1.Value;
9393
import com.google.protobuf.ByteString;
9494
import com.google.protobuf.Int32Value;
9595
import com.google.type.LatLng;
@@ -181,7 +181,7 @@ private ResourcePath decodeQueryPath(String name) {
181181
ResourcePath resource = decodeResourceName(name);
182182
if (resource.length() == 4) {
183183
// In v1beta1 queries for collections at the root did not have a trailing "/documents". In v1
184-
// all resource paths contain "/documents". Preserve the ability to read the v1beta1 form for
184+
// all resource paths contain "/documents". Preserve the ability to read the v1 form for
185185
// compatibility with queries persisted in the local query cache.
186186
return ResourcePath.EMPTY;
187187
} else {
@@ -249,9 +249,8 @@ public String databaseName() {
249249
* @param value the model to convert
250250
* @return The proto representation of the model
251251
*/
252-
public com.google.firestore.v1beta1.Value encodeValue(FieldValue value) {
253-
com.google.firestore.v1beta1.Value.Builder builder =
254-
com.google.firestore.v1beta1.Value.newBuilder();
252+
public com.google.firestore.v1.Value encodeValue(FieldValue value) {
253+
com.google.firestore.v1.Value.Builder builder = com.google.firestore.v1.Value.newBuilder();
255254

256255
if (value instanceof NullValue) {
257256
builder.setNullValueValue(0);
@@ -298,7 +297,7 @@ public com.google.firestore.v1beta1.Value encodeValue(FieldValue value) {
298297
*
299298
* @return The model equivalent of the proto data.
300299
*/
301-
public FieldValue decodeValue(com.google.firestore.v1beta1.Value proto) {
300+
public FieldValue decodeValue(com.google.firestore.v1.Value proto) {
302301
switch (proto.getValueTypeCase()) {
303302
case NULL_VALUE:
304303
return NullValue.nullValue();
@@ -334,17 +333,17 @@ public FieldValue decodeValue(com.google.firestore.v1beta1.Value proto) {
334333
}
335334
}
336335

337-
private com.google.firestore.v1beta1.ArrayValue encodeArrayValue(ArrayValue value) {
336+
private com.google.firestore.v1.ArrayValue encodeArrayValue(ArrayValue value) {
338337
List<FieldValue> internalValue = value.getInternalValue();
339-
com.google.firestore.v1beta1.ArrayValue.Builder arrayBuilder =
340-
com.google.firestore.v1beta1.ArrayValue.newBuilder();
338+
com.google.firestore.v1.ArrayValue.Builder arrayBuilder =
339+
com.google.firestore.v1.ArrayValue.newBuilder();
341340
for (FieldValue subValue : internalValue) {
342341
arrayBuilder.addValues(encodeValue(subValue));
343342
}
344343
return arrayBuilder.build();
345344
}
346345

347-
private ArrayValue decodeArrayValue(com.google.firestore.v1beta1.ArrayValue protoArray) {
346+
private ArrayValue decodeArrayValue(com.google.firestore.v1.ArrayValue protoArray) {
348347
int count = protoArray.getValuesCount();
349348
List<FieldValue> wrappedList = new ArrayList<>(count);
350349
for (int i = 0; i < count; i++) {
@@ -368,9 +367,9 @@ private ObjectValue decodeMapValue(MapValue value) {
368367
// PORTING NOTE: There's no encodeFields here because there's no way to write it that doesn't
369368
// involve creating a temporary map.
370369

371-
public ObjectValue decodeFields(Map<String, com.google.firestore.v1beta1.Value> fields) {
370+
public ObjectValue decodeFields(Map<String, com.google.firestore.v1.Value> fields) {
372371
ObjectValue result = ObjectValue.emptyObject();
373-
for (Map.Entry<String, com.google.firestore.v1beta1.Value> entry : fields.entrySet()) {
372+
for (Map.Entry<String, com.google.firestore.v1.Value> entry : fields.entrySet()) {
374373
FieldPath path = FieldPath.fromSingleSegment(entry.getKey());
375374
FieldValue value = decodeValue(entry.getValue());
376375
result = result.set(path, value);
@@ -380,9 +379,9 @@ public ObjectValue decodeFields(Map<String, com.google.firestore.v1beta1.Value>
380379

381380
// Documents
382381

383-
public com.google.firestore.v1beta1.Document encodeDocument(DocumentKey key, ObjectValue value) {
384-
com.google.firestore.v1beta1.Document.Builder builder =
385-
com.google.firestore.v1beta1.Document.newBuilder();
382+
public com.google.firestore.v1.Document encodeDocument(DocumentKey key, ObjectValue value) {
383+
com.google.firestore.v1.Document.Builder builder =
384+
com.google.firestore.v1.Document.newBuilder();
386385
builder.setName(encodeKey(key));
387386
for (Map.Entry<String, FieldValue> entry : value.getInternalValue()) {
388387
builder.putFields(entry.getKey(), encodeValue(entry.getValue()));
@@ -427,9 +426,8 @@ private NoDocument decodeMissingDocument(BatchGetDocumentsResponse response) {
427426
// Mutations
428427

429428
/** Converts a Mutation model to a Write proto */
430-
public com.google.firestore.v1beta1.Write encodeMutation(Mutation mutation) {
431-
com.google.firestore.v1beta1.Write.Builder builder =
432-
com.google.firestore.v1beta1.Write.newBuilder();
429+
public com.google.firestore.v1.Write encodeMutation(Mutation mutation) {
430+
com.google.firestore.v1.Write.Builder builder = com.google.firestore.v1.Write.newBuilder();
433431
if (mutation instanceof SetMutation) {
434432
builder.setUpdate(encodeDocument(mutation.getKey(), ((SetMutation) mutation).getValue()));
435433
} else if (mutation instanceof PatchMutation) {
@@ -455,7 +453,7 @@ public com.google.firestore.v1beta1.Write encodeMutation(Mutation mutation) {
455453
return builder.build();
456454
}
457455

458-
public Mutation decodeMutation(com.google.firestore.v1beta1.Write mutation) {
456+
public Mutation decodeMutation(com.google.firestore.v1.Write mutation) {
459457
Precondition precondition =
460458
mutation.hasCurrentDocument()
461459
? decodePrecondition(mutation.getCurrentDocument())
@@ -496,10 +494,10 @@ public Mutation decodeMutation(com.google.firestore.v1beta1.Write mutation) {
496494
}
497495
}
498496

499-
private com.google.firestore.v1beta1.Precondition encodePrecondition(Precondition precondition) {
497+
private com.google.firestore.v1.Precondition encodePrecondition(Precondition precondition) {
500498
hardAssert(!precondition.isNone(), "Can't serialize an empty precondition");
501-
com.google.firestore.v1beta1.Precondition.Builder builder =
502-
com.google.firestore.v1beta1.Precondition.newBuilder();
499+
com.google.firestore.v1.Precondition.Builder builder =
500+
com.google.firestore.v1.Precondition.newBuilder();
503501
if (precondition.getUpdateTime() != null) {
504502
return builder.setUpdateTime(encodeVersion(precondition.getUpdateTime())).build();
505503
} else if (precondition.getExists() != null) {
@@ -509,7 +507,7 @@ private com.google.firestore.v1beta1.Precondition encodePrecondition(Preconditio
509507
}
510508
}
511509

512-
private Precondition decodePrecondition(com.google.firestore.v1beta1.Precondition precondition) {
510+
private Precondition decodePrecondition(com.google.firestore.v1.Precondition precondition) {
513511
switch (precondition.getConditionTypeCase()) {
514512
case UPDATE_TIME:
515513
return Precondition.updateTime(decodeVersion(precondition.getUpdateTime()));
@@ -563,10 +561,10 @@ private DocumentTransform.FieldTransform encodeFieldTransform(FieldTransform fie
563561
}
564562
}
565563

566-
private com.google.firestore.v1beta1.ArrayValue encodeArrayTransformElements(
564+
private com.google.firestore.v1.ArrayValue encodeArrayTransformElements(
567565
List<FieldValue> elements) {
568-
com.google.firestore.v1beta1.ArrayValue.Builder arrayBuilder =
569-
com.google.firestore.v1beta1.ArrayValue.newBuilder();
566+
com.google.firestore.v1.ArrayValue.Builder arrayBuilder =
567+
com.google.firestore.v1.ArrayValue.newBuilder();
570568
for (FieldValue subValue : elements) {
571569
arrayBuilder.addValues(encodeValue(subValue));
572570
}
@@ -600,7 +598,7 @@ private FieldTransform decodeFieldTransform(DocumentTransform.FieldTransform fie
600598
}
601599

602600
private List<FieldValue> decodeArrayTransformElements(
603-
com.google.firestore.v1beta1.ArrayValue elementsProto) {
601+
com.google.firestore.v1.ArrayValue elementsProto) {
604602
int count = elementsProto.getValuesCount();
605603
List<FieldValue> result = new ArrayList<>(count);
606604
for (int i = 0; i < count; i++) {
@@ -610,7 +608,7 @@ private List<FieldValue> decodeArrayTransformElements(
610608
}
611609

612610
public MutationResult decodeMutationResult(
613-
com.google.firestore.v1beta1.WriteResult proto, SnapshotVersion commitVersion) {
611+
com.google.firestore.v1.WriteResult proto, SnapshotVersion commitVersion) {
614612
// NOTE: Deletes don't have an updateTime but the commit timestamp from the containing
615613
// CommitResponse or WriteResponse indicates essentially that the delete happened no later than
616614
// that. For our purposes we don't care exactly when the delete happened so long as we can tell
@@ -977,7 +975,7 @@ public WatchChange decodeWatchChange(ListenResponse protoChange) {
977975

978976
switch (protoChange.getResponseTypeCase()) {
979977
case TARGET_CHANGE:
980-
com.google.firestore.v1beta1.TargetChange targetChange = protoChange.getTargetChange();
978+
com.google.firestore.v1.TargetChange targetChange = protoChange.getTargetChange();
981979
WatchTargetChangeType changeType;
982980
Status cause = null;
983981
switch (targetChange.getTargetChangeType()) {
@@ -1038,7 +1036,7 @@ public WatchChange decodeWatchChange(ListenResponse protoChange) {
10381036
watchChange = new WatchChange.DocumentChange(Collections.emptyList(), removed, key, null);
10391037
break;
10401038
case FILTER:
1041-
com.google.firestore.v1beta1.ExistenceFilter protoFilter = protoChange.getFilter();
1039+
com.google.firestore.v1.ExistenceFilter protoFilter = protoChange.getFilter();
10421040
// TODO: implement existence filter parsing (see b/33076578)
10431041
ExistenceFilter filter = new ExistenceFilter(protoFilter.getCount());
10441042
int targetId = protoFilter.getTargetId();

firebase-firestore/src/main/java/com/google/firebase/firestore/remote/WatchStream.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
import com.google.firebase.firestore.util.AsyncQueue;
2222
import com.google.firebase.firestore.util.AsyncQueue.TimerId;
2323
import com.google.firebase.firestore.util.FirestoreChannel;
24-
import com.google.firestore.v1beta1.FirestoreGrpc;
25-
import com.google.firestore.v1beta1.ListenRequest;
26-
import com.google.firestore.v1beta1.ListenResponse;
24+
import com.google.firestore.v1.FirestoreGrpc;
25+
import com.google.firestore.v1.ListenRequest;
26+
import com.google.firestore.v1.ListenResponse;
2727
import com.google.protobuf.ByteString;
2828
import java.util.Map;
2929

@@ -34,7 +34,7 @@
3434
* sent to control what changes will be sent from the server for WatchChanges.
3535
*
3636
* @see <a
37-
* href="https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto#L147">firestore.proto</a>
37+
* href="https://github.com/googleapis/googleapis/blob/master/google/firestore/v1/firestore.proto#L147">firestore.proto</a>
3838
*/
3939
public class WatchStream
4040
extends AbstractStream<ListenRequest, ListenResponse, WatchStream.Callback> {
@@ -100,7 +100,7 @@ public void unwatchTarget(int targetId) {
100100
}
101101

102102
@Override
103-
public void onNext(com.google.firestore.v1beta1.ListenResponse listenResponse) {
103+
public void onNext(com.google.firestore.v1.ListenResponse listenResponse) {
104104
// A successful response means the stream is healthy
105105
backoff.reset();
106106

firebase-firestore/src/main/java/com/google/firebase/firestore/remote/WriteStream.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
import com.google.firebase.firestore.util.AsyncQueue;
2424
import com.google.firebase.firestore.util.AsyncQueue.TimerId;
2525
import com.google.firebase.firestore.util.FirestoreChannel;
26-
import com.google.firestore.v1beta1.FirestoreGrpc;
27-
import com.google.firestore.v1beta1.WriteRequest;
28-
import com.google.firestore.v1beta1.WriteResponse;
26+
import com.google.firestore.v1.FirestoreGrpc;
27+
import com.google.firestore.v1.WriteRequest;
28+
import com.google.firestore.v1.WriteResponse;
2929
import com.google.protobuf.ByteString;
3030
import java.util.ArrayList;
3131
import java.util.Collections;
@@ -45,7 +45,7 @@
4545
* okay to use the same streamToken for the calls to {@code writeMutations}.
4646
*
4747
* @see <a
48-
* href="https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto#L139">firestore.proto</a>
48+
* href="https://github.com/googleapis/googleapis/blob/master/google/firestore/v1/firestore.proto#L139">firestore.proto</a>
4949
*/
5050
public class WriteStream extends AbstractStream<WriteRequest, WriteResponse, WriteStream.Callback> {
5151

@@ -179,7 +179,7 @@ public void onNext(WriteResponse response) {
179179
int count = response.getWriteResultsCount();
180180
List<MutationResult> results = new ArrayList<>(count);
181181
for (int i = 0; i < count; i++) {
182-
com.google.firestore.v1beta1.WriteResult result = response.getWriteResults(i);
182+
com.google.firestore.v1.WriteResult result = response.getWriteResults(i);
183183
results.add(serializer.decodeMutationResult(result, commitVersion));
184184
}
185185
listener.onWriteResponse(commitVersion, results);

firebase-firestore/src/main/java/com/google/firebase/firestore/util/FirestoreChannel.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
import com.google.firebase.firestore.core.Version;
2323
import com.google.firebase.firestore.model.DatabaseId;
2424
import com.google.firebase.firestore.remote.FirestoreCallCredentials;
25-
import com.google.firestore.v1beta1.FirestoreGrpc;
26-
import com.google.firestore.v1beta1.FirestoreGrpc.FirestoreStub;
25+
import com.google.firestore.v1.FirestoreGrpc;
26+
import com.google.firestore.v1.FirestoreGrpc.FirestoreStub;
2727
import io.grpc.CallOptions;
2828
import io.grpc.ClientCall;
2929
import io.grpc.ManagedChannel;

firebase-firestore/src/proto/google/firebase/firestore/proto/maybe_document.proto

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ option java_package = "com.google.firebase.firestore.proto";
2121

2222
option objc_class_prefix = "FSTPB";
2323

24-
import "google/firestore/v1beta1/document.proto";
24+
import "google/firestore/v1/document.proto";
2525
import "google/protobuf/timestamp.proto";
2626

2727
// A message indicating that the document is known to not exist.
@@ -54,7 +54,7 @@ message MaybeDocument {
5454
NoDocument no_document = 1;
5555

5656
// The document (if it exists).
57-
google.firestore.v1beta1.Document document = 2;
57+
google.firestore.v1.Document document = 2;
5858

5959
// Used if the document is known to exist but its data is unknown.
6060
UnknownDocument unknown_document = 3;

0 commit comments

Comments
 (0)