Update dependencies

Among others, this updates minio-go, so that the new "eu-west-3" zone
for AWS is supported.
This commit is contained in:
Alexander Neumann
2018-01-23 19:40:42 +01:00
parent b63de7c798
commit 2b39f9f4b2
3435 changed files with 1317989 additions and 315639 deletions
+2 -2
View File
@@ -1,10 +1,10 @@
// Copyright 2017, Google Inc. All rights reserved.
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
+18 -65
View File
@@ -1,10 +1,10 @@
// Copyright 2017, Google Inc. All rights reserved.
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -111,8 +111,8 @@ type Client struct {
// The call options for this service.
CallOptions *CallOptions
// The metadata to be sent with each request.
Metadata metadata.MD
// The x-goog-* metadata to be sent with each request.
xGoogMetadata metadata.MD
}
// NewClient creates a new firestore client.
@@ -168,59 +168,12 @@ func (c *Client) Close() error {
func (c *Client) SetGoogleClientInfo(keyval ...string) {
kv := append([]string{"gl-go", version.Go()}, keyval...)
kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version)
c.Metadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...))
}
// DatabaseRootPath returns the path for the database root resource.
func DatabaseRootPath(project, database string) string {
return "" +
"projects/" +
project +
"/databases/" +
database +
""
}
// DocumentRootPath returns the path for the document root resource.
func DocumentRootPath(project, database string) string {
return "" +
"projects/" +
project +
"/databases/" +
database +
"/documents" +
""
}
// DocumentPathPath returns the path for the document path resource.
func DocumentPathPath(project, database, documentPath string) string {
return "" +
"projects/" +
project +
"/databases/" +
database +
"/documents/" +
documentPath +
""
}
// AnyPathPath returns the path for the any path resource.
func AnyPathPath(project, database, document, anyPath string) string {
return "" +
"projects/" +
project +
"/databases/" +
database +
"/documents/" +
document +
"/" +
anyPath +
""
c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...))
}
// GetDocument gets a single document.
func (c *Client) GetDocument(ctx context.Context, req *firestorepb.GetDocumentRequest, opts ...gax.CallOption) (*firestorepb.Document, error) {
ctx = insertMetadata(ctx, c.Metadata)
ctx = insertMetadata(ctx, c.xGoogMetadata)
opts = append(c.CallOptions.GetDocument[0:len(c.CallOptions.GetDocument):len(c.CallOptions.GetDocument)], opts...)
var resp *firestorepb.Document
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
@@ -236,7 +189,7 @@ func (c *Client) GetDocument(ctx context.Context, req *firestorepb.GetDocumentRe
// ListDocuments lists documents.
func (c *Client) ListDocuments(ctx context.Context, req *firestorepb.ListDocumentsRequest, opts ...gax.CallOption) *DocumentIterator {
ctx = insertMetadata(ctx, c.Metadata)
ctx = insertMetadata(ctx, c.xGoogMetadata)
opts = append(c.CallOptions.ListDocuments[0:len(c.CallOptions.ListDocuments):len(c.CallOptions.ListDocuments)], opts...)
it := &DocumentIterator{}
it.InternalFetch = func(pageSize int, pageToken string) ([]*firestorepb.Document, string, error) {
@@ -271,7 +224,7 @@ func (c *Client) ListDocuments(ctx context.Context, req *firestorepb.ListDocumen
// CreateDocument creates a new document.
func (c *Client) CreateDocument(ctx context.Context, req *firestorepb.CreateDocumentRequest, opts ...gax.CallOption) (*firestorepb.Document, error) {
ctx = insertMetadata(ctx, c.Metadata)
ctx = insertMetadata(ctx, c.xGoogMetadata)
opts = append(c.CallOptions.CreateDocument[0:len(c.CallOptions.CreateDocument):len(c.CallOptions.CreateDocument)], opts...)
var resp *firestorepb.Document
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
@@ -287,7 +240,7 @@ func (c *Client) CreateDocument(ctx context.Context, req *firestorepb.CreateDocu
// UpdateDocument updates or inserts a document.
func (c *Client) UpdateDocument(ctx context.Context, req *firestorepb.UpdateDocumentRequest, opts ...gax.CallOption) (*firestorepb.Document, error) {
ctx = insertMetadata(ctx, c.Metadata)
ctx = insertMetadata(ctx, c.xGoogMetadata)
opts = append(c.CallOptions.UpdateDocument[0:len(c.CallOptions.UpdateDocument):len(c.CallOptions.UpdateDocument)], opts...)
var resp *firestorepb.Document
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
@@ -303,7 +256,7 @@ func (c *Client) UpdateDocument(ctx context.Context, req *firestorepb.UpdateDocu
// DeleteDocument deletes a document.
func (c *Client) DeleteDocument(ctx context.Context, req *firestorepb.DeleteDocumentRequest, opts ...gax.CallOption) error {
ctx = insertMetadata(ctx, c.Metadata)
ctx = insertMetadata(ctx, c.xGoogMetadata)
opts = append(c.CallOptions.DeleteDocument[0:len(c.CallOptions.DeleteDocument):len(c.CallOptions.DeleteDocument)], opts...)
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
@@ -318,7 +271,7 @@ func (c *Client) DeleteDocument(ctx context.Context, req *firestorepb.DeleteDocu
// Documents returned by this method are not guaranteed to be returned in the
// same order that they were requested.
func (c *Client) BatchGetDocuments(ctx context.Context, req *firestorepb.BatchGetDocumentsRequest, opts ...gax.CallOption) (firestorepb.Firestore_BatchGetDocumentsClient, error) {
ctx = insertMetadata(ctx, c.Metadata)
ctx = insertMetadata(ctx, c.xGoogMetadata)
opts = append(c.CallOptions.BatchGetDocuments[0:len(c.CallOptions.BatchGetDocuments):len(c.CallOptions.BatchGetDocuments)], opts...)
var resp firestorepb.Firestore_BatchGetDocumentsClient
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
@@ -334,7 +287,7 @@ func (c *Client) BatchGetDocuments(ctx context.Context, req *firestorepb.BatchGe
// BeginTransaction starts a new transaction.
func (c *Client) BeginTransaction(ctx context.Context, req *firestorepb.BeginTransactionRequest, opts ...gax.CallOption) (*firestorepb.BeginTransactionResponse, error) {
ctx = insertMetadata(ctx, c.Metadata)
ctx = insertMetadata(ctx, c.xGoogMetadata)
opts = append(c.CallOptions.BeginTransaction[0:len(c.CallOptions.BeginTransaction):len(c.CallOptions.BeginTransaction)], opts...)
var resp *firestorepb.BeginTransactionResponse
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
@@ -350,7 +303,7 @@ func (c *Client) BeginTransaction(ctx context.Context, req *firestorepb.BeginTra
// Commit commits a transaction, while optionally updating documents.
func (c *Client) Commit(ctx context.Context, req *firestorepb.CommitRequest, opts ...gax.CallOption) (*firestorepb.CommitResponse, error) {
ctx = insertMetadata(ctx, c.Metadata)
ctx = insertMetadata(ctx, c.xGoogMetadata)
opts = append(c.CallOptions.Commit[0:len(c.CallOptions.Commit):len(c.CallOptions.Commit)], opts...)
var resp *firestorepb.CommitResponse
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
@@ -366,7 +319,7 @@ func (c *Client) Commit(ctx context.Context, req *firestorepb.CommitRequest, opt
// Rollback rolls back a transaction.
func (c *Client) Rollback(ctx context.Context, req *firestorepb.RollbackRequest, opts ...gax.CallOption) error {
ctx = insertMetadata(ctx, c.Metadata)
ctx = insertMetadata(ctx, c.xGoogMetadata)
opts = append(c.CallOptions.Rollback[0:len(c.CallOptions.Rollback):len(c.CallOptions.Rollback)], opts...)
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
@@ -378,7 +331,7 @@ func (c *Client) Rollback(ctx context.Context, req *firestorepb.RollbackRequest,
// RunQuery runs a query.
func (c *Client) RunQuery(ctx context.Context, req *firestorepb.RunQueryRequest, opts ...gax.CallOption) (firestorepb.Firestore_RunQueryClient, error) {
ctx = insertMetadata(ctx, c.Metadata)
ctx = insertMetadata(ctx, c.xGoogMetadata)
opts = append(c.CallOptions.RunQuery[0:len(c.CallOptions.RunQuery):len(c.CallOptions.RunQuery)], opts...)
var resp firestorepb.Firestore_RunQueryClient
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
@@ -394,7 +347,7 @@ func (c *Client) RunQuery(ctx context.Context, req *firestorepb.RunQueryRequest,
// Write streams batches of document updates and deletes, in order.
func (c *Client) Write(ctx context.Context, opts ...gax.CallOption) (firestorepb.Firestore_WriteClient, error) {
ctx = insertMetadata(ctx, c.Metadata)
ctx = insertMetadata(ctx, c.xGoogMetadata)
opts = append(c.CallOptions.Write[0:len(c.CallOptions.Write):len(c.CallOptions.Write)], opts...)
var resp firestorepb.Firestore_WriteClient
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
@@ -410,7 +363,7 @@ func (c *Client) Write(ctx context.Context, opts ...gax.CallOption) (firestorepb
// Listen listens to changes.
func (c *Client) Listen(ctx context.Context, opts ...gax.CallOption) (firestorepb.Firestore_ListenClient, error) {
ctx = insertMetadata(ctx, c.Metadata)
ctx = insertMetadata(ctx, c.xGoogMetadata)
opts = append(c.CallOptions.Listen[0:len(c.CallOptions.Listen):len(c.CallOptions.Listen)], opts...)
var resp firestorepb.Firestore_ListenClient
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
@@ -426,7 +379,7 @@ func (c *Client) Listen(ctx context.Context, opts ...gax.CallOption) (firestorep
// ListCollectionIds lists all the collection IDs underneath a document.
func (c *Client) ListCollectionIds(ctx context.Context, req *firestorepb.ListCollectionIdsRequest, opts ...gax.CallOption) *StringIterator {
ctx = insertMetadata(ctx, c.Metadata)
ctx = insertMetadata(ctx, c.xGoogMetadata)
opts = append(c.CallOptions.ListCollectionIds[0:len(c.CallOptions.ListCollectionIds):len(c.CallOptions.ListCollectionIds)], opts...)
it := &StringIterator{}
it.InternalFetch = func(pageSize int, pageToken string) ([]string, string, error) {
@@ -1,10 +1,10 @@
// Copyright 2017, Google Inc. All rights reserved.
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -43,7 +43,7 @@ func ExampleClient_GetDocument() {
}
req := &firestorepb.GetDocumentRequest{
// TODO: Fill request struct fields.
// TODO: Fill request struct fields.
}
resp, err := c.GetDocument(ctx, req)
if err != nil {
@@ -61,7 +61,7 @@ func ExampleClient_ListDocuments() {
}
req := &firestorepb.ListDocumentsRequest{
// TODO: Fill request struct fields.
// TODO: Fill request struct fields.
}
it := c.ListDocuments(ctx, req)
for {
@@ -85,7 +85,7 @@ func ExampleClient_CreateDocument() {
}
req := &firestorepb.CreateDocumentRequest{
// TODO: Fill request struct fields.
// TODO: Fill request struct fields.
}
resp, err := c.CreateDocument(ctx, req)
if err != nil {
@@ -103,7 +103,7 @@ func ExampleClient_UpdateDocument() {
}
req := &firestorepb.UpdateDocumentRequest{
// TODO: Fill request struct fields.
// TODO: Fill request struct fields.
}
resp, err := c.UpdateDocument(ctx, req)
if err != nil {
@@ -121,7 +121,7 @@ func ExampleClient_DeleteDocument() {
}
req := &firestorepb.DeleteDocumentRequest{
// TODO: Fill request struct fields.
// TODO: Fill request struct fields.
}
err = c.DeleteDocument(ctx, req)
if err != nil {
@@ -137,7 +137,7 @@ func ExampleClient_BatchGetDocuments() {
}
req := &firestorepb.BatchGetDocumentsRequest{
// TODO: Fill request struct fields.
// TODO: Fill request struct fields.
}
stream, err := c.BatchGetDocuments(ctx, req)
if err != nil {
@@ -164,7 +164,7 @@ func ExampleClient_BeginTransaction() {
}
req := &firestorepb.BeginTransactionRequest{
// TODO: Fill request struct fields.
// TODO: Fill request struct fields.
}
resp, err := c.BeginTransaction(ctx, req)
if err != nil {
@@ -182,7 +182,7 @@ func ExampleClient_Commit() {
}
req := &firestorepb.CommitRequest{
// TODO: Fill request struct fields.
// TODO: Fill request struct fields.
}
resp, err := c.Commit(ctx, req)
if err != nil {
@@ -200,7 +200,7 @@ func ExampleClient_Rollback() {
}
req := &firestorepb.RollbackRequest{
// TODO: Fill request struct fields.
// TODO: Fill request struct fields.
}
err = c.Rollback(ctx, req)
if err != nil {
@@ -216,7 +216,7 @@ func ExampleClient_RunQuery() {
}
req := &firestorepb.RunQueryRequest{
// TODO: Fill request struct fields.
// TODO: Fill request struct fields.
}
stream, err := c.RunQuery(ctx, req)
if err != nil {
@@ -247,7 +247,7 @@ func ExampleClient_Write() {
}
go func() {
reqs := []*firestorepb.WriteRequest{
// TODO: Create requests.
// TODO: Create requests.
}
for _, req := range reqs {
if err := stream.Send(req); err != nil {
@@ -281,7 +281,7 @@ func ExampleClient_Listen() {
}
go func() {
reqs := []*firestorepb.ListenRequest{
// TODO: Create requests.
// TODO: Create requests.
}
for _, req := range reqs {
if err := stream.Send(req); err != nil {
@@ -311,7 +311,7 @@ func ExampleClient_ListCollectionIds() {
}
req := &firestorepb.ListCollectionIdsRequest{
// TODO: Fill request struct fields.
// TODO: Fill request struct fields.
}
it := c.ListCollectionIds(ctx, req)
for {
+26 -26
View File
@@ -1,10 +1,10 @@
// Copyright 2017, Google Inc. All rights reserved.
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -293,7 +293,7 @@ func TestFirestoreGetDocument(t *testing.T) {
mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse)
var formattedName string = AnyPathPath("[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var formattedName string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var request = &firestorepb.GetDocumentRequest{
Name: formattedName,
}
@@ -322,7 +322,7 @@ func TestFirestoreGetDocumentError(t *testing.T) {
errCode := codes.PermissionDenied
mockFirestore.err = gstatus.Error(errCode, "test error")
var formattedName string = AnyPathPath("[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var formattedName string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var request = &firestorepb.GetDocumentRequest{
Name: formattedName,
}
@@ -355,7 +355,7 @@ func TestFirestoreListDocuments(t *testing.T) {
mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse)
var formattedParent string = AnyPathPath("[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var formattedParent string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var collectionId string = "collectionId-821242276"
var request = &firestorepb.ListDocumentsRequest{
Parent: formattedParent,
@@ -396,7 +396,7 @@ func TestFirestoreListDocumentsError(t *testing.T) {
errCode := codes.PermissionDenied
mockFirestore.err = gstatus.Error(errCode, "test error")
var formattedParent string = AnyPathPath("[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var formattedParent string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var collectionId string = "collectionId-821242276"
var request = &firestorepb.ListDocumentsRequest{
Parent: formattedParent,
@@ -428,7 +428,7 @@ func TestFirestoreCreateDocument(t *testing.T) {
mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse)
var formattedParent string = AnyPathPath("[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var formattedParent string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var collectionId string = "collectionId-821242276"
var documentId string = "documentId506676927"
var document *firestorepb.Document = &firestorepb.Document{}
@@ -463,7 +463,7 @@ func TestFirestoreCreateDocumentError(t *testing.T) {
errCode := codes.PermissionDenied
mockFirestore.err = gstatus.Error(errCode, "test error")
var formattedParent string = AnyPathPath("[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var formattedParent string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var collectionId string = "collectionId-821242276"
var documentId string = "documentId506676927"
var document *firestorepb.Document = &firestorepb.Document{}
@@ -559,7 +559,7 @@ func TestFirestoreDeleteDocument(t *testing.T) {
mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse)
var formattedName string = AnyPathPath("[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var formattedName string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var request = &firestorepb.DeleteDocumentRequest{
Name: formattedName,
}
@@ -585,7 +585,7 @@ func TestFirestoreDeleteDocumentError(t *testing.T) {
errCode := codes.PermissionDenied
mockFirestore.err = gstatus.Error(errCode, "test error")
var formattedName string = AnyPathPath("[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var formattedName string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var request = &firestorepb.DeleteDocumentRequest{
Name: formattedName,
}
@@ -618,7 +618,7 @@ func TestFirestoreBatchGetDocuments(t *testing.T) {
mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse)
var formattedDatabase string = DatabaseRootPath("[PROJECT]", "[DATABASE]")
var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]")
var documents []string = nil
var request = &firestorepb.BatchGetDocumentsRequest{
Database: formattedDatabase,
@@ -653,7 +653,7 @@ func TestFirestoreBatchGetDocumentsError(t *testing.T) {
errCode := codes.PermissionDenied
mockFirestore.err = gstatus.Error(errCode, "test error")
var formattedDatabase string = DatabaseRootPath("[PROJECT]", "[DATABASE]")
var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]")
var documents []string = nil
var request = &firestorepb.BatchGetDocumentsRequest{
Database: formattedDatabase,
@@ -689,7 +689,7 @@ func TestFirestoreBeginTransaction(t *testing.T) {
mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse)
var formattedDatabase string = DatabaseRootPath("[PROJECT]", "[DATABASE]")
var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]")
var request = &firestorepb.BeginTransactionRequest{
Database: formattedDatabase,
}
@@ -718,7 +718,7 @@ func TestFirestoreBeginTransactionError(t *testing.T) {
errCode := codes.PermissionDenied
mockFirestore.err = gstatus.Error(errCode, "test error")
var formattedDatabase string = DatabaseRootPath("[PROJECT]", "[DATABASE]")
var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]")
var request = &firestorepb.BeginTransactionRequest{
Database: formattedDatabase,
}
@@ -745,7 +745,7 @@ func TestFirestoreCommit(t *testing.T) {
mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse)
var formattedDatabase string = DatabaseRootPath("[PROJECT]", "[DATABASE]")
var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]")
var writes []*firestorepb.Write = nil
var request = &firestorepb.CommitRequest{
Database: formattedDatabase,
@@ -776,7 +776,7 @@ func TestFirestoreCommitError(t *testing.T) {
errCode := codes.PermissionDenied
mockFirestore.err = gstatus.Error(errCode, "test error")
var formattedDatabase string = DatabaseRootPath("[PROJECT]", "[DATABASE]")
var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]")
var writes []*firestorepb.Write = nil
var request = &firestorepb.CommitRequest{
Database: formattedDatabase,
@@ -805,7 +805,7 @@ func TestFirestoreRollback(t *testing.T) {
mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse)
var formattedDatabase string = DatabaseRootPath("[PROJECT]", "[DATABASE]")
var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]")
var transaction []byte = []byte("-34")
var request = &firestorepb.RollbackRequest{
Database: formattedDatabase,
@@ -833,7 +833,7 @@ func TestFirestoreRollbackError(t *testing.T) {
errCode := codes.PermissionDenied
mockFirestore.err = gstatus.Error(errCode, "test error")
var formattedDatabase string = DatabaseRootPath("[PROJECT]", "[DATABASE]")
var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]")
var transaction []byte = []byte("-34")
var request = &firestorepb.RollbackRequest{
Database: formattedDatabase,
@@ -866,7 +866,7 @@ func TestFirestoreRunQuery(t *testing.T) {
mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse)
var formattedParent string = AnyPathPath("[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var formattedParent string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var request = &firestorepb.RunQueryRequest{
Parent: formattedParent,
}
@@ -899,7 +899,7 @@ func TestFirestoreRunQueryError(t *testing.T) {
errCode := codes.PermissionDenied
mockFirestore.err = gstatus.Error(errCode, "test error")
var formattedParent string = AnyPathPath("[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var formattedParent string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var request = &firestorepb.RunQueryRequest{
Parent: formattedParent,
}
@@ -935,7 +935,7 @@ func TestFirestoreWrite(t *testing.T) {
mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse)
var formattedDatabase string = DatabaseRootPath("[PROJECT]", "[DATABASE]")
var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]")
var request = &firestorepb.WriteRequest{
Database: formattedDatabase,
}
@@ -974,7 +974,7 @@ func TestFirestoreWriteError(t *testing.T) {
errCode := codes.PermissionDenied
mockFirestore.err = gstatus.Error(errCode, "test error")
var formattedDatabase string = DatabaseRootPath("[PROJECT]", "[DATABASE]")
var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]")
var request = &firestorepb.WriteRequest{
Database: formattedDatabase,
}
@@ -1011,7 +1011,7 @@ func TestFirestoreListen(t *testing.T) {
mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse)
var formattedDatabase string = DatabaseRootPath("[PROJECT]", "[DATABASE]")
var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]")
var request = &firestorepb.ListenRequest{
Database: formattedDatabase,
}
@@ -1050,7 +1050,7 @@ func TestFirestoreListenError(t *testing.T) {
errCode := codes.PermissionDenied
mockFirestore.err = gstatus.Error(errCode, "test error")
var formattedDatabase string = DatabaseRootPath("[PROJECT]", "[DATABASE]")
var formattedDatabase string = fmt.Sprintf("projects/%s/databases/%s", "[PROJECT]", "[DATABASE]")
var request = &firestorepb.ListenRequest{
Database: formattedDatabase,
}
@@ -1093,7 +1093,7 @@ func TestFirestoreListCollectionIds(t *testing.T) {
mockFirestore.resps = append(mockFirestore.resps[:0], expectedResponse)
var formattedParent string = AnyPathPath("[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var formattedParent string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var request = &firestorepb.ListCollectionIdsRequest{
Parent: formattedParent,
}
@@ -1132,7 +1132,7 @@ func TestFirestoreListCollectionIdsError(t *testing.T) {
errCode := codes.PermissionDenied
mockFirestore.err = gstatus.Error(errCode, "test error")
var formattedParent string = AnyPathPath("[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var formattedParent string = fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", "[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]")
var request = &firestorepb.ListCollectionIdsRequest{
Parent: formattedParent,
}
+78
View File
@@ -0,0 +1,78 @@
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package firestore
// DatabaseRootPath returns the path for the database root resource.
//
// Deprecated: Use
// fmt.Sprintf("projects/%s/databases/%s", project, database)
// instead.
func DatabaseRootPath(project, database string) string {
return "" +
"projects/" +
project +
"/databases/" +
database +
""
}
// DocumentRootPath returns the path for the document root resource.
//
// Deprecated: Use
// fmt.Sprintf("projects/%s/databases/%s/documents", project, database)
// instead.
func DocumentRootPath(project, database string) string {
return "" +
"projects/" +
project +
"/databases/" +
database +
"/documents" +
""
}
// DocumentPathPath returns the path for the document path resource.
//
// Deprecated: Use
// fmt.Sprintf("projects/%s/databases/%s/documents/%s", project, database, documentPath)
// instead.
func DocumentPathPath(project, database, documentPath string) string {
return "" +
"projects/" +
project +
"/databases/" +
database +
"/documents/" +
documentPath +
""
}
// AnyPathPath returns the path for the any path resource.
//
// Deprecated: Use
// fmt.Sprintf("projects/%s/databases/%s/documents/%s/%s", project, database, document, anyPath)
// instead.
func AnyPathPath(project, database, document, anyPath string) string {
return "" +
"projects/" +
project +
"/databases/" +
database +
"/documents/" +
document +
"/" +
anyPath +
""
}
+18 -2
View File
@@ -209,7 +209,7 @@ func (c *Client) Batch() *WriteBatch {
}
// commit calls the Commit RPC outside of a transaction.
func (c *Client) commit(ctx context.Context, ws []*pb.Write) (*WriteResult, error) {
func (c *Client) commit(ctx context.Context, ws []*pb.Write) ([]*WriteResult, error) {
if err := checkTransaction(ctx); err != nil {
return nil, err
}
@@ -224,7 +224,23 @@ func (c *Client) commit(ctx context.Context, ws []*pb.Write) (*WriteResult, erro
if len(res.WriteResults) == 0 {
return nil, errors.New("firestore: missing WriteResult")
}
return writeResultFromProto(res.WriteResults[0])
var wrs []*WriteResult
for _, pwr := range res.WriteResults {
wr, err := writeResultFromProto(pwr)
if err != nil {
return nil, err
}
wrs = append(wrs, wr)
}
return wrs, nil
}
func (c *Client) commit1(ctx context.Context, ws []*pb.Write) (*WriteResult, error) {
wrs, err := c.commit(ctx, ws)
if err != nil {
return nil, err
}
return wrs[0], nil
}
// A WriteResult is returned by methods that write documents.
+4 -9
View File
@@ -17,13 +17,10 @@ package firestore
import (
"testing"
"golang.org/x/net/context"
pb "google.golang.org/genproto/googleapis/firestore/v1beta1"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"cloud.google.com/go/internal/pretty"
pb "google.golang.org/genproto/googleapis/firestore/v1beta1"
"golang.org/x/net/context"
)
var testClient = &Client{
@@ -156,10 +153,8 @@ func TestGetAll(t *testing.T) {
t.Fatal(err)
}
}
if !testEqual(got, want) {
got.c = nil
want.c = nil
t.Errorf("#%d: got %+v, want %+v", i, pretty.Value(got), pretty.Value(want))
if diff := testDiff(got, want); diff != "" {
t.Errorf("#%d: got=--, want==++\n%s", i, diff)
}
}
}
-10
View File
@@ -17,7 +17,6 @@ package firestore
import (
"math/rand"
"os"
"reflect"
"sync"
"time"
@@ -47,15 +46,6 @@ type CollectionRef struct {
Query
}
func (c1 *CollectionRef) equal(c2 *CollectionRef) bool {
return c1.c == c2.c &&
c1.parentPath == c2.parentPath &&
c1.Parent.equal(c2.Parent) &&
c1.Path == c2.Path &&
c1.ID == c2.ID &&
reflect.DeepEqual(c1.Query, c2.Query)
}
func newTopLevelCollRef(c *Client, dbPath, id string) *CollectionRef {
return &CollectionRef{
c: c,
+238
View File
@@ -0,0 +1,238 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// A runner for the cross-language tests.
package firestore
import (
"encoding/json"
"fmt"
"io/ioutil"
"path"
"path/filepath"
"strings"
"testing"
pb "cloud.google.com/go/firestore/genproto"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
ts "github.com/golang/protobuf/ptypes/timestamp"
"golang.org/x/net/context"
fspb "google.golang.org/genproto/googleapis/firestore/v1beta1"
)
func TestCrossLanguageTests(t *testing.T) {
const dir = "testdata"
fis, err := ioutil.ReadDir(dir)
if err != nil {
t.Fatal(err)
}
n := 0
for _, fi := range fis {
if strings.HasSuffix(fi.Name(), ".textproto") {
runTestFromFile(t, filepath.Join(dir, fi.Name()))
n++
}
}
t.Logf("ran %d cross-language tests", n)
}
func runTestFromFile(t *testing.T, filename string) {
bytes, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatalf("%s: %v", filename, err)
}
var test pb.Test
if err := proto.UnmarshalText(string(bytes), &test); err != nil {
t.Fatalf("unmarshalling %s: %v", filename, err)
}
msg := fmt.Sprintf("%s (file %s)", test.Description, filepath.Base(filename))
runTest(t, msg, &test)
}
func runTest(t *testing.T, msg string, test *pb.Test) {
check := func(gotErr error, wantErr bool) {
if wantErr && gotErr == nil {
t.Errorf("%s: got nil, want error", msg)
} else if !wantErr && gotErr != nil {
t.Errorf("%s: %v", msg, gotErr)
}
}
ctx := context.Background()
c, srv := newMock(t)
switch tt := test.Test.(type) {
case *pb.Test_Get:
srv.addRPC(tt.Get.Request, &fspb.Document{
CreateTime: &ts.Timestamp{},
UpdateTime: &ts.Timestamp{},
})
ref := docRefFromPath(tt.Get.DocRefPath, c)
_, err := ref.Get(ctx)
if err != nil {
t.Errorf("%s: %v", msg, err)
return
}
// Checking response would just be testing the function converting a Document
// proto to a DocumentSnapshot, hence uninteresting.
case *pb.Test_Create:
srv.addRPC(tt.Create.Request, commitResponseForSet)
ref := docRefFromPath(tt.Create.DocRefPath, c)
data, err := convertData(tt.Create.JsonData)
if err != nil {
t.Errorf("%s: %v", msg, err)
return
}
_, err = ref.Create(ctx, data)
check(err, tt.Create.IsError)
case *pb.Test_Set:
srv.addRPC(tt.Set.Request, commitResponseForSet)
ref := docRefFromPath(tt.Set.DocRefPath, c)
data, err := convertData(tt.Set.JsonData)
if err != nil {
t.Errorf("%s: %v", msg, err)
return
}
var opts []SetOption
if tt.Set.Option != nil {
opts = []SetOption{convertSetOption(tt.Set.Option)}
}
_, err = ref.Set(ctx, data, opts...)
check(err, tt.Set.IsError)
case *pb.Test_Update:
// Ignore Update test because we only support UpdatePaths.
// Not to worry, every Update test has a corresponding UpdatePaths test.
case *pb.Test_UpdatePaths:
srv.addRPC(tt.UpdatePaths.Request, commitResponseForSet)
ref := docRefFromPath(tt.UpdatePaths.DocRefPath, c)
preconds := convertPrecondition(t, tt.UpdatePaths.Precondition)
paths := convertFieldPaths(tt.UpdatePaths.FieldPaths)
var ups []Update
for i, path := range paths {
jsonValue := tt.UpdatePaths.JsonValues[i]
var val interface{}
if err := json.Unmarshal([]byte(jsonValue), &val); err != nil {
t.Fatalf("%s: %q: %v", msg, jsonValue, err)
}
ups = append(ups, Update{
FieldPath: path,
Value: convertTestValue(val),
})
}
_, err := ref.Update(ctx, ups, preconds...)
check(err, tt.UpdatePaths.IsError)
case *pb.Test_Delete:
srv.addRPC(tt.Delete.Request, commitResponseForSet)
ref := docRefFromPath(tt.Delete.DocRefPath, c)
preconds := convertPrecondition(t, tt.Delete.Precondition)
_, err := ref.Delete(ctx, preconds...)
check(err, tt.Delete.IsError)
default:
t.Fatalf("unknown test type %T", tt)
}
}
func docRefFromPath(p string, c *Client) *DocumentRef {
return &DocumentRef{
Path: p,
ID: path.Base(p),
Parent: &CollectionRef{c: c},
}
}
func convertData(jsonData string) (map[string]interface{}, error) {
var m map[string]interface{}
if err := json.Unmarshal([]byte(jsonData), &m); err != nil {
return nil, err
}
return convertTestMap(m), nil
}
func convertTestMap(m map[string]interface{}) map[string]interface{} {
for k, v := range m {
m[k] = convertTestValue(v)
}
return m
}
func convertTestValue(v interface{}) interface{} {
switch v := v.(type) {
case string:
switch v {
case "ServerTimestamp":
return ServerTimestamp
case "Delete":
return Delete
default:
return v
}
case float64:
if v == float64(int(v)) {
return int(v)
}
return v
case []interface{}:
for i, e := range v {
v[i] = convertTestValue(e)
}
return v
case map[string]interface{}:
return convertTestMap(v)
default:
return v
}
}
func convertSetOption(opt *pb.SetOption) SetOption {
if opt.All {
return MergeAll
}
return Merge(convertFieldPaths(opt.Fields)...)
}
func convertFieldPaths(fps []*pb.FieldPath) []FieldPath {
var res []FieldPath
for _, fp := range fps {
res = append(res, fp.Field)
}
return res
}
func convertPrecondition(t *testing.T, fp *fspb.Precondition) []Precondition {
if fp == nil {
return nil
}
var pc Precondition
switch fp := fp.ConditionType.(type) {
case *fspb.Precondition_Exists:
pc = exists(fp.Exists)
case *fspb.Precondition_UpdateTime:
tm, err := ptypes.Timestamp(fp.UpdateTime)
if err != nil {
t.Fatal(err)
}
pc = LastUpdateTime(tm)
default:
t.Fatalf("unknown precondition type %T", fp)
}
return []Precondition{pc}
}
+7 -13
View File
@@ -118,16 +118,10 @@ document or creates a new one.
Population: 39.14,
})
To update some fields of an existing document, use UpdateMap, UpdateStruct or
UpdatePaths. For UpdateMap, the keys of the map specify which fields to change. The
others are untouched.
To update some fields of an existing document, use Update. It takes a list of
paths to update and their corresponding values.
_, err = ca.UpdateMap(ctx, map[string]interface{}{"pop": 39.2})
For UpdateStruct, you must explicitly provide the fields to update. The field names
must match exactly.
_, err = ca.UpdateStruct(ctx, []string{"pop"}, State{Population: 39.2})
_, err = ca.Update(ctx, []firestore.Update{{Path: "capital", Value: "Sacramento"}})
Use DocumentRef.Delete to delete a document.
@@ -143,7 +137,8 @@ write happen atomically with a single RPC.
if err != nil {
// TODO: Handle error.
}
_, err = ca.UpdateStruct(ctx, []string{"capital"}, State{Capital: "Sacramento"},
_, err = ca.Update(ctx,
[]firestore.Update{{Path: "capital", Value: "Sacramento"}},
firestore.LastUpdateTime(docsnap.UpdateTime))
Here we update a doc only if it hasn't changed since we read it.
@@ -157,7 +152,7 @@ atomically.
writeResults, err := client.Batch().
Create(ny, State{Capital: "Albany"}).
UpdateStruct(ca, []string{"capital"}, State{Capital: "Sacramento"}).
Update(ca, []firestore.Update{{Path: "capital", Value: "Sacramento"}}).
Delete(client.Doc("States/WestDakota")).
Commit(ctx)
@@ -205,8 +200,7 @@ read and write methods of the Transaction passed to it.
if err != nil {
return err
}
return tx.UpdateStruct(ny, []string{"pop"},
State{Population: pop.(float64) + 0.2})
return tx.Update(ny, []firestore.Update{{Path: "pop", Value: pop.(float64) + 0.2}})
})
if err != nil {
// TODO: Handle error.
+205 -259
View File
@@ -16,6 +16,7 @@ package firestore
import (
"errors"
"fmt"
"reflect"
"sort"
@@ -48,19 +49,14 @@ func newDocRef(parent *CollectionRef, id string) *DocumentRef {
}
}
func (d1 *DocumentRef) equal(d2 *DocumentRef) bool {
if d1 == nil || d2 == nil {
return d1 == d2
}
return d1.Parent.equal(d2.Parent) && d1.Path == d2.Path && d1.ID == d2.ID
}
// Collection returns a reference to sub-collection of this document.
func (d *DocumentRef) Collection(id string) *CollectionRef {
return newCollRefWithParent(d.Parent.c, d, id)
}
// Get retrieves the document. It returns an error if the document does not exist.
// Get retrieves the document. It returns a NotFound error if the document does not exist.
// You can test for NotFound with
// grpc.Code(err) == codes.NotFound
func (d *DocumentRef) Get(ctx context.Context) (*DocumentSnapshot, error) {
if err := checkTransaction(ctx); err != nil {
return nil, err
@@ -70,7 +66,6 @@ func (d *DocumentRef) Get(ctx context.Context) (*DocumentSnapshot, error) {
}
doc, err := d.Parent.c.c.GetDocument(withResourceHeader(ctx, d.Parent.c.path()),
&pb.GetDocumentRequest{Name: d.Path})
// TODO(jba): verify that GetDocument returns NOT_FOUND.
if err != nil {
return nil, err
}
@@ -114,11 +109,27 @@ func (d *DocumentRef) Get(ctx context.Context) (*DocumentSnapshot, error) {
// the field has the zero value, the server will populate the stored document with
// the time that the request is processed.
func (d *DocumentRef) Create(ctx context.Context, data interface{}) (*WriteResult, error) {
ws, err := d.newReplaceWrites(data, nil, Exists(false))
ws, err := d.newCreateWrites(data)
if err != nil {
return nil, err
}
return d.Parent.c.commit(ctx, ws)
return d.Parent.c.commit1(ctx, ws)
}
func (d *DocumentRef) newCreateWrites(data interface{}) ([]*pb.Write, error) {
if d == nil {
return nil, errNilDocRef
}
doc, serverTimestampPaths, err := toProtoDocument(data)
if err != nil {
return nil, err
}
doc.Name = d.Path
pc, err := exists(false).preconditionProto()
if err != nil {
return nil, err
}
return d.newUpdateWithTransform(doc, nil, pc, serverTimestampPaths, false), nil
}
// Set creates or overwrites the document with the given data. See DocumentRef.Create
@@ -126,148 +137,73 @@ func (d *DocumentRef) Create(ctx context.Context, data interface{}) (*WriteResul
// completely. Specify one of the Merge options to preserve an existing document's
// fields.
func (d *DocumentRef) Set(ctx context.Context, data interface{}, opts ...SetOption) (*WriteResult, error) {
ws, err := d.newReplaceWrites(data, opts, nil)
ws, err := d.newSetWrites(data, opts)
if err != nil {
return nil, err
}
return d.Parent.c.commit(ctx, ws)
return d.Parent.c.commit1(ctx, ws)
}
// Delete deletes the document. If the document doesn't exist, it does nothing
// and returns no error.
func (d *DocumentRef) Delete(ctx context.Context, preconds ...Precondition) (*WriteResult, error) {
ws, err := d.newDeleteWrites(preconds)
if err != nil {
return nil, err
}
return d.Parent.c.commit(ctx, ws)
}
func (d *DocumentRef) newReplaceWrites(data interface{}, opts []SetOption, p Precondition) ([]*pb.Write, error) {
func (d *DocumentRef) newSetWrites(data interface{}, opts []SetOption) ([]*pb.Write, error) {
if d == nil {
return nil, errNilDocRef
}
origFieldPaths, allPaths, err := processSetOptions(opts)
if err != nil {
return nil, err
if data == nil {
return nil, errors.New("firestore: nil document contents")
}
isMerge := len(origFieldPaths) > 0 || allPaths // was some Merge option specified?
doc, serverTimestampPaths, err := toProtoDocument(data)
if err != nil {
return nil, err
}
if len(origFieldPaths) > 0 {
// Keep only data fields corresponding to the given field paths.
doc.Fields = applyFieldPaths(doc.Fields, origFieldPaths, nil)
}
doc.Name = d.Path
var fieldPaths []FieldPath
if allPaths {
// MergeAll was passed. Check that the data is a map, and extract its field paths.
v := reflect.ValueOf(data)
if v.Kind() != reflect.Map {
return nil, errors.New("firestore: MergeAll can only be specified with map data")
}
fieldPaths = fieldPathsFromMap(v, nil)
} else if len(origFieldPaths) > 0 {
// Remove server timestamp paths that are not in the list of paths to merge.
// Note: this is technically O(n^2), but it is unlikely that there is more
// than one server timestamp path.
serverTimestampPaths = removePathsIf(serverTimestampPaths, func(fp FieldPath) bool {
return !fp.in(origFieldPaths)
})
// Remove server timestamp fields from fieldPaths. Those fields were removed
// from the document by toProtoDocument, so they should not be in the update
// mask.
// Note: this is technically O(n^2), but it is unlikely that there is
// more than one server timestamp path.
fieldPaths = removePathsIf(origFieldPaths, func(fp FieldPath) bool {
return fp.in(serverTimestampPaths)
})
// Check that all the remaining field paths in the merge option are in the document.
for _, fp := range fieldPaths {
if _, err := valueAtPath(fp, doc.Fields); err != nil {
return nil, err
}
}
}
var pc *pb.Precondition
if p != nil {
pc, err = p.preconditionProto()
if len(opts) == 0 { // Set without merge
doc, serverTimestampPaths, err := toProtoDocument(data)
if err != nil {
return nil, err
}
doc.Name = d.Path
return d.newUpdateWithTransform(doc, nil, nil, serverTimestampPaths, true), nil
}
var w *pb.Write
switch {
case len(fieldPaths) > 0:
// There are field paths, so we need an update mask.
sfps := toServiceFieldPaths(fieldPaths)
sort.Strings(sfps) // TODO(jba): make tests pass without this
w = &pb.Write{
Operation: &pb.Write_Update{doc},
UpdateMask: &pb.DocumentMask{FieldPaths: sfps},
CurrentDocument: pc,
}
case isMerge && pc != nil:
// There were field paths, but they all got removed.
// The write does nothing but enforce the precondition.
w = &pb.Write{CurrentDocument: pc}
case !isMerge && (pc != nil || doc.Fields != nil):
// Set without merge, so no update mask.
w = &pb.Write{
Operation: &pb.Write_Update{doc},
CurrentDocument: pc,
}
// Set with merge.
// This is just like Update, except for the existence precondition.
// So we turn data into a list of (FieldPath, interface{}) pairs (fpv's), as we do
// for Update.
fieldPaths, allPaths, err := processSetOptions(opts)
if err != nil {
return nil, err
}
return d.writeWithTransform(w, serverTimestampPaths), nil
}
// Create a new map that contains only the field paths in fps.
func applyFieldPaths(fields map[string]*pb.Value, fps []FieldPath, root FieldPath) map[string]*pb.Value {
r := map[string]*pb.Value{}
for k, v := range fields {
kpath := root.with(k)
if kpath.in(fps) {
r[k] = v
} else if mv := v.GetMapValue(); mv != nil {
if m2 := applyFieldPaths(mv.Fields, fps, kpath); m2 != nil {
r[k] = &pb.Value{&pb.Value_MapValue{&pb.MapValue{m2}}}
var fpvs []fpv
v := reflect.ValueOf(data)
if allPaths {
// Set with MergeAll. Collect all the leaves of the map.
if v.Kind() != reflect.Map {
return nil, errors.New("firestore: MergeAll can only be specified with map data")
}
fpvsFromData(v, nil, &fpvs)
} else {
// Set with merge paths. Collect only the values at the given paths.
for _, fp := range fieldPaths {
val, err := getAtPath(v, fp)
if err != nil {
return nil, err
}
fpvs = append(fpvs, fpv{fp, val})
}
}
if len(r) == 0 {
return nil
}
return r
return d.fpvsToWrites(fpvs, nil)
}
func fieldPathsFromMap(vmap reflect.Value, prefix FieldPath) []FieldPath {
// vmap is a map and its keys are strings.
// Each map key denotes a field; no splitting or escaping.
var fps []FieldPath
for _, k := range vmap.MapKeys() {
v := vmap.MapIndex(k)
fp := prefix.with(k.String())
if vm := extractMap(v); vm.IsValid() {
fps = append(fps, fieldPathsFromMap(vm, fp)...)
} else if v.Interface() != ServerTimestamp {
// ServerTimestamp fields do not go into the update mask.
fps = append(fps, fp)
}
}
return fps
}
func extractMap(v reflect.Value) reflect.Value {
// fpvsFromData converts v into a list of (FieldPath, value) pairs.
func fpvsFromData(v reflect.Value, prefix FieldPath, fpvs *[]fpv) {
switch v.Kind() {
case reflect.Map:
return v
for _, k := range v.MapKeys() {
fpvsFromData(v.MapIndex(k), prefix.with(k.String()), fpvs)
}
case reflect.Interface:
return extractMap(v.Elem())
fpvsFromData(v.Elem(), prefix, fpvs)
default:
return reflect.Value{}
var val interface{}
if v.IsValid() {
val = v.Interface()
}
*fpvs = append(*fpvs, fpv{prefix, val})
}
}
@@ -283,6 +219,16 @@ func removePathsIf(fps []FieldPath, pred func(FieldPath) bool) []FieldPath {
return result
}
// Delete deletes the document. If the document doesn't exist, it does nothing
// and returns no error.
func (d *DocumentRef) Delete(ctx context.Context, preconds ...Precondition) (*WriteResult, error) {
ws, err := d.newDeleteWrites(preconds)
if err != nil {
return nil, err
}
return d.Parent.c.commit1(ctx, ws)
}
func (d *DocumentRef) newDeleteWrites(preconds []Precondition) ([]*pb.Write, error) {
if d == nil {
return nil, errNilDocRef
@@ -297,101 +243,119 @@ func (d *DocumentRef) newDeleteWrites(preconds []Precondition) ([]*pb.Write, err
}}, nil
}
func (d *DocumentRef) newUpdateMapWrites(data map[string]interface{}, preconds []Precondition) ([]*pb.Write, error) {
// Collect all the (top-level) keys map; they will comprise the update mask.
// Also, translate the map into a sequence of FieldPathUpdates.
var fps []FieldPath
var fpus []FieldPathUpdate
for k, v := range data {
fp, err := parseDotSeparatedString(k)
func (d *DocumentRef) newUpdatePathWrites(updates []Update, preconds []Precondition) ([]*pb.Write, error) {
if len(updates) == 0 {
return nil, errors.New("firestore: no paths to update")
}
var fpvs []fpv
for _, u := range updates {
v, err := u.process()
if err != nil {
return nil, err
}
fps = append(fps, fp)
fpus = append(fpus, FieldPathUpdate{Path: fp, Value: v})
}
// Check that there are no duplicate field paths, and that no field
// path is a prefix of another.
if err := checkNoDupOrPrefix(fps); err != nil {
return nil, err
}
// Re-create the map from the field paths and their corresponding values. A field path
// with a Delete value will not appear in the map but it will appear in the
// update mask, which will cause it to be deleted.
m := createMapFromFieldPathUpdates(fpus)
return d.newUpdateWrites(m, fps, preconds)
}
func (d *DocumentRef) newUpdateStructWrites(fieldPaths []string, data interface{}, preconds []Precondition) ([]*pb.Write, error) {
if !isStructOrStructPtr(data) {
return nil, errors.New("firestore: data is not struct or struct pointer")
}
fps, err := parseDotSeparatedStrings(fieldPaths)
if err != nil {
return nil, err
}
if err := checkNoDupOrPrefix(fps); err != nil {
return nil, err
}
return d.newUpdateWrites(data, fps, preconds)
}
func (d *DocumentRef) newUpdatePathWrites(data []FieldPathUpdate, preconds []Precondition) ([]*pb.Write, error) {
var fps []FieldPath
for _, fpu := range data {
if err := fpu.Path.validate(); err != nil {
return nil, err
}
fps = append(fps, fpu.Path)
}
if err := checkNoDupOrPrefix(fps); err != nil {
return nil, err
}
m := createMapFromFieldPathUpdates(data)
return d.newUpdateWrites(m, fps, preconds)
}
// newUpdateWrites creates Write operations for an update.
func (d *DocumentRef) newUpdateWrites(data interface{}, fieldPaths []FieldPath, preconds []Precondition) ([]*pb.Write, error) {
if len(fieldPaths) == 0 {
return nil, errors.New("firestore: no paths to update")
}
if d == nil {
return nil, errNilDocRef
fpvs = append(fpvs, v)
}
pc, err := processPreconditionsForUpdate(preconds)
if err != nil {
return nil, err
}
doc, serverTimestampPaths, err := toProtoDocument(data)
if err != nil {
return d.fpvsToWrites(fpvs, pc)
}
func (d *DocumentRef) fpvsToWrites(fpvs []fpv, pc *pb.Precondition) ([]*pb.Write, error) {
// Make sure there are no duplications or prefixes among the field paths.
var fps []FieldPath
for _, fpv := range fpvs {
fps = append(fps, fpv.fieldPath)
}
if err := checkNoDupOrPrefix(fps); err != nil {
return nil, err
}
sfps := toServiceFieldPaths(fieldPaths)
doc.Name = d.Path
return d.writeWithTransform(&pb.Write{
Operation: &pb.Write_Update{doc},
UpdateMask: &pb.DocumentMask{FieldPaths: sfps},
CurrentDocument: pc,
}, serverTimestampPaths), nil
// Process each fpv.
var updatePaths, transformPaths []FieldPath
doc := &pb.Document{
Name: d.Path,
Fields: map[string]*pb.Value{},
}
for _, fpv := range fpvs {
switch fpv.value {
case Delete:
// Send the field path without a corresponding value.
updatePaths = append(updatePaths, fpv.fieldPath)
case ServerTimestamp:
// Use the path in a transform operation.
transformPaths = append(transformPaths, fpv.fieldPath)
default:
updatePaths = append(updatePaths, fpv.fieldPath)
// Convert the value to a proto and put it into the document.
v := reflect.ValueOf(fpv.value)
pv, sawServerTimestamp, err := toProtoValue(v)
if err != nil {
return nil, err
}
setAtPath(doc.Fields, fpv.fieldPath, pv)
// Also accumulate any serverTimestamp values within the value.
if sawServerTimestamp {
stps, err := extractTransformPaths(v, nil)
if err != nil {
return nil, err
}
for _, p := range stps {
transformPaths = append(transformPaths, fpv.fieldPath.concat(p))
}
}
}
}
return d.newUpdateWithTransform(doc, updatePaths, pc, transformPaths, false), nil
}
var requestTimeTransform = &pb.DocumentTransform_FieldTransform_SetToServerValue{
pb.DocumentTransform_FieldTransform_REQUEST_TIME,
}
func (d *DocumentRef) writeWithTransform(w *pb.Write, serverTimestampFieldPaths []FieldPath) []*pb.Write {
// newUpdateWithTransform constructs operations for a commit. Most generally, it
// returns an update operation followed by a transform.
//
// If there are no serverTimestampPaths, the transform is omitted.
//
// If doc.Fields is empty, there are no updatePaths, and there is no precondition,
// the update is omitted, unless updateOnEmpty is true.
func (d *DocumentRef) newUpdateWithTransform(doc *pb.Document, updatePaths []FieldPath, pc *pb.Precondition, serverTimestampPaths []FieldPath, updateOnEmpty bool) []*pb.Write {
// Remove server timestamp fields from updatePaths. Those fields were removed
// from the document by toProtoDocument, so they should not be in the update
// mask.
// Note: this is technically O(n^2), but it is unlikely that there is
// more than one server timestamp path.
updatePaths = removePathsIf(updatePaths, func(fp FieldPath) bool {
return fp.in(serverTimestampPaths)
})
var ws []*pb.Write
if w != nil {
if updateOnEmpty || len(doc.Fields) > 0 ||
len(updatePaths) > 0 || (pc != nil && len(serverTimestampPaths) == 0) {
var mask *pb.DocumentMask
if len(updatePaths) > 0 {
sfps := toServiceFieldPaths(updatePaths)
sort.Strings(sfps) // TODO(jba): make tests pass without this
mask = &pb.DocumentMask{FieldPaths: sfps}
}
w := &pb.Write{
Operation: &pb.Write_Update{doc},
UpdateMask: mask,
CurrentDocument: pc,
}
ws = append(ws, w)
pc = nil // If the precondition is in the write, we don't need it in the transform.
}
if len(serverTimestampFieldPaths) > 0 {
ws = append(ws, d.newTransform(serverTimestampFieldPaths))
if len(serverTimestampPaths) > 0 || pc != nil {
ws = append(ws, d.newTransform(serverTimestampPaths, pc))
}
return ws
}
func (d *DocumentRef) newTransform(serverTimestampFieldPaths []FieldPath) *pb.Write {
func (d *DocumentRef) newTransform(serverTimestampFieldPaths []FieldPath, pc *pb.Precondition) *pb.Write {
sort.Sort(byPath(serverTimestampFieldPaths)) // TODO(jba): make tests pass without this
var fts []*pb.DocumentTransform_FieldTransform
for _, p := range serverTimestampFieldPaths {
@@ -408,17 +372,18 @@ func (d *DocumentRef) newTransform(serverTimestampFieldPaths []FieldPath) *pb.Wr
// TODO(jba): should the transform have the same preconditions as the write?
},
},
CurrentDocument: pc,
}
}
type sentinel int
const (
// Delete is used as a value in a call to UpdateMap to indicate that the
// corresponding key should be deleted.
// Delete is used as a value in a call to Update or Set with merge to indicate
// that the corresponding key should be deleted.
Delete sentinel = iota
// ServerTimestamp is used as a value in a call to UpdateMap to indicate that the
// ServerTimestamp is used as a value in a call to Update to indicate that the
// key's value should be set to the time at which the server processed
// the request.
ServerTimestamp
@@ -435,33 +400,6 @@ func (s sentinel) String() string {
}
}
// UpdateMap updates the document using the given data. Map keys replace the stored
// values, but other fields of the stored document are untouched.
// See DocumentRef.Create for acceptable map values.
//
// If a map key is a multi-element field path, like "a.b", then only key "b" of
// the map value at "a" is changed; the rest of the map is preserved.
// For example, if the stored data is
// {"a": {"b": 1, "c": 2}}
// then
// UpdateMap({"a": {"b": 3}}) => {"a": {"b": 3}}
// while
// UpdateMap({"a.b": 3}) => {"a": {"b": 3, "c": 2}}
//
// To delete a key, specify it in the input with a value of firestore.Delete.
//
// Field paths expressed as map keys must not contain any of the runes "˜*/[]".
// Use UpdatePaths instead for such paths.
//
// UpdateMap returns an error if the document does not exist.
func (d *DocumentRef) UpdateMap(ctx context.Context, data map[string]interface{}, preconds ...Precondition) (*WriteResult, error) {
ws, err := d.newUpdateMapWrites(data, preconds)
if err != nil {
return nil, err
}
return d.Parent.c.commit(ctx, ws)
}
func isStructOrStructPtr(x interface{}) bool {
v := reflect.ValueOf(x)
if v.Kind() == reflect.Struct {
@@ -473,42 +411,50 @@ func isStructOrStructPtr(x interface{}) bool {
return false
}
// UpdateStruct updates the given field paths of the stored document from the fields
// of data, which must be a struct or a pointer to a struct. Other fields of the
// stored document are untouched.
// See DocumentRef.Create for the acceptable values of the struct's fields.
// An Update describes an update to a value referred to by a path.
// An Update should have either a non-empty Path or a non-empty FieldPath,
// but not both.
//
// Each element of fieldPaths is a single field or a dot-separated sequence of
// fields, none of which contain the runes "˜*/[]".
//
// If an element of fieldPaths does not have a corresponding field in the struct,
// that key is deleted from the stored document.
//
// UpdateStruct returns an error if the document does not exist.
func (d *DocumentRef) UpdateStruct(ctx context.Context, fieldPaths []string, data interface{}, preconds ...Precondition) (*WriteResult, error) {
ws, err := d.newUpdateStructWrites(fieldPaths, data, preconds)
if err != nil {
return nil, err
}
return d.Parent.c.commit(ctx, ws)
}
// A FieldPathUpdate describes an update to a value referred to by a FieldPath.
// See DocumentRef.Create for acceptable values.
// To delete a field, specify firestore.Delete as the value.
type FieldPathUpdate struct {
Path FieldPath
Value interface{}
type Update struct {
Path string // Will be split on dots, and must not contain any of "˜*/[]".
FieldPath FieldPath
Value interface{}
}
// UpdatePaths updates the document using the given data. The values at the given
// An fpv is a pair of validated FieldPath and value.
type fpv struct {
fieldPath FieldPath
value interface{}
}
func (u *Update) process() (fpv, error) {
if (u.Path != "") == (u.FieldPath != nil) {
return fpv{}, fmt.Errorf("firestore: update %+v should have exactly one of Path or FieldPath", u)
}
fp := u.FieldPath
var err error
if fp == nil {
fp, err = parseDotSeparatedString(u.Path)
if err != nil {
return fpv{}, err
}
}
if err := fp.validate(); err != nil {
return fpv{}, err
}
return fpv{fp, u.Value}, nil
}
// Update updates the document. The values at the given
// field paths are replaced, but other fields of the stored document are untouched.
func (d *DocumentRef) UpdatePaths(ctx context.Context, data []FieldPathUpdate, preconds ...Precondition) (*WriteResult, error) {
ws, err := d.newUpdatePathWrites(data, preconds)
func (d *DocumentRef) Update(ctx context.Context, updates []Update, preconds ...Precondition) (*WriteResult, error) {
ws, err := d.newUpdatePathWrites(updates, preconds)
if err != nil {
return nil, err
}
return d.Parent.c.commit(ctx, ws)
return d.Parent.c.commit1(ctx, ws)
}
// Collections returns an interator over the immediate sub-collections of the document.
+118 -563
View File
@@ -22,7 +22,6 @@ import (
pb "google.golang.org/genproto/googleapis/firestore/v1beta1"
"github.com/golang/protobuf/proto"
"golang.org/x/net/context"
"google.golang.org/genproto/googleapis/type/latlng"
"google.golang.org/grpc"
@@ -76,246 +75,34 @@ func TestDocGet(t *testing.T) {
}
func TestDocSet(t *testing.T) {
// Most tests for Set are in the cross-language tests.
ctx := context.Background()
c, srv := newMock(t)
for _, test := range []struct {
desc string
data interface{}
opt SetOption
write map[string]*pb.Value
mask []string
transform []string
isErr bool
}{
{
desc: "Set with no options",
data: map[string]interface{}{"a": 1},
write: map[string]*pb.Value{"a": intval(1)},
},
{
desc: "Merge with a field",
data: map[string]interface{}{"a": 1, "b": 2},
opt: Merge("a"),
write: map[string]*pb.Value{"a": intval(1)},
mask: []string{"a"},
},
{
desc: "Merge field is not a leaf",
data: map[string]interface{}{
"a": map[string]interface{}{"b": 1, "c": 2},
"d": 3,
},
opt: Merge("a"),
write: map[string]*pb.Value{"a": mapval(map[string]*pb.Value{
"b": intval(1),
"c": intval(2),
})},
mask: []string{"a"},
},
{
desc: "MergeAll",
data: map[string]interface{}{"a": 1, "b": 2},
opt: MergeAll,
write: map[string]*pb.Value{"a": intval(1), "b": intval(2)},
mask: []string{"a", "b"},
},
{
desc: "MergeAll with nested fields",
data: map[string]interface{}{
"a": 1,
"b": map[string]interface{}{"c": 2},
},
opt: MergeAll,
write: map[string]*pb.Value{
"a": intval(1),
"b": mapval(map[string]*pb.Value{"c": intval(2)}),
},
mask: []string{"a", "b.c"},
},
{
desc: "Merge with FieldPaths",
data: map[string]interface{}{"*": map[string]interface{}{"~": true}},
opt: MergePaths([]string{"*", "~"}),
write: map[string]*pb.Value{
"*": mapval(map[string]*pb.Value{
"~": boolval(true),
}),
},
mask: []string{"`*`.`~`"},
},
{
desc: "Merge with a struct and FieldPaths",
data: struct {
A map[string]bool `firestore:"*"`
}{A: map[string]bool{"~": true}},
opt: MergePaths([]string{"*", "~"}),
write: map[string]*pb.Value{
"*": mapval(map[string]*pb.Value{
"~": boolval(true),
}),
},
mask: []string{"`*`.`~`"},
},
{
desc: "a ServerTimestamp field becomes a transform",
data: map[string]interface{}{"a": 1, "b": ServerTimestamp},
write: map[string]*pb.Value{"a": intval(1)},
transform: []string{"b"},
},
{
desc: "a ServerTimestamp alone",
data: map[string]interface{}{"b": ServerTimestamp},
write: nil,
transform: []string{"b"},
},
{
desc: "a ServerTimestamp alone with a path",
data: map[string]interface{}{"b": ServerTimestamp},
opt: MergePaths([]string{"b"}),
write: nil,
transform: []string{"b"},
},
{
desc: "nested ServerTimestamp field",
data: map[string]interface{}{
"a": 1,
"b": map[string]interface{}{"c": ServerTimestamp},
},
write: map[string]*pb.Value{"a": intval(1)},
transform: []string{"b.c"},
},
{
desc: "multiple ServerTimestamp fields",
data: map[string]interface{}{
"a": 1,
"b": ServerTimestamp,
"c": map[string]interface{}{"d": ServerTimestamp},
},
write: map[string]*pb.Value{"a": intval(1)},
transform: []string{"b", "c.d"},
},
{
desc: "ServerTimestamp with MergeAll",
data: map[string]interface{}{"a": 1, "b": ServerTimestamp},
opt: MergeAll,
write: map[string]*pb.Value{"a": intval(1)},
mask: []string{"a"},
transform: []string{"b"},
},
{
desc: "ServerTimestamp with Merge of both fields",
data: map[string]interface{}{"a": 1, "b": ServerTimestamp},
opt: Merge("a", "b"),
write: map[string]*pb.Value{"a": intval(1)},
mask: []string{"a"},
transform: []string{"b"},
},
{
desc: "If is ServerTimestamp not in Merge, no transform",
data: map[string]interface{}{"a": 1, "b": ServerTimestamp},
opt: Merge("a"),
write: map[string]*pb.Value{"a": intval(1)},
mask: []string{"a"},
},
{
desc: "If no ordinary values in Merge, no write",
data: map[string]interface{}{"a": 1, "b": ServerTimestamp},
opt: Merge("b"),
transform: []string{"b"},
},
{
desc: "Merge fields must all be present in data.",
data: map[string]interface{}{"a": 1},
opt: Merge("b", "a"),
isErr: true,
},
{
desc: "MergeAll cannot be used with structs",
data: struct{ A int }{A: 1},
opt: MergeAll,
isErr: true,
},
{
desc: "Delete cannot appear in data",
data: map[string]interface{}{"a": 1, "b": Delete},
isErr: true,
},
{
desc: "Delete cannot even appear in an unmerged field (allow?)",
data: map[string]interface{}{"a": 1, "b": Delete},
opt: Merge("a"),
isErr: true,
},
} {
srv.reset()
if !test.isErr {
var writes []*pb.Write
if test.write != nil || test.mask != nil {
w := &pb.Write{}
if test.write != nil {
w.Operation = &pb.Write_Update{
Update: &pb.Document{
Name: "projects/projectID/databases/(default)/documents/C/d",
Fields: test.write,
},
}
}
if test.mask != nil {
w.UpdateMask = &pb.DocumentMask{FieldPaths: test.mask}
}
writes = append(writes, w)
}
if test.transform != nil {
var fts []*pb.DocumentTransform_FieldTransform
for _, p := range test.transform {
fts = append(fts, &pb.DocumentTransform_FieldTransform{
FieldPath: p,
TransformType: requestTimeTransform,
})
}
writes = append(writes, &pb.Write{
Operation: &pb.Write_Transform{
&pb.DocumentTransform{
Document: "projects/projectID/databases/(default)/documents/C/d",
FieldTransforms: fts,
doc := c.Collection("C").Doc("d")
// Merge with a struct and FieldPaths.
srv.addRPC(&pb.CommitRequest{
Database: "projects/projectID/databases/(default)",
Writes: []*pb.Write{
{
Operation: &pb.Write_Update{
Update: &pb.Document{
Name: "projects/projectID/databases/(default)/documents/C/d",
Fields: map[string]*pb.Value{
"*": mapval(map[string]*pb.Value{
"~": boolval(true),
}),
},
},
})
}
srv.addRPC(&pb.CommitRequest{
Database: "projects/projectID/databases/(default)",
Writes: writes,
}, commitResponseForSet)
}
var opts []SetOption
if test.opt != nil {
opts = []SetOption{test.opt}
}
wr, err := c.Collection("C").Doc("d").Set(ctx, test.data, opts...)
if test.isErr && err == nil {
t.Errorf("%s: got nil, want error")
continue
}
if !test.isErr && err != nil {
t.Errorf("%s: %v", test.desc, err)
continue
}
if err == nil && !testEqual(wr, writeResultForSet) {
t.Errorf("%s: got %v, want %v", test.desc, wr, writeResultForSet)
}
}
}
func TestDocCreate(t *testing.T) {
ctx := context.Background()
c, srv := newMock(t)
wantReq := commitRequestForSet()
wantReq.Writes[0].CurrentDocument = &pb.Precondition{
ConditionType: &pb.Precondition_Exists{false},
}
srv.addRPC(wantReq, commitResponseForSet)
wr, err := c.Collection("C").Doc("d").Create(ctx, testData)
},
UpdateMask: &pb.DocumentMask{FieldPaths: []string{"`*`.`~`"}},
},
},
}, commitResponseForSet)
data := struct {
A map[string]bool `firestore:"*"`
}{A: map[string]bool{"~": true}}
wr, err := doc.Set(ctx, data, Merge([]string{"*", "~"}))
if err != nil {
t.Fatal(err)
}
@@ -323,8 +110,20 @@ func TestDocCreate(t *testing.T) {
t.Errorf("got %v, want %v", wr, writeResultForSet)
}
// MergeAll cannot be used with structs.
_, err = doc.Set(ctx, data, MergeAll)
if err == nil {
t.Errorf("got nil, want error")
}
}
func TestDocCreate(t *testing.T) {
// Verify creation with structs. In particular, make sure zero values
// are handled well.
// Other tests for Create are handled by the cross-language tests.
ctx := context.Background()
c, srv := newMock(t)
type create struct {
Time time.Time
Bytes []byte
@@ -353,7 +152,7 @@ func TestDocCreate(t *testing.T) {
},
commitResponseForSet,
)
_, err = c.Collection("C").Doc("d").Create(ctx, &create{})
_, err := c.Collection("C").Doc("d").Create(ctx, &create{})
if err != nil {
t.Fatal(err)
}
@@ -381,351 +180,59 @@ func TestDocDelete(t *testing.T) {
}
}
func TestDocDeleteLastUpdateTime(t *testing.T) {
ctx := context.Background()
c, srv := newMock(t)
wantReq := &pb.CommitRequest{
Database: "projects/projectID/databases/(default)",
Writes: []*pb.Write{
{
Operation: &pb.Write_Delete{"projects/projectID/databases/(default)/documents/C/d"},
CurrentDocument: &pb.Precondition{
ConditionType: &pb.Precondition_UpdateTime{aTimestamp2},
},
}},
}
srv.addRPC(wantReq, commitResponseForSet)
wr, err := c.Collection("C").Doc("d").Delete(ctx, LastUpdateTime(aTime2))
if err != nil {
t.Fatal(err)
}
if !testEqual(wr, writeResultForSet) {
t.Errorf("got %+v, want %+v", wr, writeResultForSet)
}
}
var (
testData = map[string]interface{}{"a": 1}
testFields = map[string]*pb.Value{"a": intval(1)}
)
func TestUpdateMap(t *testing.T) {
ctx := context.Background()
c, srv := newMock(t)
// Update is tested by the cross-language tests.
func TestFPVsFromData(t *testing.T) {
type S struct{ X int }
for _, test := range []struct {
data map[string]interface{}
wantFields map[string]*pb.Value
wantPaths []string
in interface{}
want []fpv
}{
{
data: map[string]interface{}{"a.b": 1},
wantFields: map[string]*pb.Value{
"a": mapval(map[string]*pb.Value{"b": intval(1)}),
},
wantPaths: []string{"a.b"},
in: nil,
want: []fpv{{nil, nil}},
},
{
data: map[string]interface{}{
in: map[string]interface{}{"a": nil},
want: []fpv{{[]string{"a"}, nil}},
},
{
in: map[string]interface{}{"a": 1},
want: []fpv{{[]string{"a"}, 1}},
},
{
in: map[string]interface{}{
"a": 1,
"b": Delete,
"b": map[string]interface{}{"c": 2},
},
wantFields: map[string]*pb.Value{"a": intval(1)},
wantPaths: []string{"a", "b"},
want: []fpv{{[]string{"a"}, 1}, {[]string{"b", "c"}, 2}},
},
{
in: map[string]interface{}{"s": &S{X: 3}},
want: []fpv{{[]string{"s"}, &S{X: 3}}},
},
} {
srv.reset()
wantReq := &pb.CommitRequest{
Database: "projects/projectID/databases/(default)",
Writes: []*pb.Write{{
Operation: &pb.Write_Update{
Update: &pb.Document{
Name: "projects/projectID/databases/(default)/documents/C/d",
Fields: test.wantFields,
}},
UpdateMask: &pb.DocumentMask{FieldPaths: test.wantPaths},
CurrentDocument: &pb.Precondition{
ConditionType: &pb.Precondition_Exists{true},
},
}},
}
// Sort update masks, because map iteration order is random.
sort.Strings(wantReq.Writes[0].UpdateMask.FieldPaths)
srv.addRPCAdjust(wantReq, commitResponseForSet, func(gotReq proto.Message) {
sort.Strings(gotReq.(*pb.CommitRequest).Writes[0].UpdateMask.FieldPaths)
})
wr, err := c.Collection("C").Doc("d").UpdateMap(ctx, test.data)
if err != nil {
t.Fatal(err)
}
if !testEqual(wr, writeResultForSet) {
t.Errorf("%v:\ngot %+v, want %+v", test.data, wr, writeResultForSet)
}
}
}
func TestUpdateMapLastUpdateTime(t *testing.T) {
ctx := context.Background()
c, srv := newMock(t)
wantReq := &pb.CommitRequest{
Database: "projects/projectID/databases/(default)",
Writes: []*pb.Write{{
Operation: &pb.Write_Update{
Update: &pb.Document{
Name: "projects/projectID/databases/(default)/documents/C/d",
Fields: map[string]*pb.Value{"a": intval(1)},
}},
UpdateMask: &pb.DocumentMask{FieldPaths: []string{"a"}},
CurrentDocument: &pb.Precondition{
ConditionType: &pb.Precondition_UpdateTime{aTimestamp2},
},
}},
}
srv.addRPC(wantReq, commitResponseForSet)
wr, err := c.Collection("C").Doc("d").UpdateMap(ctx, map[string]interface{}{"a": 1}, LastUpdateTime(aTime2))
if err != nil {
t.Fatal(err)
}
if !testEqual(wr, writeResultForSet) {
t.Errorf("got %v, want %v", wr, writeResultForSet)
}
}
func TestUpdateMapErrors(t *testing.T) {
ctx := context.Background()
c, _ := newMock(t)
for _, in := range []map[string]interface{}{
nil, // no paths
map[string]interface{}{"a~b": 1}, // invalid character
map[string]interface{}{"a..b": 1}, // empty path component
map[string]interface{}{"a.b": 1, "a": 2}, // prefix
} {
_, err := c.Collection("C").Doc("d").UpdateMap(ctx, in)
if err == nil {
t.Errorf("%v: got nil, want error", in)
}
}
}
func TestUpdateStruct(t *testing.T) {
type update struct{ A int }
c, srv := newMock(t)
wantReq := &pb.CommitRequest{
Database: "projects/projectID/databases/(default)",
Writes: []*pb.Write{{
Operation: &pb.Write_Update{
Update: &pb.Document{
Name: "projects/projectID/databases/(default)/documents/C/d",
Fields: map[string]*pb.Value{"A": intval(2)},
},
},
UpdateMask: &pb.DocumentMask{FieldPaths: []string{"A", "b.c"}},
CurrentDocument: &pb.Precondition{
ConditionType: &pb.Precondition_Exists{true},
},
}},
}
srv.addRPC(wantReq, commitResponseForSet)
wr, err := c.Collection("C").Doc("d").
UpdateStruct(context.Background(), []string{"A", "b.c"}, &update{A: 2})
if err != nil {
t.Fatal(err)
}
if !testEqual(wr, writeResultForSet) {
t.Errorf("got %+v, want %+v", wr, writeResultForSet)
}
}
func TestUpdateStructErrors(t *testing.T) {
type update struct{ A int }
ctx := context.Background()
c, _ := newMock(t)
doc := c.Collection("C").Doc("d")
for _, test := range []struct {
desc string
fields []string
data interface{}
}{
{
desc: "data is not a struct or *struct",
data: map[string]interface{}{"a": 1},
},
{
desc: "no paths",
fields: nil,
data: update{},
},
{
desc: "empty",
fields: []string{""},
data: update{},
},
{
desc: "empty component",
fields: []string{"a.b..c"},
data: update{},
},
{
desc: "duplicate field",
fields: []string{"a", "b", "c", "a"},
data: update{},
},
{
desc: "invalid character",
fields: []string{"a", "b]"},
data: update{},
},
{
desc: "prefix",
fields: []string{"a", "b", "c", "b.c"},
data: update{},
},
} {
_, err := doc.UpdateStruct(ctx, test.fields, test.data)
if err == nil {
t.Errorf("%s: got nil, want error", test.desc)
}
}
}
func TestUpdatePaths(t *testing.T) {
ctx := context.Background()
c, srv := newMock(t)
for _, test := range []struct {
data []FieldPathUpdate
wantFields map[string]*pb.Value
wantPaths []string
}{
{
data: []FieldPathUpdate{
{Path: []string{"*", "~"}, Value: 1},
{Path: []string{"*", "/"}, Value: 2},
},
wantFields: map[string]*pb.Value{
"*": mapval(map[string]*pb.Value{
"~": intval(1),
"/": intval(2),
}),
},
wantPaths: []string{"`*`.`~`", "`*`.`/`"},
},
{
data: []FieldPathUpdate{
{Path: []string{"*"}, Value: 1},
{Path: []string{"]"}, Value: Delete},
},
wantFields: map[string]*pb.Value{"*": intval(1)},
wantPaths: []string{"`*`", "`]`"},
},
} {
srv.reset()
wantReq := &pb.CommitRequest{
Database: "projects/projectID/databases/(default)",
Writes: []*pb.Write{{
Operation: &pb.Write_Update{
Update: &pb.Document{
Name: "projects/projectID/databases/(default)/documents/C/d",
Fields: test.wantFields,
}},
UpdateMask: &pb.DocumentMask{FieldPaths: test.wantPaths},
CurrentDocument: &pb.Precondition{
ConditionType: &pb.Precondition_Exists{true},
},
}},
}
// Sort update masks, because map iteration order is random.
sort.Strings(wantReq.Writes[0].UpdateMask.FieldPaths)
srv.addRPCAdjust(wantReq, commitResponseForSet, func(gotReq proto.Message) {
sort.Strings(gotReq.(*pb.CommitRequest).Writes[0].UpdateMask.FieldPaths)
})
wr, err := c.Collection("C").Doc("d").UpdatePaths(ctx, test.data)
if err != nil {
t.Fatal(err)
}
if !testEqual(wr, writeResultForSet) {
t.Errorf("%v:\ngot %+v, want %+v", test.data, wr, writeResultForSet)
}
}
}
func TestUpdatePathsErrors(t *testing.T) {
fpu := func(s ...string) FieldPathUpdate { return FieldPathUpdate{Path: s} }
ctx := context.Background()
c, _ := newMock(t)
doc := c.Collection("C").Doc("d")
for _, test := range []struct {
desc string
data []FieldPathUpdate
}{
{"no updates", nil},
{"empty", []FieldPathUpdate{fpu("")}},
{"empty component", []FieldPathUpdate{fpu("*", "")}},
{"duplicate field", []FieldPathUpdate{fpu("~"), fpu("*"), fpu("~")}},
{"prefix", []FieldPathUpdate{fpu("*", "a"), fpu("b"), fpu("*", "a", "b")}},
} {
_, err := doc.UpdatePaths(ctx, test.data)
if err == nil {
t.Errorf("%s: got nil, want error", test.desc)
}
}
}
func TestApplyFieldPaths(t *testing.T) {
submap := mapval(map[string]*pb.Value{
"b": intval(1),
"c": intval(2),
})
fields := map[string]*pb.Value{
"a": submap,
"d": intval(3),
}
for _, test := range []struct {
fps []FieldPath
want map[string]*pb.Value
}{
{nil, nil},
{[]FieldPath{[]string{"z"}}, nil},
{[]FieldPath{[]string{"a"}}, map[string]*pb.Value{"a": submap}},
{[]FieldPath{[]string{"a", "b", "c"}}, nil},
{[]FieldPath{[]string{"d"}}, map[string]*pb.Value{"d": intval(3)}},
{
[]FieldPath{[]string{"d"}, []string{"a", "c"}},
map[string]*pb.Value{
"a": mapval(map[string]*pb.Value{"c": intval(2)}),
"d": intval(3),
},
},
} {
got := applyFieldPaths(fields, test.fps, nil)
if !testEqual(got, test.want) {
t.Errorf("%v:\ngot %v\nwant \n%v", test.fps, got, test.want)
}
}
}
func TestFieldPathsFromMap(t *testing.T) {
for _, test := range []struct {
in map[string]interface{}
want []string
}{
{nil, nil},
{map[string]interface{}{"a": 1}, []string{"a"}},
{map[string]interface{}{
"a": 1,
"b": map[string]interface{}{"c": 2},
}, []string{"a", "b.c"}},
} {
fps := fieldPathsFromMap(reflect.ValueOf(test.in), nil)
got := toServiceFieldPaths(fps)
sort.Strings(got)
var got []fpv
fpvsFromData(reflect.ValueOf(test.in), nil, &got)
sort.Sort(byFieldPath(got))
if !testEqual(got, test.want) {
t.Errorf("%+v: got %v, want %v", test.in, got, test.want)
}
}
}
type byFieldPath []fpv
func (b byFieldPath) Len() int { return len(b) }
func (b byFieldPath) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b byFieldPath) Less(i, j int) bool { return b[i].fieldPath.less(b[j].fieldPath) }
func commitRequestForSet() *pb.CommitRequest {
return &pb.CommitRequest{
Database: "projects/projectID/databases/(default)",
@@ -741,3 +248,51 @@ func commitRequestForSet() *pb.CommitRequest {
},
}
}
func TestUpdateProcess(t *testing.T) {
for _, test := range []struct {
in Update
want fpv
wantErr bool
}{
{
in: Update{Path: "a", Value: 1},
want: fpv{fieldPath: []string{"a"}, value: 1},
},
{
in: Update{Path: "c.d", Value: Delete},
want: fpv{fieldPath: []string{"c", "d"}, value: Delete},
},
{
in: Update{FieldPath: []string{"*", "~"}, Value: ServerTimestamp},
want: fpv{fieldPath: []string{"*", "~"}, value: ServerTimestamp},
},
{
in: Update{Path: "*"},
wantErr: true, // bad rune in path
},
{
in: Update{Path: "a", FieldPath: []string{"b"}},
wantErr: true, // both Path and FieldPath
},
{
in: Update{Value: 1},
wantErr: true, // neither Path nor FieldPath
},
{
in: Update{FieldPath: []string{"", "a"}},
wantErr: true, // empty FieldPath component
},
} {
got, err := test.in.process()
if test.wantErr {
if err == nil {
t.Errorf("%+v: got nil, want error", test.in)
}
} else if err != nil {
t.Errorf("%+v: got error %v, want nil", test.in, err)
} else if !testEqual(got, test.want) {
t.Errorf("%+v: got %+v, want %+v", test.in, got, test.want)
}
}
}
+4 -16
View File
@@ -22,7 +22,6 @@ import (
pb "google.golang.org/genproto/googleapis/firestore/v1beta1"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
)
@@ -47,17 +46,6 @@ type DocumentSnapshot struct {
proto *pb.Document
}
func (d1 *DocumentSnapshot) equal(d2 *DocumentSnapshot) bool {
if d1 == nil || d2 == nil {
return d1 == d2
}
return d1.Ref.equal(d2.Ref) &&
d1.CreateTime.Equal(d2.CreateTime) &&
d1.UpdateTime.Equal(d2.UpdateTime) &&
d1.c == d2.c &&
proto.Equal(d1.proto, d2.proto)
}
// Data returns the DocumentSnapshot's fields as a map.
// It is equivalent to
// var m map[string]interface{}
@@ -103,15 +91,15 @@ func (d *DocumentSnapshot) DataTo(p interface{}) error {
return setFromProtoValue(p, &pb.Value{&pb.Value_MapValue{&pb.MapValue{d.proto.Fields}}}, d.c)
}
// DataAt returns the data value denoted by fieldPath.
// DataAt returns the data value denoted by path.
//
// The fieldPath argument can be a single field or a dot-separated sequence of
// The path argument can be a single field or a dot-separated sequence of
// fields, and must not contain any of the runes "˜*/[]". Use DataAtPath instead for
// such a path.
//
// See DocumentSnapshot.DataTo for how Firestore values are converted to Go values.
func (d *DocumentSnapshot) DataAt(fieldPath string) (interface{}, error) {
fp, err := parseDotSeparatedString(fieldPath)
func (d *DocumentSnapshot) DataAt(path string) (interface{}, error) {
fp, err := parseDotSeparatedString(path)
if err != nil {
return nil, err
}
+6 -49
View File
@@ -262,13 +262,13 @@ func ExampleDocumentRef_Set_merge() {
// To do a merging Set with struct data, specify the exact fields to overwrite.
// MergeAll is disallowed here, because it would probably be a mistake: the "capital"
// field would be overwritten with the empty string.
_, err = client.Doc("States/Alabama").Set(ctx, State{Population: 5.2}, firestore.Merge("pop"))
_, err = client.Doc("States/Alabama").Set(ctx, State{Population: 5.2}, firestore.Merge([]string{"pop"}))
if err != nil {
// TODO: Handle error.
}
}
func ExampleDocumentRef_UpdateMap() {
func ExampleDocumentRef_Update() {
ctx := context.Background()
client, err := firestore.NewClient(ctx, "project-id")
if err != nil {
@@ -277,50 +277,9 @@ func ExampleDocumentRef_UpdateMap() {
defer client.Close()
tenn := client.Doc("States/Tennessee")
wr, err := tenn.UpdateMap(ctx, map[string]interface{}{"pop": 6.6})
if err != nil {
// TODO: Handle error.
}
fmt.Println(wr.UpdateTime)
}
func ExampleDocumentRef_UpdateStruct() {
ctx := context.Background()
client, err := firestore.NewClient(ctx, "project-id")
if err != nil {
// TODO: Handle error.
}
defer client.Close()
type State struct {
Capital string `firestore:"capital"`
Population float64 `firestore:"pop"` // in millions
}
tenn := client.Doc("States/Tennessee")
wr, err := tenn.UpdateStruct(ctx, []string{"pop"}, State{
Capital: "does not matter",
Population: 6.6,
})
if err != nil {
// TODO: Handle error.
}
fmt.Println(wr.UpdateTime)
}
func ExampleDocumentRef_UpdatePaths() {
ctx := context.Background()
client, err := firestore.NewClient(ctx, "project-id")
if err != nil {
// TODO: Handle error.
}
defer client.Close()
tenn := client.Doc("States/Tennessee")
wr, err := tenn.UpdatePaths(ctx, []firestore.FieldPathUpdate{
{Path: []string{"pop"}, Value: 6.6},
// This odd field path cannot be expressed using the dot-separated form:
{Path: []string{".", "*", "/"}, Value: "odd"},
wr, err := tenn.Update(ctx, []firestore.Update{
{Path: "pop", Value: 6.6},
{FieldPath: []string{".", "*", "/"}, Value: "odd"},
})
if err != nil {
// TODO: Handle error.
@@ -542,9 +501,7 @@ func ExampleClient_RunTransaction() {
if err != nil {
return err
}
return tx.UpdateMap(nm, map[string]interface{}{
"pop": pop.(float64) + 0.2,
})
return tx.Update(nm, []firestore.Update{{Path: "pop", Value: pop.(float64) + 0.2}})
})
if err != nil {
// TODO: Handle error.
+82 -29
View File
@@ -18,9 +18,14 @@ import (
"bytes"
"errors"
"fmt"
"reflect"
"regexp"
"sort"
"strings"
"cloud.google.com/go/internal/atomiccache"
"cloud.google.com/go/internal/fields"
pb "google.golang.org/genproto/googleapis/firestore/v1beta1"
)
// A FieldPath is a non-empty sequence of non-empty fields that reference a value.
@@ -54,18 +59,6 @@ func parseDotSeparatedString(s string) (FieldPath, error) {
return fp, nil
}
func parseDotSeparatedStrings(strs []string) ([]FieldPath, error) {
var fps []FieldPath
for _, s := range strs {
fp, err := parseDotSeparatedString(s)
if err != nil {
return nil, err
}
fps = append(fps, fp)
}
return fps, nil
}
func (fp1 FieldPath) equal(fp2 FieldPath) bool {
if len(fp1) != len(fp2) {
return false
@@ -118,6 +111,14 @@ func (fp FieldPath) with(k string) FieldPath {
return append(r, k)
}
// concat creates a new FieldPath consisting of fp1 followed by fp2.
func (fp1 FieldPath) concat(fp2 FieldPath) FieldPath {
r := make(FieldPath, len(fp1)+len(fp2))
copy(r, fp1)
copy(r[len(fp1):], fp2)
return r
}
// in reports whether fp is equal to one of the fps.
func (fp FieldPath) in(fps []FieldPath) bool {
for _, e := range fps {
@@ -149,39 +150,91 @@ func (b byPath) Len() int { return len(b) }
func (b byPath) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b byPath) Less(i, j int) bool { return b[i].less(b[j]) }
// createMapFromFieldPathUpdates uses fpus to construct a valid Firestore data value
// in the form of a map. It assumes the FieldPaths in fpus have already been
// validated and checked for prefixes. If any field path is associated with the
// Delete value, it is not stored in the map.
func createMapFromFieldPathUpdates(fpus []FieldPathUpdate) map[string]interface{} {
m := map[string]interface{}{}
for _, fpu := range fpus {
if fpu.Value != Delete {
setAtPath(m, fpu.Path, fpu.Value)
}
}
return m
}
// setAtPath sets val at the location in m specified by fp, creating sub-maps as
// needed. m must not be nil. fp is assumed to be valid.
func setAtPath(m map[string]interface{}, fp FieldPath, val interface{}) {
func setAtPath(m map[string]*pb.Value, fp FieldPath, val *pb.Value) {
if val == nil {
return
}
if len(fp) == 1 {
m[fp[0]] = val
} else {
v, ok := m[fp[0]]
if !ok {
v = map[string]interface{}{}
v = &pb.Value{&pb.Value_MapValue{&pb.MapValue{map[string]*pb.Value{}}}}
m[fp[0]] = v
}
// The type assertion below cannot fail, because setAtPath is only called
// with either an empty map or one filled by setAtPath itself, and the
// set of FieldPaths it is called with has been checked to make sure that
// no path is the prefix of any other.
setAtPath(v.(map[string]interface{}), fp[1:], val)
setAtPath(v.GetMapValue().Fields, fp[1:], val)
}
}
// getAtPath gets the value in data referred to by fp. The data argument can
// be a map or a struct.
// Compare with valueAtPath, which does the same thing for a document.
func getAtPath(v reflect.Value, fp FieldPath) (interface{}, error) {
var err error
for _, k := range fp {
v, err = getAtField(v, k)
if err != nil {
return nil, err
}
}
return v.Interface(), nil
}
// getAtField returns the equivalent of v[k], if v is a map, or v.k if v is a struct.
func getAtField(v reflect.Value, k string) (reflect.Value, error) {
switch v.Kind() {
case reflect.Map:
if r := v.MapIndex(reflect.ValueOf(k)); r.IsValid() {
return r, nil
}
case reflect.Struct:
fm, err := fieldMap(v.Type())
if err != nil {
return reflect.Value{}, err
}
if f, ok := fm[k]; ok {
return v.FieldByIndex(f.Index), nil
}
case reflect.Interface:
return getAtField(v.Elem(), k)
case reflect.Ptr:
return getAtField(v.Elem(), k)
}
return reflect.Value{}, fmt.Errorf("firestore: no field %q for value %#v", k, v)
}
// fieldMapCache holds maps from from Firestore field name to struct field,
// keyed by struct type.
// TODO(jba): replace with sync.Map for Go 1.9.
var fieldMapCache atomiccache.Cache
func fieldMap(t reflect.Type) (map[string]fields.Field, error) {
x := fieldMapCache.Get(t, func() interface{} {
fieldList, err := fieldCache.Fields(t)
if err != nil {
return err
}
m := map[string]fields.Field{}
for _, f := range fieldList {
m[f.Name] = f
}
return m
})
if err, ok := x.(error); ok {
return nil, err
}
return x.(map[string]fields.Field), nil
}
// toServiceFieldPath converts fp the form required by the Firestore service.
// It assumes fp has been validated.
func (fp FieldPath) toServiceFieldPath() string {
+106 -26
View File
@@ -15,8 +15,11 @@
package firestore
import (
"reflect"
"strings"
"testing"
"cloud.google.com/go/internal/testutil"
)
func TestFieldPathValidate(t *testing.T) {
@@ -72,46 +75,123 @@ func TestCheckForPrefix(t *testing.T) {
}
}
// Convenience function for creating a FieldPathUpdate.
func fpu(val int, fields ...string) FieldPathUpdate {
return FieldPathUpdate{Path: fields, Value: val}
}
func TestGetAtPath(t *testing.T) {
type S struct {
X int
Y int `firestore:"y"`
M map[string]interface{}
Next *S
}
func TestCreateMapFromFieldPathUpdates(t *testing.T) {
type M map[string]interface{}
const fail = "ERROR" // value for expected error
for _, test := range []struct {
in []FieldPathUpdate
want M
val interface{}
fp FieldPath
want interface{}
}{
{
in: nil,
want: M{},
val: map[string]int(nil),
fp: nil,
want: map[string]int(nil),
},
{
in: []FieldPathUpdate{fpu(1, "a"), fpu(2, "b")},
want: M{"a": 1, "b": 2},
val: 1,
fp: nil,
want: 1,
},
{
in: []FieldPathUpdate{fpu(1, "a", "b"), fpu(2, "c")},
want: M{"a": map[string]interface{}{"b": 1}, "c": 2},
val: 1,
fp: []string{"a"},
want: fail,
},
{
in: []FieldPathUpdate{fpu(1, "a", "b"), fpu(2, "c", "d")},
want: M{
"a": map[string]interface{}{"b": 1},
"c": map[string]interface{}{"d": 2},
val: map[string]int{"a": 2},
fp: []string{"a"},
want: 2,
},
{
val: map[string]int{"a": 2},
fp: []string{"b"},
want: fail,
},
{
val: map[string]interface{}{"a": map[string]int{"b": 3}},
fp: []string{"a", "b"},
want: 3,
},
{
val: map[string]interface{}{"a": map[string]int{"b": 3}},
fp: []string{"a", "b", "c"},
want: fail,
},
{
val: S{X: 1, Y: 2},
fp: nil,
want: S{X: 1, Y: 2},
},
{
val: S{X: 1, Y: 2},
fp: []string{"X"},
want: 1,
},
{
val: S{X: 1, Y: 2},
fp: []string{"Y"},
want: fail, // because Y is tagged with name "y"
},
{
val: S{X: 1, Y: 2},
fp: []string{"y"},
want: 2,
},
{
val: &S{X: 1},
fp: []string{"X"},
want: 1,
},
{
val: &S{X: 1, Next: nil},
fp: []string{"Next"},
want: (*S)(nil),
},
{
val: &S{X: 1, Next: nil},
fp: []string{"Next", "Next"},
want: fail,
},
{
val: map[string]S{"a": S{X: 1, Y: 2}},
fp: []string{"a", "y"},
want: 2,
},
{
val: map[string]S{"a": S{X: 1, Y: 2}},
fp: []string{"a", "z"},
want: fail,
},
{
val: map[string]*S{
"a": &S{
M: map[string]interface{}{
"b": S{
Next: &S{
X: 17,
},
},
},
},
},
},
{
in: []FieldPathUpdate{fpu(1, "a", "b"), fpu(2, "a", "c")},
want: M{"a": map[string]interface{}{"b": 1, "c": 2}},
fp: []string{"a", "M", "b", "Next", "X"},
want: 17,
},
} {
gotm := createMapFromFieldPathUpdates(test.in)
got := M(gotm)
if !testEqual(got, test.want) {
t.Errorf("%v: got %#v, want %#v", test.in, got, test.want)
got, err := getAtPath(reflect.ValueOf(test.val), test.fp)
if err != nil && test.want != fail {
t.Errorf("%+v: got error <%v>, want nil", test, err)
}
if err == nil && !testutil.Equal(got, test.want) {
t.Errorf("%+v: got %v, want %v, want nil", test, got, test.want)
}
}
}
+32 -9
View File
@@ -110,33 +110,56 @@ func setReflectFromProtoValue(v reflect.Value, vproto *pb.Value, c *Client) erro
v.SetString(x.StringValue)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
x, ok := val.(*pb.Value_IntegerValue)
if !ok {
var i int64
switch x := val.(type) {
case *pb.Value_IntegerValue:
i = x.IntegerValue
case *pb.Value_DoubleValue:
f := x.DoubleValue
i = int64(f)
if float64(i) != f {
return fmt.Errorf("firestore: float %f does not fit into %s", f, v.Type())
}
default:
return typeErr()
}
i := x.IntegerValue
if v.OverflowInt(i) {
return overflowErr(v, i)
}
v.SetInt(i)
case reflect.Uint8, reflect.Uint16, reflect.Uint32:
x, ok := val.(*pb.Value_IntegerValue)
if !ok {
var u uint64
switch x := val.(type) {
case *pb.Value_IntegerValue:
u = uint64(x.IntegerValue)
case *pb.Value_DoubleValue:
f := x.DoubleValue
u = uint64(f)
if float64(u) != f {
return fmt.Errorf("firestore: float %f does not fit into %s", f, v.Type())
}
default:
return typeErr()
}
u := uint64(x.IntegerValue)
if v.OverflowUint(u) {
return overflowErr(v, u)
}
v.SetUint(u)
case reflect.Float32, reflect.Float64:
x, ok := val.(*pb.Value_DoubleValue)
if !ok {
var f float64
switch x := val.(type) {
case *pb.Value_DoubleValue:
f = x.DoubleValue
case *pb.Value_IntegerValue:
f = float64(x.IntegerValue)
if int64(f) != x.IntegerValue {
return overflowErr(v, x.IntegerValue)
}
default:
return typeErr()
}
f := x.DoubleValue
if v.OverflowFloat(f) {
return overflowErr(v, f)
}
+10 -5
View File
@@ -166,6 +166,8 @@ func testSetFromProtoValue(t *testing.T, prefix string, r tester) {
{&u16, r.Int(1), uint16(1)},
{&u32, r.Int(1), uint32(1)},
{&b, r.Bool(true), true},
{&i, r.Float(1), int(1)}, // can put a float with no fractional part into an int
{pi, r.Int(1), float64(1)}, // can put an int into a float
} {
if err := r.Set(test.in, test.val); err != nil {
t.Errorf("%s: #%d: got error %v", prefix, i, err)
@@ -212,7 +214,7 @@ func TestSetFromProtoValueNoJSON(t *testing.T) {
func TestSetFromProtoValueErrors(t *testing.T) {
c := &Client{}
ival := intval(3)
for _, test := range []struct {
for i, test := range []struct {
in interface{}
val *pb.Value
}{
@@ -230,19 +232,22 @@ func TestSetFromProtoValueErrors(t *testing.T) {
{new(*latlng.LatLng), ival},
{new(time.Time), ival},
{new(string), ival},
{new(float64), ival},
{new([]byte), ival},
{new([]int), ival},
{new([1]int), ival},
{new(map[string]int), ival},
{new(*bool), ival},
{new(struct{}), ival},
{new(int), floatval(3)},
{new(uint16), floatval(3)},
{new(int), floatval(2.5)}, // float must be integral
{new(uint16), intval(-1)}, // uint cannot be negative
{new(int16), floatval(math.MaxFloat32)}, // doesn't fit
{new(uint16), floatval(math.MaxFloat32)}, // doesn't fit
{new(float32),
&pb.Value{&pb.Value_IntegerValue{math.MaxInt64}}}, // overflow
} {
err := setFromProtoValue(test.in, test.val, c)
if err == nil {
t.Errorf("%v, %v: got nil, want error", test.in, test.val)
t.Errorf("#%d: %v, %v: got nil, want error", i, test.in, test.val)
}
}
}
+3
View File
@@ -0,0 +1,3 @@
The contents of this directory are copied from
github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/genproto.
+662
View File
@@ -0,0 +1,662 @@
// Code generated by protoc-gen-go.
// source: test.proto
// DO NOT EDIT!
/*
Package tests is a generated protocol buffer package.
It is generated from these files:
test.proto
It has these top-level messages:
Test
GetTest
CreateTest
SetTest
UpdateTest
UpdatePathsTest
DeleteTest
SetOption
FieldPath
*/
package tests
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import google_firestore_v1beta14 "google.golang.org/genproto/googleapis/firestore/v1beta1"
import google_firestore_v1beta1 "google.golang.org/genproto/googleapis/firestore/v1beta1"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// A Test describes a single client method call and its expected result.
type Test struct {
Description string `protobuf:"bytes,1,opt,name=description" json:"description,omitempty"`
// Types that are valid to be assigned to Test:
// *Test_Get
// *Test_Create
// *Test_Set
// *Test_Update
// *Test_UpdatePaths
// *Test_Delete
Test isTest_Test `protobuf_oneof:"test"`
}
func (m *Test) Reset() { *m = Test{} }
func (m *Test) String() string { return proto.CompactTextString(m) }
func (*Test) ProtoMessage() {}
func (*Test) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
type isTest_Test interface {
isTest_Test()
}
type Test_Get struct {
Get *GetTest `protobuf:"bytes,2,opt,name=get,oneof"`
}
type Test_Create struct {
Create *CreateTest `protobuf:"bytes,3,opt,name=create,oneof"`
}
type Test_Set struct {
Set *SetTest `protobuf:"bytes,4,opt,name=set,oneof"`
}
type Test_Update struct {
Update *UpdateTest `protobuf:"bytes,5,opt,name=update,oneof"`
}
type Test_UpdatePaths struct {
UpdatePaths *UpdatePathsTest `protobuf:"bytes,6,opt,name=update_paths,json=updatePaths,oneof"`
}
type Test_Delete struct {
Delete *DeleteTest `protobuf:"bytes,7,opt,name=delete,oneof"`
}
func (*Test_Get) isTest_Test() {}
func (*Test_Create) isTest_Test() {}
func (*Test_Set) isTest_Test() {}
func (*Test_Update) isTest_Test() {}
func (*Test_UpdatePaths) isTest_Test() {}
func (*Test_Delete) isTest_Test() {}
func (m *Test) GetTest() isTest_Test {
if m != nil {
return m.Test
}
return nil
}
func (m *Test) GetDescription() string {
if m != nil {
return m.Description
}
return ""
}
func (m *Test) GetGet() *GetTest {
if x, ok := m.GetTest().(*Test_Get); ok {
return x.Get
}
return nil
}
func (m *Test) GetCreate() *CreateTest {
if x, ok := m.GetTest().(*Test_Create); ok {
return x.Create
}
return nil
}
func (m *Test) GetSet() *SetTest {
if x, ok := m.GetTest().(*Test_Set); ok {
return x.Set
}
return nil
}
func (m *Test) GetUpdate() *UpdateTest {
if x, ok := m.GetTest().(*Test_Update); ok {
return x.Update
}
return nil
}
func (m *Test) GetUpdatePaths() *UpdatePathsTest {
if x, ok := m.GetTest().(*Test_UpdatePaths); ok {
return x.UpdatePaths
}
return nil
}
func (m *Test) GetDelete() *DeleteTest {
if x, ok := m.GetTest().(*Test_Delete); ok {
return x.Delete
}
return nil
}
// XXX_OneofFuncs is for the internal use of the proto package.
func (*Test) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
return _Test_OneofMarshaler, _Test_OneofUnmarshaler, _Test_OneofSizer, []interface{}{
(*Test_Get)(nil),
(*Test_Create)(nil),
(*Test_Set)(nil),
(*Test_Update)(nil),
(*Test_UpdatePaths)(nil),
(*Test_Delete)(nil),
}
}
func _Test_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
m := msg.(*Test)
// test
switch x := m.Test.(type) {
case *Test_Get:
b.EncodeVarint(2<<3 | proto.WireBytes)
if err := b.EncodeMessage(x.Get); err != nil {
return err
}
case *Test_Create:
b.EncodeVarint(3<<3 | proto.WireBytes)
if err := b.EncodeMessage(x.Create); err != nil {
return err
}
case *Test_Set:
b.EncodeVarint(4<<3 | proto.WireBytes)
if err := b.EncodeMessage(x.Set); err != nil {
return err
}
case *Test_Update:
b.EncodeVarint(5<<3 | proto.WireBytes)
if err := b.EncodeMessage(x.Update); err != nil {
return err
}
case *Test_UpdatePaths:
b.EncodeVarint(6<<3 | proto.WireBytes)
if err := b.EncodeMessage(x.UpdatePaths); err != nil {
return err
}
case *Test_Delete:
b.EncodeVarint(7<<3 | proto.WireBytes)
if err := b.EncodeMessage(x.Delete); err != nil {
return err
}
case nil:
default:
return fmt.Errorf("Test.Test has unexpected type %T", x)
}
return nil
}
func _Test_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
m := msg.(*Test)
switch tag {
case 2: // test.get
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
msg := new(GetTest)
err := b.DecodeMessage(msg)
m.Test = &Test_Get{msg}
return true, err
case 3: // test.create
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
msg := new(CreateTest)
err := b.DecodeMessage(msg)
m.Test = &Test_Create{msg}
return true, err
case 4: // test.set
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
msg := new(SetTest)
err := b.DecodeMessage(msg)
m.Test = &Test_Set{msg}
return true, err
case 5: // test.update
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
msg := new(UpdateTest)
err := b.DecodeMessage(msg)
m.Test = &Test_Update{msg}
return true, err
case 6: // test.update_paths
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
msg := new(UpdatePathsTest)
err := b.DecodeMessage(msg)
m.Test = &Test_UpdatePaths{msg}
return true, err
case 7: // test.delete
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
msg := new(DeleteTest)
err := b.DecodeMessage(msg)
m.Test = &Test_Delete{msg}
return true, err
default:
return false, nil
}
}
func _Test_OneofSizer(msg proto.Message) (n int) {
m := msg.(*Test)
// test
switch x := m.Test.(type) {
case *Test_Get:
s := proto.Size(x.Get)
n += proto.SizeVarint(2<<3 | proto.WireBytes)
n += proto.SizeVarint(uint64(s))
n += s
case *Test_Create:
s := proto.Size(x.Create)
n += proto.SizeVarint(3<<3 | proto.WireBytes)
n += proto.SizeVarint(uint64(s))
n += s
case *Test_Set:
s := proto.Size(x.Set)
n += proto.SizeVarint(4<<3 | proto.WireBytes)
n += proto.SizeVarint(uint64(s))
n += s
case *Test_Update:
s := proto.Size(x.Update)
n += proto.SizeVarint(5<<3 | proto.WireBytes)
n += proto.SizeVarint(uint64(s))
n += s
case *Test_UpdatePaths:
s := proto.Size(x.UpdatePaths)
n += proto.SizeVarint(6<<3 | proto.WireBytes)
n += proto.SizeVarint(uint64(s))
n += s
case *Test_Delete:
s := proto.Size(x.Delete)
n += proto.SizeVarint(7<<3 | proto.WireBytes)
n += proto.SizeVarint(uint64(s))
n += s
case nil:
default:
panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
}
return n
}
// Call to the DocumentRef.Get method.
type GetTest struct {
// The path of the doc, e.g. "projects/projectID/databases/(default)/documents/C/d"
DocRefPath string `protobuf:"bytes,1,opt,name=doc_ref_path,json=docRefPath" json:"doc_ref_path,omitempty"`
// The request that the call should send to the Firestore service.
Request *google_firestore_v1beta14.GetDocumentRequest `protobuf:"bytes,2,opt,name=request" json:"request,omitempty"`
}
func (m *GetTest) Reset() { *m = GetTest{} }
func (m *GetTest) String() string { return proto.CompactTextString(m) }
func (*GetTest) ProtoMessage() {}
func (*GetTest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *GetTest) GetDocRefPath() string {
if m != nil {
return m.DocRefPath
}
return ""
}
func (m *GetTest) GetRequest() *google_firestore_v1beta14.GetDocumentRequest {
if m != nil {
return m.Request
}
return nil
}
// Call to DocumentRef.Create.
type CreateTest struct {
// The path of the doc, e.g. "projects/projectID/databases/(default)/documents/C/d"
DocRefPath string `protobuf:"bytes,1,opt,name=doc_ref_path,json=docRefPath" json:"doc_ref_path,omitempty"`
// The data passed to Create, as JSON. The strings "Delete" and "ServerTimestamp"
// denote the two special sentinel values. Values that could be interpreted as integers
// (i.e. digit strings) should be treated as integers.
JsonData string `protobuf:"bytes,2,opt,name=json_data,json=jsonData" json:"json_data,omitempty"`
// The request that the call should generate.
Request *google_firestore_v1beta14.CommitRequest `protobuf:"bytes,3,opt,name=request" json:"request,omitempty"`
// If true, the call should result in an error without generating a request.
// If this is true, request should not be set.
IsError bool `protobuf:"varint,4,opt,name=is_error,json=isError" json:"is_error,omitempty"`
}
func (m *CreateTest) Reset() { *m = CreateTest{} }
func (m *CreateTest) String() string { return proto.CompactTextString(m) }
func (*CreateTest) ProtoMessage() {}
func (*CreateTest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *CreateTest) GetDocRefPath() string {
if m != nil {
return m.DocRefPath
}
return ""
}
func (m *CreateTest) GetJsonData() string {
if m != nil {
return m.JsonData
}
return ""
}
func (m *CreateTest) GetRequest() *google_firestore_v1beta14.CommitRequest {
if m != nil {
return m.Request
}
return nil
}
func (m *CreateTest) GetIsError() bool {
if m != nil {
return m.IsError
}
return false
}
// A call to DocumentRef.Set.
type SetTest struct {
DocRefPath string `protobuf:"bytes,1,opt,name=doc_ref_path,json=docRefPath" json:"doc_ref_path,omitempty"`
Option *SetOption `protobuf:"bytes,2,opt,name=option" json:"option,omitempty"`
JsonData string `protobuf:"bytes,3,opt,name=json_data,json=jsonData" json:"json_data,omitempty"`
Request *google_firestore_v1beta14.CommitRequest `protobuf:"bytes,4,opt,name=request" json:"request,omitempty"`
IsError bool `protobuf:"varint,5,opt,name=is_error,json=isError" json:"is_error,omitempty"`
}
func (m *SetTest) Reset() { *m = SetTest{} }
func (m *SetTest) String() string { return proto.CompactTextString(m) }
func (*SetTest) ProtoMessage() {}
func (*SetTest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
func (m *SetTest) GetDocRefPath() string {
if m != nil {
return m.DocRefPath
}
return ""
}
func (m *SetTest) GetOption() *SetOption {
if m != nil {
return m.Option
}
return nil
}
func (m *SetTest) GetJsonData() string {
if m != nil {
return m.JsonData
}
return ""
}
func (m *SetTest) GetRequest() *google_firestore_v1beta14.CommitRequest {
if m != nil {
return m.Request
}
return nil
}
func (m *SetTest) GetIsError() bool {
if m != nil {
return m.IsError
}
return false
}
// A call to the form of DocumentRef.Update that represents the data as a map
// or dictionary.
type UpdateTest struct {
DocRefPath string `protobuf:"bytes,1,opt,name=doc_ref_path,json=docRefPath" json:"doc_ref_path,omitempty"`
Precondition *google_firestore_v1beta1.Precondition `protobuf:"bytes,2,opt,name=precondition" json:"precondition,omitempty"`
JsonData string `protobuf:"bytes,3,opt,name=json_data,json=jsonData" json:"json_data,omitempty"`
Request *google_firestore_v1beta14.CommitRequest `protobuf:"bytes,4,opt,name=request" json:"request,omitempty"`
IsError bool `protobuf:"varint,5,opt,name=is_error,json=isError" json:"is_error,omitempty"`
}
func (m *UpdateTest) Reset() { *m = UpdateTest{} }
func (m *UpdateTest) String() string { return proto.CompactTextString(m) }
func (*UpdateTest) ProtoMessage() {}
func (*UpdateTest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
func (m *UpdateTest) GetDocRefPath() string {
if m != nil {
return m.DocRefPath
}
return ""
}
func (m *UpdateTest) GetPrecondition() *google_firestore_v1beta1.Precondition {
if m != nil {
return m.Precondition
}
return nil
}
func (m *UpdateTest) GetJsonData() string {
if m != nil {
return m.JsonData
}
return ""
}
func (m *UpdateTest) GetRequest() *google_firestore_v1beta14.CommitRequest {
if m != nil {
return m.Request
}
return nil
}
func (m *UpdateTest) GetIsError() bool {
if m != nil {
return m.IsError
}
return false
}
// A call to the form of DocumentRef.Update that represents the data as a list
// of field paths and their values.
type UpdatePathsTest struct {
DocRefPath string `protobuf:"bytes,1,opt,name=doc_ref_path,json=docRefPath" json:"doc_ref_path,omitempty"`
Precondition *google_firestore_v1beta1.Precondition `protobuf:"bytes,2,opt,name=precondition" json:"precondition,omitempty"`
// parallel sequences: field_paths[i] corresponds to json_values[i]
FieldPaths []*FieldPath `protobuf:"bytes,3,rep,name=field_paths,json=fieldPaths" json:"field_paths,omitempty"`
JsonValues []string `protobuf:"bytes,4,rep,name=json_values,json=jsonValues" json:"json_values,omitempty"`
Request *google_firestore_v1beta14.CommitRequest `protobuf:"bytes,5,opt,name=request" json:"request,omitempty"`
IsError bool `protobuf:"varint,6,opt,name=is_error,json=isError" json:"is_error,omitempty"`
}
func (m *UpdatePathsTest) Reset() { *m = UpdatePathsTest{} }
func (m *UpdatePathsTest) String() string { return proto.CompactTextString(m) }
func (*UpdatePathsTest) ProtoMessage() {}
func (*UpdatePathsTest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
func (m *UpdatePathsTest) GetDocRefPath() string {
if m != nil {
return m.DocRefPath
}
return ""
}
func (m *UpdatePathsTest) GetPrecondition() *google_firestore_v1beta1.Precondition {
if m != nil {
return m.Precondition
}
return nil
}
func (m *UpdatePathsTest) GetFieldPaths() []*FieldPath {
if m != nil {
return m.FieldPaths
}
return nil
}
func (m *UpdatePathsTest) GetJsonValues() []string {
if m != nil {
return m.JsonValues
}
return nil
}
func (m *UpdatePathsTest) GetRequest() *google_firestore_v1beta14.CommitRequest {
if m != nil {
return m.Request
}
return nil
}
func (m *UpdatePathsTest) GetIsError() bool {
if m != nil {
return m.IsError
}
return false
}
// A call to DocmentRef.Delete
type DeleteTest struct {
DocRefPath string `protobuf:"bytes,1,opt,name=doc_ref_path,json=docRefPath" json:"doc_ref_path,omitempty"`
Precondition *google_firestore_v1beta1.Precondition `protobuf:"bytes,2,opt,name=precondition" json:"precondition,omitempty"`
Request *google_firestore_v1beta14.CommitRequest `protobuf:"bytes,3,opt,name=request" json:"request,omitempty"`
IsError bool `protobuf:"varint,4,opt,name=is_error,json=isError" json:"is_error,omitempty"`
}
func (m *DeleteTest) Reset() { *m = DeleteTest{} }
func (m *DeleteTest) String() string { return proto.CompactTextString(m) }
func (*DeleteTest) ProtoMessage() {}
func (*DeleteTest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
func (m *DeleteTest) GetDocRefPath() string {
if m != nil {
return m.DocRefPath
}
return ""
}
func (m *DeleteTest) GetPrecondition() *google_firestore_v1beta1.Precondition {
if m != nil {
return m.Precondition
}
return nil
}
func (m *DeleteTest) GetRequest() *google_firestore_v1beta14.CommitRequest {
if m != nil {
return m.Request
}
return nil
}
func (m *DeleteTest) GetIsError() bool {
if m != nil {
return m.IsError
}
return false
}
// An option to the DocumentRef.Set call.
type SetOption struct {
All bool `protobuf:"varint,1,opt,name=all" json:"all,omitempty"`
Fields []*FieldPath `protobuf:"bytes,2,rep,name=fields" json:"fields,omitempty"`
}
func (m *SetOption) Reset() { *m = SetOption{} }
func (m *SetOption) String() string { return proto.CompactTextString(m) }
func (*SetOption) ProtoMessage() {}
func (*SetOption) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
func (m *SetOption) GetAll() bool {
if m != nil {
return m.All
}
return false
}
func (m *SetOption) GetFields() []*FieldPath {
if m != nil {
return m.Fields
}
return nil
}
// A field path.
type FieldPath struct {
Field []string `protobuf:"bytes,1,rep,name=field" json:"field,omitempty"`
}
func (m *FieldPath) Reset() { *m = FieldPath{} }
func (m *FieldPath) String() string { return proto.CompactTextString(m) }
func (*FieldPath) ProtoMessage() {}
func (*FieldPath) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
func (m *FieldPath) GetField() []string {
if m != nil {
return m.Field
}
return nil
}
func init() {
proto.RegisterType((*Test)(nil), "tests.Test")
proto.RegisterType((*GetTest)(nil), "tests.GetTest")
proto.RegisterType((*CreateTest)(nil), "tests.CreateTest")
proto.RegisterType((*SetTest)(nil), "tests.SetTest")
proto.RegisterType((*UpdateTest)(nil), "tests.UpdateTest")
proto.RegisterType((*UpdatePathsTest)(nil), "tests.UpdatePathsTest")
proto.RegisterType((*DeleteTest)(nil), "tests.DeleteTest")
proto.RegisterType((*SetOption)(nil), "tests.SetOption")
proto.RegisterType((*FieldPath)(nil), "tests.FieldPath")
}
func init() { proto.RegisterFile("test.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 559 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xc4, 0x55, 0x4d, 0x6f, 0xd3, 0x40,
0x10, 0xc5, 0x71, 0xe2, 0x24, 0x93, 0x08, 0xca, 0x0a, 0x21, 0x53, 0x0e, 0x18, 0x4b, 0x40, 0x24,
0x50, 0xaa, 0xc0, 0x91, 0x13, 0x34, 0xb4, 0x88, 0x0b, 0xd5, 0x16, 0xb8, 0x46, 0xae, 0x3d, 0x09,
0x46, 0x8e, 0xd7, 0xec, 0xae, 0xfb, 0x9f, 0x38, 0x72, 0xe7, 0x47, 0x70, 0xe4, 0x8f, 0x70, 0x47,
0xfb, 0xe1, 0xda, 0x06, 0x59, 0xca, 0xa1, 0xb4, 0xb7, 0xf5, 0x9b, 0x37, 0x1f, 0xef, 0xcd, 0x6e,
0x02, 0x20, 0x51, 0xc8, 0x79, 0xc1, 0x99, 0x64, 0x64, 0xa0, 0xce, 0x62, 0x7f, 0xb6, 0x61, 0x6c,
0x93, 0xe1, 0xc1, 0x3a, 0xe5, 0x28, 0x24, 0xe3, 0x78, 0x70, 0xbe, 0x38, 0x43, 0x19, 0x2d, 0x6a,
0xc4, 0x24, 0xec, 0x3f, 0xea, 0x64, 0xc6, 0x6c, 0xbb, 0x65, 0xb9, 0xa1, 0x85, 0x3f, 0x7a, 0xd0,
0xff, 0x80, 0x42, 0x92, 0x00, 0x26, 0x09, 0x8a, 0x98, 0xa7, 0x85, 0x4c, 0x59, 0xee, 0x3b, 0x81,
0x33, 0x1b, 0xd3, 0x26, 0x44, 0x42, 0x70, 0x37, 0x28, 0xfd, 0x5e, 0xe0, 0xcc, 0x26, 0xcf, 0x6f,
0xce, 0xf5, 0x40, 0xf3, 0x63, 0x94, 0x2a, 0xfd, 0xed, 0x0d, 0xaa, 0x82, 0xe4, 0x29, 0x78, 0x31,
0xc7, 0x48, 0xa2, 0xef, 0x6a, 0xda, 0x6d, 0x4b, 0x3b, 0xd4, 0xa0, 0x65, 0x5a, 0x8a, 0x2a, 0x28,
0x50, 0xfa, 0xfd, 0x56, 0xc1, 0xd3, 0xba, 0xa0, 0x30, 0x05, 0xcb, 0x22, 0x51, 0x05, 0x07, 0xad,
0x82, 0x1f, 0x35, 0x58, 0x15, 0x34, 0x14, 0xf2, 0x12, 0xa6, 0xe6, 0xb4, 0x2a, 0x22, 0xf9, 0x59,
0xf8, 0x9e, 0x4e, 0xb9, 0xdb, 0x4a, 0x39, 0x51, 0x11, 0x9b, 0x37, 0x29, 0x6b, 0x48, 0x75, 0x4a,
0x30, 0x43, 0x89, 0xfe, 0xb0, 0xd5, 0x69, 0xa9, 0xc1, 0xaa, 0x93, 0xa1, 0xbc, 0xf6, 0xa0, 0xaf,
0xa2, 0xa1, 0x80, 0xa1, 0x75, 0x80, 0x04, 0x30, 0x4d, 0x58, 0xbc, 0xe2, 0xb8, 0xd6, 0xdd, 0xad,
0x83, 0x90, 0xb0, 0x98, 0xe2, 0x5a, 0xb5, 0x20, 0x47, 0x30, 0xe4, 0xf8, 0xb5, 0x44, 0x51, 0x99,
0xf8, 0x6c, 0x6e, 0x96, 0x34, 0xaf, 0x97, 0x67, 0x97, 0xa4, 0x7c, 0x5d, 0xb2, 0xb8, 0xdc, 0x62,
0x2e, 0xa9, 0xc9, 0xa1, 0x55, 0x72, 0xf8, 0xcd, 0x01, 0xa8, 0x0d, 0xdd, 0xa1, 0xf1, 0x7d, 0x18,
0x7f, 0x11, 0x2c, 0x5f, 0x25, 0x91, 0x8c, 0x74, 0xeb, 0x31, 0x1d, 0x29, 0x60, 0x19, 0xc9, 0x88,
0xbc, 0xaa, 0xa7, 0x32, 0x3b, 0x7b, 0xd2, 0x3d, 0xd5, 0x21, 0xdb, 0x6e, 0xd3, 0x7f, 0x06, 0x22,
0xf7, 0x60, 0x94, 0x8a, 0x15, 0x72, 0xce, 0xb8, 0xde, 0xe6, 0x88, 0x0e, 0x53, 0xf1, 0x46, 0x7d,
0x86, 0x3f, 0x1d, 0x18, 0x9e, 0xee, 0xec, 0xd0, 0x0c, 0x3c, 0x66, 0xee, 0x9f, 0x31, 0x68, 0xaf,
0xbe, 0x14, 0xef, 0x35, 0x4e, 0x6d, 0xbc, 0x2d, 0xc9, 0xed, 0x96, 0xd4, 0xbf, 0x04, 0x49, 0x83,
0xb6, 0xa4, 0xdf, 0x0e, 0x40, 0x7d, 0xfd, 0x76, 0x50, 0xf5, 0x0e, 0xa6, 0x05, 0xc7, 0x98, 0xe5,
0x49, 0xda, 0xd0, 0xf6, 0xb8, 0x7b, 0xa6, 0x93, 0x06, 0x9b, 0xb6, 0x72, 0xaf, 0x53, 0xf7, 0xf7,
0x1e, 0xdc, 0xfa, 0xeb, 0x0d, 0x5d, 0xb1, 0xf8, 0x05, 0x4c, 0xd6, 0x29, 0x66, 0x89, 0x7d, 0xde,
0x6e, 0xe0, 0x36, 0xee, 0xc8, 0x91, 0x8a, 0xa8, 0x96, 0x14, 0xd6, 0xd5, 0x51, 0x90, 0x07, 0x30,
0xd1, 0x7e, 0x9d, 0x47, 0x59, 0x89, 0xc2, 0xef, 0x07, 0xae, 0x9a, 0x4f, 0x41, 0x9f, 0x34, 0xd2,
0xf4, 0x6c, 0x70, 0x09, 0x9e, 0x79, 0x6d, 0xcf, 0x7e, 0x39, 0x00, 0xf5, 0x0f, 0xc8, 0x15, 0xdb,
0xf5, 0x7f, 0x5f, 0xf6, 0x31, 0x8c, 0x2f, 0x9e, 0x25, 0xd9, 0x03, 0x37, 0xca, 0x32, 0xad, 0x67,
0x44, 0xd5, 0x51, 0x3d, 0x65, 0xbd, 0x06, 0xe1, 0xf7, 0x3a, 0xd6, 0x64, 0xe3, 0xe1, 0x43, 0x18,
0x5f, 0x80, 0xe4, 0x0e, 0x0c, 0x34, 0xec, 0x3b, 0x7a, 0x53, 0xe6, 0xe3, 0xcc, 0xd3, 0x7f, 0x56,
0x2f, 0xfe, 0x04, 0x00, 0x00, 0xff, 0xff, 0x6c, 0x8e, 0x38, 0xdd, 0x12, 0x07, 0x00, 0x00,
}
+62 -165
View File
@@ -226,10 +226,7 @@ func TestIntegration_Get(t *testing.T) {
ctx := context.Background()
doc := integrationColl(t).NewDoc()
mustCreate("Get #1", t, doc, integrationTestMap)
ds, err := doc.Get(ctx)
if err != nil {
t.Fatal(err)
}
ds := mustGet("Get #1", t, doc)
if ds.CreateTime != ds.UpdateTime {
t.Errorf("create time %s != update time %s", ds.CreateTime, ds.UpdateTime)
}
@@ -237,7 +234,19 @@ func TestIntegration_Get(t *testing.T) {
if want := wantIntegrationTestMap; !testEqual(got, want) {
t.Errorf("got\n%v\nwant\n%v", pretty.Value(got), pretty.Value(want))
}
_, err = integrationColl(t).NewDoc().Get(ctx)
doc = integrationColl(t).NewDoc()
empty := map[string]interface{}{}
mustCreate("Get empty", t, doc, empty)
ds = mustGet("Get empty", t, doc)
if ds.CreateTime != ds.UpdateTime {
t.Errorf("create time %s != update time %s", ds.CreateTime, ds.UpdateTime)
}
if got, want := ds.Data(), empty; !testEqual(got, want) {
t.Errorf("got\n%v\nwant\n%v", pretty.Value(got), pretty.Value(want))
}
_, err := integrationColl(t).NewDoc().Get(ctx)
codeEq(t, "Get on a missing doc", codes.NotFound, err)
}
@@ -304,12 +313,8 @@ func TestIntegration_Set(t *testing.T) {
if !wr1.UpdateTime.Before(wr2.UpdateTime) {
t.Errorf("update time did not increase: old=%s, new=%s", wr1.UpdateTime, wr2.UpdateTime)
}
ds, err := doc.Get(ctx)
if err != nil {
t.Fatal(err)
}
got := ds.Data()
if !testEqual(got, newData) {
ds := mustGet("Set #1", t, doc)
if got := ds.Data(); !testEqual(got, newData) {
t.Errorf("got %v, want %v", got, newData)
}
@@ -321,21 +326,17 @@ func TestIntegration_Set(t *testing.T) {
// SetOptions:
// Only fields mentioned in the Merge option will be changed.
// In this case, "str" will not be changed to "1".
wr3, err := doc.Set(ctx, newData, Merge("x", "y"))
wr3, err := doc.Set(ctx, newData, Merge([]string{"x"}, []string{"y"}))
if err != nil {
t.Fatal(err)
}
ds, err = doc.Get(ctx)
if err != nil {
t.Fatal(err)
}
got = ds.Data()
ds = mustGet("Set #2", t, doc)
want := map[string]interface{}{
"str": "change",
"x": "2",
"y": "3",
}
if !testEqual(got, want) {
if got := ds.Data(); !testEqual(got, want) {
t.Errorf("got %v, want %v", got, want)
}
if !wr2.UpdateTime.Before(wr3.UpdateTime) {
@@ -348,17 +349,13 @@ func TestIntegration_Set(t *testing.T) {
if err != nil {
t.Fatal(err)
}
ds, err = doc.Get(ctx)
if err != nil {
t.Fatal(err)
}
got = ds.Data()
ds = mustGet("Set #3", t, doc)
want = map[string]interface{}{
"str": "change",
"x": "4",
"y": "5",
}
if !testEqual(got, want) {
if got := ds.Data(); !testEqual(got, want) {
t.Errorf("got %v, want %v", got, want)
}
if !wr3.UpdateTime.Before(wr4.UpdateTime) {
@@ -392,100 +389,22 @@ func TestIntegration_Delete(t *testing.T) {
er(doc.Delete(ctx, LastUpdateTime(wr.UpdateTime))))
}
func TestIntegration_UpdateMap(t *testing.T) {
func TestIntegration_Update(t *testing.T) {
ctx := context.Background()
doc := integrationColl(t).NewDoc()
mustCreate("UpdateMap", t, doc, integrationTestMap)
um := map[string]interface{}{
"bool": false,
"time": 17,
"null": Delete,
"noSuchField": Delete, // deleting a non-existent field is a no-op
mustCreate("Update", t, doc, integrationTestMap)
fpus := []Update{
{Path: "bool", Value: false},
{Path: "time", Value: 17},
{FieldPath: []string{"*", "`"}, Value: 18},
{Path: "null", Value: Delete},
{Path: "noSuchField", Value: Delete}, // deleting a non-existent field is a no-op
}
wr, err := doc.UpdateMap(ctx, um)
if err != nil {
t.Fatal(err)
}
ds, err := doc.Get(ctx)
if err != nil {
t.Fatal(err)
}
got := ds.Data()
want := copyMap(wantIntegrationTestMap)
want["bool"] = false
want["time"] = int64(17)
delete(want, "null")
if !testEqual(got, want) {
t.Errorf("got\n%#v\nwant\n%#v", got, want)
}
er := func(_ *WriteResult, err error) error { return err }
codeEq(t, "UpdateMap on missing doc", codes.NotFound,
er(integrationColl(t).NewDoc().UpdateMap(ctx, um)))
codeEq(t, "UpdateMap with wrong LastUpdateTime", codes.FailedPrecondition,
er(doc.UpdateMap(ctx, um, LastUpdateTime(wr.UpdateTime.Add(-time.Millisecond)))))
codeEq(t, "UpdateMap with right LastUpdateTime", codes.OK,
er(doc.UpdateMap(ctx, um, LastUpdateTime(wr.UpdateTime))))
codeEq(t, "just server transform", codes.OK,
er(doc.UpdateMap(ctx, map[string]interface{}{"a": ServerTimestamp})))
}
func TestIntegration_UpdateStruct(t *testing.T) {
ctx := context.Background()
doc := integrationColl(t).NewDoc()
mustCreate("UpdateStruct", t, doc, integrationTestStruct)
fields := []string{"Bool", "Time", "Null", "noSuchField"}
wr, err := doc.UpdateStruct(ctx, fields,
integrationTestStructType{
Bool: false,
Time: aTime2,
})
if err != nil {
t.Fatal(err)
}
ds, err := doc.Get(ctx)
if err != nil {
t.Fatal(err)
}
var got integrationTestStructType
if err := ds.DataTo(&got); err != nil {
t.Fatal(err)
}
want := integrationTestStruct
want.Bool = false
want.Time = aTime2
if !testEqual(got, want) {
t.Errorf("got\n%#v\nwant\n%#v", got, want)
}
er := func(_ *WriteResult, err error) error { return err }
codeEq(t, "UpdateStruct on missing doc", codes.NotFound,
er(integrationColl(t).NewDoc().UpdateStruct(ctx, fields, integrationTestStruct)))
codeEq(t, "UpdateStruct with wrong LastUpdateTime", codes.FailedPrecondition,
er(doc.UpdateStruct(ctx, fields, integrationTestStruct, LastUpdateTime(wr.UpdateTime.Add(-time.Millisecond)))))
codeEq(t, "UpdateStruct with right LastUpdateTime", codes.OK,
er(doc.UpdateStruct(ctx, fields, integrationTestStruct, LastUpdateTime(wr.UpdateTime))))
}
func TestIntegration_UpdatePaths(t *testing.T) {
ctx := context.Background()
doc := integrationColl(t).NewDoc()
mustCreate("UpdatePaths", t, doc, integrationTestMap)
fpus := []FieldPathUpdate{
{Path: []string{"bool"}, Value: false},
{Path: []string{"time"}, Value: 17},
{Path: []string{"*", "`"}, Value: 18},
{Path: []string{"null"}, Value: Delete},
{Path: []string{"noSuchField"}, Value: Delete}, // deleting a non-existent field is a no-op
}
wr, err := doc.UpdatePaths(ctx, fpus)
if err != nil {
t.Fatal(err)
}
ds, err := doc.Get(ctx)
wr, err := doc.Update(ctx, fpus)
if err != nil {
t.Fatal(err)
}
ds := mustGet("Update", t, doc)
got := ds.Data()
want := copyMap(wantIntegrationTestMap)
want["bool"] = false
@@ -498,12 +417,12 @@ func TestIntegration_UpdatePaths(t *testing.T) {
er := func(_ *WriteResult, err error) error { return err }
codeEq(t, "UpdatePaths on missing doc", codes.NotFound,
er(integrationColl(t).NewDoc().UpdatePaths(ctx, fpus)))
codeEq(t, "UpdatePaths with wrong LastUpdateTime", codes.FailedPrecondition,
er(doc.UpdatePaths(ctx, fpus, LastUpdateTime(wr.UpdateTime.Add(-time.Millisecond)))))
codeEq(t, "UpdatePaths with right LastUpdateTime", codes.OK,
er(doc.UpdatePaths(ctx, fpus, LastUpdateTime(wr.UpdateTime))))
codeEq(t, "Update on missing doc", codes.NotFound,
er(integrationColl(t).NewDoc().Update(ctx, fpus)))
codeEq(t, "Update with wrong LastUpdateTime", codes.FailedPrecondition,
er(doc.Update(ctx, fpus, LastUpdateTime(wr.UpdateTime.Add(-time.Millisecond)))))
codeEq(t, "Update with right LastUpdateTime", codes.OK,
er(doc.Update(ctx, fpus, LastUpdateTime(wr.UpdateTime))))
}
func TestIntegration_Collections(t *testing.T) {
@@ -557,16 +476,12 @@ func TestIntegration_ServerTimestamp(t *testing.T) {
D: map[string]interface{}{"x": ServerTimestamp},
// E is unset, so will get the server timestamp.
}
ctx := context.Background()
doc := integrationColl(t).NewDoc()
// Bound times of the RPC, with some slack for clock skew.
start := time.Now()
mustCreate("ServerTimestamp", t, doc, data)
end := time.Now()
ds, err := doc.Get(ctx)
if err != nil {
t.Fatal(err)
}
ds := mustGet("ServerTimestamp", t, doc)
var got S
if err := ds.DataTo(&got); err != nil {
t.Fatal(err)
@@ -594,24 +509,18 @@ func TestIntegration_MergeServerTimestamp(t *testing.T) {
if err != nil {
t.Fatal(err)
}
docSnap, err := doc.Get(ctx)
if err != nil {
t.Fatal(err)
}
docSnap := mustGet("MergeST #1", t, doc)
data1 := docSnap.Data()
// Merge with a document with a different value of "a". However,
// specify only "b" in the list of merge fields.
_, err = doc.Set(ctx,
map[string]interface{}{"a": 2, "b": ServerTimestamp},
Merge("b"))
Merge([]string{"b"}))
if err != nil {
t.Fatal(err)
}
// The result should leave "a" unchanged, while "b" is updated.
docSnap, err = doc.Get(ctx)
if err != nil {
t.Fatal(err)
}
docSnap = mustGet("MergeST #2", t, doc)
data2 := docSnap.Data()
if got, want := data2["a"], data1["a"]; got != want {
t.Errorf("got %v, want %v", got, want)
@@ -637,11 +546,7 @@ func TestIntegration_MergeNestedServerTimestamp(t *testing.T) {
if err != nil {
t.Fatal(err)
}
docSnap, err := doc.Get(ctx)
if err != nil {
t.Fatal(err)
}
data1 := docSnap.Data()
data1 := mustGet("MergeNST #1", t, doc).Data()
// Merge with a document with a different value of "a". However,
// specify only "c.d" in the list of merge fields.
_, err = doc.Set(ctx,
@@ -650,16 +555,12 @@ func TestIntegration_MergeNestedServerTimestamp(t *testing.T) {
"b": ServerTimestamp,
"c": map[string]interface{}{"d": ServerTimestamp},
},
Merge("c.d"))
Merge([]string{"c", "d"}))
if err != nil {
t.Fatal(err)
}
// The result should leave "a" and "b" unchanged, while "c.d" is updated.
docSnap, err = doc.Get(ctx)
if err != nil {
t.Fatal(err)
}
data2 := docSnap.Data()
data2 := mustGet("MergeNST #2", t, doc).Data()
if got, want := data2["a"], data1["a"]; got != want {
t.Errorf("a: got %v, want %v", got, want)
}
@@ -682,8 +583,8 @@ func TestIntegration_WriteBatch(t *testing.T) {
doc2 := iColl.NewDoc()
b.Create(doc1, integrationTestMap)
b.Set(doc2, integrationTestMap)
b.UpdateMap(doc1, map[string]interface{}{"bool": false})
b.UpdateMap(doc1, map[string]interface{}{"str": Delete})
b.Update(doc1, []Update{{Path: "bool", Value: false}})
b.Update(doc1, []Update{{Path: "str", Value: Delete}})
wrs, err := b.Commit(ctx)
if err != nil {
@@ -692,22 +593,14 @@ func TestIntegration_WriteBatch(t *testing.T) {
if got, want := len(wrs), 4; got != want {
t.Fatalf("got %d WriteResults, want %d", got, want)
}
ds, err := doc1.Get(ctx)
if err != nil {
t.Fatal(err)
}
got1 := ds.Data()
got1 := mustGet("WriteBatch #1", t, doc1).Data()
want := copyMap(wantIntegrationTestMap)
want["bool"] = false
delete(want, "str")
if !testEqual(got1, want) {
t.Errorf("got\n%#v\nwant\n%#v", got1, want)
}
ds, err = doc2.Get(ctx)
if err != nil {
t.Fatal(err)
}
got2 := ds.Data()
got2 := mustGet("WriteBatch #2", t, doc2).Data()
if !testEqual(got2, wantIntegrationTestMap) {
t.Errorf("got\n%#v\nwant\n%#v", got2, wantIntegrationTestMap)
}
@@ -878,14 +771,13 @@ func TestIntegration_RunTransaction(t *testing.T) {
if err != nil {
return err
}
err = tx.UpdateStruct(patDoc, []string{"Score"},
Player{Score: int(score.(int64) + 7)})
err = tx.Update(patDoc, []Update{{Path: "Score", Value: int(score.(int64) + 7)}})
if err != nil {
return err
}
// Since the Star field is called "*", we must use UpdatePaths to change it.
err = tx.UpdatePaths(patDoc,
[]FieldPathUpdate{{Path: []string{"*"}, Value: !star.(bool)}})
// Since the Star field is called "*", we must use Update to change it.
err = tx.Update(patDoc,
[]Update{{FieldPath: []string{"*"}, Value: !star.(bool)}})
if err != nil {
return err
}
@@ -896,10 +788,7 @@ func TestIntegration_RunTransaction(t *testing.T) {
if err != nil {
t.Fatal(err)
}
ds, err := patDoc.Get(ctx)
if err != nil {
t.Fatal(err)
}
ds := mustGet("RunTransaction", t, patDoc)
var got Player
if err := ds.DataTo(&got); err != nil {
t.Fatal(err)
@@ -938,6 +827,14 @@ func mustCreate(msg string, t *testing.T, doc *DocumentRef, data interface{}) *W
return wr
}
func mustGet(msg string, t *testing.T, doc *DocumentRef) *DocumentSnapshot {
d, err := doc.Get(context.Background())
if err != nil {
t.Fatalf("%s: getting: %v", msg, err)
}
return d
}
func copyMap(m map[string]interface{}) map[string]interface{} {
c := map[string]interface{}{}
for k, v := range m {
@@ -948,7 +845,7 @@ func copyMap(m map[string]interface{}) map[string]interface{} {
func checkTimeBetween(t *testing.T, got, low, high time.Time) {
// Allow slack for clock skew.
const slack = 2 * time.Second
const slack = 4 * time.Second
low = low.Add(-slack)
high = high.Add(slack)
if got.Before(low) || got.After(high) {
+10 -4
View File
@@ -2,9 +2,15 @@
SHELL=/bin/bash
../doc.go: doc-snippets.go doc.template snipdoc.awk
../doc.go: build doc-snippets.go doc.template snipdoc.awk
@tmp=$$(mktemp) && \
awk -f snipdoc.awk doc-snippets.go doc.template > $$tmp && \
chmod +w ../doc.go && \
mv $$tmp ../doc.go && \
chmod -w ../doc.go
chmod +w $@ && \
mv $$tmp $@ && \
chmod -w $@
@echo "wrote $@"
.PHONY: build
build:
go build doc-snippets.go
+8 -11
View File
@@ -89,6 +89,10 @@ func f1() {
})
//]
//[ docref.Update
_, err = ca.Update(ctx, []firestore.Update{{Path: "capital", Value: "Sacramento"}})
//]
//[ docref.Delete
_, err = ny.Delete(ctx)
//]
@@ -98,21 +102,15 @@ func f1() {
if err != nil {
// TODO: Handle error.
}
_, err = ca.UpdateStruct(ctx, []string{"capital"}, State{Capital: "Sacramento"},
_, err = ca.Update(ctx,
[]firestore.Update{{Path: "capital", Value: "Sacramento"}},
firestore.LastUpdateTime(docsnap.UpdateTime))
//]
//[ docref.UpdateMap
_, err = ca.UpdateMap(ctx, map[string]interface{}{"pop": 39.2})
//]
//[ docref.UpdateStruct
_, err = ca.UpdateStruct(ctx, []string{"pop"}, State{Population: 39.2})
//]
//[ WriteBatch
writeResults, err := client.Batch().
Create(ny, State{Capital: "Albany"}).
UpdateStruct(ca, []string{"capital"}, State{Capital: "Sacramento"}).
Update(ca, []firestore.Update{{Path: "capital", Value: "Sacramento"}}).
Delete(client.Doc("States/WestDakota")).
Commit(ctx)
//]
@@ -154,8 +152,7 @@ func txn() {
if err != nil {
return err
}
return tx.UpdateStruct(ny, []string{"pop"},
State{Population: pop.(float64) + 0.2})
return tx.Update(ny, []firestore.Update{{Path: "pop", Value: pop.(float64) + 0.2}})
})
if err != nil {
// TODO: Handle error.
+4 -10
View File
@@ -16,7 +16,7 @@
/*
Package firestore provides a client for reading and writing to a Cloud Firestore
database.
database.
See https://cloud.google.com/firestore/docs for an introduction
to Cloud Firestore and additional help on using the Firestore API.
@@ -80,16 +80,10 @@ document or creates a new one.
[docref.Set]
To update some fields of an existing document, use UpdateMap, UpdateStruct or
UpdatePaths. For UpdateMap, the keys of the map specify which fields to change. The
others are untouched.
To update some fields of an existing document, use Update. It takes a list of
paths to update and their corresponding values.
[docref.UpdateMap]
For UpdateStruct, you must explicitly provide the fields to update. The field names
must match exactly.
[docref.UpdateStruct]
[docref.Update]
Use DocumentRef.Delete to delete a document.
+16 -21
View File
@@ -30,9 +30,14 @@ type Precondition interface {
preconditionProto() (*pb.Precondition, error)
}
// Exists returns a Precondition that checks for the existence or non-existence
// of a resource before writing to it. If the check fails, the write does not occur.
func Exists(b bool) Precondition { return exists(b) }
// Exists is a Precondition that checks for the existence of a resource before
// writing to it. If the check fails, the write does not occur.
var Exists Precondition
func init() {
// Initialize here so godoc doesn't show the internal value.
Exists = exists(true)
}
type exists bool
@@ -42,7 +47,13 @@ func (e exists) preconditionProto() (*pb.Precondition, error) {
}, nil
}
func (e exists) String() string { return fmt.Sprintf("Exists(%t)", e) }
func (e exists) String() string {
if e {
return "Exists"
} else {
return "DoesNotExist"
}
}
// LastUpdateTime returns a Precondition that checks that a resource must exist and
// must have last been updated at the given time. If the check fails, the write
@@ -116,23 +127,7 @@ var MergeAll SetOption = merge{all: true}
// overwritten. Other fields on the existing document will be untouched. It is an
// error if a provided field path does not refer to a value in the data passed to
// Set.
//
// Each element of fieldPaths must be a single field or a dot-separated sequence of
// fields, none of which contain the runes "˜*/[]". Use MergePaths instead for such
// paths.
func Merge(fieldPaths ...string) SetOption {
fps, err := parseDotSeparatedStrings(fieldPaths)
if err != nil {
return merge{err: err}
}
return merge{paths: fps}
}
// MergePaths returns a SetOption that causes only the given field paths to be
// overwritten. Other fields on the existing document will be untouched. It is an
// error if a provided field path does not refer to a value in the data passed to
// Set.
func MergePaths(fps ...FieldPath) SetOption {
func Merge(fps ...FieldPath) SetOption {
for _, fp := range fps {
if err := fp.validate(); err != nil {
return merge{err: err}
+9 -13
View File
@@ -35,7 +35,7 @@ func TestProcessPreconditionsForVerify(t *testing.T) {
want: nil,
},
{
in: []Precondition{Exists(true)},
in: []Precondition{Exists},
want: &pb.Precondition{&pb.Precondition_Exists{true}},
},
{
@@ -43,11 +43,11 @@ func TestProcessPreconditionsForVerify(t *testing.T) {
want: &pb.Precondition{&pb.Precondition_UpdateTime{aTimestamp}},
},
{
in: []Precondition{Exists(true), LastUpdateTime(aTime)},
in: []Precondition{Exists, LastUpdateTime(aTime)},
wantErr: true,
},
{
in: []Precondition{Exists(true), Exists(true)},
in: []Precondition{Exists, Exists},
wantErr: true,
},
} {
@@ -78,7 +78,7 @@ func TestProcessPreconditionsForDelete(t *testing.T) {
want: nil,
},
{
in: []Precondition{Exists(true)},
in: []Precondition{Exists},
want: &pb.Precondition{&pb.Precondition_Exists{true}},
},
{
@@ -86,11 +86,11 @@ func TestProcessPreconditionsForDelete(t *testing.T) {
want: &pb.Precondition{&pb.Precondition_UpdateTime{aTimestamp}},
},
{
in: []Precondition{Exists(true), LastUpdateTime(aTime)},
in: []Precondition{Exists, LastUpdateTime(aTime)},
wantErr: true,
},
{
in: []Precondition{Exists(true), Exists(true)},
in: []Precondition{Exists, Exists},
wantErr: true,
},
} {
@@ -122,11 +122,7 @@ func TestProcessPreconditionsForUpdate(t *testing.T) {
},
{
in: []Precondition{Exists(true)},
wantErr: true,
},
{
in: []Precondition{Exists(false)},
in: []Precondition{Exists},
wantErr: true,
},
{
@@ -134,11 +130,11 @@ func TestProcessPreconditionsForUpdate(t *testing.T) {
want: &pb.Precondition{&pb.Precondition_UpdateTime{aTimestamp}},
},
{
in: []Precondition{Exists(true), LastUpdateTime(aTime)},
in: []Precondition{Exists, LastUpdateTime(aTime)},
wantErr: true,
},
{
in: []Precondition{Exists(true), Exists(true)},
in: []Precondition{Exists, Exists},
wantErr: true,
},
} {
+16 -7
View File
@@ -51,11 +51,13 @@ type Query struct {
// in queries.
const DocumentID = "__name__"
// Select returns a new Query that specifies the field paths
// Select returns a new Query that specifies the paths
// to return from the result documents.
func (q Query) Select(fieldPaths ...string) Query {
// Each path argument can be a single field or a dot-separated sequence of
// fields, and must not contain any of the runes "˜*/[]".
func (q Query) Select(paths ...string) Query {
var fps []FieldPath
for _, s := range fieldPaths {
for _, s := range paths {
fp, err := parseDotSeparatedString(s)
if err != nil {
q.err = err
@@ -80,8 +82,11 @@ func (q Query) SelectPaths(fieldPaths ...FieldPath) Query {
// Where returns a new Query that filters the set of results.
// A Query can have multiple filters.
func (q Query) Where(fieldPath, op string, value interface{}) Query {
fp, err := parseDotSeparatedString(fieldPath)
// The path argument can be a single field or a dot-separated sequence of
// fields, and must not contain any of the runes "˜*/[]".
// The op argument must be one of "==", "<", "<=", ">" or ">=".
func (q Query) Where(path, op string, value interface{}) Query {
fp, err := parseDotSeparatedString(path)
if err != nil {
q.err = err
return q
@@ -92,6 +97,7 @@ func (q Query) Where(fieldPath, op string, value interface{}) Query {
// WherePath returns a new Query that filters the set of results.
// A Query can have multiple filters.
// The op argument must be one of "==", "<", "<=", ">" or ">=".
func (q Query) WherePath(fp FieldPath, op string, value interface{}) Query {
q.filters = append(append([]filter(nil), q.filters...), filter{fp, op, value})
return q
@@ -112,9 +118,12 @@ const (
// returned. A Query can have multiple OrderBy/OrderByPath specifications. OrderBy
// appends the specification to the list of existing ones.
//
// The path argument can be a single field or a dot-separated sequence of
// fields, and must not contain any of the runes "˜*/[]".
//
// To order by document name, use the special field path DocumentID.
func (q Query) OrderBy(fieldPath string, dir Direction) Query {
fp, err := parseDotSeparatedString(fieldPath)
func (q Query) OrderBy(path string, dir Direction) Query {
fp, err := parseDotSeparatedString(path)
if err != nil {
q.err = err
return q
+11
View File
@@ -0,0 +1,11 @@
# Copy textproto files in this directory from the source of truth.
SRC=$(GOPATH)/src/github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/testdata
.PHONY: refresh
refresh:
-rm *.textproto
cp $(SRC)/*.textproto .
openssl dgst -sha1 $(SRC)/tests.binprotos > VERSION
+1
View File
@@ -0,0 +1 @@
SHA1(/usr/local/google/home/jba/go/src/github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/testdata/tests.binprotos)= b0fbaaac8664945cb4f5667da092a6f9ededc57e
+27
View File
@@ -0,0 +1,27 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A simple call, resulting in a single update operation.
description: "create: basic"
create: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": 1}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
current_document: <
exists: false
>
>
>
>
+61
View File
@@ -0,0 +1,61 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A call to a write method with complicated input data.
description: "create: complex"
create: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": [1, 2.5], \"b\": {\"c\": [\"three\", {\"d\": true}]}}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
array_value: <
values: <
integer_value: 1
>
values: <
double_value: 2.5
>
>
>
>
fields: <
key: "b"
value: <
map_value: <
fields: <
key: "c"
value: <
array_value: <
values: <
string_value: "three"
>
values: <
map_value: <
fields: <
key: "d"
value: <
boolean_value: true
>
>
>
>
>
>
>
>
>
>
>
current_document: <
exists: false
>
>
>
>
@@ -0,0 +1,13 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The Delete sentinel must be the value of a field. Deletes are implemented by
# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not
# support array indexing.
description: "create: Delete cannot be anywhere inside an array value"
create: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": [1, {\"b\": \"Delete\"}]}"
is_error: true
>
@@ -0,0 +1,13 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The Delete sentinel must be the value of a field. Deletes are implemented by
# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not
# support array indexing.
description: "create: Delete cannot be in an array value"
create: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": [1, 2, \"Delete\"]}"
is_error: true
>
+20
View File
@@ -0,0 +1,20 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
description: "create: creating or setting an empty map"
create: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
>
current_document: <
exists: false
>
>
>
>
+11
View File
@@ -0,0 +1,11 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The Delete sentinel cannot be used in Create, or in Set without a Merge option.
description: "create: Delete cannot appear in data"
create: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": 1, \"b\": \"Delete\"}"
is_error: true
>
+40
View File
@@ -0,0 +1,40 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# Create and Set treat their map keys literally. They do not split on dots.
description: "create: don\342\200\231t split on dots"
create: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{ \"a.b\": { \"c.d\": 1 }, \"e\": 2 }"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a.b"
value: <
map_value: <
fields: <
key: "c.d"
value: <
integer_value: 1
>
>
>
>
>
fields: <
key: "e"
value: <
integer_value: 2
>
>
>
current_document: <
exists: false
>
>
>
>
@@ -0,0 +1,41 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# Create and Set treat their map keys literally. They do not escape special
# characters.
description: "create: non-alpha characters in map keys"
create: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{ \"*\": { \".\": 1 }, \"~\": 2 }"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "*"
value: <
map_value: <
fields: <
key: "."
value: <
integer_value: 1
>
>
>
>
>
fields: <
key: "~"
value: <
integer_value: 2
>
>
>
current_document: <
exists: false
>
>
>
>
@@ -0,0 +1,26 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# If the only values in the input are ServerTimestamps, then no update operation
# should be produced.
description: "create: ServerTimestamp alone"
create: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": \"ServerTimestamp\"}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
transform: <
document: "projects/projectID/databases/(default)/documents/C/d"
field_transforms: <
field_path: "a"
set_to_server_value: REQUEST_TIME
>
>
current_document: <
exists: false
>
>
>
>
@@ -0,0 +1,41 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A document can have more than one ServerTimestamp field. Since all the
# ServerTimestamp fields are removed, the only field in the update is "a".
description: "create: multiple ServerTimestamp fields"
create: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\", \"c\": {\"d\": \"ServerTimestamp\"}}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
current_document: <
exists: false
>
>
writes: <
transform: <
document: "projects/projectID/databases/(default)/documents/C/d"
field_transforms: <
field_path: "b"
set_to_server_value: REQUEST_TIME
>
field_transforms: <
field_path: "c.d"
set_to_server_value: REQUEST_TIME
>
>
>
>
>
@@ -0,0 +1,38 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A ServerTimestamp value can occur at any depth. In this case, the transform
# applies to the field path "b.c". Since "c" is removed from the update, "b"
# becomes empty, so it is also removed from the update.
description: "create: nested ServerTimestamp field"
create: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": 1, \"b\": {\"c\": \"ServerTimestamp\"}}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
current_document: <
exists: false
>
>
writes: <
transform: <
document: "projects/projectID/databases/(default)/documents/C/d"
field_transforms: <
field_path: "b.c"
set_to_server_value: REQUEST_TIME
>
>
>
>
>
@@ -0,0 +1,12 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# There cannot be an array value anywhere on the path from the document root to
# the ServerTimestamp sentinel. Firestore transforms don't support array indexing.
description: "create: ServerTimestamp cannot be anywhere inside an array value"
create: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": [1, {\"b\": \"ServerTimestamp\"}]}"
is_error: true
>
@@ -0,0 +1,12 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The ServerTimestamp sentinel must be the value of a field. Firestore transforms
# don't support array indexing.
description: "create: ServerTimestamp cannot be in an array value"
create: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": [1, 2, \"ServerTimestamp\"]}"
is_error: true
>
+39
View File
@@ -0,0 +1,39 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A key with the special ServerTimestamp sentinel is removed from the data in the
# update operation. Instead it appears in a separate Transform operation. Note
# that in these tests, the string "ServerTimestamp" should be replaced with the
# special ServerTimestamp value.
description: "create: ServerTimestamp with data"
create: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
current_document: <
exists: false
>
>
writes: <
transform: <
document: "projects/projectID/databases/(default)/documents/C/d"
field_transforms: <
field_path: "b"
set_to_server_value: REQUEST_TIME
>
>
>
>
>
@@ -0,0 +1,21 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# Delete supports an exists precondition.
description: "delete: delete with exists precondition"
delete: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
precondition: <
exists: true
>
request: <
database: "projects/projectID/databases/(default)"
writes: <
delete: "projects/projectID/databases/(default)/documents/C/d"
current_document: <
exists: true
>
>
>
>
@@ -0,0 +1,15 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# An ordinary Delete call.
description: "delete: delete without precondition"
delete: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
request: <
database: "projects/projectID/databases/(default)"
writes: <
delete: "projects/projectID/databases/(default)/documents/C/d"
>
>
>
@@ -0,0 +1,25 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# Delete supports a last-update-time precondition.
description: "delete: delete with last-update-time precondition"
delete: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
precondition: <
update_time: <
seconds: 42
>
>
request: <
database: "projects/projectID/databases/(default)"
writes: <
delete: "projects/projectID/databases/(default)/documents/C/d"
current_document: <
update_time: <
seconds: 42
>
>
>
>
>
-23
View File
@@ -1,23 +0,0 @@
{
"comment": "Field path test suite. Backslash must be escaped in JSON strings.",
"good": [
["a", "a"],
["__x__", "__x__"],
["aBc0_d1", "aBc0_d1"],
["a.b", "a", "b"],
["`a`", "a"],
["`a`.b", "a", "b"],
["`a`.`b`.c", "a", "b", "c"],
["`a.b`.c", "a.b", "c"],
["`..`.`...`", "..", "..."],
["` `", " "],
["`\t\t`.` x\t`", "\t\t", " x\t"],
["`\b\f\r`", "\b\f\r"],
["`a\\`b`.`c\\\\d`", "a`b", "c\\d"],
["`\\\\`.`\\`\\``", "\\", "``"]
],
"bad": ["", " ", "\t", "a.", ".a", "a.b.", "a..b",
"`", "``", "`a", "a`", "a`b", "`a`b", "a`b`", "`a`.b`c`",
"\\", "\\`"]
}
+12
View File
@@ -0,0 +1,12 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A call to DocumentRef.Get.
description: "get: get a document"
get: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
request: <
name: "projects/projectID/databases/(default)/documents/C/d"
>
>
+24
View File
@@ -0,0 +1,24 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A simple call, resulting in a single update operation.
description: "set: basic"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": 1}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
>
>
>
+58
View File
@@ -0,0 +1,58 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A call to a write method with complicated input data.
description: "set: complex"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": [1, 2.5], \"b\": {\"c\": [\"three\", {\"d\": true}]}}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
array_value: <
values: <
integer_value: 1
>
values: <
double_value: 2.5
>
>
>
>
fields: <
key: "b"
value: <
map_value: <
fields: <
key: "c"
value: <
array_value: <
values: <
string_value: "three"
>
values: <
map_value: <
fields: <
key: "d"
value: <
boolean_value: true
>
>
>
>
>
>
>
>
>
>
>
>
>
>
@@ -0,0 +1,28 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A Delete sentinel can appear with a merge option. If the delete paths are the
# only ones to be merged, then no document is sent, just an update mask.
description: "set-merge: Delete with merge"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
fields: <
field: "b"
field: "c"
>
>
json_data: "{\"a\": 1, \"b\": {\"c\": \"Delete\"}}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
>
update_mask: <
field_paths: "b.c"
>
>
>
>
+37
View File
@@ -0,0 +1,37 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A Delete sentinel can appear with a merge option.
description: "set-merge: Delete with merge"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
fields: <
field: "a"
>
fields: <
field: "b"
field: "c"
>
>
json_data: "{\"a\": 1, \"b\": {\"c\": \"Delete\"}}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
update_mask: <
field_paths: "a"
field_paths: "b.c"
>
>
>
>
@@ -0,0 +1,31 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A Delete sentinel can appear with a mergeAll option.
description: "set: Delete with MergeAll"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
all: true
>
json_data: "{\"a\": 1, \"b\": {\"c\": \"Delete\"}}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
update_mask: <
field_paths: "a"
field_paths: "b.c"
>
>
>
>
@@ -0,0 +1,13 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The Delete sentinel must be the value of a field. Deletes are implemented by
# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not
# support array indexing.
description: "set: Delete cannot be anywhere inside an array value"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": [1, {\"b\": \"Delete\"}]}"
is_error: true
>
@@ -0,0 +1,13 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The Delete sentinel must be the value of a field. Deletes are implemented by
# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not
# support array indexing.
description: "set: Delete cannot be in an array value"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": [1, 2, \"Delete\"]}"
is_error: true
>
@@ -0,0 +1,17 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The client signals an error if the Delete sentinel is in the input data, but not
# selected by a merge option, because this is most likely a programming bug.
description: "set-merge: Delete cannot appear in an unmerged field"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
fields: <
field: "a"
>
>
json_data: "{\"a\": 1, \"b\": \"Delete\"}"
is_error: true
>
@@ -0,0 +1,19 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# If a Delete is part of the value at a merge path, then the user is confused:
# their merge path says "replace this entire value" but their Delete says "delete
# this part of the value". This should be an error, just as if they specified
# Delete in a Set with no merge.
description: "set-merge: Delete cannot appear as part of a merge path"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
fields: <
field: "h"
>
>
json_data: "{\"h\": {\"g\": \"Delete\"}}"
is_error: true
>
@@ -0,0 +1,12 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# Without a merge option, Set replaces the document with the input data. A Delete
# sentinel in the data makes no sense in this case.
description: "set: Delete cannot appear unless a merge option is specified"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": 1, \"b\": \"Delete\"}"
is_error: true
>
+17
View File
@@ -0,0 +1,17 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
description: "set: creating or setting an empty map"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
>
>
>
>
+40
View File
@@ -0,0 +1,40 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A merge with fields that use special characters.
description: "set-merge: Merge with FieldPaths"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
fields: <
field: "*"
field: "~"
>
>
json_data: "{\"*\": {\"~\": true}}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "*"
value: <
map_value: <
fields: <
key: "~"
value: <
boolean_value: true
>
>
>
>
>
>
update_mask: <
field_paths: "`*`.`~`"
>
>
>
>
@@ -0,0 +1,41 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A merge option where the field is not at top level. Only fields mentioned in the
# option are present in the update operation.
description: "set-merge: Merge with a nested field"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
fields: <
field: "h"
field: "g"
>
>
json_data: "{\"h\": {\"g\": 4, \"f\": 5}}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "h"
value: <
map_value: <
fields: <
key: "g"
value: <
integer_value: 4
>
>
>
>
>
>
update_mask: <
field_paths: "h.g"
>
>
>
>
@@ -0,0 +1,46 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# If a field path is in a merge option, the value at that path replaces the stored
# value. That is true even if the value is complex.
description: "set-merge: Merge field is not a leaf"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
fields: <
field: "h"
>
>
json_data: "{\"h\": {\"f\": 5, \"g\": 6}, \"e\": 7}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "h"
value: <
map_value: <
fields: <
key: "f"
value: <
integer_value: 5
>
>
fields: <
key: "g"
value: <
integer_value: 6
>
>
>
>
>
>
update_mask: <
field_paths: "h"
>
>
>
>
@@ -0,0 +1,21 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The prefix would make the other path meaningless, so this is probably a
# programming error.
description: "set-merge: One merge path cannot be the prefix of another"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
fields: <
field: "a"
>
fields: <
field: "a"
field: "b"
>
>
json_data: "{\"a\": {\"b\": 1}}"
is_error: true
>
@@ -0,0 +1,20 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The client signals an error if a merge option mentions a path that is not in the
# input data.
description: "set-merge: Merge fields must all be present in data"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
fields: <
field: "b"
>
fields: <
field: "a"
>
>
json_data: "{\"a\": 1}"
is_error: true
>
+32
View File
@@ -0,0 +1,32 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# Fields in the input data but not in a merge option are pruned.
description: "set-merge: Merge with a field"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
fields: <
field: "a"
>
>
json_data: "{\"a\": 1, \"b\": 2}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
update_mask: <
field_paths: "a"
>
>
>
>
@@ -0,0 +1,15 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# It makes no sense to specify MergeAll and provide no data, so we disallow it on
# the client.
description: "set: MergeAll cannot be specified with empty data."
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
all: true
>
json_data: "{}"
is_error: true
>
@@ -0,0 +1,45 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# MergeAll with nested fields results in an update mask that includes entries for
# all the leaf fields.
description: "set: MergeAll with nested fields"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
all: true
>
json_data: "{\"h\": { \"g\": 3, \"f\": 4 }}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "h"
value: <
map_value: <
fields: <
key: "f"
value: <
integer_value: 4
>
>
fields: <
key: "g"
value: <
integer_value: 3
>
>
>
>
>
>
update_mask: <
field_paths: "h.f"
field_paths: "h.g"
>
>
>
>
+37
View File
@@ -0,0 +1,37 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The MergeAll option with a simple piece of data.
description: "set: MergeAll"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
all: true
>
json_data: "{\"a\": 1, \"b\": 2}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
fields: <
key: "b"
value: <
integer_value: 2
>
>
>
update_mask: <
field_paths: "a"
field_paths: "b"
>
>
>
>
+11
View File
@@ -0,0 +1,11 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The Delete sentinel cannot be used in Create, or in Set without a Merge option.
description: "set: Delete cannot appear in data"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": 1, \"b\": \"Delete\"}"
is_error: true
>
+37
View File
@@ -0,0 +1,37 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# Create and Set treat their map keys literally. They do not split on dots.
description: "set: don\342\200\231t split on dots"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{ \"a.b\": { \"c.d\": 1 }, \"e\": 2 }"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a.b"
value: <
map_value: <
fields: <
key: "c.d"
value: <
integer_value: 1
>
>
>
>
>
fields: <
key: "e"
value: <
integer_value: 2
>
>
>
>
>
>
@@ -0,0 +1,38 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# Create and Set treat their map keys literally. They do not escape special
# characters.
description: "set: non-alpha characters in map keys"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{ \"*\": { \".\": 1 }, \"~\": 2 }"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "*"
value: <
map_value: <
fields: <
key: "."
value: <
integer_value: 1
>
>
>
>
>
fields: <
key: "~"
value: <
integer_value: 2
>
>
>
>
>
>
@@ -0,0 +1,26 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# If the only values in the input are ServerTimestamps, then no update operation
# should be produced.
description: "set: ServerTimestamp alone with MergeAll"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
all: true
>
json_data: "{\"a\": \"ServerTimestamp\"}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
transform: <
document: "projects/projectID/databases/(default)/documents/C/d"
field_transforms: <
field_path: "a"
set_to_server_value: REQUEST_TIME
>
>
>
>
>
+28
View File
@@ -0,0 +1,28 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# If the only values in the input are ServerTimestamps, then an update operation
# with an empty map should be produced.
description: "set: ServerTimestamp alone"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": \"ServerTimestamp\"}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
>
>
writes: <
transform: <
document: "projects/projectID/databases/(default)/documents/C/d"
field_transforms: <
field_path: "a"
set_to_server_value: REQUEST_TIME
>
>
>
>
>
@@ -0,0 +1,45 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# Just as when no merge option is specified, ServerTimestamp sentinel values are
# removed from the data in the update operation and become transforms.
description: "set-merge: ServerTimestamp with Merge of both fields"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
fields: <
field: "a"
>
fields: <
field: "b"
>
>
json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
update_mask: <
field_paths: "a"
>
>
writes: <
transform: <
document: "projects/projectID/databases/(default)/documents/C/d"
field_transforms: <
field_path: "b"
set_to_server_value: REQUEST_TIME
>
>
>
>
>
@@ -0,0 +1,37 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# If a field path is in a merge option, the value at that path replaces the stored
# value. If the value has only ServerTimestamps, they become transforms and we
# clear the value by including the field path in the update mask.
description: "set-merge: non-leaf merge field with ServerTimestamp alone"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
fields: <
field: "h"
>
>
json_data: "{\"h\": {\"g\": \"ServerTimestamp\"}, \"e\": 7}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
>
update_mask: <
field_paths: "h"
>
>
writes: <
transform: <
document: "projects/projectID/databases/(default)/documents/C/d"
field_transforms: <
field_path: "h.g"
set_to_server_value: REQUEST_TIME
>
>
>
>
>
@@ -0,0 +1,49 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# If a field path is in a merge option, the value at that path replaces the stored
# value, and ServerTimestamps inside that value become transforms as usual.
description: "set-merge: non-leaf merge field with ServerTimestamp"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
fields: <
field: "h"
>
>
json_data: "{\"h\": {\"f\": 5, \"g\": \"ServerTimestamp\"}, \"e\": 7}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "h"
value: <
map_value: <
fields: <
key: "f"
value: <
integer_value: 5
>
>
>
>
>
>
update_mask: <
field_paths: "h"
>
>
writes: <
transform: <
document: "projects/projectID/databases/(default)/documents/C/d"
field_transforms: <
field_path: "h.g"
set_to_server_value: REQUEST_TIME
>
>
>
>
>
@@ -0,0 +1,28 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# If all the fields in the merge option have ServerTimestamp values, then no
# update operation is produced, only a transform.
description: "set-merge: If no ordinary values in Merge, no write"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
fields: <
field: "b"
>
>
json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
transform: <
document: "projects/projectID/databases/(default)/documents/C/d"
field_transforms: <
field_path: "b"
set_to_server_value: REQUEST_TIME
>
>
>
>
>
@@ -0,0 +1,40 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# Just as when no merge option is specified, ServerTimestamp sentinel values are
# removed from the data in the update operation and become transforms.
description: "set: ServerTimestamp with MergeAll"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
all: true
>
json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
update_mask: <
field_paths: "a"
>
>
writes: <
transform: <
document: "projects/projectID/databases/(default)/documents/C/d"
field_transforms: <
field_path: "b"
set_to_server_value: REQUEST_TIME
>
>
>
>
>
+38
View File
@@ -0,0 +1,38 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A document can have more than one ServerTimestamp field. Since all the
# ServerTimestamp fields are removed, the only field in the update is "a".
description: "set: multiple ServerTimestamp fields"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\", \"c\": {\"d\": \"ServerTimestamp\"}}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
>
writes: <
transform: <
document: "projects/projectID/databases/(default)/documents/C/d"
field_transforms: <
field_path: "b"
set_to_server_value: REQUEST_TIME
>
field_transforms: <
field_path: "c.d"
set_to_server_value: REQUEST_TIME
>
>
>
>
>
+35
View File
@@ -0,0 +1,35 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A ServerTimestamp value can occur at any depth. In this case, the transform
# applies to the field path "b.c". Since "c" is removed from the update, "b"
# becomes empty, so it is also removed from the update.
description: "set: nested ServerTimestamp field"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": 1, \"b\": {\"c\": \"ServerTimestamp\"}}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
>
writes: <
transform: <
document: "projects/projectID/databases/(default)/documents/C/d"
field_transforms: <
field_path: "b.c"
set_to_server_value: REQUEST_TIME
>
>
>
>
>
@@ -0,0 +1,12 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# There cannot be an array value anywhere on the path from the document root to
# the ServerTimestamp sentinel. Firestore transforms don't support array indexing.
description: "set: ServerTimestamp cannot be anywhere inside an array value"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": [1, {\"b\": \"ServerTimestamp\"}]}"
is_error: true
>
+12
View File
@@ -0,0 +1,12 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The ServerTimestamp sentinel must be the value of a field. Firestore transforms
# don't support array indexing.
description: "set: ServerTimestamp cannot be in an array value"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": [1, 2, \"ServerTimestamp\"]}"
is_error: true
>
+33
View File
@@ -0,0 +1,33 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# If the ServerTimestamp value is not mentioned in a merge option, then it is
# pruned from the data but does not result in a transform.
description: "set-merge: If is ServerTimestamp not in Merge, no transform"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
option: <
fields: <
field: "a"
>
>
json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
update_mask: <
field_paths: "a"
>
>
>
>
+36
View File
@@ -0,0 +1,36 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A key with the special ServerTimestamp sentinel is removed from the data in the
# update operation. Instead it appears in a separate Transform operation. Note
# that in these tests, the string "ServerTimestamp" should be replaced with the
# special ServerTimestamp value.
description: "set: ServerTimestamp with data"
set: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": 1, \"b\": \"ServerTimestamp\"}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
>
writes: <
transform: <
document: "projects/projectID/databases/(default)/documents/C/d"
field_transforms: <
field_path: "b"
set_to_server_value: REQUEST_TIME
>
>
>
>
>
+12
View File
@@ -0,0 +1,12 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The keys of the data given to Update are interpreted, unlike those of Create and
# Set. They cannot contain special characters.
description: "update: invalid character"
update: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a~b\": 1}"
is_error: true
>
+30
View File
@@ -0,0 +1,30 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A simple call, resulting in a single update operation.
description: "update: basic"
update: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": 1}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
update_mask: <
field_paths: "a"
>
current_document: <
exists: true
>
>
>
>
+65
View File
@@ -0,0 +1,65 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A call to a write method with complicated input data.
description: "update: complex"
update: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": [1, 2.5], \"b\": {\"c\": [\"three\", {\"d\": true}]}}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
array_value: <
values: <
integer_value: 1
>
values: <
double_value: 2.5
>
>
>
>
fields: <
key: "b"
value: <
map_value: <
fields: <
key: "c"
value: <
array_value: <
values: <
string_value: "three"
>
values: <
map_value: <
fields: <
key: "d"
value: <
boolean_value: true
>
>
>
>
>
>
>
>
>
>
>
update_mask: <
field_paths: "a"
field_paths: "b"
>
current_document: <
exists: true
>
>
>
>
@@ -0,0 +1,25 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# If the input data consists solely of Deletes, then the update operation has no
# map, just an update mask.
description: "update: Delete alone"
update: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": \"Delete\"}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
>
update_mask: <
field_paths: "a"
>
current_document: <
exists: true
>
>
>
>
+46
View File
@@ -0,0 +1,46 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# After expanding top-level dotted fields, fields with Delete values are pruned
# from the output data, but appear in the update mask.
description: "update: Delete with a dotted field"
update: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": 1, \"b.c\": \"Delete\", \"b.d\": 2}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
fields: <
key: "b"
value: <
map_value: <
fields: <
key: "d"
value: <
integer_value: 2
>
>
>
>
>
>
update_mask: <
field_paths: "a"
field_paths: "b.c"
field_paths: "b.d"
>
current_document: <
exists: true
>
>
>
>
@@ -0,0 +1,11 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The Delete sentinel must be the value of a top-level key.
description: "update: Delete cannot be nested"
update: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": {\"b\": \"Delete\"}}"
is_error: true
>
@@ -0,0 +1,13 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The Delete sentinel must be the value of a field. Deletes are implemented by
# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not
# support array indexing.
description: "update: Delete cannot be anywhere inside an array value"
update: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": [1, {\"b\": \"Delete\"}]}"
is_error: true
>
@@ -0,0 +1,13 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The Delete sentinel must be the value of a field. Deletes are implemented by
# turning the path to the Delete sentinel into a FieldPath, and FieldPaths do not
# support array indexing.
description: "update: Delete cannot be in an array value"
update: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": [1, 2, \"Delete\"]}"
is_error: true
>
+32
View File
@@ -0,0 +1,32 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# If a field's value is the Delete sentinel, then it doesn't appear in the update
# data, but does in the mask.
description: "update: Delete"
update: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a\": 1, \"b\": \"Delete\"}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
update_mask: <
field_paths: "a"
field_paths: "b"
>
current_document: <
exists: true
>
>
>
>
@@ -0,0 +1,14 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The Update method does not support an explicit exists precondition.
description: "update: Exists precondition is invalid"
update: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
precondition: <
exists: true
>
json_data: "{\"a\": 1}"
is_error: true
>
@@ -0,0 +1,11 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# Empty fields are not allowed.
description: "update: empty field path component"
update: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{\"a..b\": 1}"
is_error: true
>
@@ -0,0 +1,11 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# It is a client-side error to call Update with empty data.
description: "update: no paths"
update: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
json_data: "{}"
is_error: true
>
@@ -0,0 +1,33 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A simple call, resulting in a single update operation.
description: "update-paths: basic"
update_paths: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
field_paths: <
field: "a"
>
json_values: "1"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
integer_value: 1
>
>
>
update_mask: <
field_paths: "a"
>
current_document: <
exists: true
>
>
>
>
@@ -0,0 +1,72 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# A call to a write method with complicated input data.
description: "update-paths: complex"
update_paths: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
field_paths: <
field: "a"
>
field_paths: <
field: "b"
>
json_values: "[1, 2.5]"
json_values: "{\"c\": [\"three\", {\"d\": true}]}"
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
fields: <
key: "a"
value: <
array_value: <
values: <
integer_value: 1
>
values: <
double_value: 2.5
>
>
>
>
fields: <
key: "b"
value: <
map_value: <
fields: <
key: "c"
value: <
array_value: <
values: <
string_value: "three"
>
values: <
map_value: <
fields: <
key: "d"
value: <
boolean_value: true
>
>
>
>
>
>
>
>
>
>
>
update_mask: <
field_paths: "a"
field_paths: "b"
>
current_document: <
exists: true
>
>
>
>
@@ -0,0 +1,28 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# If the input data consists solely of Deletes, then the update operation has no
# map, just an update mask.
description: "update-paths: Delete alone"
update_paths: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
field_paths: <
field: "a"
>
json_values: "\"Delete\""
request: <
database: "projects/projectID/databases/(default)"
writes: <
update: <
name: "projects/projectID/databases/(default)/documents/C/d"
>
update_mask: <
field_paths: "a"
>
current_document: <
exists: true
>
>
>
>
@@ -0,0 +1,14 @@
# DO NOT MODIFY. This file was generated by
# github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/cmd/generate-firestore-tests/generate-firestore-tests.go.
# The Delete sentinel must be the value of a top-level key.
description: "update-paths: Delete cannot be nested"
update_paths: <
doc_ref_path: "projects/projectID/databases/(default)/documents/C/d"
field_paths: <
field: "a"
>
json_values: "{\"b\": \"Delete\"}"
is_error: true
>

Some files were not shown because too many files have changed in this diff Show More