mirror of
https://github.com/restic/restic.git
synced 2026-10-06 10:50:31 +00:00
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:
+2
-2
@@ -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
@@ -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) {
|
||||
|
||||
+15
-15
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
|
||||
The contents of this directory are copied from
|
||||
github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/genproto.
|
||||
|
||||
+662
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1 @@
|
||||
SHA1(/usr/local/google/home/jba/go/src/github.com/GoogleCloudPlatform/google-cloud-common/testing/firestore/testdata/tests.binprotos)= b0fbaaac8664945cb4f5667da092a6f9ededc57e
|
||||
+27
@@ -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
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
Generated
Vendored
+13
@@ -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
|
||||
>
|
||||
+13
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+41
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+26
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+41
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+38
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+12
@@ -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
|
||||
>
|
||||
+12
@@ -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
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+21
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+15
@@ -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"
|
||||
>
|
||||
>
|
||||
>
|
||||
+25
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+28
@@ -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
@@ -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"
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+31
@@ -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"
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+13
@@ -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
|
||||
>
|
||||
+13
@@ -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
|
||||
>
|
||||
+17
@@ -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
|
||||
>
|
||||
+19
@@ -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
|
||||
>
|
||||
+12
@@ -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
@@ -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
@@ -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: "`*`.`~`"
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+41
@@ -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"
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+46
@@ -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"
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+21
@@ -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
|
||||
>
|
||||
+20
@@ -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
@@ -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"
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+15
@@ -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
|
||||
>
|
||||
+45
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+38
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+26
@@ -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
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+45
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
Generated
Vendored
+37
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+49
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+28
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+40
@@ -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
@@ -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
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+12
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+25
@@ -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
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+11
@@ -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
|
||||
>
|
||||
Generated
Vendored
+13
@@ -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
|
||||
>
|
||||
+13
@@ -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
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+14
@@ -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
|
||||
>
|
||||
Generated
Vendored
+11
@@ -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
|
||||
>
|
||||
+11
@@ -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
|
||||
>
|
||||
+33
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+72
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+28
@@ -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
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
+14
@@ -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
Reference in New Issue
Block a user