diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e9253304..7c4b83697 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ [1]: https://www.npmjs.com/package/@google-cloud/firestore?activeTab=versions +## [6.6.0](https://github.com/googleapis/nodejs-firestore/compare/v6.5.0...v6.6.0) (2023-05-18) + + +### Features + +* Add ApiScope and COLLECTION_RECURSIVE query_scope for Firestore index ([#1849](https://github.com/googleapis/nodejs-firestore/issues/1849)) ([b671452](https://github.com/googleapis/nodejs-firestore/commit/b6714528956f3907b1ca4aded372592ef00d34d6)) +* Add bloom filter related proto fields ([#1843](https://github.com/googleapis/nodejs-firestore/issues/1843)) ([b64e0c1](https://github.com/googleapis/nodejs-firestore/commit/b64e0c15d0f824a688ff42a8ad940b520f87cf9b)) +* Add support for environment variable FIRESTORE_PREFER_REST ([#1848](https://github.com/googleapis/nodejs-firestore/issues/1848)) ([96b1d2a](https://github.com/googleapis/nodejs-firestore/commit/96b1d2ab3248f6c4bb70d1cf735aea827a2a13da)) + ## [6.5.0](https://github.com/googleapis/nodejs-firestore/compare/v6.4.3...v6.5.0) (2023-03-06) diff --git a/dev/protos/google/api/client.proto b/dev/protos/google/api/client.proto index 227ccf3a5..6d01954ee 100644 --- a/dev/protos/google/api/client.proto +++ b/dev/protos/google/api/client.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -112,7 +112,9 @@ message CommonLanguageSettings { // Details about how and where to publish client libraries. message ClientLibrarySettings { - // Version of the API to apply these settings to. + // Version of the API to apply these settings to. This is the full protobuf + // package for the API, ending in the version element. + // Examples: "google.cloud.speech.v1" and "google.spanner.admin.database.v1". string version = 1; // Launch stage of this version of the API. @@ -155,7 +157,7 @@ message Publishing { // long-running operation pattern. repeated MethodSettings method_settings = 2; - // Link to a place that API users can report issues. Example: + // Link to a *public* URI where users can report issues. Example: // https://issuetracker.google.com/issues/new?component=190865&template=1161103 string new_issue_uri = 101; @@ -186,6 +188,10 @@ message Publishing { // times in this list, then the last one wins. Settings from earlier // settings with the same version string are discarded. repeated ClientLibrarySettings library_settings = 109; + + // Optional link to proto reference documentation. Example: + // https://cloud.google.com/pubsub/lite/docs/reference/rpc + string proto_reference_documentation_uri = 110; } // Settings for Java client libraries. @@ -251,6 +257,36 @@ message NodeSettings { message DotnetSettings { // Some settings. CommonLanguageSettings common = 1; + + // Map from original service names to renamed versions. + // This is used when the default generated types + // would cause a naming conflict. (Neither name is + // fully-qualified.) + // Example: Subscriber to SubscriberServiceApi. + map renamed_services = 2; + + // Map from full resource types to the effective short name + // for the resource. This is used when otherwise resource + // named from different services would cause naming collisions. + // Example entry: + // "datalabeling.googleapis.com/Dataset": "DataLabelingDataset" + map renamed_resources = 3; + + // List of full resource types to ignore during generation. + // This is typically used for API-specific Location resources, + // which should be handled by the generator as if they were actually + // the common Location resources. + // Example entry: "documentai.googleapis.com/Location" + repeated string ignored_resources = 4; + + // Namespaces which must be aliased in snippets due to + // a known (but non-generator-predictable) naming collision + repeated string forced_namespace_aliases = 5; + + // Method signatures (in the form "service.method(signature)") + // which are provided separately, so shouldn't be generated. + // Snippets *calling* these methods are still generated, however. + repeated string handwritten_signatures = 6; } // Settings for Ruby client libraries. @@ -302,8 +338,8 @@ message MethodSettings { // Example of a YAML configuration:: // // publishing: - // method_behavior: - // - selector: CreateAdDomain + // method_settings: + // - selector: google.cloud.speech.v2.Speech.BatchRecognize // long_running: // initial_poll_delay: // seconds: 60 # 1 minute @@ -332,6 +368,15 @@ enum ClientLibraryOrganization { // Street View Org. STREET_VIEW = 4; + + // Shopping Org. + SHOPPING = 5; + + // Geo Org. + GEO = 6; + + // Generative AI - https://developers.generativeai.google + GENERATIVE_AI = 7; } // To where should client libraries be published? diff --git a/dev/protos/google/api/field_behavior.proto b/dev/protos/google/api/field_behavior.proto index c4abe3b67..1a3a2f2fb 100644 --- a/dev/protos/google/api/field_behavior.proto +++ b/dev/protos/google/api/field_behavior.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/dev/protos/google/api/http.proto b/dev/protos/google/api/http.proto index 113fa936a..31d867a27 100644 --- a/dev/protos/google/api/http.proto +++ b/dev/protos/google/api/http.proto @@ -1,4 +1,4 @@ -// Copyright 2015 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -210,15 +210,18 @@ message Http { // 1. Leaf request fields (recursive expansion nested messages in the request // message) are classified into three categories: // - Fields referred by the path template. They are passed via the URL path. -// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They are passed via the HTTP +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They +// are passed via the HTTP // request body. // - All other fields are passed via the URL query parameters, and the // parameter name is the field path in the request message. A repeated // field can be represented as multiple query parameters under the same // name. -// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL query parameter, all fields +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL +// query parameter, all fields // are passed via URL path and HTTP request body. -// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP request body, all +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP +// request body, all // fields are passed via URL path and URL query parameters. // // ### Path template syntax @@ -313,7 +316,8 @@ message Http { message HttpRule { // Selects a method to which this rule applies. // - // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. string selector = 1; // Determines the URL pattern is matched by this rules. This pattern can be diff --git a/dev/protos/google/api/launch_stage.proto b/dev/protos/google/api/launch_stage.proto index 6524db575..9802de795 100644 --- a/dev/protos/google/api/launch_stage.proto +++ b/dev/protos/google/api/launch_stage.proto @@ -1,4 +1,4 @@ -// Copyright 2015 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/dev/protos/google/api/resource.proto b/dev/protos/google/api/resource.proto index 0ce0344f5..bf0cbec5d 100644 --- a/dev/protos/google/api/resource.proto +++ b/dev/protos/google/api/resource.proto @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/dev/protos/google/firestore/admin/v1/database.proto b/dev/protos/google/firestore/admin/v1/database.proto index 3f242c3e1..5d9b762a1 100644 --- a/dev/protos/google/firestore/admin/v1/database.proto +++ b/dev/protos/google/firestore/admin/v1/database.proto @@ -20,7 +20,7 @@ import "google/api/field_behavior.proto"; import "google/api/resource.proto"; option csharp_namespace = "Google.Cloud.Firestore.Admin.V1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/admin/v1;admin"; +option go_package = "cloud.google.com/go/firestore/apiv1/admin/adminpb;adminpb"; option java_multiple_files = true; option java_outer_classname = "DatabaseProto"; option java_package = "com.google.firestore.admin.v1"; diff --git a/dev/protos/google/firestore/admin/v1/field.proto b/dev/protos/google/firestore/admin/v1/field.proto index 0bbb11d8a..5cd1cd481 100644 --- a/dev/protos/google/firestore/admin/v1/field.proto +++ b/dev/protos/google/firestore/admin/v1/field.proto @@ -21,7 +21,7 @@ import "google/api/resource.proto"; import "google/firestore/admin/v1/index.proto"; option csharp_namespace = "Google.Cloud.Firestore.Admin.V1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/admin/v1;admin"; +option go_package = "cloud.google.com/go/firestore/apiv1/admin/adminpb;adminpb"; option java_multiple_files = true; option java_outer_classname = "FieldProto"; option java_package = "com.google.firestore.admin.v1"; diff --git a/dev/protos/google/firestore/admin/v1/firestore_admin.proto b/dev/protos/google/firestore/admin/v1/firestore_admin.proto index c493673ae..71f849d0b 100644 --- a/dev/protos/google/firestore/admin/v1/firestore_admin.proto +++ b/dev/protos/google/firestore/admin/v1/firestore_admin.proto @@ -28,7 +28,7 @@ import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; option csharp_namespace = "Google.Cloud.Firestore.Admin.V1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/admin/v1;admin"; +option go_package = "cloud.google.com/go/firestore/apiv1/admin/adminpb;adminpb"; option java_multiple_files = true; option java_outer_classname = "FirestoreAdminProto"; option java_package = "com.google.firestore.admin.v1"; diff --git a/dev/protos/google/firestore/admin/v1/index.proto b/dev/protos/google/firestore/admin/v1/index.proto index 066d4109f..2567da650 100644 --- a/dev/protos/google/firestore/admin/v1/index.proto +++ b/dev/protos/google/firestore/admin/v1/index.proto @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ package google.firestore.admin.v1; import "google/api/resource.proto"; option csharp_namespace = "Google.Cloud.Firestore.Admin.V1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/admin/v1;admin"; +option go_package = "cloud.google.com/go/firestore/apiv1/admin/adminpb;adminpb"; option java_multiple_files = true; option java_outer_classname = "IndexProto"; option java_package = "com.google.firestore.admin.v1"; @@ -50,6 +50,21 @@ message Index { // against all collections that has the collection id specified by the // index. COLLECTION_GROUP = 2; + + // Include all the collections's ancestor in the index. Only available for + // Datastore Mode databases. + COLLECTION_RECURSIVE = 3; + } + + // API Scope defines the APIs (Firestore Native, or Firestore in + // Datastore Mode) that are supported for queries. + enum ApiScope { + // The index can only be used by the Firestore Native query API. + // This is the default. + ANY_API = 0; + + // The index can only be used by the Firestore in Datastore Mode query API. + DATASTORE_MODE_API = 1; } // A field in an index. @@ -138,14 +153,17 @@ message Index { // time, and that have the same collection id as this index. QueryScope query_scope = 2; + // The API scope supported by this index. + ApiScope api_scope = 5; + // The fields supported by this index. // - // For composite indexes, this is always 2 or more fields. - // The last field entry is always for the field path `__name__`. If, on - // creation, `__name__` was not specified as the last field, it will be added - // automatically with the same direction as that of the last field defined. If - // the final field in a composite index is not directional, the `__name__` - // will be ordered ASCENDING (unless explicitly specified). + // For composite indexes, this requires a minimum of 2 and a maximum of 100 + // fields. The last field entry is always for the field path `__name__`. If, + // on creation, `__name__` was not specified as the last field, it will be + // added automatically with the same direction as that of the last field + // defined. If the final field in a composite index is not directional, the + // `__name__` will be ordered ASCENDING (unless explicitly specified). // // For single field indexes, this will always be exactly one entry with a // field path equal to the field path of the associated field. diff --git a/dev/protos/google/firestore/admin/v1/location.proto b/dev/protos/google/firestore/admin/v1/location.proto index 8f7519c4d..abf836d92 100644 --- a/dev/protos/google/firestore/admin/v1/location.proto +++ b/dev/protos/google/firestore/admin/v1/location.proto @@ -17,7 +17,7 @@ syntax = "proto3"; package google.firestore.admin.v1; option csharp_namespace = "Google.Cloud.Firestore.Admin.V1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/admin/v1;admin"; +option go_package = "cloud.google.com/go/firestore/apiv1/admin/adminpb;adminpb"; option java_multiple_files = true; option java_outer_classname = "LocationProto"; option java_package = "com.google.firestore.admin.v1"; diff --git a/dev/protos/google/firestore/admin/v1/operation.proto b/dev/protos/google/firestore/admin/v1/operation.proto index 654a6ad6c..6b0562293 100644 --- a/dev/protos/google/firestore/admin/v1/operation.proto +++ b/dev/protos/google/firestore/admin/v1/operation.proto @@ -20,7 +20,7 @@ import "google/firestore/admin/v1/index.proto"; import "google/protobuf/timestamp.proto"; option csharp_namespace = "Google.Cloud.Firestore.Admin.V1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/admin/v1;admin"; +option go_package = "cloud.google.com/go/firestore/apiv1/admin/adminpb;adminpb"; option java_multiple_files = true; option java_outer_classname = "OperationProto"; option java_package = "com.google.firestore.admin.v1"; diff --git a/dev/protos/google/firestore/v1/aggregation_result.proto b/dev/protos/google/firestore/v1/aggregation_result.proto index dc0d172a2..05fea5da9 100644 --- a/dev/protos/google/firestore/v1/aggregation_result.proto +++ b/dev/protos/google/firestore/v1/aggregation_result.proto @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ package google.firestore.v1; import "google/firestore/v1/document.proto"; option csharp_namespace = "Google.Cloud.Firestore.V1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/v1;firestore"; +option go_package = "cloud.google.com/go/firestore/apiv1/firestorepb;firestorepb"; option java_multiple_files = true; option java_outer_classname = "AggregationResultProto"; option java_package = "com.google.firestore.v1"; diff --git a/dev/protos/google/firestore/v1/bloom_filter.proto b/dev/protos/google/firestore/v1/bloom_filter.proto new file mode 100644 index 000000000..c00bb9c17 --- /dev/null +++ b/dev/protos/google/firestore/v1/bloom_filter.proto @@ -0,0 +1,73 @@ +// Copyright 2023 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 +// +// 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. + +syntax = "proto3"; + +package google.firestore.v1; + +option csharp_namespace = "Google.Cloud.Firestore.V1"; +option go_package = "cloud.google.com/go/firestore/apiv1/firestorepb;firestorepb"; +option java_multiple_files = true; +option java_outer_classname = "BloomFilterProto"; +option java_package = "com.google.firestore.v1"; +option objc_class_prefix = "GCFS"; +option php_namespace = "Google\\Cloud\\Firestore\\V1"; +option ruby_package = "Google::Cloud::Firestore::V1"; + +// A sequence of bits, encoded in a byte array. +// +// Each byte in the `bitmap` byte array stores 8 bits of the sequence. The only +// exception is the last byte, which may store 8 _or fewer_ bits. The `padding` +// defines the number of bits of the last byte to be ignored as "padding". The +// values of these "padding" bits are unspecified and must be ignored. +// +// To retrieve the first bit, bit 0, calculate: `(bitmap[0] & 0x01) != 0`. +// To retrieve the second bit, bit 1, calculate: `(bitmap[0] & 0x02) != 0`. +// To retrieve the third bit, bit 2, calculate: `(bitmap[0] & 0x04) != 0`. +// To retrieve the fourth bit, bit 3, calculate: `(bitmap[0] & 0x08) != 0`. +// To retrieve bit n, calculate: `(bitmap[n / 8] & (0x01 << (n % 8))) != 0`. +// +// The "size" of a `BitSequence` (the number of bits it contains) is calculated +// by this formula: `(bitmap.length * 8) - padding`. +message BitSequence { + // The bytes that encode the bit sequence. + // May have a length of zero. + bytes bitmap = 1; + + // The number of bits of the last byte in `bitmap` to ignore as "padding". + // If the length of `bitmap` is zero, then this value must be `0`. + // Otherwise, this value must be between 0 and 7, inclusive. + int32 padding = 2; +} + +// A bloom filter (https://en.wikipedia.org/wiki/Bloom_filter). +// +// The bloom filter hashes the entries with MD5 and treats the resulting 128-bit +// hash as 2 distinct 64-bit hash values, interpreted as unsigned integers +// using 2's complement encoding. +// +// These two hash values, named `h1` and `h2`, are then used to compute the +// `hash_count` hash values using the formula, starting at `i=0`: +// +// h(i) = h1 + (i * h2) +// +// These resulting values are then taken modulo the number of bits in the bloom +// filter to get the bits of the bloom filter to test for the given entry. +message BloomFilter { + // The bloom filter data. + BitSequence bits = 1; + + // The number of hashes used by the algorithm. + int32 hash_count = 2; +} diff --git a/dev/protos/google/firestore/v1/common.proto b/dev/protos/google/firestore/v1/common.proto index c1bd92d8f..29cde08d3 100644 --- a/dev/protos/google/firestore/v1/common.proto +++ b/dev/protos/google/firestore/v1/common.proto @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ package google.firestore.v1; import "google/protobuf/timestamp.proto"; option csharp_namespace = "Google.Cloud.Firestore.V1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/v1;firestore"; +option go_package = "cloud.google.com/go/firestore/apiv1/firestorepb;firestorepb"; option java_multiple_files = true; option java_outer_classname = "CommonProto"; option java_package = "com.google.firestore.v1"; diff --git a/dev/protos/google/firestore/v1/document.proto b/dev/protos/google/firestore/v1/document.proto index 41283588e..795200498 100644 --- a/dev/protos/google/firestore/v1/document.proto +++ b/dev/protos/google/firestore/v1/document.proto @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ import "google/protobuf/timestamp.proto"; import "google/type/latlng.proto"; option csharp_namespace = "Google.Cloud.Firestore.V1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/v1;firestore"; +option go_package = "cloud.google.com/go/firestore/apiv1/firestorepb;firestorepb"; option java_multiple_files = true; option java_outer_classname = "DocumentProto"; option java_package = "com.google.firestore.v1"; diff --git a/dev/protos/google/firestore/v1/firestore.proto b/dev/protos/google/firestore/v1/firestore.proto index b62bf64e5..c6f7b921c 100644 --- a/dev/protos/google/firestore/v1/firestore.proto +++ b/dev/protos/google/firestore/v1/firestore.proto @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -26,10 +26,11 @@ import "google/firestore/v1/query.proto"; import "google/firestore/v1/write.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/timestamp.proto"; +import "google/protobuf/wrappers.proto"; import "google/rpc/status.proto"; option csharp_namespace = "Google.Cloud.Firestore.V1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/v1;firestore"; +option go_package = "cloud.google.com/go/firestore/apiv1/firestorepb;firestorepb"; option java_multiple_files = true; option java_outer_classname = "FirestoreProto"; option java_package = "com.google.firestore.v1"; @@ -178,7 +179,7 @@ service Firestore { } // Streams batches of document updates and deletes, in order. This method is - // only available via the gRPC API (not REST). + // only available via gRPC or WebChannel (not REST). rpc Write(stream WriteRequest) returns (stream WriteResponse) { option (google.api.http) = { post: "/v1/{database=projects/*/databases/*}/documents:write" @@ -186,8 +187,8 @@ service Firestore { }; } - // Listens to changes. This method is only available via the gRPC API (not - // REST). + // Listens to changes. This method is only available via gRPC or WebChannel + // (not REST). rpc Listen(stream ListenRequest) returns (stream ListenResponse) { option (google.api.http) = { post: "/v1/{database=projects/*/databases/*}/documents:listen" @@ -655,7 +656,14 @@ message RunAggregationQueryResponse { // a new transaction. bytes transaction = 2; - // The time at which the aggregate value is valid for. + // The time at which the aggregate result was computed. This is always + // monotonically increasing; in this case, the previous AggregationResult in + // the result stream are guaranteed not to have changed between their + // `read_time` and this one. + // + // If the query returns no results, a response with `read_time` and no + // `result` will be sent, and this represents the time at which the query + // was run. google.protobuf.Timestamp read_time = 3; } @@ -925,6 +933,14 @@ message Target { // If the target should be removed once it is current and consistent. bool once = 6; + + // The number of documents that last matched the query at the resume token or + // read time. + // + // This value is only relevant when a `resume_type` is provided. This value + // being present and greater than zero signals that the client wants + // `ExistenceFilter.unchanged_names` to be included in the response. + google.protobuf.Int32Value expected_count = 12; } // Targets being watched have changed. diff --git a/dev/protos/google/firestore/v1/query.proto b/dev/protos/google/firestore/v1/query.proto index 6d8ffce80..8f89c48d9 100644 --- a/dev/protos/google/firestore/v1/query.proto +++ b/dev/protos/google/firestore/v1/query.proto @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ import "google/firestore/v1/document.proto"; import "google/protobuf/wrappers.proto"; option csharp_namespace = "Google.Cloud.Firestore.V1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/v1;firestore"; +option go_package = "cloud.google.com/go/firestore/apiv1/firestorepb;firestorepb"; option java_multiple_files = true; option java_outer_classname = "QueryProto"; option java_package = "com.google.firestore.v1"; @@ -136,8 +136,9 @@ message StructuredQuery { // // Requires: // - // * That `value` is a non-empty `ArrayValue` with at most 10 values. - // * No other `IN` or `ARRAY_CONTAINS_ANY` or `NOT_IN`. + // * That `value` is a non-empty `ArrayValue`, subject to disjunction + // limits. + // * No `NOT_IN` filters in the same query. IN = 8; // The given `field` is an array that contains any of the values in the @@ -145,8 +146,10 @@ message StructuredQuery { // // Requires: // - // * That `value` is a non-empty `ArrayValue` with at most 10 values. - // * No other `IN` or `ARRAY_CONTAINS_ANY` or `NOT_IN`. + // * That `value` is a non-empty `ArrayValue`, subject to disjunction + // limits. + // * No other `ARRAY_CONTAINS_ANY` filters within the same disjunction. + // * No `NOT_IN` filters in the same query. ARRAY_CONTAINS_ANY = 9; // The value of the `field` is not in the given array. @@ -154,7 +157,7 @@ message StructuredQuery { // Requires: // // * That `value` is a non-empty `ArrayValue` with at most 10 values. - // * No other `IN`, `ARRAY_CONTAINS_ANY`, `NOT_IN`, `NOT_EQUAL`, + // * No other `OR`, `IN`, `ARRAY_CONTAINS_ANY`, `NOT_IN`, `NOT_EQUAL`, // `IS_NOT_NULL`, or `IS_NOT_NAN`. // * That `field` comes first in the `order_by`. NOT_IN = 10; @@ -251,7 +254,11 @@ message StructuredQuery { repeated FieldReference fields = 2; } - // The projection to return. + // Optional sub-set of the fields to return. + // + // This acts as a [DocumentMask][google.firestore.v1.DocumentMask] over the + // documents returned from a query. When not set, assumes that the caller + // wants all fields returned. Projection select = 1; // The collections to query. @@ -349,7 +356,7 @@ message StructuredQuery { // Firestore query for running an aggregation over a // [StructuredQuery][google.firestore.v1.StructuredQuery]. message StructuredAggregationQuery { - // Defines a aggregation that produces a single result. + // Defines an aggregation that produces a single result. message Aggregation { // Count of documents that match the query. // @@ -360,7 +367,7 @@ message StructuredAggregationQuery { // count. // // This provides a way to set an upper bound on the number of documents - // to scan, limiting latency and cost. + // to scan, limiting latency, and cost. // // Unspecified is interpreted as no bound. // @@ -394,7 +401,7 @@ message StructuredAggregationQuery { // COUNT_UP_TO(1) AS count_up_to_1, // COUNT_UP_TO(2), // COUNT_UP_TO(3) AS count_up_to_3, - // COUNT_UP_TO(4) + // COUNT(*) // OVER ( // ... // ); @@ -407,7 +414,7 @@ message StructuredAggregationQuery { // COUNT_UP_TO(1) AS count_up_to_1, // COUNT_UP_TO(2) AS field_1, // COUNT_UP_TO(3) AS count_up_to_3, - // COUNT_UP_TO(4) AS field_2 + // COUNT(*) AS field_2 // OVER ( // ... // ); diff --git a/dev/protos/google/firestore/v1/write.proto b/dev/protos/google/firestore/v1/write.proto index 3cc3b8429..4ed0f5475 100644 --- a/dev/protos/google/firestore/v1/write.proto +++ b/dev/protos/google/firestore/v1/write.proto @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,12 +16,13 @@ syntax = "proto3"; package google.firestore.v1; +import "google/firestore/v1/bloom_filter.proto"; import "google/firestore/v1/common.proto"; import "google/firestore/v1/document.proto"; import "google/protobuf/timestamp.proto"; option csharp_namespace = "Google.Cloud.Firestore.V1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/v1;firestore"; +option go_package = "cloud.google.com/go/firestore/apiv1/firestorepb;firestorepb"; option java_multiple_files = true; option java_outer_classname = "WriteProto"; option java_package = "com.google.firestore.v1"; @@ -264,4 +265,19 @@ message ExistenceFilter { // If different from the count of documents in the client that match, the // client must manually determine which documents no longer match the target. int32 count = 2; + + // A bloom filter that contains the UTF-8 byte encodings of the resource names + // of the documents that match + // [target_id][google.firestore.v1.ExistenceFilter.target_id], in the form + // `projects/{project_id}/databases/{database_id}/documents/{document_path}` + // that have NOT changed since the query results indicated by the resume token + // or timestamp given in `Target.resume_type`. + // + // This bloom filter may be omitted at the server's discretion, such as if it + // is deemed that the client will not make use of it or if it is too + // computationally expensive to calculate or transmit. Clients must gracefully + // handle this field being absent by falling back to the logic used before + // this field existed; that is, re-add the target without a resume token to + // figure out which documents in the client's cache are out of sync. + BloomFilter unchanged_names = 3; } diff --git a/dev/protos/google/firestore/v1beta1/common.proto b/dev/protos/google/firestore/v1beta1/common.proto index af26d0b6f..1a18c41bc 100644 --- a/dev/protos/google/firestore/v1beta1/common.proto +++ b/dev/protos/google/firestore/v1beta1/common.proto @@ -19,7 +19,7 @@ package google.firestore.v1beta1; import "google/protobuf/timestamp.proto"; option csharp_namespace = "Google.Cloud.Firestore.V1Beta1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/v1beta1;firestore"; +option go_package = "cloud.google.com/go/firestore/apiv1beta1/firestorepb;firestorepb"; option java_multiple_files = true; option java_outer_classname = "CommonProto"; option java_package = "com.google.firestore.v1beta1"; diff --git a/dev/protos/google/firestore/v1beta1/document.proto b/dev/protos/google/firestore/v1beta1/document.proto index e425cd6e8..de192806d 100644 --- a/dev/protos/google/firestore/v1beta1/document.proto +++ b/dev/protos/google/firestore/v1beta1/document.proto @@ -21,7 +21,7 @@ import "google/protobuf/timestamp.proto"; import "google/type/latlng.proto"; option csharp_namespace = "Google.Cloud.Firestore.V1Beta1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/v1beta1;firestore"; +option go_package = "cloud.google.com/go/firestore/apiv1beta1/firestorepb;firestorepb"; option java_multiple_files = true; option java_outer_classname = "DocumentProto"; option java_package = "com.google.firestore.v1beta1"; diff --git a/dev/protos/google/firestore/v1beta1/firestore.proto b/dev/protos/google/firestore/v1beta1/firestore.proto index 1fd3a58f3..047f029e4 100644 --- a/dev/protos/google/firestore/v1beta1/firestore.proto +++ b/dev/protos/google/firestore/v1beta1/firestore.proto @@ -28,7 +28,7 @@ import "google/protobuf/timestamp.proto"; import "google/rpc/status.proto"; option csharp_namespace = "Google.Cloud.Firestore.V1Beta1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/v1beta1;firestore"; +option go_package = "cloud.google.com/go/firestore/apiv1beta1/firestorepb;firestorepb"; option java_multiple_files = true; option java_outer_classname = "FirestoreProto"; option java_package = "com.google.firestore.v1beta1"; diff --git a/dev/protos/google/firestore/v1beta1/query.proto b/dev/protos/google/firestore/v1beta1/query.proto index cfb5401e1..7d7ef11ce 100644 --- a/dev/protos/google/firestore/v1beta1/query.proto +++ b/dev/protos/google/firestore/v1beta1/query.proto @@ -20,7 +20,7 @@ import "google/firestore/v1beta1/document.proto"; import "google/protobuf/wrappers.proto"; option csharp_namespace = "Google.Cloud.Firestore.V1Beta1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/v1beta1;firestore"; +option go_package = "cloud.google.com/go/firestore/apiv1beta1/firestorepb;firestorepb"; option java_multiple_files = true; option java_outer_classname = "QueryProto"; option java_package = "com.google.firestore.v1beta1"; diff --git a/dev/protos/google/firestore/v1beta1/write.proto b/dev/protos/google/firestore/v1beta1/write.proto index 3fd1b5965..124526a59 100644 --- a/dev/protos/google/firestore/v1beta1/write.proto +++ b/dev/protos/google/firestore/v1beta1/write.proto @@ -21,7 +21,7 @@ import "google/firestore/v1beta1/document.proto"; import "google/protobuf/timestamp.proto"; option csharp_namespace = "Google.Cloud.Firestore.V1Beta1"; -option go_package = "google.golang.org/genproto/googleapis/firestore/v1beta1;firestore"; +option go_package = "cloud.google.com/go/firestore/apiv1beta1/firestorepb;firestorepb"; option java_multiple_files = true; option java_outer_classname = "WriteProto"; option java_package = "com.google.firestore.v1beta1"; diff --git a/dev/protos/google/longrunning/operations.proto b/dev/protos/google/longrunning/operations.proto index c1fdc6f52..c8fda207e 100644 --- a/dev/protos/google/longrunning/operations.proto +++ b/dev/protos/google/longrunning/operations.proto @@ -26,7 +26,7 @@ import "google/protobuf/descriptor.proto"; option cc_enable_arenas = true; option csharp_namespace = "Google.LongRunning"; -option go_package = "google.golang.org/genproto/googleapis/longrunning;longrunning"; +option go_package = "cloud.google.com/go/longrunning/autogen/longrunningpb;longrunningpb"; option java_multiple_files = true; option java_outer_classname = "OperationsProto"; option java_package = "com.google.longrunning"; diff --git a/dev/protos/google/protobuf/any.proto b/dev/protos/google/protobuf/any.proto index 561da0cb1..eff44e509 100644 --- a/dev/protos/google/protobuf/any.proto +++ b/dev/protos/google/protobuf/any.proto @@ -68,7 +68,7 @@ option csharp_namespace = "Google.Protobuf.WellKnownTypes"; // foo = any.unpack(Foo.getDefaultInstance()); // } // -// Example 3: Pack and unpack a message in Python. +// Example 3: Pack and unpack a message in Python. // // foo = Foo(...) // any = Any() @@ -78,7 +78,7 @@ option csharp_namespace = "Google.Protobuf.WellKnownTypes"; // any.Unpack(foo) // ... // -// Example 4: Pack and unpack a message in Go +// Example 4: Pack and unpack a message in Go // // foo := &pb.Foo{...} // any, err := anypb.New(foo) @@ -98,7 +98,7 @@ option csharp_namespace = "Google.Protobuf.WellKnownTypes"; // name "y.z". // // JSON -// +// ==== // The JSON representation of an `Any` value uses the regular // representation of the deserialized, embedded message, with an // additional field `@type` which contains the type URL. Example: @@ -149,7 +149,8 @@ message Any { // // Note: this functionality is not currently available in the official // protobuf release, and it is not used for type URLs beginning with - // type.googleapis.com. + // type.googleapis.com. As of May 2023, there are no widely used type server + // implementations and no plans to implement one. // // Schemes other than `http`, `https` (or the empty scheme) might be // used with implementation specific semantics. diff --git a/dev/protos/google/protobuf/descriptor.proto b/dev/protos/google/protobuf/descriptor.proto index 3b3867543..c994d4a7b 100644 --- a/dev/protos/google/protobuf/descriptor.proto +++ b/dev/protos/google/protobuf/descriptor.proto @@ -133,6 +133,49 @@ message ExtensionRangeOptions { // The parser stores options it doesn't recognize here. See above. repeated UninterpretedOption uninterpreted_option = 999; + message Declaration { + // The extension number declared within the extension range. + optional int32 number = 1; + + // The fully-qualified name of the extension field. There must be a leading + // dot in front of the full name. + optional string full_name = 2; + + // The fully-qualified type name of the extension field. Unlike + // Metadata.type, Declaration.type must have a leading dot for messages + // and enums. + optional string type = 3; + + // Deprecated. Please use "repeated". + optional bool is_repeated = 4 [deprecated = true]; + + // If true, indicates that the number is reserved in the extension range, + // and any extension field with the number will fail to compile. Set this + // when a declared extension field is deleted. + optional bool reserved = 5; + + // If true, indicates that the extension must be defined as repeated. + // Otherwise the extension must be defined as optional. + optional bool repeated = 6; + } + + // For external users: DO NOT USE. We are in the process of open sourcing + // extension declaration and executing internal cleanups before it can be + // used externally. + repeated Declaration declaration = 2 [retention = RETENTION_SOURCE]; + + // The verification state of the extension range. + enum VerificationState { + // All the extensions of the range must be declared. + DECLARATION = 0; + UNVERIFIED = 1; + } + + // The verification state of the range. + // TODO(b/278783756): flip the default to DECLARATION once all empty ranges + // are marked as UNVERIFIED. + optional VerificationState verification = 3 [default = UNVERIFIED]; + // Clients can define custom options in extensions of this message. See above. extensions 1000 to max; } @@ -540,13 +583,21 @@ message MessageOptions { message FieldOptions { // The ctype option instructs the C++ code generator to use a different // representation of the field than it normally would. See the specific - // options below. This option is not yet implemented in the open source - // release -- sorry, we'll try to include it in a future version! + // options below. This option is only implemented to support use of + // [ctype=CORD] and [ctype=STRING] (the default) on non-repeated fields of + // type "bytes" in the open source release -- sorry, we'll try to include + // other types in a future version! optional CType ctype = 1 [default = STRING]; enum CType { // Default mode. STRING = 0; + // The option [ctype=CORD] may be applied to a non-repeated field of type + // "bytes". It indicates that in C++, the data should be stored in a Cord + // instead of a string. For very large strings, this may reduce memory + // fragmentation. It may also allow better performance when parsing from a + // Cord, or when parsing with aliasing enabled, as the parsed Cord may then + // alias the original buffer. CORD = 1; STRING_PIECE = 2; @@ -659,7 +710,7 @@ message FieldOptions { TARGET_TYPE_METHOD = 9; } - optional OptionTargetType target = 18; + repeated OptionTargetType targets = 19; // The parser stores options it doesn't recognize here. See above. repeated UninterpretedOption uninterpreted_option = 999; @@ -668,9 +719,11 @@ message FieldOptions { extensions 1000 to max; reserved 4; // removed jtype + optional OptionTargetType target_obsolete_do_not_use = 18 [deprecated = true]; } message OneofOptions { + // The parser stores options it doesn't recognize here. See above. repeated UninterpretedOption uninterpreted_option = 999; diff --git a/dev/protos/google/protobuf/timestamp.proto b/dev/protos/google/protobuf/timestamp.proto index 2fb527c0c..fd0bc07dc 100644 --- a/dev/protos/google/protobuf/timestamp.proto +++ b/dev/protos/google/protobuf/timestamp.proto @@ -127,7 +127,7 @@ option csharp_namespace = "Google.Protobuf.WellKnownTypes"; // [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with // the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use // the Joda Time's [`ISODateTimeFormat.dateTime()`]( -// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D +// http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime() // ) to obtain a formatter capable of generating timestamps in this format. // message Timestamp { diff --git a/dev/src/filter.ts b/dev/src/filter.ts index d12eaa8cc..7b50b1efb 100644 --- a/dev/src/filter.ts +++ b/dev/src/filter.ts @@ -94,7 +94,7 @@ export abstract class Filter { /** * Creates and returns a new [Filter]{@link Filter} that is a * conjunction of the given {@link Filter}s. A conjunction filter includes - * a document if it satisfies any of the given {@link Filter}s. + * a document if it satisfies all of the given {@link Filter}s. * * The returned Filter can be applied to [Query.where()]{@link Query#where}, * [Filter.or()]{@link Filter#or}, or [Filter.and()]{@link Filter#and}. When diff --git a/dev/src/index.ts b/dev/src/index.ts index 74a81f300..9abb4964d 100644 --- a/dev/src/index.ts +++ b/dev/src/index.ts @@ -60,6 +60,7 @@ import { isPermanentRpcError, requestTag, wrapError, + tryGetPreferRestEnvironmentVariable, } from './util'; import { validateBoolean, @@ -534,7 +535,12 @@ export class Firestore implements firestore.Firestore { * for communication from that point forward. Currently the only operation * that requires gRPC is creating a snapshot listener with the method * `DocumentReference.onSnapshot()`, `CollectionReference.onSnapshot()`, or - * `Query.onSnapshot()`. + * `Query.onSnapshot()`. If specified, this setting value will take precedent over the + * environment variable `FIRESTORE_PREFER_REST`. If not specified, the + * SDK will use the value specified in the environment variable `FIRESTORE_PREFER_REST`. + * Valid values of `FIRESTORE_PREFER_REST` are `true` ('1') or `false` (`0`). Values are + * not case-sensitive. Any other value for the environment variable will be ignored and + * a warning will be logged to the console. */ constructor(settings?: firestore.Settings) { const libraryHeader = { @@ -663,6 +669,16 @@ export class Firestore implements firestore.Firestore { let url: URL | null = null; + // If preferRest is not specified in settings, but is set as environment variable, + // then use the environment variable value. + const preferRestEnvValue = tryGetPreferRestEnvironmentVariable(); + if (settings.preferRest === undefined && preferRestEnvValue !== undefined) { + settings = { + ...settings, + preferRest: preferRestEnvValue, + }; + } + // If the environment variable is set, it should always take precedence // over any user passed in settings. if (process.env.FIRESTORE_EMULATOR_HOST) { diff --git a/dev/src/util.ts b/dev/src/util.ts index 5a189269c..cf91f4ee0 100644 --- a/dev/src/util.ts +++ b/dev/src/util.ts @@ -217,3 +217,32 @@ export function wrapError(err: Error, stack: string): Error { err.stack += '\nCaused by: ' + stack; return err; } + +/** + * Parses the value of the environment variable FIRESTORE_PREFER_REST, and + * returns a value indicating if the environment variable enables or disables + * preferRest. + * + * This function will warn to the console if the environment variable is set + * to an unsupported value. + * + * @return `true` if the environment variable enables `preferRest`, + * `false` if the environment variable disables `preferRest`, or `undefined` + * if the environment variable is not set or is set to an unsupported value. + */ +export function tryGetPreferRestEnvironmentVariable(): boolean | undefined { + const rawValue = process.env.FIRESTORE_PREFER_REST?.trim().toLowerCase(); + if (rawValue === undefined) { + return undefined; + } else if (rawValue === '1' || rawValue === 'true') { + return true; + } else if (rawValue === '0' || rawValue === 'false') { + return false; + } else { + // eslint-disable-next-line no-console + console.warn( + `An unsupported value was specified for the environment variable FIRESTORE_PREFER_REST. Value ${rawValue} is unsupported.` + ); + return undefined; + } +} diff --git a/dev/src/v1/firestore_client.ts b/dev/src/v1/firestore_client.ts index 000c07cf4..48cf4f453 100644 --- a/dev/src/v1/firestore_client.ts +++ b/dev/src/v1/firestore_client.ts @@ -1326,7 +1326,7 @@ export class FirestoreClient { /** * Streams batches of document updates and deletes, in order. This method is - * only available via the gRPC API (not REST). + * only available via gRPC or WebChannel (not REST). * * @param {object} [options] * Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details. @@ -1346,8 +1346,8 @@ export class FirestoreClient { } /** - * Listens to changes. This method is only available via the gRPC API (not - * REST). + * Listens to changes. This method is only available via gRPC or WebChannel + * (not REST). * * @param {object} [options] * Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details. diff --git a/dev/src/v1/firestore_proto_list.json b/dev/src/v1/firestore_proto_list.json index ebe9cbb60..5c2cd76e2 100644 --- a/dev/src/v1/firestore_proto_list.json +++ b/dev/src/v1/firestore_proto_list.json @@ -1,5 +1,6 @@ [ "../../protos/google/firestore/v1/aggregation_result.proto", + "../../protos/google/firestore/v1/bloom_filter.proto", "../../protos/google/firestore/v1/common.proto", "../../protos/google/firestore/v1/document.proto", "../../protos/google/firestore/v1/firestore.proto", diff --git a/dev/system-test/firestore.ts b/dev/system-test/firestore.ts index d87114381..964b832e4 100644 --- a/dev/system-test/firestore.ts +++ b/dev/system-test/firestore.ts @@ -84,7 +84,6 @@ if (process.env.NODE_ENV === 'DEBUG') { function getTestRoot(settings: Settings = {}) { const firestore = new Firestore({ ...settings, - preferRest: !!process.env.USE_REST_FALLBACK, }); return firestore.collection(`node_${version}_${autoId()}`); } diff --git a/dev/test/index.ts b/dev/test/index.ts index 4fa7d6e93..12df809ec 100644 --- a/dev/test/index.ts +++ b/dev/test/index.ts @@ -639,6 +639,62 @@ describe('instantiation', () => { ); }); + describe('preferRest configuration', () => { + let originalValue: string | undefined; + + beforeEach(() => { + originalValue = process.env.FIRESTORE_PREFER_REST; + }); + + afterEach(() => { + if (originalValue === undefined) { + delete process.env.FIRESTORE_PREFER_REST; + } else { + process.env.FIRESTORE_PREFER_REST = originalValue; + } + }); + + it('preferRest is disabled (falsy) by default', async () => { + delete process.env.FIRESTORE_PREFER_REST; + const firestore = new Firestore.Firestore({}); + // Convention with settings is to leave undefined settings as + // undefined. We could test for undefined here, but converting + // to boolean and testing for falsy-ness is consistent with the + // code that consumes settings. + expect(!!firestore['_settings'].preferRest).to.be.false; + }); + + it('preferRest can be enabled by setting', async () => { + delete process.env.FIRESTORE_PREFER_REST; + const firestore = new Firestore.Firestore({ + preferRest: true, + }); + expect(firestore['_settings'].preferRest).to.be.true; + }); + + it('preferRest can be enabled by environment variable', async () => { + process.env.FIRESTORE_PREFER_REST = 'true'; + const firestore = new Firestore.Firestore({}); + expect(firestore['_settings'].preferRest).to.be.true; + }); + + it('the preferRest value from settings takes precedent over the environment var - disable', async () => { + process.env.FIRESTORE_PREFER_REST = 'true'; + const firestore = new Firestore.Firestore({ + preferRest: false, + }); + expect(firestore['_settings'].preferRest).to.be.false; + }); + + it('the preferRest value from settings takes precedent over the environment var - enable', async () => { + process.env.FIRESTORE_PREFER_REST = 'false'; + const firestore = new Firestore.Firestore({ + preferRest: true, + }); + expect(firestore['_settings'].preferRest).to.be.true; + }); + }); + it('exports all types', () => { // Ordering as per firestore.d.ts expect(Firestore.Firestore).to.exist; diff --git a/dev/test/util.ts b/dev/test/util.ts index c4687eef7..7806f74d2 100644 --- a/dev/test/util.ts +++ b/dev/test/util.ts @@ -14,7 +14,8 @@ import {describe, it} from 'mocha'; import {expect} from 'chai'; -import {isPlainObject} from '../src/util'; +import {isPlainObject, tryGetPreferRestEnvironmentVariable} from '../src/util'; +import * as sinon from 'sinon'; describe('isPlainObject()', () => { it('allows Object.create()', () => { @@ -33,4 +34,71 @@ describe('isPlainObject()', () => { expect(isPlainObject(new Foo())).to.be.false; expect(isPlainObject(Object.create(new Foo()))).to.be.false; }); + + describe('tryGetPreferRestEnvironmentVariable', () => { + const sandbox = sinon.createSandbox(); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let warnSpy: any; + let originalValue: string | undefined; + + beforeEach(() => { + warnSpy = sandbox.spy(console, 'warn'); + originalValue = process.env.FIRESTORE_PREFER_REST; + }); + + afterEach(() => { + sandbox.restore(); + if (originalValue === undefined) { + delete process.env.FIRESTORE_PREFER_REST; + } else { + process.env.FIRESTORE_PREFER_REST = originalValue; + } + }); + + it('reads true', async () => { + process.env.FIRESTORE_PREFER_REST = 'true'; + expect(tryGetPreferRestEnvironmentVariable()).to.be.true; + }); + + it('reads 1', async () => { + process.env.FIRESTORE_PREFER_REST = '1'; + expect(tryGetPreferRestEnvironmentVariable()).to.be.true; + }); + + it('reads false', async () => { + process.env.FIRESTORE_PREFER_REST = 'false'; + expect(tryGetPreferRestEnvironmentVariable()).to.be.false; + }); + + it('reads 0', async () => { + process.env.FIRESTORE_PREFER_REST = '0'; + expect(tryGetPreferRestEnvironmentVariable()).to.be.false; + }); + + it('ignores case', async () => { + process.env.FIRESTORE_PREFER_REST = 'True'; + expect(tryGetPreferRestEnvironmentVariable()).to.be.true; + }); + + it('trims whitespace', async () => { + process.env.FIRESTORE_PREFER_REST = ' true '; + expect(tryGetPreferRestEnvironmentVariable()).to.be.true; + }); + + it('returns undefined when the environment variable is not set', async () => { + delete process.env.FIRESTORE_PREFER_REST; + expect(tryGetPreferRestEnvironmentVariable()).to.be.undefined; + expect(warnSpy.calledOnce).to.be.false; + }); + + it('returns undefined and warns when the environment variable is set to an unsupported value', async () => { + process.env.FIRESTORE_PREFER_REST = 'enable'; + expect(tryGetPreferRestEnvironmentVariable()).to.be.undefined; + expect(warnSpy.calledOnce).to.be.true; + expect(warnSpy.getCall(0).args[0]).to.match( + /unsupported value.*FIRESTORE_PREFER_REST/ + ); + }); + }); }); diff --git a/dev/test/util/helpers.ts b/dev/test/util/helpers.ts index 476804b37..ea9b1802f 100644 --- a/dev/test/util/helpers.ts +++ b/dev/test/util/helpers.ts @@ -35,7 +35,7 @@ import {GapicClient} from '../../src/types'; import api = proto.google.firestore.v1; let SSL_CREDENTIALS: grpc.ChannelCredentials | null = null; -if (!process.env.USE_REST_FALLBACK) { +if (!isPreferRest()) { const grpc = require('google-gax').grpc; SSL_CREDENTIALS = grpc.credentials.createInsecure(); } @@ -444,3 +444,16 @@ export async function collect( } return values; } + +/** + * Returns a value indicating whether preferRest is enabled + * via the environment variable `FIRESTORE_PREFER_REST`. + * + * @return `true` if preferRest is enabled via the environment variable `FIRESTORE_PREFER_REST`. + */ +export function isPreferRest(): boolean { + return ( + process.env.FIRESTORE_PREFER_REST === '1' || + process.env.FIRESTORE_PREFER_REST === 'true' + ); +} diff --git a/package.json b/package.json index b17b62c14..37d003e5f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@google-cloud/firestore", "description": "Firestore Client Library for Node.js", - "version": "6.5.0", + "version": "6.6.0", "license": "Apache-2.0", "author": "Google Inc.", "engines": { @@ -30,9 +30,9 @@ "scripts": { "predocs": "npm run compile", "docs": "jsdoc -c .jsdoc.js", - "system-test:rest": "USE_REST_FALLBACK=YES mocha build/system-test --timeout 600000", + "system-test:rest": "FIRESTORE_PREFER_REST=true mocha build/system-test --timeout 600000", "system-test:grpc": "mocha build/system-test --timeout 600000", - "system-test:emulator:rest": "FIRESTORE_EMULATOR_HOST=localhost:8080 USE_REST_FALLBACK=YES mocha build/system-test --timeout 600000", + "system-test:emulator:rest": "FIRESTORE_EMULATOR_HOST=localhost:8080 FIRESTORE_PREFER_REST=true mocha build/system-test --timeout 600000", "system-test:emulator:grpc": "FIRESTORE_EMULATOR_HOST=localhost:8080 mocha build/system-test --timeout 600000", "system-test": "npm run system-test:grpc && npm run system-test:rest", "system-test:emulator": "npm run system-test:emulator:grpc && npm run system-test:emulator:rest", diff --git a/samples/package.json b/samples/package.json index af501f537..1cd21edeb 100644 --- a/samples/package.json +++ b/samples/package.json @@ -11,7 +11,7 @@ "test": "mocha --timeout 600000" }, "dependencies": { - "@google-cloud/firestore": "^6.5.0" + "@google-cloud/firestore": "^6.6.0" }, "devDependencies": { "chai": "^4.2.0", diff --git a/types/firestore.d.ts b/types/firestore.d.ts index 964538197..5c5459c44 100644 --- a/types/firestore.d.ts +++ b/types/firestore.d.ts @@ -2513,7 +2513,7 @@ declare namespace FirebaseFirestore { /** * Creates and returns a new [Filter]{@link Filter} that is a * conjunction of the given {@link Filter}s. A conjunction filter includes - * a document if it satisfies any of the given {@link Filter}s. + * a document if it satisfies all of the given {@link Filter}s. * * The returned Filter can be applied to [Query.where()]{@link Query#where}, * [Filter.or()]{@link Filter#or}, or [Filter.and()]{@link Filter#and}. When