From 087e029f949f10a520af801798df63b156a7a2d6 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 24 Oct 2023 17:46:22 +0200 Subject: [PATCH 01/30] chore(deps): update dependency @types/node to v20 (#1928) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 61f7851cd..80449453a 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "@types/duplexify": "^3.5.0", "@types/extend": "^3.0.0", "@types/mocha": "^9.0.0", - "@types/node": "^18.0.0", + "@types/node": "^20.0.0", "@types/sinon": "^10.0.0", "@types/through2": "^2.0.34", "c8": "^8.0.0", From 5da7138abc15e99edc51fdfd25a70bf5eb0c97b9 Mon Sep 17 00:00:00 2001 From: Ehsan Date: Tue, 24 Oct 2023 18:29:22 -0700 Subject: [PATCH 02/30] docs: Improve documentation for `DocumentReference.create`. (#1926) Co-authored-by: Mark Duckworth <1124037+MarkDuckworth@users.noreply.github.com> --- dev/src/reference.ts | 2 +- types/firestore.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/src/reference.ts b/dev/src/reference.ts index 79626b479..cad75511a 100644 --- a/dev/src/reference.ts +++ b/dev/src/reference.ts @@ -371,7 +371,7 @@ export class DocumentReference< * * @param {DocumentData} data An object that contains the fields and data to * serialize as the document. - * @throws {Error} If the provided input is not a valid Firestore document. + * @throws {Error} If the provided input is not a valid Firestore document or if the document already exists. * @returns {Promise.} A Promise that resolves with the * write time of this create. * diff --git a/types/firestore.d.ts b/types/firestore.d.ts index ce6c9adb2..18d240a44 100644 --- a/types/firestore.d.ts +++ b/types/firestore.d.ts @@ -1273,7 +1273,7 @@ declare namespace FirebaseFirestore { * provided object values. The write fails if the document already exists * * @param data The object data to serialize as the document. - * @throws Error If the provided input is not a valid Firestore document. + * @throws {Error} If the provided input is not a valid Firestore document or if the document already exists. * @return A Promise resolved with the write time of this create. */ create(data: WithFieldValue): Promise; From 8a4ae5b07712588bd0deae917d9e17f34f1ebb60 Mon Sep 17 00:00:00 2001 From: Tom Andersen Date: Thu, 9 Nov 2023 11:54:49 -0500 Subject: [PATCH 03/30] fix: Remove temporary header encoding workaround (#1935) * fix: Remove header encoding workaround * remove unused import --- dev/src/index.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/dev/src/index.ts b/dev/src/index.ts index 1a95d689b..e797669b3 100644 --- a/dev/src/index.ts +++ b/dev/src/index.ts @@ -84,7 +84,6 @@ import { RECURSIVE_DELETE_MIN_PENDING_OPS, RecursiveDelete, } from './recursive-delete'; -import {stringify} from 'querystring'; export { CollectionReference, @@ -593,21 +592,6 @@ export class Firestore implements firestore.Firestore { } } - // TODO (multi-db) Revert this override of gax.routingHeader.fromParams - // after a permanent fix is applied. See b/292075646 - // This override of the routingHeader.fromParams does not - // encode forward slash characters. This is a temporary fix for b/291780066 - gax.routingHeader.fromParams = params => { - return stringify(params, undefined, undefined, { - encodeURIComponent: (val: string) => { - return val - .split('/') - .map(component => encodeURIComponent(component)) - .join('/'); - }, - }); - }; - if (this._settings.ssl === false) { const grpcModule = this._settings.grpc ?? require('google-gax').grpc; const sslCreds = grpcModule.credentials.createInsecure(); From 53d7c2f504a7cd23c8a23a513568a0825559775a Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 09:25:11 -0700 Subject: [PATCH 04/30] chore: update cloud-rad version to ^0.4.0 (#1940) Source-Link: https://github.com/googleapis/synthtool/commit/1063ef32bfe41b112bade7a2dfad4e84d0058ebd Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-nodejs:latest@sha256:e92044720ab3cb6984a70b0c6001081204375959ba3599ef6c42dd99a7783a67 Co-authored-by: Owl Bot --- .eslintignore | 1 + .github/.OwlBot.lock.yaml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.eslintignore b/.eslintignore index ea5b04aeb..c4a0963e9 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,3 +5,4 @@ build/ docs/ protos/ samples/generated/ +system-test/**/fixtures diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 807a89161..638efabfb 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -13,5 +13,5 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-nodejs:latest - digest: sha256:8b6a07a38d1583d96b6e251ba208bd4ef0bc2a0cc37471ffc518841651d15bd6 -# created: 2023-09-25T22:18:27.595486267Z + digest: sha256:e92044720ab3cb6984a70b0c6001081204375959ba3599ef6c42dd99a7783a67 +# created: 2023-11-10T00:24:05.581078808Z From b29d0deaf3e927c7f56adee7de3252d8a38af4e5 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Mon, 13 Nov 2023 23:29:24 +0100 Subject: [PATCH 05/30] chore(deps): update dependency sinon to v17 (#1925) --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 80449453a..eba52a91c 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "@types/extend": "^3.0.0", "@types/mocha": "^9.0.0", "@types/node": "^20.0.0", - "@types/sinon": "^10.0.0", + "@types/sinon": "^17.0.0", "@types/through2": "^2.0.34", "c8": "^8.0.0", "chai": "^4.1.2", @@ -90,7 +90,7 @@ "mocha": "^9.2.2", "protobufjs-cli": "^1.1.2", "proxyquire": "^2.1.3", - "sinon": "^16.0.0", + "sinon": "^17.0.0", "through2": "^4.0.0", "ts-node": "^10.0.0", "typescript": "^5.2.2" From f83a995f2bcb5fe4b35124d6a48d27a47a6b27fd Mon Sep 17 00:00:00 2001 From: dansaadati Date: Fri, 17 Nov 2023 11:53:54 -0800 Subject: [PATCH 06/30] chore: update cloud-rad version to ^0.4.0 (#1947) --- .kokoro/release/docs-devsite.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.kokoro/release/docs-devsite.sh b/.kokoro/release/docs-devsite.sh index 2198e67fe..b6a4662b8 100755 --- a/.kokoro/release/docs-devsite.sh +++ b/.kokoro/release/docs-devsite.sh @@ -25,5 +25,5 @@ if [[ -z "$CREDENTIALS" ]]; then fi npm install -npm install --no-save @google-cloud/cloud-rad@^0.2.5 -npx @google-cloud/cloud-rad \ No newline at end of file +npm install --no-save @google-cloud/cloud-rad@^0.4.0 +npx @google-cloud/cloud-rad From 170d05b1fa6c720d1109506ed3d3feb525c16efe Mon Sep 17 00:00:00 2001 From: Mark Duckworth <1124037+MarkDuckworth@users.noreply.github.com> Date: Fri, 17 Nov 2023 14:06:14 -0700 Subject: [PATCH 07/30] fix: update retry policy to not retry streams that have not made progress (#1946) fix: update retry policy to not retry streams with retryable error that have not made progress receiving documents --- dev/src/index.ts | 2 +- dev/src/reference.ts | 122 +++++++++++-------- dev/src/util.ts | 15 +++ dev/test/aggregateQuery.ts | 41 ++++++- dev/test/query.ts | 239 ++++++++++++++++++++++++++++++++++++- 5 files changed, 367 insertions(+), 52 deletions(-) diff --git a/dev/src/index.ts b/dev/src/index.ts index e797669b3..47fe60eb0 100644 --- a/dev/src/index.ts +++ b/dev/src/index.ts @@ -1637,7 +1637,7 @@ export class Firestore implements firestore.Firestore { function streamReady(): void { if (!streamInitialized) { streamInitialized = true; - logger('Firestore._initializeStream', requestTag, 'Releasing stream'); + logger('Firestore._initializeStream', requestTag, 'Stream ready'); resolve(resultStream); } } diff --git a/dev/src/reference.ts b/dev/src/reference.ts index cad75511a..012634c8f 100644 --- a/dev/src/reference.ts +++ b/dev/src/reference.ts @@ -44,6 +44,7 @@ import {defaultConverter} from './types'; import { autoId, Deferred, + getTotalTimeout, isPermanentRpcError, mapToArray, requestTag, @@ -2569,6 +2570,15 @@ export class Query< return isPermanentRpcError(err, methodName); } + _hasRetryTimedOut(methodName: string, startTime: number): boolean { + const totalTimeout = getTotalTimeout(methodName); + if (totalTimeout === 0) { + return false; + } + + return Date.now() - startTime >= totalTimeout; + } + /** * Internal streaming method that accepts an optional transaction ID. * @@ -2579,6 +2589,7 @@ export class Query< */ _stream(transactionId?: Uint8Array): NodeJS.ReadableStream { const tag = requestTag(); + const startTime = Date.now(); let lastReceivedDocument: QueryDocumentSnapshot< AppModelType, @@ -2638,8 +2649,9 @@ export class Query< let streamActive: Deferred; do { streamActive = new Deferred(); + const methodName = 'runQuery'; backendStream = await this._firestore.requestStream( - 'runQuery', + methodName, /* bidirectional= */ false, request, tag @@ -2656,12 +2668,28 @@ export class Query< 'Query failed with retryable stream error:', err ); - // Enqueue a "no-op" write into the stream and resume the query - // once it is processed. This allows any enqueued results to be - // consumed before resuming the query so that the query resumption - // can start at the correct document. + + // Enqueue a "no-op" write into the stream and wait for it to be + // read by the downstream consumer. This ensures that all enqueued + // results in the stream are consumed, which will give us an accurate + // value for `lastReceivedDocument`. stream.write(NOOP_MESSAGE, () => { - if (lastReceivedDocument) { + if (this._hasRetryTimedOut(methodName, startTime)) { + logger( + 'Query._stream', + tag, + 'Query failed with retryable stream error but the total retry timeout has exceeded.' + ); + stream.destroy(err); + streamActive.resolve(/* active= */ false); + } else if (lastReceivedDocument) { + logger( + 'Query._stream', + tag, + 'Query failed with retryable stream error and progress was made receiving ' + + 'documents, so the stream is being retried.' + ); + // Restart the query but use the last document we received as // the query cursor. Note that we do not use backoff here. The // call to `requestStream()` will backoff should the restart @@ -2673,8 +2701,21 @@ export class Query< } else { request = this.startAfter(lastReceivedDocument).toProto(); } + + // Set lastReceivedDocument to null before each retry attempt to ensure the retry makes progress + lastReceivedDocument = null; + + streamActive.resolve(/* active= */ true); + } else { + logger( + 'Query._stream', + tag, + 'Query failed with retryable stream error however no progress was made receiving ' + + 'documents, so the stream is being closed.' + ); + stream.destroy(err); + streamActive.resolve(/* active= */ false); } - streamActive.resolve(/* active= */ true); }); } else { logger( @@ -3320,48 +3361,33 @@ export class AggregateQuery< // catch below. const request = this.toProto(transactionId); - let streamActive: Deferred; - do { - streamActive = new Deferred(); - const backendStream = await firestore.requestStream( - 'runAggregationQuery', - /* bidirectional= */ false, - request, - tag - ); - stream.on('close', () => { - backendStream.resume(); - backendStream.end(); - }); - backendStream.on('error', err => { - backendStream.unpipe(stream); - // If a non-transactional query failed, attempt to restart. - // Transactional queries are retried via the transaction runner. - if ( - !transactionId && - !isPermanentRpcError(err, 'runAggregationQuery') - ) { - logger( - 'AggregateQuery._stream', - tag, - 'AggregateQuery failed with retryable stream error:', - err - ); - streamActive.resolve(/* active= */ true); - } else { - logger( - 'AggregateQuery._stream', - tag, - 'AggregateQuery failed with stream error:', - err - ); - stream.destroy(err); - streamActive.resolve(/* active= */ false); - } - }); + const backendStream = await firestore.requestStream( + 'runAggregationQuery', + /* bidirectional= */ false, + request, + tag + ); + stream.on('close', () => { backendStream.resume(); - backendStream.pipe(stream); - } while (await streamActive.promise); + backendStream.end(); + }); + backendStream.on('error', err => { + // TODO(group-by) When group-by queries are supported for aggregates + // consider implementing retries if the stream is making progress + // receiving results for groups. See the use of lastReceivedDocument + // in the retry strategy for runQuery. + + backendStream.unpipe(stream); + logger( + 'AggregateQuery._stream', + tag, + 'AggregateQuery failed with stream error:', + err + ); + stream.destroy(err); + }); + backendStream.resume(); + backendStream.pipe(stream); }) .catch(e => stream.destroy(e)); diff --git a/dev/src/util.ts b/dev/src/util.ts index c68695f82..667959402 100644 --- a/dev/src/util.ts +++ b/dev/src/util.ts @@ -178,6 +178,21 @@ export function getRetryCodes(methodName: string): number[] { return getServiceConfig(methodName)?.retry?.retryCodes ?? []; } +/** + * Gets the total timeout in milliseconds from the retry settings in + * the service config for the given RPC. If the total timeout is not + * set, then `0` is returned. + * + * @private + * @internal + */ +export function getTotalTimeout(methodName: string): number { + return ( + getServiceConfig(methodName)?.retry?.backoffSettings?.totalTimeoutMillis ?? + 0 + ); +} + /** * Returns the backoff setting from the service configuration. * @private diff --git a/dev/test/aggregateQuery.ts b/dev/test/aggregateQuery.ts index ff81a254d..b3e9490fb 100644 --- a/dev/test/aggregateQuery.ts +++ b/dev/test/aggregateQuery.ts @@ -126,19 +126,31 @@ describe('aggregate query interface', () => { }); }); - it('handles stream exception at initialization', () => { + it('handles stream exception at initialization', async () => { + let attempts = 0; const query = firestore.collection('collectionId').count(); query._stream = () => { + ++attempts; throw new Error('Expected error'); }; - return expect(query.get()).to.eventually.rejectedWith('Expected error'); + await query + .get() + .then(() => { + throw new Error('Unexpected success in Promise'); + }) + .catch(err => { + expect(err.message).to.equal('Expected error'); + expect(attempts).to.equal(1); + }); }); it('handles stream exception during initialization', async () => { + let attempts = 0; const overrides: ApiOverride = { runAggregationQuery: () => { + ++attempts; return stream(new Error('Expected error')); }, }; @@ -152,6 +164,31 @@ describe('aggregate query interface', () => { }) .catch(err => { expect(err.message).to.equal('Expected error'); + expect(attempts).to.equal(5); + }); + }); + + it('handles message without result during initialization', async () => { + let attempts = 0; + const overrides: ApiOverride = { + runAggregationQuery: () => { + ++attempts; + return stream({readTime: {seconds: 5, nanos: 6}}); + }, + }; + firestore = await createInstance(overrides); + + const query = firestore.collection('collectionId').count(); + await query + .get() + .then(() => { + throw new Error('Unexpected success in Promise'); + }) + .catch(err => { + expect(err.message).to.equal( + 'RunAggregationQueryResponse is missing result' + ); + expect(attempts).to.equal(1); }); }); }); diff --git a/dev/test/query.ts b/dev/test/query.ts index da2c8c079..97dce3453 100644 --- a/dev/test/query.ts +++ b/dev/test/query.ts @@ -50,7 +50,7 @@ import { writeResult, } from './util/helpers'; -import {GoogleError} from 'google-gax'; +import {GoogleError, Status} from 'google-gax'; import api = google.firestore.v1; import protobuf = google.protobuf; import {Filter} from '../src/filter'; @@ -449,6 +449,14 @@ export function result( } } +export function heartbeat(count: number): api.IRunQueryResponse { + return { + document: null, + readTime: {seconds: 5, nanos: 6}, + skippedResults: count, + }; +} + describe('query interface', () => { let firestore: Firestore; @@ -714,9 +722,11 @@ describe('query interface', () => { }); it('handles stream exception at initialization', () => { + let attempts = 0; const query = firestore.collection('collectionId'); query._stream = () => { + ++attempts; throw new Error('Expected error'); }; @@ -727,12 +737,16 @@ describe('query interface', () => { }) .catch(err => { expect(err.message).to.equal('Expected error'); + expect(attempts).to.equal(1); }); }); it('handles stream exception during initialization', () => { + let attempts = 0; + const overrides: ApiOverride = { runQuery: () => { + ++attempts; return stream(new Error('Expected error')); }, }; @@ -747,6 +761,7 @@ describe('query interface', () => { }) .catch(err => { expect(err.message).to.equal('Expected error'); + expect(attempts).to.equal(5); }); }); }); @@ -773,6 +788,228 @@ describe('query interface', () => { }); }); + it('handles stream exception after initialization and heartbeat', () => { + const deadlineExceededErr = new GoogleError(); + deadlineExceededErr.code = Status.DEADLINE_EXCEEDED; + deadlineExceededErr.message = 'DEADLINE_EXCEEDED error message'; + + let attempts = 0; + + const overrides: ApiOverride = { + runQuery: () => { + ++attempts; + + // A heartbeat message will initialize the stream, but it is not + // a document, so it does not represent progress made on the stream. + return stream(heartbeat(1000), deadlineExceededErr); + }, + }; + + return createInstance(overrides).then(firestoreInstance => { + firestore = firestoreInstance; + return firestore + .collection('collectionId') + .get() + .then(() => { + throw new Error('Unexpected success in Promise'); + }) + .catch(err => { + expect(err.message).to.equal('DEADLINE_EXCEEDED error message'); + + // The heartbeat initialized the stream before there was a stream + // exception, so we only expect a single attempt at streaming. + expect(attempts).to.equal(1); + }); + }); + }); + + function handlesRetryableExceptionUntilProgressStops( + withHeartbeat: boolean + ): Promise { + let attempts = 0; + + // count of stream initializations that are successful + // and make progress (a document is received before error) + const initializationsWithProgress = 10; + + // Receiving these error codes on a stream after the stream has received document data + // should result in the stream retrying indefinitely. + const retryableErrorCodes = [ + Status.DEADLINE_EXCEEDED, + Status.UNAVAILABLE, + Status.INTERNAL, + Status.UNAVAILABLE, + ]; + + const overrides: ApiOverride = { + runQuery: x => { + ++attempts; + + // Validate that runQuery is called with cursor of the lastReceivedDocument + // for all attempts except but the first. + if (attempts > 1) { + const docPath = + x?.structuredQuery?.startAt?.values?.[0].referenceValue || ''; + const docId = docPath.substring(docPath.lastIndexOf('/')); + expect(docId).to.equal( + `/id-${Math.min(initializationsWithProgress, attempts - 1)}` + ); + expect(x?.structuredQuery?.orderBy?.length).to.equal(1); + expect(x?.structuredQuery?.orderBy?.[0].field?.fieldPath).to.equal( + '__name__' + ); + } + + const streamElements = []; + + // A heartbeat is a message that may be received on a stream while + // a query is running. If the test is configured `withHeartbeat = true` + // then the fake stream will include a heartbeat before the first + // document. This heartbeat message has the side effect of initializing + // the stream, but it does not represent progress of streaming the results. + // Testing with and without heartbeats allows us to evaluate different + // retry logic in the SDK. + if (withHeartbeat) { + streamElements.push(heartbeat(1000)); + } + + // For the first X initializations, the stream will make progress + // receiving documents in the result set. + // For the X+1 attempt, the stream will not make progress before + // the error. If a stream gets an error without progress, the + // retry policy will close the stream. + if (attempts <= initializationsWithProgress) { + streamElements.push(result(`id-${attempts}`)); + } + + // Create an error with one of the retryable error codes + const googleError = new GoogleError(); + googleError.code = + retryableErrorCodes[attempts % retryableErrorCodes.length]; + googleError.message = 'test error message'; + streamElements.push(googleError); + + return stream(...streamElements); + }, + }; + + return createInstance(overrides).then(firestoreInstance => { + firestore = firestoreInstance; + const query = firestore.collection('collectionId'); + query._hasRetryTimedOut = () => false; + return query + .get() + .then(() => { + throw new Error('Unexpected success in Promise'); + }) + .catch(err => { + expect(err.message).to.equal('test error message'); + + // Assert that runQuery was retried the expected number + // of times based on the test configuration. + // + // If the test is running with heartbeat messages, then + // the test will always validate retry logic for an + // initialized stream. + // + // If the test is running without heartbeat messages, + // then the test will validate retry logic for both + // initialized and uninitialized streams. Specifically, + // the last retry will fail with an uninitialized stream. + const initilizationRetries = withHeartbeat ? 1 : 5; + expect(attempts).to.equal( + initializationsWithProgress + initilizationRetries + ); + }); + }); + } + + it('handles retryable exception until progress stops with heartbeat', async () => { + await handlesRetryableExceptionUntilProgressStops(true); + }); + + it('handles retryable exception until progress stops without heartbeat', async () => { + await handlesRetryableExceptionUntilProgressStops(false); + }); + + it('handles retryable exception with progress until timeout', async () => { + let attempts = 0; + + const overrides: ApiOverride = { + runQuery: () => { + ++attempts; + + const streamElements = []; + streamElements.push(result(`id-${attempts}`)); + + // Create an error with a retryable error code + const googleError = new GoogleError(); + googleError.code = Status.DEADLINE_EXCEEDED; + googleError.message = 'test error message'; + streamElements.push(googleError); + + return stream(...streamElements); + }, + }; + + return createInstance(overrides).then(firestoreInstance => { + firestore = firestoreInstance; + const query = firestore.collection('collectionId'); + // Fake our timeout check to fail after 10 retry attempts + query._hasRetryTimedOut = (methodName, startTime) => { + expect(methodName).to.equal('runQuery'); + expect(startTime).to.be.lessThanOrEqual(Date.now()); + return attempts >= 10; + }; + + return query + .get() + .then(() => { + throw new Error('Unexpected success in Promise'); + }) + .catch(err => { + expect(err.message).to.equal('test error message'); + + expect(attempts).to.equal(10); + }); + }); + }); + + it('handles non-retryable after recieving data (with get())', () => { + let attempts = 0; + + const overrides: ApiOverride = { + runQuery: () => { + ++attempts; + + const streamElements = []; + streamElements.push(result(`id-${attempts}`)); + + // Create an error with one of the retryable error codes + const googleError = new GoogleError(); + googleError.code = Status.UNKNOWN; + googleError.message = 'test error message'; + streamElements.push(googleError); + + return stream(...streamElements); + }, + }; + + return createInstance(overrides).then(firestoreInstance => { + firestore = firestoreInstance; + return firestore + .collection('collectionId') + .get() + .then(() => { + throw new Error('Unexpected success in Promise'); + }) + .catch(err => { + expect(err.message).to.equal('test error message'); + expect(attempts).to.equal(1); + }); + }); + }); + it('handles stream exception after initialization (with stream())', done => { const responses = [ () => stream(result('first'), new Error('Expected error')), From 47768485c1c5502ed9c7471501a809a252046b6c Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 12:05:53 -0700 Subject: [PATCH 08/30] chore(main): release 7.1.1 (#1939) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- samples/package.json | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21bf71f7e..854fe4ac9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ [1]: https://www.npmjs.com/package/@google-cloud/firestore?activeTab=versions +## [7.1.1](https://github.com/googleapis/nodejs-firestore/compare/v7.1.0...v7.1.1) (2023-11-20) + + +### Bug Fixes + +* Remove temporary header encoding workaround ([#1935](https://github.com/googleapis/nodejs-firestore/issues/1935)) ([8a4ae5b](https://github.com/googleapis/nodejs-firestore/commit/8a4ae5b07712588bd0deae917d9e17f34f1ebb60)) +* Update retry policy to not retry streams that have not made progress receiving documents ([170d05b](https://github.com/googleapis/nodejs-firestore/commit/170d05b1fa6c720d1109506ed3d3feb525c16efe)) + ## [7.1.0](https://github.com/googleapis/nodejs-firestore/compare/v7.0.0...v7.1.0) (2023-10-11) diff --git a/package.json b/package.json index eba52a91c..04fcec5b1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@google-cloud/firestore", "description": "Firestore Client Library for Node.js", - "version": "7.1.0", + "version": "7.1.1", "license": "Apache-2.0", "author": "Google Inc.", "engines": { diff --git a/samples/package.json b/samples/package.json index 5b69e81ea..a0f594283 100644 --- a/samples/package.json +++ b/samples/package.json @@ -11,7 +11,7 @@ "test": "mocha --timeout 600000" }, "dependencies": { - "@google-cloud/firestore": "^7.1.0" + "@google-cloud/firestore": "^7.1.1" }, "devDependencies": { "chai": "^4.2.0", From 7e916562d0d91aa706225958cc4faa038078075c Mon Sep 17 00:00:00 2001 From: Mark Duckworth <1124037+MarkDuckworth@users.noreply.github.com> Date: Thu, 7 Dec 2023 17:02:18 -0700 Subject: [PATCH 09/30] ci: Fix build issue b/315204283 (#1954) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes a type inference issue that appeared in the tests with a recent version of TypeScript. Fixes b/315204283 πŸ¦• --- dev/test/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/test/types.ts b/dev/test/types.ts index 0d150c6c8..2240cdaf9 100644 --- a/dev/test/types.ts +++ b/dev/test/types.ts @@ -86,7 +86,7 @@ describe('FirestoreTypeConverter', () => { stringProperty: string; numberProperty: number; } - const converter = { + const converter: FirestoreDataConverter = { toFirestore( modelObject: PartialWithFieldValue, options?: SetOptions From 3497215afcec3bfafb815986d6306d54a2d31a0e Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Tue, 12 Dec 2023 12:21:33 -0500 Subject: [PATCH 10/30] docs: Re-write the sample code for FirestoreDataConverter. (#1958) --- types/firestore.d.ts | 177 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 150 insertions(+), 27 deletions(-) diff --git a/types/firestore.d.ts b/types/firestore.d.ts index 18d240a44..8d19fd5cf 100644 --- a/types/firestore.d.ts +++ b/types/firestore.d.ts @@ -151,40 +151,163 @@ declare namespace FirebaseFirestore { * storing and retrieving objects from Firestore. * * @example - * class Post { - * constructor(readonly title: string, readonly author: string) {} * - * toString(): string { - * return this.title + ', by ' + this.author; - * } + * Simple Example + * + * const numberConverter = { + * toFirestore(value: WithFieldValue) { + * return { value }; + * }, + * fromFirestore(snapshot: QueryDocumentSnapshot) { + * return snapshot.data().value as number; + * } + * }; + * + * async function simpleDemo(db: Firestore): Promise { + * const documentRef = db.doc('values/value123').withConverter(numberConverter); + * + * // converters are used with `setDoc`, `addDoc`, and `getDoc` + * await documentRef.set(42); + * const snapshot1 = await documentRef.get(); + * assertEqual(snapshot1.data(), 42); + * + * // converters are not used when writing data with `updateDoc` + * await documentRef.update({ value: 999 }); + * const snapshot2 = await documentRef.get(); + * assertEqual(snapshot2.data(), 999); + * } + * + * Advanced Example + * + * // The Post class is a model that is used by our application. + * // This class may have properties and methods that are specific + * // to our application execution, which do not need to be persisted + * // to Firestore. + * class Post { + * constructor( + * readonly title: string, + * readonly author: string, + * readonly lastUpdatedMillis: number + * ) {} + * toString(): string { + * return `${this.title} by ${this.author}`; + * } * } * + * // The PostDbModel represents how we want our posts to be stored + * // in Firestore. This DbModel has different properties (`ttl`, + * // `aut`, and `lut`) from the Post class we use in our application. * interface PostDbModel { - * title: string; - * author: string; + * ttl: string; + * aut: { firstName: string; lastName: string }; + * lut: Timestamp; * } * - * const postConverter = { - * toFirestore(post: Post): PostDbModel { - * return {title: post.title, author: post.author}; - * }, - * fromFirestore( - * snapshot: FirebaseFirestore.QueryDocumentSnapshot - * ): Post { - * const data = snapshot.data() as PostDbModel; - * return new Post(data.title, data.author); - * } - * }; + * // The `PostConverter` implements `FirestoreDataConverter` and specifies + * // how the Firestore SDK can convert `Post` objects to `PostDbModel` + * // objects and vice versa. + * class PostConverter implements FirestoreDataConverter { + * toFirestore(post: WithFieldValue): WithFieldValue { + * return { + * ttl: post.title, + * aut: this._autFromAuthor(post.author), + * lut: this._lutFromLastUpdatedMillis(post.lastUpdatedMillis) + * }; + * } + * + * fromFirestore(snapshot: QueryDocumentSnapshot): Post { + * const data = snapshot.data() as PostDbModel; + * const author = `${data.aut.firstName} ${data.aut.lastName}`; + * return new Post(data.ttl, author, data.lut.toMillis()); + * } + * + * _autFromAuthor( + * author: string | FieldValue + * ): { firstName: string; lastName: string } | FieldValue { + * if (typeof author !== 'string') { + * // `author` is a FieldValue, so just return it. + * return author; + * } + * const [firstName, lastName] = author.split(' '); + * return {firstName, lastName}; + * } + * + * _lutFromLastUpdatedMillis( + * lastUpdatedMillis: number | FieldValue + * ): Timestamp | FieldValue { + * if (typeof lastUpdatedMillis !== 'number') { + * // `lastUpdatedMillis` must be a FieldValue, so just return it. + * return lastUpdatedMillis; + * } + * return Timestamp.fromMillis(lastUpdatedMillis); + * } + * } + * + * async function advancedDemo(db: Firestore): Promise { + * // Create a `DocumentReference` with a `FirestoreDataConverter`. + * const documentRef = db.doc('posts/post123').withConverter(new PostConverter()); + * + * // The `data` argument specified to `DocumentReference.set()` is type + * // checked by the TypeScript compiler to be compatible with `Post`. Since + * // the `data` argument is typed as `WithFieldValue` rather than just + * // `Post`, this allows properties of the `data` argument to also be special + * // Firestore values that perform server-side mutations, such as + * // `FieldValue.arrayRemove()`, `FieldValue.delete()`, and + * // `FieldValue.serverTimestamp()`. + * await documentRef.set({ + * title: 'My Life', + * author: 'Foo Bar', + * lastUpdatedMillis: FieldValue.serverTimestamp() + * }); + * + * // The TypeScript compiler will fail to compile if the `data` argument + * // to `DocumentReference.set()` is _not_ compatible with + * // `WithFieldValue`. This type checking prevents the caller from + * // specifying objects with incorrect properties or property values. + * // @ts-expect-error "Argument of type { ttl: string; } is not assignable + * // to parameter of type WithFieldValue" + * await documentRef.set(documentRef, { ttl: 'The Title' }); + * + * // When retrieving a document with `DocumentReference.get()` the + * // `DocumentSnapshot` object's `data()` method returns a `Post`, rather + * // than a generic object, which would have been returned if the + * // `DocumentReference` did _not_ have a `FirestoreDataConverter` + * // attached to it. + * const snapshot1: DocumentSnapshot = await documentRef.get(); + * const post1: Post = snapshot1.data()!; + * if (post1) { + * assertEqual(post1.title, 'My Life'); + * assertEqual(post1.author, 'Foo Bar'); + * } + * + * // The `data` argument specified to `DocumentReference.update()` is type + * // checked by the TypeScript compiler to be compatible with + * // `PostDbModel`. Note that unlike `DocumentReference.set()`, whose + * // `data` argument must be compatible with `Post`, the `data` argument + * // to `update()` must be compatible with `PostDbModel`. Similar to + * // `set()`, since the `data` argument is typed as + * // `WithFieldValue` rather than just `PostDbModel`, this + * // allows properties of the `data` argument to also be those special + * // Firestore values, like `FieldValue.arrayRemove()`, + * // `FieldValue.delete()`, and `FieldValue.serverTimestamp()`. + * await documentRef.update({ + * 'aut.firstName': 'NewFirstName', + * lut: FieldValue.serverTimestamp() + * }); * - * const postSnap = await Firestore() - * .collection('posts') - * .withConverter(postConverter) - * .doc().get(); - * const post = postSnap.data(); - * if (post !== undefined) { - * post.title; // string - * post.toString(); // Should be defined - * post.someNonExistentProperty; // TS error + * // The TypeScript compiler will fail to compile if the `data` argument + * // to `DocumentReference.update()` is _not_ compatible with + * // `WithFieldValue`. This type checking prevents the caller + * // from specifying objects with incorrect properties or property values. + * // @ts-expect-error "Argument of type { title: string; } is not + * // assignable to parameter of type WithFieldValue" + * await documentRef.update({ title: 'New Title' }); + * const snapshot2: DocumentSnapshot = await documentRef.get(); + * const post2: Post = snapshot2.data()!; + * if (post2) { + * assertEqual(post2.title, 'My Life'); + * assertEqual(post2.author, 'NewFirstName Bar'); + * } * } */ export interface FirestoreDataConverter< From 85f100833c29b6cfefca19c0b526c336aca1184d Mon Sep 17 00:00:00 2001 From: cherylEnkidu <96084918+cherylEnkidu@users.noreply.github.com> Date: Thu, 14 Dec 2023 15:52:20 -0500 Subject: [PATCH 11/30] test: Add test coverage for logical termination (#1743) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test: Add test coverage for logical termination * improve tests * remove .only * πŸ¦‰ Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Address feedback * Address commends 2 --------- Co-authored-by: Owl Bot --- dev/system-test/firestore.ts | 120 ++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 2 deletions(-) diff --git a/dev/system-test/firestore.ts b/dev/system-test/firestore.ts index 57161a433..1944e0563 100644 --- a/dev/system-test/firestore.ts +++ b/dev/system-test/firestore.ts @@ -1313,6 +1313,42 @@ describe('DocumentReference class', () => { }); }); +describe('runs query on a large collection', () => { + let firestore: Firestore; + let randomCol: CollectionReference; + + beforeEach(async () => { + firestore = new Firestore({}); + randomCol = getTestRoot(firestore); + + const promises: Array>> = []; + for (let i = 0; i < 1000; i++) { + promises.push(randomCol.add({foo: 'a'})); + } + await Promise.all(promises); + }); + + afterEach(() => verifyInstance(firestore)); + + it('can get()', () => { + return randomCol.get().then(res => { + expect(res.size).to.equal(1000); + }); + }); + + it('can stream()', async () => { + let received = 0; + const stream = randomCol.stream(); + + for await (const doc of stream) { + expect(doc).to.be.an.instanceOf(QueryDocumentSnapshot); + ++received; + } + + expect(received).to.equal(1000); + }); +}); + describe('Query class', () => { interface PaginatedResults { pages: number; @@ -1647,12 +1683,62 @@ describe('Query class', () => { }); }); - it('has limit() method', async () => { + it('can run get() on empty collection', async () => { + return randomCol.get().then(res => { + return expect(res.empty); + }); + }); + + it('can run stream() on empty collection', async () => { + let received = 0; + const stream = randomCol.stream(); + + for await (const doc of stream) { + expect(doc).to.be.an.instanceOf(QueryDocumentSnapshot); + ++received; + } + + expect(received).to.equal(0); + }); + + it('has limit() method on get()', async () => { await addDocs({foo: 'a'}, {foo: 'b'}); const res = await randomCol.orderBy('foo').limit(1).get(); expectDocs(res, {foo: 'a'}); }); + it('has limit() method on stream()', async () => { + let received = 0; + await addDocs({foo: 'a'}, {foo: 'b'}); + + const stream = randomCol.orderBy('foo').limit(1).stream(); + for await (const doc of stream) { + expect(doc).to.be.an.instanceOf(QueryDocumentSnapshot); + ++received; + } + + expect(received).to.equal(1); + }); + + it('can run limit(num), where num is larger than the collection size on get()', async () => { + await addDocs({foo: 'a'}, {foo: 'b'}); + const res = await randomCol.orderBy('foo').limit(3).get(); + expectDocs(res, {foo: 'a'}, {foo: 'b'}); + }); + + it('can run limit(num), where num is larger than the collection size on stream()', async () => { + let received = 0; + await addDocs({foo: 'a'}, {foo: 'b'}); + + const stream = randomCol.orderBy('foo').limit(3).stream(); + for await (const doc of stream) { + expect(doc).to.be.an.instanceOf(QueryDocumentSnapshot); + ++received; + } + + expect(received).to.equal(2); + }); + it('has limitToLast() method', async () => { await addDocs({doc: 1}, {doc: 2}, {doc: 3}); const res = await randomCol.orderBy('doc').limitToLast(2).get(); @@ -1670,12 +1756,42 @@ describe('Query class', () => { expectDocs(res, {doc: 2}, {doc: 3}, {doc: 4}); }); - it('has offset() method', async () => { + it('can use offset() method with get()', async () => { await addDocs({foo: 'a'}, {foo: 'b'}); const res = await randomCol.orderBy('foo').offset(1).get(); expectDocs(res, {foo: 'b'}); }); + it('can use offset() method with stream()', async () => { + let received = 0; + await addDocs({foo: 'a'}, {foo: 'b'}); + + const stream = randomCol.orderBy('foo').offset(1).stream(); + for await (const doc of stream) { + expect(doc).to.be.an.instanceOf(QueryDocumentSnapshot); + ++received; + } + + expect(received).to.equal(1); + }); + + it('can run offset(num), where num is larger than the collection size on get()', async () => { + await addDocs({foo: 'a'}, {foo: 'b'}); + const res = await randomCol.orderBy('foo').offset(3).get(); + expect(res.empty); + }); + + it('can run offset(num), where num is larger than the collection size on stream()', async () => { + let received = 0; + await addDocs({foo: 'a'}, {foo: 'b'}); + const stream = randomCol.orderBy('foo').offset(3).stream(); + for await (const doc of stream) { + expect(doc).to.be.an.instanceOf(QueryDocumentSnapshot); + ++received; + } + expect(received).to.equal(0); + }); + it('supports Unicode in document names', async () => { const collRef = randomCol.doc('Π΄ΠΎΠ±Ρ€ΠΎΠ΅ΡƒΡ‚Ρ€ΠΎ').collection('coll'); await collRef.add({}); From 2e4e83d79fa94dfb3b4f668180a1485c8e4c9ab1 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Fri, 15 Dec 2023 10:43:11 -0500 Subject: [PATCH 12/30] Firestore: Add test that verifies aggregate query error message when missing index (#1960) --- dev/system-test/firestore.ts | 50 ++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/dev/system-test/firestore.ts b/dev/system-test/firestore.ts index 1944e0563..2b63a2846 100644 --- a/dev/system-test/firestore.ts +++ b/dev/system-test/firestore.ts @@ -3184,6 +3184,28 @@ describe('count queries', () => { await runQueryAndExpectCount(count5, 1); }); } + + // Only verify the error message for missing indexes when running against + // production, since the Firestore Emulator does not require index creation + // and will, therefore, never fail in this situation. + // eslint-disable-next-line no-restricted-properties + (process.env.FIRESTORE_EMULATOR_HOST === undefined ? it : it.skip)( + 'count query error message contains console link if missing index', + () => { + const query = randomCol.where('key1', '==', 42).where('key2', '<', 42); + const countQuery = query.count(); + const databaseId = query.firestore._settings.databaseId ?? '(default)'; + // TODO(b/316359394) Remove this check for the default databases once + // cl/582465034 is rolled out to production. + if (databaseId === '(default)') { + return expect(countQuery.get()).to.be.eventually.rejectedWith( + /index.*https:\/\/console\.firebase\.google\.com/ + ); + } else { + return expect(countQuery.get()).to.be.eventually.rejectedWith(/index/); + } + } + ); }); describe('count queries using aggregate api', () => { @@ -3540,15 +3562,27 @@ describe('Aggregation queries', () => { // production, since the Firestore Emulator does not require index creation // and will, therefore, never fail in this situation. // eslint-disable-next-line no-restricted-properties - (process.env.FIRESTORE_EMULATOR_HOST === undefined ? it.skip : it)( - 'aggregate() error message is good if missing index', - async () => { + (process.env.FIRESTORE_EMULATOR_HOST === undefined ? it : it.skip)( + 'aggregate query error message contains console link if missing index', + () => { const query = col.where('key1', '==', 42).where('key2', '<', 42); - await expect( - query.aggregate({count: AggregateField.count()}).get() - ).to.be.eventually.rejectedWith( - /index.*https:\/\/console\.firebase\.google\.com/ - ); + const aggregateQuery = query.aggregate({ + count: AggregateField.count(), + sum: AggregateField.sum('pages'), + average: AggregateField.average('pages'), + }); + const databaseId = query.firestore._settings.databaseId ?? '(default)'; + // TODO(b/316359394) Remove this check for the default databases once + // cl/582465034 is rolled out to production. + if (databaseId === '(default)') { + return expect(aggregateQuery.get()).to.be.eventually.rejectedWith( + /index.*https:\/\/console\.firebase\.google\.com/ + ); + } else { + return expect(aggregateQuery.get()).to.be.eventually.rejectedWith( + /index/ + ); + } } ); From d9c867fc5048748e5af378fd8465303224668781 Mon Sep 17 00:00:00 2001 From: Dennis Kugelmann Date: Thu, 21 Dec 2023 16:56:17 +0200 Subject: [PATCH 13/30] feat: Expose databaseId and projectId getter on Firestore class (#1937) Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Make sure to open an issue as a [bug/issue](https://togithub.com/googleapis/nodejs-firestore/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea - [ ] Ensure the tests and linter pass - [ ] Code coverage does not decrease (if any source code was changed) - [ ] Appropriate docs were updated (if necessary) Fixes #1936 --- dev/src/index.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/dev/src/index.ts b/dev/src/index.ts index 47fe60eb0..a50d10f46 100644 --- a/dev/src/index.ts +++ b/dev/src/index.ts @@ -773,9 +773,6 @@ export class Firestore implements firestore.Firestore { /** * Returns the Database ID for this Firestore instance. - * - * @private - * @internal */ get databaseId(): string { return this._databaseId || DEFAULT_DATABASE_ID; From 1d76546a781527ef5b9085fc9f189c024355ae9f Mon Sep 17 00:00:00 2001 From: Tom Andersen Date: Fri, 5 Jan 2024 16:21:37 -0500 Subject: [PATCH 14/30] fix: Make transaction rollback best effort. (#1967) * Make rollback best effort. * Fix test --- dev/src/transaction.ts | 15 ++++++++++++++- dev/test/transaction.ts | 1 - 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/dev/src/transaction.ts b/dev/src/transaction.ts index a5690d3cc..222e535fe 100644 --- a/dev/src/transaction.ts +++ b/dev/src/transaction.ts @@ -481,7 +481,20 @@ export class Transaction implements firestore.Transaction { transaction: this._transactionId, }; - return this._firestore.request('rollback', request, this._requestTag); + const promise: Promise = this._firestore.request( + 'rollback', + request, + this._requestTag + ); + + return promise.catch(reason => { + logger( + 'Firestore.runTransaction', + this._requestTag, + 'Best effort to rollback failed with error:', + reason + ); + }); } /** diff --git a/dev/test/transaction.ts b/dev/test/transaction.ts index 19088425d..9477ae3f6 100644 --- a/dev/test/transaction.ts +++ b/dev/test/transaction.ts @@ -574,7 +574,6 @@ describe('failed transactions', () => { begin({transactionId: 'foo1'}), commit('foo1', /* writes=*/ undefined, serverError), rollback('foo1', serverError), - rollback('foo1'), backoff(), begin({ transactionId: 'foo2', From 8f77b68bf70b04a66e982067aa246de0596e4ea2 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 14:06:10 -0700 Subject: [PATCH 15/30] feat: add new types QueryMode, QueryPlan, ResultSetStats (#1911) feat: add new types QueryMode, QueryPlan, ResultSetStats feat: add QueryMode field to RunQueryRequest feat: add ResultSetStats field to RunQueryResponse feat: add QueryMode field to RunAggregationQueryRequest feat: add ResultSetStats field to RunAggregationQueryResponse PiperOrigin-RevId: 595771083 Source-Link: https://github.com/googleapis/googleapis/commit/20278077049fb3ab78b365dc4a105d95140c2484 Source-Link: https://github.com/googleapis/googleapis-gen/commit/5407e2b6863928c26f52db9f347c6b5556e702f2 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNTQwN2UyYjY4NjM5MjhjMjZmNTJkYjlmMzQ3YzZiNTU1NmU3MDJmMiJ9 fix: correct long audio synthesis HTTP binding docs: Deprecate the custom voice usage field PiperOrigin-RevId: 595119987 Source-Link: https://github.com/googleapis/googleapis/commit/c22f4081fe394091ff2bb35b39b604ebb0e903cb Source-Link: https://github.com/googleapis/googleapis-gen/commit/4e9ca63d2cc7933eb7c383ce8b794fce152ea2fc Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNGU5Y2E2M2QyY2M3OTMzZWI3YzM4M2NlOGI3OTRmY2UxNTJlYTJmYyJ9 feat: add DeleteDatabase API and delete protection docs: update Database API description PiperOrigin-RevId: 591922567 Source-Link: https://github.com/googleapis/googleapis/commit/204f2aefbfa20f65c15de103ceca5d49c4b60082 Source-Link: https://github.com/googleapis/googleapis-gen/commit/bd9865000350465271911d99689b7561158855d4 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiYmQ5ODY1MDAwMzUwNDY1MjcxOTExZDk5Njg5Yjc1NjExNTg4NTVkNCJ9 feat: expose Firestore PITR fields in Database to stable feat: expose Firestore snapshot_time field in export API to stable feat: expose Firestore namespace ID fields in import/export APIs to stable docs: assorted typo fixes and whitespace updates PiperOrigin-RevId: 587811576 Source-Link: https://github.com/googleapis/googleapis/commit/fbe1c8e68ea9acd93658aaaab414033096bf34f4 Source-Link: https://github.com/googleapis/googleapis-gen/commit/5fe704d94fa2d9a1dce752942b7308873124b7dd Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNWZlNzA0ZDk0ZmEyZDlhMWRjZTc1Mjk0MmI3MzA4ODczMTI0YjdkZCJ9 build: update Node.js generator to compile protos PiperOrigin-RevId: 582493526 Source-Link: https://github.com/googleapis/googleapis/commit/7c4e4b52369c9f6ac3e78f945d36fc833f2280de Source-Link: https://github.com/googleapis/googleapis-gen/commit/368cfb651016d6a93ca6e488cbc34e2d1d9d212c Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMzY4Y2ZiNjUxMDE2ZDZhOTNjYTZlNDg4Y2JjMzRlMmQxZDlkMjEyYyJ9 chore: expand Target.target_id docs chore: improve FieldReference.field_path docs chore: (preview only) expose Query Profile API PiperOrigin-RevId: 570489360 Source-Link: https://github.com/googleapis/googleapis/commit/5c5f6b7f6d970caf1b414545ff49b3a54617dd26 Source-Link: https://github.com/googleapis/googleapis-gen/commit/cb29ed56b738ab04360d2e1cbf3b1bc8785f8ca1 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiY2IyOWVkNTZiNzM4YWIwNDM2MGQyZTFjYmYzYjFiYzg3ODVmOGNhMSJ9 --------- Co-authored-by: Owl Bot Co-authored-by: sofisl <55454395+sofisl@users.noreply.github.com> --- .jsdoc.js | 4 +- dev/protos/admin_v1.json | 143 ++++- dev/protos/firestore_admin_v1_proto_api.d.ts | 219 ++++++- dev/protos/firestore_admin_v1_proto_api.js | 599 +++++++++++++++++- dev/protos/firestore_v1_proto_api.d.ts | 140 +++- dev/protos/firestore_v1_proto_api.js | 372 ++++++++++- dev/protos/firestore_v1beta1_proto_api.d.ts | 10 +- dev/protos/firestore_v1beta1_proto_api.js | 32 +- dev/protos/google/api/client.proto | 13 + dev/protos/google/api/field_behavior.proto | 14 + .../google/firestore/admin/v1/database.proto | 96 ++- .../firestore/admin/v1/firestore_admin.proto | 81 ++- .../google/firestore/admin/v1/location.proto | 9 +- .../google/firestore/admin/v1/operation.proto | 42 +- .../google/firestore/v1/firestore.proto | 42 +- dev/protos/google/firestore/v1/query.proto | 5 +- .../google/firestore/v1/query_profile.proto | 76 +++ dev/protos/google/protobuf/descriptor.proto | 157 +++-- dev/protos/update.sh | 10 +- dev/protos/v1.json | 57 +- dev/protos/v1beta1.json | 8 +- dev/src/v1/firestore_admin_client.ts | 193 +++++- dev/src/v1/firestore_admin_client_config.json | 4 + dev/src/v1/firestore_client.ts | 27 +- dev/src/v1/firestore_proto_list.json | 1 + dev/src/v1beta1/firestore_client.ts | 17 +- dev/test/gapic_firestore_admin_v1.ts | 194 ++++++ dev/test/gapic_firestore_v1.ts | 14 +- dev/test/gapic_firestore_v1beta1.ts | 10 +- owlbot.py | 10 +- scripts/license.js | 30 +- 31 files changed, 2484 insertions(+), 145 deletions(-) create mode 100644 dev/protos/google/firestore/v1/query_profile.proto diff --git a/.jsdoc.js b/.jsdoc.js index 5097cdd38..16cbe2420 100644 --- a/.jsdoc.js +++ b/.jsdoc.js @@ -1,4 +1,4 @@ -// Copyright 2023 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -40,7 +40,7 @@ module.exports = { includePattern: '\\.js$' }, templates: { - copyright: 'Copyright 2023 Google LLC', + copyright: 'Copyright 2024 Google LLC', includeDate: false, sourceFiles: false, systemName: '@google-cloud/firestore', diff --git a/dev/protos/admin_v1.json b/dev/protos/admin_v1.json index 764a98236..26ed54624 100644 --- a/dev/protos/admin_v1.json +++ b/dev/protos/admin_v1.json @@ -31,6 +31,27 @@ "type": "string", "id": 1 }, + "uid": { + "type": "string", + "id": 3, + "options": { + "(google.api.field_behavior)": "OUTPUT_ONLY" + } + }, + "createTime": { + "type": "google.protobuf.Timestamp", + "id": 5, + "options": { + "(google.api.field_behavior)": "OUTPUT_ONLY" + } + }, + "updateTime": { + "type": "google.protobuf.Timestamp", + "id": 6, + "options": { + "(google.api.field_behavior)": "OUTPUT_ONLY" + } + }, "locationId": { "type": "string", "id": 9 @@ -43,6 +64,24 @@ "type": "ConcurrencyMode", "id": 15 }, + "versionRetentionPeriod": { + "type": "google.protobuf.Duration", + "id": 17, + "options": { + "(google.api.field_behavior)": "OUTPUT_ONLY" + } + }, + "earliestVersionTime": { + "type": "google.protobuf.Timestamp", + "id": 18, + "options": { + "(google.api.field_behavior)": "OUTPUT_ONLY" + } + }, + "pointInTimeRecoveryEnablement": { + "type": "PointInTimeRecoveryEnablement", + "id": 21 + }, "appEngineIntegrationMode": { "type": "AppEngineIntegrationMode", "id": 19 @@ -54,6 +93,10 @@ "(google.api.field_behavior)": "OUTPUT_ONLY" } }, + "deleteProtectionState": { + "type": "DeleteProtectionState", + "id": 22 + }, "etag": { "type": "string", "id": 99 @@ -75,12 +118,26 @@ "OPTIMISTIC_WITH_ENTITY_GROUPS": 3 } }, + "PointInTimeRecoveryEnablement": { + "values": { + "POINT_IN_TIME_RECOVERY_ENABLEMENT_UNSPECIFIED": 0, + "POINT_IN_TIME_RECOVERY_ENABLED": 1, + "POINT_IN_TIME_RECOVERY_DISABLED": 2 + } + }, "AppEngineIntegrationMode": { "values": { "APP_ENGINE_INTEGRATION_MODE_UNSPECIFIED": 0, "ENABLED": 1, "DISABLED": 2 } + }, + "DeleteProtectionState": { + "values": { + "DELETE_PROTECTION_STATE_UNSPECIFIED": 0, + "DELETE_PROTECTION_DISABLED": 1, + "DELETE_PROTECTION_ENABLED": 2 + } } } }, @@ -450,6 +507,32 @@ } } ] + }, + "DeleteDatabase": { + "requestType": "DeleteDatabaseRequest", + "responseType": "google.longrunning.Operation", + "options": { + "(google.api.http).delete": "/v1/{name=projects/*/databases/*}", + "(google.api.method_signature)": "name", + "(google.longrunning.operation_info).response_type": "Database", + "(google.longrunning.operation_info).metadata_type": "DeleteDatabaseMetadata" + }, + "parsedOptions": [ + { + "(google.api.http)": { + "delete": "/v1/{name=projects/*/databases/*}" + } + }, + { + "(google.api.method_signature)": "name" + }, + { + "(google.longrunning.operation_info)": { + "response_type": "Database", + "metadata_type": "DeleteDatabaseMetadata" + } + } + ] } } }, @@ -500,6 +583,11 @@ "rule": "repeated", "type": "Database", "id": 1 + }, + "unreachable": { + "rule": "repeated", + "type": "string", + "id": 3 } } }, @@ -533,6 +621,25 @@ "UpdateDatabaseMetadata": { "fields": {} }, + "DeleteDatabaseRequest": { + "fields": { + "name": { + "type": "string", + "id": 1, + "options": { + "(google.api.field_behavior)": "REQUIRED", + "(google.api.resource_reference).type": "firestore.googleapis.com/Database" + } + }, + "etag": { + "type": "string", + "id": 3 + } + } + }, + "DeleteDatabaseMetadata": { + "fields": {} + }, "CreateIndexRequest": { "fields": { "parent": { @@ -695,6 +802,15 @@ "outputUriPrefix": { "type": "string", "id": 3 + }, + "namespaceIds": { + "rule": "repeated", + "type": "string", + "id": 4 + }, + "snapshotTime": { + "type": "google.protobuf.Timestamp", + "id": 5 } } }, @@ -716,6 +832,11 @@ "inputUriPrefix": { "type": "string", "id": 3 + }, + "namespaceIds": { + "rule": "repeated", + "type": "string", + "id": 4 } } }, @@ -949,6 +1070,15 @@ "outputUriPrefix": { "type": "string", "id": 7 + }, + "namespaceIds": { + "rule": "repeated", + "type": "string", + "id": 8 + }, + "snapshotTime": { + "type": "google.protobuf.Timestamp", + "id": 9 } } }, @@ -982,6 +1112,11 @@ "inputUriPrefix": { "type": "string", "id": 7 + }, + "namespaceIds": { + "rule": "repeated", + "type": "string", + "id": 8 } } }, @@ -1346,6 +1481,11 @@ "longRunning": { "type": "LongRunning", "id": 2 + }, + "autoPopulatedFields": { + "rule": "repeated", + "type": "string", + "id": 3 } }, "nested": { @@ -1405,7 +1545,8 @@ "INPUT_ONLY": 4, "IMMUTABLE": 5, "UNORDERED_LIST": 6, - "NON_EMPTY_DEFAULT": 7 + "NON_EMPTY_DEFAULT": 7, + "IDENTIFIER": 8 } }, "LaunchStage": { diff --git a/dev/protos/firestore_admin_v1_proto_api.d.ts b/dev/protos/firestore_admin_v1_proto_api.d.ts index a7548dc5c..158a732c1 100644 --- a/dev/protos/firestore_admin_v1_proto_api.d.ts +++ b/dev/protos/firestore_admin_v1_proto_api.d.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,15 @@ export namespace google { /** Database name */ name?: (string|null); + /** Database uid */ + uid?: (string|null); + + /** Database createTime */ + createTime?: (google.protobuf.ITimestamp|null); + + /** Database updateTime */ + updateTime?: (google.protobuf.ITimestamp|null); + /** Database locationId */ locationId?: (string|null); @@ -43,12 +52,24 @@ export namespace google { /** Database concurrencyMode */ concurrencyMode?: (google.firestore.admin.v1.Database.ConcurrencyMode|null); + /** Database versionRetentionPeriod */ + versionRetentionPeriod?: (google.protobuf.IDuration|null); + + /** Database earliestVersionTime */ + earliestVersionTime?: (google.protobuf.ITimestamp|null); + + /** Database pointInTimeRecoveryEnablement */ + pointInTimeRecoveryEnablement?: (google.firestore.admin.v1.Database.PointInTimeRecoveryEnablement|null); + /** Database appEngineIntegrationMode */ appEngineIntegrationMode?: (google.firestore.admin.v1.Database.AppEngineIntegrationMode|null); /** Database keyPrefix */ keyPrefix?: (string|null); + /** Database deleteProtectionState */ + deleteProtectionState?: (google.firestore.admin.v1.Database.DeleteProtectionState|null); + /** Database etag */ etag?: (string|null); } @@ -65,6 +86,15 @@ export namespace google { /** Database name. */ public name: string; + /** Database uid. */ + public uid: string; + + /** Database createTime. */ + public createTime?: (google.protobuf.ITimestamp|null); + + /** Database updateTime. */ + public updateTime?: (google.protobuf.ITimestamp|null); + /** Database locationId. */ public locationId: string; @@ -74,12 +104,24 @@ export namespace google { /** Database concurrencyMode. */ public concurrencyMode: google.firestore.admin.v1.Database.ConcurrencyMode; + /** Database versionRetentionPeriod. */ + public versionRetentionPeriod?: (google.protobuf.IDuration|null); + + /** Database earliestVersionTime. */ + public earliestVersionTime?: (google.protobuf.ITimestamp|null); + + /** Database pointInTimeRecoveryEnablement. */ + public pointInTimeRecoveryEnablement: google.firestore.admin.v1.Database.PointInTimeRecoveryEnablement; + /** Database appEngineIntegrationMode. */ public appEngineIntegrationMode: google.firestore.admin.v1.Database.AppEngineIntegrationMode; /** Database keyPrefix. */ public keyPrefix: string; + /** Database deleteProtectionState. */ + public deleteProtectionState: google.firestore.admin.v1.Database.DeleteProtectionState; + /** Database etag. */ public etag: string; @@ -122,9 +164,17 @@ export namespace google { type ConcurrencyMode = "CONCURRENCY_MODE_UNSPECIFIED"| "OPTIMISTIC"| "PESSIMISTIC"| "OPTIMISTIC_WITH_ENTITY_GROUPS"; + /** PointInTimeRecoveryEnablement enum. */ + type PointInTimeRecoveryEnablement = + "POINT_IN_TIME_RECOVERY_ENABLEMENT_UNSPECIFIED"| "POINT_IN_TIME_RECOVERY_ENABLED"| "POINT_IN_TIME_RECOVERY_DISABLED"; + /** AppEngineIntegrationMode enum. */ type AppEngineIntegrationMode = "APP_ENGINE_INTEGRATION_MODE_UNSPECIFIED"| "ENABLED"| "DISABLED"; + + /** DeleteProtectionState enum. */ + type DeleteProtectionState = + "DELETE_PROTECTION_STATE_UNSPECIFIED"| "DELETE_PROTECTION_DISABLED"| "DELETE_PROTECTION_ENABLED"; } /** Properties of a Field. */ @@ -503,6 +553,20 @@ export namespace google { * @returns Promise */ public updateDatabase(request: google.firestore.admin.v1.IUpdateDatabaseRequest): Promise; + + /** + * Calls DeleteDatabase. + * @param request DeleteDatabaseRequest message or plain object + * @param callback Node-style callback called with the error, if any, and Operation + */ + public deleteDatabase(request: google.firestore.admin.v1.IDeleteDatabaseRequest, callback: google.firestore.admin.v1.FirestoreAdmin.DeleteDatabaseCallback): void; + + /** + * Calls DeleteDatabase. + * @param request DeleteDatabaseRequest message or plain object + * @returns Promise + */ + public deleteDatabase(request: google.firestore.admin.v1.IDeleteDatabaseRequest): Promise; } namespace FirestoreAdmin { @@ -597,6 +661,13 @@ export namespace google { * @param [response] Operation */ type UpdateDatabaseCallback = (error: (Error|null), response?: google.longrunning.Operation) => void; + + /** + * Callback as used by {@link google.firestore.admin.v1.FirestoreAdmin#deleteDatabase}. + * @param error Error, if any + * @param [response] Operation + */ + type DeleteDatabaseCallback = (error: (Error|null), response?: google.longrunning.Operation) => void; } /** Properties of a ListDatabasesRequest. */ @@ -754,6 +825,9 @@ export namespace google { /** ListDatabasesResponse databases */ databases?: (google.firestore.admin.v1.IDatabase[]|null); + + /** ListDatabasesResponse unreachable */ + unreachable?: (string[]|null); } /** Represents a ListDatabasesResponse. */ @@ -768,6 +842,9 @@ export namespace google { /** ListDatabasesResponse databases. */ public databases: google.firestore.admin.v1.IDatabase[]; + /** ListDatabasesResponse unreachable. */ + public unreachable: string[]; + /** * Creates a ListDatabasesResponse message from a plain object. Also converts values to their respective internal types. * @param object Plain object @@ -941,6 +1018,102 @@ export namespace google { public static getTypeUrl(typeUrlPrefix?: string): string; } + /** Properties of a DeleteDatabaseRequest. */ + interface IDeleteDatabaseRequest { + + /** DeleteDatabaseRequest name */ + name?: (string|null); + + /** DeleteDatabaseRequest etag */ + etag?: (string|null); + } + + /** Represents a DeleteDatabaseRequest. */ + class DeleteDatabaseRequest implements IDeleteDatabaseRequest { + + /** + * Constructs a new DeleteDatabaseRequest. + * @param [properties] Properties to set + */ + constructor(properties?: google.firestore.admin.v1.IDeleteDatabaseRequest); + + /** DeleteDatabaseRequest name. */ + public name: string; + + /** DeleteDatabaseRequest etag. */ + public etag: string; + + /** + * Creates a DeleteDatabaseRequest message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns DeleteDatabaseRequest + */ + public static fromObject(object: { [k: string]: any }): google.firestore.admin.v1.DeleteDatabaseRequest; + + /** + * Creates a plain object from a DeleteDatabaseRequest message. Also converts values to other types if specified. + * @param message DeleteDatabaseRequest + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: google.firestore.admin.v1.DeleteDatabaseRequest, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this DeleteDatabaseRequest to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for DeleteDatabaseRequest + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + + /** Properties of a DeleteDatabaseMetadata. */ + interface IDeleteDatabaseMetadata { + } + + /** Represents a DeleteDatabaseMetadata. */ + class DeleteDatabaseMetadata implements IDeleteDatabaseMetadata { + + /** + * Constructs a new DeleteDatabaseMetadata. + * @param [properties] Properties to set + */ + constructor(properties?: google.firestore.admin.v1.IDeleteDatabaseMetadata); + + /** + * Creates a DeleteDatabaseMetadata message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns DeleteDatabaseMetadata + */ + public static fromObject(object: { [k: string]: any }): google.firestore.admin.v1.DeleteDatabaseMetadata; + + /** + * Creates a plain object from a DeleteDatabaseMetadata message. Also converts values to other types if specified. + * @param message DeleteDatabaseMetadata + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: google.firestore.admin.v1.DeleteDatabaseMetadata, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this DeleteDatabaseMetadata to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for DeleteDatabaseMetadata + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + /** Properties of a CreateIndexRequest. */ interface ICreateIndexRequest { @@ -1444,6 +1617,12 @@ export namespace google { /** ExportDocumentsRequest outputUriPrefix */ outputUriPrefix?: (string|null); + + /** ExportDocumentsRequest namespaceIds */ + namespaceIds?: (string[]|null); + + /** ExportDocumentsRequest snapshotTime */ + snapshotTime?: (google.protobuf.ITimestamp|null); } /** Represents an ExportDocumentsRequest. */ @@ -1464,6 +1643,12 @@ export namespace google { /** ExportDocumentsRequest outputUriPrefix. */ public outputUriPrefix: string; + /** ExportDocumentsRequest namespaceIds. */ + public namespaceIds: string[]; + + /** ExportDocumentsRequest snapshotTime. */ + public snapshotTime?: (google.protobuf.ITimestamp|null); + /** * Creates an ExportDocumentsRequest message from a plain object. Also converts values to their respective internal types. * @param object Plain object @@ -1504,6 +1689,9 @@ export namespace google { /** ImportDocumentsRequest inputUriPrefix */ inputUriPrefix?: (string|null); + + /** ImportDocumentsRequest namespaceIds */ + namespaceIds?: (string[]|null); } /** Represents an ImportDocumentsRequest. */ @@ -1524,6 +1712,9 @@ export namespace google { /** ImportDocumentsRequest inputUriPrefix. */ public inputUriPrefix: string; + /** ImportDocumentsRequest namespaceIds. */ + public namespaceIds: string[]; + /** * Creates an ImportDocumentsRequest message from a plain object. Also converts values to their respective internal types. * @param object Plain object @@ -2066,6 +2257,12 @@ export namespace google { /** ExportDocumentsMetadata outputUriPrefix */ outputUriPrefix?: (string|null); + + /** ExportDocumentsMetadata namespaceIds */ + namespaceIds?: (string[]|null); + + /** ExportDocumentsMetadata snapshotTime */ + snapshotTime?: (google.protobuf.ITimestamp|null); } /** Represents an ExportDocumentsMetadata. */ @@ -2098,6 +2295,12 @@ export namespace google { /** ExportDocumentsMetadata outputUriPrefix. */ public outputUriPrefix: string; + /** ExportDocumentsMetadata namespaceIds. */ + public namespaceIds: string[]; + + /** ExportDocumentsMetadata snapshotTime. */ + public snapshotTime?: (google.protobuf.ITimestamp|null); + /** * Creates an ExportDocumentsMetadata message from a plain object. Also converts values to their respective internal types. * @param object Plain object @@ -2150,6 +2353,9 @@ export namespace google { /** ImportDocumentsMetadata inputUriPrefix */ inputUriPrefix?: (string|null); + + /** ImportDocumentsMetadata namespaceIds */ + namespaceIds?: (string[]|null); } /** Represents an ImportDocumentsMetadata. */ @@ -2182,6 +2388,9 @@ export namespace google { /** ImportDocumentsMetadata inputUriPrefix. */ public inputUriPrefix: string; + /** ImportDocumentsMetadata namespaceIds. */ + public namespaceIds: string[]; + /** * Creates an ImportDocumentsMetadata message from a plain object. Also converts values to their respective internal types. * @param object Plain object @@ -3222,6 +3431,9 @@ export namespace google { /** MethodSettings longRunning */ longRunning?: (google.api.MethodSettings.ILongRunning|null); + + /** MethodSettings autoPopulatedFields */ + autoPopulatedFields?: (string[]|null); } /** Represents a MethodSettings. */ @@ -3239,6 +3451,9 @@ export namespace google { /** MethodSettings longRunning. */ public longRunning?: (google.api.MethodSettings.ILongRunning|null); + /** MethodSettings autoPopulatedFields. */ + public autoPopulatedFields: string[]; + /** * Creates a MethodSettings message from a plain object. Also converts values to their respective internal types. * @param object Plain object @@ -3347,7 +3562,7 @@ export namespace google { /** FieldBehavior enum. */ type FieldBehavior = - "FIELD_BEHAVIOR_UNSPECIFIED"| "OPTIONAL"| "REQUIRED"| "OUTPUT_ONLY"| "INPUT_ONLY"| "IMMUTABLE"| "UNORDERED_LIST"| "NON_EMPTY_DEFAULT"; + "FIELD_BEHAVIOR_UNSPECIFIED"| "OPTIONAL"| "REQUIRED"| "OUTPUT_ONLY"| "INPUT_ONLY"| "IMMUTABLE"| "UNORDERED_LIST"| "NON_EMPTY_DEFAULT"| "IDENTIFIER"; /** LaunchStage enum. */ type LaunchStage = diff --git a/dev/protos/firestore_admin_v1_proto_api.js b/dev/protos/firestore_admin_v1_proto_api.js index cf1dd4c84..4314f54a8 100644 --- a/dev/protos/firestore_admin_v1_proto_api.js +++ b/dev/protos/firestore_admin_v1_proto_api.js @@ -1,5 +1,5 @@ /*! - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,11 +75,18 @@ * @memberof google.firestore.admin.v1 * @interface IDatabase * @property {string|null} [name] Database name + * @property {string|null} [uid] Database uid + * @property {google.protobuf.ITimestamp|null} [createTime] Database createTime + * @property {google.protobuf.ITimestamp|null} [updateTime] Database updateTime * @property {string|null} [locationId] Database locationId * @property {google.firestore.admin.v1.Database.DatabaseType|null} [type] Database type * @property {google.firestore.admin.v1.Database.ConcurrencyMode|null} [concurrencyMode] Database concurrencyMode + * @property {google.protobuf.IDuration|null} [versionRetentionPeriod] Database versionRetentionPeriod + * @property {google.protobuf.ITimestamp|null} [earliestVersionTime] Database earliestVersionTime + * @property {google.firestore.admin.v1.Database.PointInTimeRecoveryEnablement|null} [pointInTimeRecoveryEnablement] Database pointInTimeRecoveryEnablement * @property {google.firestore.admin.v1.Database.AppEngineIntegrationMode|null} [appEngineIntegrationMode] Database appEngineIntegrationMode * @property {string|null} [keyPrefix] Database keyPrefix + * @property {google.firestore.admin.v1.Database.DeleteProtectionState|null} [deleteProtectionState] Database deleteProtectionState * @property {string|null} [etag] Database etag */ @@ -106,6 +113,30 @@ */ Database.prototype.name = ""; + /** + * Database uid. + * @member {string} uid + * @memberof google.firestore.admin.v1.Database + * @instance + */ + Database.prototype.uid = ""; + + /** + * Database createTime. + * @member {google.protobuf.ITimestamp|null|undefined} createTime + * @memberof google.firestore.admin.v1.Database + * @instance + */ + Database.prototype.createTime = null; + + /** + * Database updateTime. + * @member {google.protobuf.ITimestamp|null|undefined} updateTime + * @memberof google.firestore.admin.v1.Database + * @instance + */ + Database.prototype.updateTime = null; + /** * Database locationId. * @member {string} locationId @@ -130,6 +161,30 @@ */ Database.prototype.concurrencyMode = 0; + /** + * Database versionRetentionPeriod. + * @member {google.protobuf.IDuration|null|undefined} versionRetentionPeriod + * @memberof google.firestore.admin.v1.Database + * @instance + */ + Database.prototype.versionRetentionPeriod = null; + + /** + * Database earliestVersionTime. + * @member {google.protobuf.ITimestamp|null|undefined} earliestVersionTime + * @memberof google.firestore.admin.v1.Database + * @instance + */ + Database.prototype.earliestVersionTime = null; + + /** + * Database pointInTimeRecoveryEnablement. + * @member {google.firestore.admin.v1.Database.PointInTimeRecoveryEnablement} pointInTimeRecoveryEnablement + * @memberof google.firestore.admin.v1.Database + * @instance + */ + Database.prototype.pointInTimeRecoveryEnablement = 0; + /** * Database appEngineIntegrationMode. * @member {google.firestore.admin.v1.Database.AppEngineIntegrationMode} appEngineIntegrationMode @@ -146,6 +201,14 @@ */ Database.prototype.keyPrefix = ""; + /** + * Database deleteProtectionState. + * @member {google.firestore.admin.v1.Database.DeleteProtectionState} deleteProtectionState + * @memberof google.firestore.admin.v1.Database + * @instance + */ + Database.prototype.deleteProtectionState = 0; + /** * Database etag. * @member {string} etag @@ -168,6 +231,18 @@ var message = new $root.google.firestore.admin.v1.Database(); if (object.name != null) message.name = String(object.name); + if (object.uid != null) + message.uid = String(object.uid); + if (object.createTime != null) { + if (typeof object.createTime !== "object") + throw TypeError(".google.firestore.admin.v1.Database.createTime: object expected"); + message.createTime = $root.google.protobuf.Timestamp.fromObject(object.createTime); + } + if (object.updateTime != null) { + if (typeof object.updateTime !== "object") + throw TypeError(".google.firestore.admin.v1.Database.updateTime: object expected"); + message.updateTime = $root.google.protobuf.Timestamp.fromObject(object.updateTime); + } if (object.locationId != null) message.locationId = String(object.locationId); switch (object.type) { @@ -214,6 +289,36 @@ message.concurrencyMode = 3; break; } + if (object.versionRetentionPeriod != null) { + if (typeof object.versionRetentionPeriod !== "object") + throw TypeError(".google.firestore.admin.v1.Database.versionRetentionPeriod: object expected"); + message.versionRetentionPeriod = $root.google.protobuf.Duration.fromObject(object.versionRetentionPeriod); + } + if (object.earliestVersionTime != null) { + if (typeof object.earliestVersionTime !== "object") + throw TypeError(".google.firestore.admin.v1.Database.earliestVersionTime: object expected"); + message.earliestVersionTime = $root.google.protobuf.Timestamp.fromObject(object.earliestVersionTime); + } + switch (object.pointInTimeRecoveryEnablement) { + default: + if (typeof object.pointInTimeRecoveryEnablement === "number") { + message.pointInTimeRecoveryEnablement = object.pointInTimeRecoveryEnablement; + break; + } + break; + case "POINT_IN_TIME_RECOVERY_ENABLEMENT_UNSPECIFIED": + case 0: + message.pointInTimeRecoveryEnablement = 0; + break; + case "POINT_IN_TIME_RECOVERY_ENABLED": + case 1: + message.pointInTimeRecoveryEnablement = 1; + break; + case "POINT_IN_TIME_RECOVERY_DISABLED": + case 2: + message.pointInTimeRecoveryEnablement = 2; + break; + } switch (object.appEngineIntegrationMode) { default: if (typeof object.appEngineIntegrationMode === "number") { @@ -236,6 +341,26 @@ } if (object.keyPrefix != null) message.keyPrefix = String(object.keyPrefix); + switch (object.deleteProtectionState) { + default: + if (typeof object.deleteProtectionState === "number") { + message.deleteProtectionState = object.deleteProtectionState; + break; + } + break; + case "DELETE_PROTECTION_STATE_UNSPECIFIED": + case 0: + message.deleteProtectionState = 0; + break; + case "DELETE_PROTECTION_DISABLED": + case 1: + message.deleteProtectionState = 1; + break; + case "DELETE_PROTECTION_ENABLED": + case 2: + message.deleteProtectionState = 2; + break; + } if (object.etag != null) message.etag = String(object.etag); return message; @@ -256,25 +381,46 @@ var object = {}; if (options.defaults) { object.name = ""; + object.uid = ""; + object.createTime = null; + object.updateTime = null; object.locationId = ""; object.type = options.enums === String ? "DATABASE_TYPE_UNSPECIFIED" : 0; object.concurrencyMode = options.enums === String ? "CONCURRENCY_MODE_UNSPECIFIED" : 0; + object.versionRetentionPeriod = null; + object.earliestVersionTime = null; object.appEngineIntegrationMode = options.enums === String ? "APP_ENGINE_INTEGRATION_MODE_UNSPECIFIED" : 0; object.keyPrefix = ""; + object.pointInTimeRecoveryEnablement = options.enums === String ? "POINT_IN_TIME_RECOVERY_ENABLEMENT_UNSPECIFIED" : 0; + object.deleteProtectionState = options.enums === String ? "DELETE_PROTECTION_STATE_UNSPECIFIED" : 0; object.etag = ""; } if (message.name != null && message.hasOwnProperty("name")) object.name = message.name; + if (message.uid != null && message.hasOwnProperty("uid")) + object.uid = message.uid; + if (message.createTime != null && message.hasOwnProperty("createTime")) + object.createTime = $root.google.protobuf.Timestamp.toObject(message.createTime, options); + if (message.updateTime != null && message.hasOwnProperty("updateTime")) + object.updateTime = $root.google.protobuf.Timestamp.toObject(message.updateTime, options); if (message.locationId != null && message.hasOwnProperty("locationId")) object.locationId = message.locationId; if (message.type != null && message.hasOwnProperty("type")) object.type = options.enums === String ? $root.google.firestore.admin.v1.Database.DatabaseType[message.type] === undefined ? message.type : $root.google.firestore.admin.v1.Database.DatabaseType[message.type] : message.type; if (message.concurrencyMode != null && message.hasOwnProperty("concurrencyMode")) object.concurrencyMode = options.enums === String ? $root.google.firestore.admin.v1.Database.ConcurrencyMode[message.concurrencyMode] === undefined ? message.concurrencyMode : $root.google.firestore.admin.v1.Database.ConcurrencyMode[message.concurrencyMode] : message.concurrencyMode; + if (message.versionRetentionPeriod != null && message.hasOwnProperty("versionRetentionPeriod")) + object.versionRetentionPeriod = $root.google.protobuf.Duration.toObject(message.versionRetentionPeriod, options); + if (message.earliestVersionTime != null && message.hasOwnProperty("earliestVersionTime")) + object.earliestVersionTime = $root.google.protobuf.Timestamp.toObject(message.earliestVersionTime, options); if (message.appEngineIntegrationMode != null && message.hasOwnProperty("appEngineIntegrationMode")) object.appEngineIntegrationMode = options.enums === String ? $root.google.firestore.admin.v1.Database.AppEngineIntegrationMode[message.appEngineIntegrationMode] === undefined ? message.appEngineIntegrationMode : $root.google.firestore.admin.v1.Database.AppEngineIntegrationMode[message.appEngineIntegrationMode] : message.appEngineIntegrationMode; if (message.keyPrefix != null && message.hasOwnProperty("keyPrefix")) object.keyPrefix = message.keyPrefix; + if (message.pointInTimeRecoveryEnablement != null && message.hasOwnProperty("pointInTimeRecoveryEnablement")) + object.pointInTimeRecoveryEnablement = options.enums === String ? $root.google.firestore.admin.v1.Database.PointInTimeRecoveryEnablement[message.pointInTimeRecoveryEnablement] === undefined ? message.pointInTimeRecoveryEnablement : $root.google.firestore.admin.v1.Database.PointInTimeRecoveryEnablement[message.pointInTimeRecoveryEnablement] : message.pointInTimeRecoveryEnablement; + if (message.deleteProtectionState != null && message.hasOwnProperty("deleteProtectionState")) + object.deleteProtectionState = options.enums === String ? $root.google.firestore.admin.v1.Database.DeleteProtectionState[message.deleteProtectionState] === undefined ? message.deleteProtectionState : $root.google.firestore.admin.v1.Database.DeleteProtectionState[message.deleteProtectionState] : message.deleteProtectionState; if (message.etag != null && message.hasOwnProperty("etag")) object.etag = message.etag; return object; @@ -340,6 +486,22 @@ return values; })(); + /** + * PointInTimeRecoveryEnablement enum. + * @name google.firestore.admin.v1.Database.PointInTimeRecoveryEnablement + * @enum {string} + * @property {string} POINT_IN_TIME_RECOVERY_ENABLEMENT_UNSPECIFIED=POINT_IN_TIME_RECOVERY_ENABLEMENT_UNSPECIFIED POINT_IN_TIME_RECOVERY_ENABLEMENT_UNSPECIFIED value + * @property {string} POINT_IN_TIME_RECOVERY_ENABLED=POINT_IN_TIME_RECOVERY_ENABLED POINT_IN_TIME_RECOVERY_ENABLED value + * @property {string} POINT_IN_TIME_RECOVERY_DISABLED=POINT_IN_TIME_RECOVERY_DISABLED POINT_IN_TIME_RECOVERY_DISABLED value + */ + Database.PointInTimeRecoveryEnablement = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "POINT_IN_TIME_RECOVERY_ENABLEMENT_UNSPECIFIED"] = "POINT_IN_TIME_RECOVERY_ENABLEMENT_UNSPECIFIED"; + values[valuesById[1] = "POINT_IN_TIME_RECOVERY_ENABLED"] = "POINT_IN_TIME_RECOVERY_ENABLED"; + values[valuesById[2] = "POINT_IN_TIME_RECOVERY_DISABLED"] = "POINT_IN_TIME_RECOVERY_DISABLED"; + return values; + })(); + /** * AppEngineIntegrationMode enum. * @name google.firestore.admin.v1.Database.AppEngineIntegrationMode @@ -356,6 +518,22 @@ return values; })(); + /** + * DeleteProtectionState enum. + * @name google.firestore.admin.v1.Database.DeleteProtectionState + * @enum {string} + * @property {string} DELETE_PROTECTION_STATE_UNSPECIFIED=DELETE_PROTECTION_STATE_UNSPECIFIED DELETE_PROTECTION_STATE_UNSPECIFIED value + * @property {string} DELETE_PROTECTION_DISABLED=DELETE_PROTECTION_DISABLED DELETE_PROTECTION_DISABLED value + * @property {string} DELETE_PROTECTION_ENABLED=DELETE_PROTECTION_ENABLED DELETE_PROTECTION_ENABLED value + */ + Database.DeleteProtectionState = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "DELETE_PROTECTION_STATE_UNSPECIFIED"] = "DELETE_PROTECTION_STATE_UNSPECIFIED"; + values[valuesById[1] = "DELETE_PROTECTION_DISABLED"] = "DELETE_PROTECTION_DISABLED"; + values[valuesById[2] = "DELETE_PROTECTION_ENABLED"] = "DELETE_PROTECTION_ENABLED"; + return values; + })(); + return Database; })(); @@ -1231,6 +1409,39 @@ * @variation 2 */ + /** + * Callback as used by {@link google.firestore.admin.v1.FirestoreAdmin#deleteDatabase}. + * @memberof google.firestore.admin.v1.FirestoreAdmin + * @typedef DeleteDatabaseCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {google.longrunning.Operation} [response] Operation + */ + + /** + * Calls DeleteDatabase. + * @function deleteDatabase + * @memberof google.firestore.admin.v1.FirestoreAdmin + * @instance + * @param {google.firestore.admin.v1.IDeleteDatabaseRequest} request DeleteDatabaseRequest message or plain object + * @param {google.firestore.admin.v1.FirestoreAdmin.DeleteDatabaseCallback} callback Node-style callback called with the error, if any, and Operation + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(FirestoreAdmin.prototype.deleteDatabase = function deleteDatabase(request, callback) { + return this.rpcCall(deleteDatabase, $root.google.firestore.admin.v1.DeleteDatabaseRequest, $root.google.longrunning.Operation, request, callback); + }, "name", { value: "DeleteDatabase" }); + + /** + * Calls DeleteDatabase. + * @function deleteDatabase + * @memberof google.firestore.admin.v1.FirestoreAdmin + * @instance + * @param {google.firestore.admin.v1.IDeleteDatabaseRequest} request DeleteDatabaseRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + return FirestoreAdmin; })(); @@ -1548,6 +1759,7 @@ * @memberof google.firestore.admin.v1 * @interface IListDatabasesResponse * @property {Array.|null} [databases] ListDatabasesResponse databases + * @property {Array.|null} [unreachable] ListDatabasesResponse unreachable */ /** @@ -1560,6 +1772,7 @@ */ function ListDatabasesResponse(properties) { this.databases = []; + this.unreachable = []; if (properties) for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -1574,6 +1787,14 @@ */ ListDatabasesResponse.prototype.databases = $util.emptyArray; + /** + * ListDatabasesResponse unreachable. + * @member {Array.} unreachable + * @memberof google.firestore.admin.v1.ListDatabasesResponse + * @instance + */ + ListDatabasesResponse.prototype.unreachable = $util.emptyArray; + /** * Creates a ListDatabasesResponse message from a plain object. Also converts values to their respective internal types. * @function fromObject @@ -1596,6 +1817,13 @@ message.databases[i] = $root.google.firestore.admin.v1.Database.fromObject(object.databases[i]); } } + if (object.unreachable) { + if (!Array.isArray(object.unreachable)) + throw TypeError(".google.firestore.admin.v1.ListDatabasesResponse.unreachable: array expected"); + message.unreachable = []; + for (var i = 0; i < object.unreachable.length; ++i) + message.unreachable[i] = String(object.unreachable[i]); + } return message; }; @@ -1612,13 +1840,20 @@ if (!options) options = {}; var object = {}; - if (options.arrays || options.defaults) + if (options.arrays || options.defaults) { object.databases = []; + object.unreachable = []; + } if (message.databases && message.databases.length) { object.databases = []; for (var j = 0; j < message.databases.length; ++j) object.databases[j] = $root.google.firestore.admin.v1.Database.toObject(message.databases[j], options); } + if (message.unreachable && message.unreachable.length) { + object.unreachable = []; + for (var j = 0; j < message.unreachable.length; ++j) + object.unreachable[j] = message.unreachable[j]; + } return object; }; @@ -1947,6 +2182,198 @@ return UpdateDatabaseMetadata; })(); + v1.DeleteDatabaseRequest = (function() { + + /** + * Properties of a DeleteDatabaseRequest. + * @memberof google.firestore.admin.v1 + * @interface IDeleteDatabaseRequest + * @property {string|null} [name] DeleteDatabaseRequest name + * @property {string|null} [etag] DeleteDatabaseRequest etag + */ + + /** + * Constructs a new DeleteDatabaseRequest. + * @memberof google.firestore.admin.v1 + * @classdesc Represents a DeleteDatabaseRequest. + * @implements IDeleteDatabaseRequest + * @constructor + * @param {google.firestore.admin.v1.IDeleteDatabaseRequest=} [properties] Properties to set + */ + function DeleteDatabaseRequest(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * DeleteDatabaseRequest name. + * @member {string} name + * @memberof google.firestore.admin.v1.DeleteDatabaseRequest + * @instance + */ + DeleteDatabaseRequest.prototype.name = ""; + + /** + * DeleteDatabaseRequest etag. + * @member {string} etag + * @memberof google.firestore.admin.v1.DeleteDatabaseRequest + * @instance + */ + DeleteDatabaseRequest.prototype.etag = ""; + + /** + * Creates a DeleteDatabaseRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof google.firestore.admin.v1.DeleteDatabaseRequest + * @static + * @param {Object.} object Plain object + * @returns {google.firestore.admin.v1.DeleteDatabaseRequest} DeleteDatabaseRequest + */ + DeleteDatabaseRequest.fromObject = function fromObject(object) { + if (object instanceof $root.google.firestore.admin.v1.DeleteDatabaseRequest) + return object; + var message = new $root.google.firestore.admin.v1.DeleteDatabaseRequest(); + if (object.name != null) + message.name = String(object.name); + if (object.etag != null) + message.etag = String(object.etag); + return message; + }; + + /** + * Creates a plain object from a DeleteDatabaseRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof google.firestore.admin.v1.DeleteDatabaseRequest + * @static + * @param {google.firestore.admin.v1.DeleteDatabaseRequest} message DeleteDatabaseRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + DeleteDatabaseRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.name = ""; + object.etag = ""; + } + if (message.name != null && message.hasOwnProperty("name")) + object.name = message.name; + if (message.etag != null && message.hasOwnProperty("etag")) + object.etag = message.etag; + return object; + }; + + /** + * Converts this DeleteDatabaseRequest to JSON. + * @function toJSON + * @memberof google.firestore.admin.v1.DeleteDatabaseRequest + * @instance + * @returns {Object.} JSON object + */ + DeleteDatabaseRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for DeleteDatabaseRequest + * @function getTypeUrl + * @memberof google.firestore.admin.v1.DeleteDatabaseRequest + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + DeleteDatabaseRequest.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/google.firestore.admin.v1.DeleteDatabaseRequest"; + }; + + return DeleteDatabaseRequest; + })(); + + v1.DeleteDatabaseMetadata = (function() { + + /** + * Properties of a DeleteDatabaseMetadata. + * @memberof google.firestore.admin.v1 + * @interface IDeleteDatabaseMetadata + */ + + /** + * Constructs a new DeleteDatabaseMetadata. + * @memberof google.firestore.admin.v1 + * @classdesc Represents a DeleteDatabaseMetadata. + * @implements IDeleteDatabaseMetadata + * @constructor + * @param {google.firestore.admin.v1.IDeleteDatabaseMetadata=} [properties] Properties to set + */ + function DeleteDatabaseMetadata(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Creates a DeleteDatabaseMetadata message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof google.firestore.admin.v1.DeleteDatabaseMetadata + * @static + * @param {Object.} object Plain object + * @returns {google.firestore.admin.v1.DeleteDatabaseMetadata} DeleteDatabaseMetadata + */ + DeleteDatabaseMetadata.fromObject = function fromObject(object) { + if (object instanceof $root.google.firestore.admin.v1.DeleteDatabaseMetadata) + return object; + return new $root.google.firestore.admin.v1.DeleteDatabaseMetadata(); + }; + + /** + * Creates a plain object from a DeleteDatabaseMetadata message. Also converts values to other types if specified. + * @function toObject + * @memberof google.firestore.admin.v1.DeleteDatabaseMetadata + * @static + * @param {google.firestore.admin.v1.DeleteDatabaseMetadata} message DeleteDatabaseMetadata + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + DeleteDatabaseMetadata.toObject = function toObject() { + return {}; + }; + + /** + * Converts this DeleteDatabaseMetadata to JSON. + * @function toJSON + * @memberof google.firestore.admin.v1.DeleteDatabaseMetadata + * @instance + * @returns {Object.} JSON object + */ + DeleteDatabaseMetadata.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for DeleteDatabaseMetadata + * @function getTypeUrl + * @memberof google.firestore.admin.v1.DeleteDatabaseMetadata + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + DeleteDatabaseMetadata.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/google.firestore.admin.v1.DeleteDatabaseMetadata"; + }; + + return DeleteDatabaseMetadata; + })(); + v1.CreateIndexRequest = (function() { /** @@ -3017,6 +3444,8 @@ * @property {string|null} [name] ExportDocumentsRequest name * @property {Array.|null} [collectionIds] ExportDocumentsRequest collectionIds * @property {string|null} [outputUriPrefix] ExportDocumentsRequest outputUriPrefix + * @property {Array.|null} [namespaceIds] ExportDocumentsRequest namespaceIds + * @property {google.protobuf.ITimestamp|null} [snapshotTime] ExportDocumentsRequest snapshotTime */ /** @@ -3029,6 +3458,7 @@ */ function ExportDocumentsRequest(properties) { this.collectionIds = []; + this.namespaceIds = []; if (properties) for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -3059,6 +3489,22 @@ */ ExportDocumentsRequest.prototype.outputUriPrefix = ""; + /** + * ExportDocumentsRequest namespaceIds. + * @member {Array.} namespaceIds + * @memberof google.firestore.admin.v1.ExportDocumentsRequest + * @instance + */ + ExportDocumentsRequest.prototype.namespaceIds = $util.emptyArray; + + /** + * ExportDocumentsRequest snapshotTime. + * @member {google.protobuf.ITimestamp|null|undefined} snapshotTime + * @memberof google.firestore.admin.v1.ExportDocumentsRequest + * @instance + */ + ExportDocumentsRequest.prototype.snapshotTime = null; + /** * Creates an ExportDocumentsRequest message from a plain object. Also converts values to their respective internal types. * @function fromObject @@ -3082,6 +3528,18 @@ } if (object.outputUriPrefix != null) message.outputUriPrefix = String(object.outputUriPrefix); + if (object.namespaceIds) { + if (!Array.isArray(object.namespaceIds)) + throw TypeError(".google.firestore.admin.v1.ExportDocumentsRequest.namespaceIds: array expected"); + message.namespaceIds = []; + for (var i = 0; i < object.namespaceIds.length; ++i) + message.namespaceIds[i] = String(object.namespaceIds[i]); + } + if (object.snapshotTime != null) { + if (typeof object.snapshotTime !== "object") + throw TypeError(".google.firestore.admin.v1.ExportDocumentsRequest.snapshotTime: object expected"); + message.snapshotTime = $root.google.protobuf.Timestamp.fromObject(object.snapshotTime); + } return message; }; @@ -3098,11 +3556,14 @@ if (!options) options = {}; var object = {}; - if (options.arrays || options.defaults) + if (options.arrays || options.defaults) { object.collectionIds = []; + object.namespaceIds = []; + } if (options.defaults) { object.name = ""; object.outputUriPrefix = ""; + object.snapshotTime = null; } if (message.name != null && message.hasOwnProperty("name")) object.name = message.name; @@ -3113,6 +3574,13 @@ } if (message.outputUriPrefix != null && message.hasOwnProperty("outputUriPrefix")) object.outputUriPrefix = message.outputUriPrefix; + if (message.namespaceIds && message.namespaceIds.length) { + object.namespaceIds = []; + for (var j = 0; j < message.namespaceIds.length; ++j) + object.namespaceIds[j] = message.namespaceIds[j]; + } + if (message.snapshotTime != null && message.hasOwnProperty("snapshotTime")) + object.snapshotTime = $root.google.protobuf.Timestamp.toObject(message.snapshotTime, options); return object; }; @@ -3154,6 +3622,7 @@ * @property {string|null} [name] ImportDocumentsRequest name * @property {Array.|null} [collectionIds] ImportDocumentsRequest collectionIds * @property {string|null} [inputUriPrefix] ImportDocumentsRequest inputUriPrefix + * @property {Array.|null} [namespaceIds] ImportDocumentsRequest namespaceIds */ /** @@ -3166,6 +3635,7 @@ */ function ImportDocumentsRequest(properties) { this.collectionIds = []; + this.namespaceIds = []; if (properties) for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -3196,6 +3666,14 @@ */ ImportDocumentsRequest.prototype.inputUriPrefix = ""; + /** + * ImportDocumentsRequest namespaceIds. + * @member {Array.} namespaceIds + * @memberof google.firestore.admin.v1.ImportDocumentsRequest + * @instance + */ + ImportDocumentsRequest.prototype.namespaceIds = $util.emptyArray; + /** * Creates an ImportDocumentsRequest message from a plain object. Also converts values to their respective internal types. * @function fromObject @@ -3219,6 +3697,13 @@ } if (object.inputUriPrefix != null) message.inputUriPrefix = String(object.inputUriPrefix); + if (object.namespaceIds) { + if (!Array.isArray(object.namespaceIds)) + throw TypeError(".google.firestore.admin.v1.ImportDocumentsRequest.namespaceIds: array expected"); + message.namespaceIds = []; + for (var i = 0; i < object.namespaceIds.length; ++i) + message.namespaceIds[i] = String(object.namespaceIds[i]); + } return message; }; @@ -3235,8 +3720,10 @@ if (!options) options = {}; var object = {}; - if (options.arrays || options.defaults) + if (options.arrays || options.defaults) { object.collectionIds = []; + object.namespaceIds = []; + } if (options.defaults) { object.name = ""; object.inputUriPrefix = ""; @@ -3250,6 +3737,11 @@ } if (message.inputUriPrefix != null && message.hasOwnProperty("inputUriPrefix")) object.inputUriPrefix = message.inputUriPrefix; + if (message.namespaceIds && message.namespaceIds.length) { + object.namespaceIds = []; + for (var j = 0; j < message.namespaceIds.length; ++j) + object.namespaceIds[j] = message.namespaceIds[j]; + } return object; }; @@ -4620,6 +5112,8 @@ * @property {google.firestore.admin.v1.IProgress|null} [progressBytes] ExportDocumentsMetadata progressBytes * @property {Array.|null} [collectionIds] ExportDocumentsMetadata collectionIds * @property {string|null} [outputUriPrefix] ExportDocumentsMetadata outputUriPrefix + * @property {Array.|null} [namespaceIds] ExportDocumentsMetadata namespaceIds + * @property {google.protobuf.ITimestamp|null} [snapshotTime] ExportDocumentsMetadata snapshotTime */ /** @@ -4632,6 +5126,7 @@ */ function ExportDocumentsMetadata(properties) { this.collectionIds = []; + this.namespaceIds = []; if (properties) for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -4694,6 +5189,22 @@ */ ExportDocumentsMetadata.prototype.outputUriPrefix = ""; + /** + * ExportDocumentsMetadata namespaceIds. + * @member {Array.} namespaceIds + * @memberof google.firestore.admin.v1.ExportDocumentsMetadata + * @instance + */ + ExportDocumentsMetadata.prototype.namespaceIds = $util.emptyArray; + + /** + * ExportDocumentsMetadata snapshotTime. + * @member {google.protobuf.ITimestamp|null|undefined} snapshotTime + * @memberof google.firestore.admin.v1.ExportDocumentsMetadata + * @instance + */ + ExportDocumentsMetadata.prototype.snapshotTime = null; + /** * Creates an ExportDocumentsMetadata message from a plain object. Also converts values to their respective internal types. * @function fromObject @@ -4775,6 +5286,18 @@ } if (object.outputUriPrefix != null) message.outputUriPrefix = String(object.outputUriPrefix); + if (object.namespaceIds) { + if (!Array.isArray(object.namespaceIds)) + throw TypeError(".google.firestore.admin.v1.ExportDocumentsMetadata.namespaceIds: array expected"); + message.namespaceIds = []; + for (var i = 0; i < object.namespaceIds.length; ++i) + message.namespaceIds[i] = String(object.namespaceIds[i]); + } + if (object.snapshotTime != null) { + if (typeof object.snapshotTime !== "object") + throw TypeError(".google.firestore.admin.v1.ExportDocumentsMetadata.snapshotTime: object expected"); + message.snapshotTime = $root.google.protobuf.Timestamp.fromObject(object.snapshotTime); + } return message; }; @@ -4791,8 +5314,10 @@ if (!options) options = {}; var object = {}; - if (options.arrays || options.defaults) + if (options.arrays || options.defaults) { object.collectionIds = []; + object.namespaceIds = []; + } if (options.defaults) { object.startTime = null; object.endTime = null; @@ -4800,6 +5325,7 @@ object.progressDocuments = null; object.progressBytes = null; object.outputUriPrefix = ""; + object.snapshotTime = null; } if (message.startTime != null && message.hasOwnProperty("startTime")) object.startTime = $root.google.protobuf.Timestamp.toObject(message.startTime, options); @@ -4818,6 +5344,13 @@ } if (message.outputUriPrefix != null && message.hasOwnProperty("outputUriPrefix")) object.outputUriPrefix = message.outputUriPrefix; + if (message.namespaceIds && message.namespaceIds.length) { + object.namespaceIds = []; + for (var j = 0; j < message.namespaceIds.length; ++j) + object.namespaceIds[j] = message.namespaceIds[j]; + } + if (message.snapshotTime != null && message.hasOwnProperty("snapshotTime")) + object.snapshotTime = $root.google.protobuf.Timestamp.toObject(message.snapshotTime, options); return object; }; @@ -4863,6 +5396,7 @@ * @property {google.firestore.admin.v1.IProgress|null} [progressBytes] ImportDocumentsMetadata progressBytes * @property {Array.|null} [collectionIds] ImportDocumentsMetadata collectionIds * @property {string|null} [inputUriPrefix] ImportDocumentsMetadata inputUriPrefix + * @property {Array.|null} [namespaceIds] ImportDocumentsMetadata namespaceIds */ /** @@ -4875,6 +5409,7 @@ */ function ImportDocumentsMetadata(properties) { this.collectionIds = []; + this.namespaceIds = []; if (properties) for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -4937,6 +5472,14 @@ */ ImportDocumentsMetadata.prototype.inputUriPrefix = ""; + /** + * ImportDocumentsMetadata namespaceIds. + * @member {Array.} namespaceIds + * @memberof google.firestore.admin.v1.ImportDocumentsMetadata + * @instance + */ + ImportDocumentsMetadata.prototype.namespaceIds = $util.emptyArray; + /** * Creates an ImportDocumentsMetadata message from a plain object. Also converts values to their respective internal types. * @function fromObject @@ -5018,6 +5561,13 @@ } if (object.inputUriPrefix != null) message.inputUriPrefix = String(object.inputUriPrefix); + if (object.namespaceIds) { + if (!Array.isArray(object.namespaceIds)) + throw TypeError(".google.firestore.admin.v1.ImportDocumentsMetadata.namespaceIds: array expected"); + message.namespaceIds = []; + for (var i = 0; i < object.namespaceIds.length; ++i) + message.namespaceIds[i] = String(object.namespaceIds[i]); + } return message; }; @@ -5034,8 +5584,10 @@ if (!options) options = {}; var object = {}; - if (options.arrays || options.defaults) + if (options.arrays || options.defaults) { object.collectionIds = []; + object.namespaceIds = []; + } if (options.defaults) { object.startTime = null; object.endTime = null; @@ -5061,6 +5613,11 @@ } if (message.inputUriPrefix != null && message.hasOwnProperty("inputUriPrefix")) object.inputUriPrefix = message.inputUriPrefix; + if (message.namespaceIds && message.namespaceIds.length) { + object.namespaceIds = []; + for (var j = 0; j < message.namespaceIds.length; ++j) + object.namespaceIds[j] = message.namespaceIds[j]; + } return object; }; @@ -7567,6 +8124,7 @@ * @interface IMethodSettings * @property {string|null} [selector] MethodSettings selector * @property {google.api.MethodSettings.ILongRunning|null} [longRunning] MethodSettings longRunning + * @property {Array.|null} [autoPopulatedFields] MethodSettings autoPopulatedFields */ /** @@ -7578,6 +8136,7 @@ * @param {google.api.IMethodSettings=} [properties] Properties to set */ function MethodSettings(properties) { + this.autoPopulatedFields = []; if (properties) for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -7600,6 +8159,14 @@ */ MethodSettings.prototype.longRunning = null; + /** + * MethodSettings autoPopulatedFields. + * @member {Array.} autoPopulatedFields + * @memberof google.api.MethodSettings + * @instance + */ + MethodSettings.prototype.autoPopulatedFields = $util.emptyArray; + /** * Creates a MethodSettings message from a plain object. Also converts values to their respective internal types. * @function fromObject @@ -7619,6 +8186,13 @@ throw TypeError(".google.api.MethodSettings.longRunning: object expected"); message.longRunning = $root.google.api.MethodSettings.LongRunning.fromObject(object.longRunning); } + if (object.autoPopulatedFields) { + if (!Array.isArray(object.autoPopulatedFields)) + throw TypeError(".google.api.MethodSettings.autoPopulatedFields: array expected"); + message.autoPopulatedFields = []; + for (var i = 0; i < object.autoPopulatedFields.length; ++i) + message.autoPopulatedFields[i] = String(object.autoPopulatedFields[i]); + } return message; }; @@ -7635,6 +8209,8 @@ if (!options) options = {}; var object = {}; + if (options.arrays || options.defaults) + object.autoPopulatedFields = []; if (options.defaults) { object.selector = ""; object.longRunning = null; @@ -7643,6 +8219,11 @@ object.selector = message.selector; if (message.longRunning != null && message.hasOwnProperty("longRunning")) object.longRunning = $root.google.api.MethodSettings.LongRunning.toObject(message.longRunning, options); + if (message.autoPopulatedFields && message.autoPopulatedFields.length) { + object.autoPopulatedFields = []; + for (var j = 0; j < message.autoPopulatedFields.length; ++j) + object.autoPopulatedFields[j] = message.autoPopulatedFields[j]; + } return object; }; @@ -7879,6 +8460,7 @@ * @property {string} IMMUTABLE=IMMUTABLE IMMUTABLE value * @property {string} UNORDERED_LIST=UNORDERED_LIST UNORDERED_LIST value * @property {string} NON_EMPTY_DEFAULT=NON_EMPTY_DEFAULT NON_EMPTY_DEFAULT value + * @property {string} IDENTIFIER=IDENTIFIER IDENTIFIER value */ api.FieldBehavior = (function() { var valuesById = {}, values = Object.create(valuesById); @@ -7890,6 +8472,7 @@ values[valuesById[5] = "IMMUTABLE"] = "IMMUTABLE"; values[valuesById[6] = "UNORDERED_LIST"] = "UNORDERED_LIST"; values[valuesById[7] = "NON_EMPTY_DEFAULT"] = "NON_EMPTY_DEFAULT"; + values[valuesById[8] = "IDENTIFIER"] = "IDENTIFIER"; return values; })(); @@ -11167,6 +11750,10 @@ case 7: message[".google.api.fieldBehavior"][i] = 7; break; + case "IDENTIFIER": + case 8: + message[".google.api.fieldBehavior"][i] = 8; + break; } } if (object[".google.api.resourceReference"] != null) { diff --git a/dev/protos/firestore_v1_proto_api.d.ts b/dev/protos/firestore_v1_proto_api.d.ts index 293e4fa51..1036ea689 100644 --- a/dev/protos/firestore_v1_proto_api.d.ts +++ b/dev/protos/firestore_v1_proto_api.d.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -4914,6 +4914,9 @@ export namespace google { /** RunQueryRequest readTime */ readTime?: (google.protobuf.ITimestamp|null); + + /** RunQueryRequest mode */ + mode?: (google.firestore.v1.QueryMode|null); } /** Represents a RunQueryRequest. */ @@ -4940,6 +4943,9 @@ export namespace google { /** RunQueryRequest readTime. */ public readTime?: (google.protobuf.ITimestamp|null); + /** RunQueryRequest mode. */ + public mode: google.firestore.v1.QueryMode; + /** RunQueryRequest queryType. */ public queryType?: "structuredQuery"; @@ -4992,6 +4998,9 @@ export namespace google { /** RunQueryResponse done */ done?: (boolean|null); + + /** RunQueryResponse stats */ + stats?: (google.firestore.v1.IResultSetStats|null); } /** Represents a RunQueryResponse. */ @@ -5018,6 +5027,9 @@ export namespace google { /** RunQueryResponse done. */ public done?: (boolean|null); + /** RunQueryResponse stats. */ + public stats?: (google.firestore.v1.IResultSetStats|null); + /** RunQueryResponse continuationSelector. */ public continuationSelector?: "done"; @@ -5067,6 +5079,9 @@ export namespace google { /** RunAggregationQueryRequest readTime */ readTime?: (google.protobuf.ITimestamp|null); + + /** RunAggregationQueryRequest mode */ + mode?: (google.firestore.v1.QueryMode|null); } /** Represents a RunAggregationQueryRequest. */ @@ -5093,6 +5108,9 @@ export namespace google { /** RunAggregationQueryRequest readTime. */ public readTime?: (google.protobuf.ITimestamp|null); + /** RunAggregationQueryRequest mode. */ + public mode: google.firestore.v1.QueryMode; + /** RunAggregationQueryRequest queryType. */ public queryType?: "structuredAggregationQuery"; @@ -5139,6 +5157,9 @@ export namespace google { /** RunAggregationQueryResponse readTime */ readTime?: (google.protobuf.ITimestamp|null); + + /** RunAggregationQueryResponse stats */ + stats?: (google.firestore.v1.IResultSetStats|null); } /** Represents a RunAggregationQueryResponse. */ @@ -5159,6 +5180,9 @@ export namespace google { /** RunAggregationQueryResponse readTime. */ public readTime?: (google.protobuf.ITimestamp|null); + /** RunAggregationQueryResponse stats. */ + public stats?: (google.firestore.v1.IResultSetStats|null); + /** * Creates a RunAggregationQueryResponse message from a plain object. Also converts values to their respective internal types. * @param object Plain object @@ -6122,6 +6146,112 @@ export namespace google { public static getTypeUrl(typeUrlPrefix?: string): string; } + /** QueryMode enum. */ + type QueryMode = + "NORMAL"| "PLAN"| "PROFILE"; + + /** Properties of a QueryPlan. */ + interface IQueryPlan { + + /** QueryPlan planInfo */ + planInfo?: (google.protobuf.IStruct|null); + } + + /** Represents a QueryPlan. */ + class QueryPlan implements IQueryPlan { + + /** + * Constructs a new QueryPlan. + * @param [properties] Properties to set + */ + constructor(properties?: google.firestore.v1.IQueryPlan); + + /** QueryPlan planInfo. */ + public planInfo?: (google.protobuf.IStruct|null); + + /** + * Creates a QueryPlan message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns QueryPlan + */ + public static fromObject(object: { [k: string]: any }): google.firestore.v1.QueryPlan; + + /** + * Creates a plain object from a QueryPlan message. Also converts values to other types if specified. + * @param message QueryPlan + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: google.firestore.v1.QueryPlan, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this QueryPlan to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for QueryPlan + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + + /** Properties of a ResultSetStats. */ + interface IResultSetStats { + + /** ResultSetStats queryPlan */ + queryPlan?: (google.firestore.v1.IQueryPlan|null); + + /** ResultSetStats queryStats */ + queryStats?: (google.protobuf.IStruct|null); + } + + /** Represents a ResultSetStats. */ + class ResultSetStats implements IResultSetStats { + + /** + * Constructs a new ResultSetStats. + * @param [properties] Properties to set + */ + constructor(properties?: google.firestore.v1.IResultSetStats); + + /** ResultSetStats queryPlan. */ + public queryPlan?: (google.firestore.v1.IQueryPlan|null); + + /** ResultSetStats queryStats. */ + public queryStats?: (google.protobuf.IStruct|null); + + /** + * Creates a ResultSetStats message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns ResultSetStats + */ + public static fromObject(object: { [k: string]: any }): google.firestore.v1.ResultSetStats; + + /** + * Creates a plain object from a ResultSetStats message. Also converts values to other types if specified. + * @param message ResultSetStats + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: google.firestore.v1.ResultSetStats, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this ResultSetStats to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for ResultSetStats + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + /** Properties of a StructuredQuery. */ interface IStructuredQuery { @@ -8438,6 +8568,9 @@ export namespace google { /** MethodSettings longRunning */ longRunning?: (google.api.MethodSettings.ILongRunning|null); + + /** MethodSettings autoPopulatedFields */ + autoPopulatedFields?: (string[]|null); } /** Represents a MethodSettings. */ @@ -8455,6 +8588,9 @@ export namespace google { /** MethodSettings longRunning. */ public longRunning?: (google.api.MethodSettings.ILongRunning|null); + /** MethodSettings autoPopulatedFields. */ + public autoPopulatedFields: string[]; + /** * Creates a MethodSettings message from a plain object. Also converts values to their respective internal types. * @param object Plain object @@ -8563,7 +8699,7 @@ export namespace google { /** FieldBehavior enum. */ type FieldBehavior = - "FIELD_BEHAVIOR_UNSPECIFIED"| "OPTIONAL"| "REQUIRED"| "OUTPUT_ONLY"| "INPUT_ONLY"| "IMMUTABLE"| "UNORDERED_LIST"| "NON_EMPTY_DEFAULT"; + "FIELD_BEHAVIOR_UNSPECIFIED"| "OPTIONAL"| "REQUIRED"| "OUTPUT_ONLY"| "INPUT_ONLY"| "IMMUTABLE"| "UNORDERED_LIST"| "NON_EMPTY_DEFAULT"| "IDENTIFIER"; /** LaunchStage enum. */ type LaunchStage = diff --git a/dev/protos/firestore_v1_proto_api.js b/dev/protos/firestore_v1_proto_api.js index 0fe6356c0..2261afc81 100644 --- a/dev/protos/firestore_v1_proto_api.js +++ b/dev/protos/firestore_v1_proto_api.js @@ -1,5 +1,5 @@ /*! - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -4295,6 +4295,10 @@ case 7: message[".google.api.fieldBehavior"][i] = 7; break; + case "IDENTIFIER": + case 8: + message[".google.api.fieldBehavior"][i] = 8; + break; } } if (object[".google.api.resourceReference"] != null) { @@ -11572,6 +11576,7 @@ * @property {Uint8Array|null} [transaction] RunQueryRequest transaction * @property {google.firestore.v1.ITransactionOptions|null} [newTransaction] RunQueryRequest newTransaction * @property {google.protobuf.ITimestamp|null} [readTime] RunQueryRequest readTime + * @property {google.firestore.v1.QueryMode|null} [mode] RunQueryRequest mode */ /** @@ -11629,6 +11634,14 @@ */ RunQueryRequest.prototype.readTime = null; + /** + * RunQueryRequest mode. + * @member {google.firestore.v1.QueryMode} mode + * @memberof google.firestore.v1.RunQueryRequest + * @instance + */ + RunQueryRequest.prototype.mode = 0; + // OneOf field names bound to virtual getters and setters var $oneOfFields; @@ -11688,6 +11701,26 @@ throw TypeError(".google.firestore.v1.RunQueryRequest.readTime: object expected"); message.readTime = $root.google.protobuf.Timestamp.fromObject(object.readTime); } + switch (object.mode) { + default: + if (typeof object.mode === "number") { + message.mode = object.mode; + break; + } + break; + case "NORMAL": + case 0: + message.mode = 0; + break; + case "PLAN": + case 1: + message.mode = 1; + break; + case "PROFILE": + case 2: + message.mode = 2; + break; + } return message; }; @@ -11704,8 +11737,10 @@ if (!options) options = {}; var object = {}; - if (options.defaults) + if (options.defaults) { object.parent = ""; + object.mode = options.enums === String ? "NORMAL" : 0; + } if (message.parent != null && message.hasOwnProperty("parent")) object.parent = message.parent; if (message.structuredQuery != null && message.hasOwnProperty("structuredQuery")) { @@ -11728,6 +11763,8 @@ if (options.oneofs) object.consistencySelector = "readTime"; } + if (message.mode != null && message.hasOwnProperty("mode")) + object.mode = options.enums === String ? $root.google.firestore.v1.QueryMode[message.mode] === undefined ? message.mode : $root.google.firestore.v1.QueryMode[message.mode] : message.mode; return object; }; @@ -11771,6 +11808,7 @@ * @property {google.protobuf.ITimestamp|null} [readTime] RunQueryResponse readTime * @property {number|null} [skippedResults] RunQueryResponse skippedResults * @property {boolean|null} [done] RunQueryResponse done + * @property {google.firestore.v1.IResultSetStats|null} [stats] RunQueryResponse stats */ /** @@ -11828,6 +11866,14 @@ */ RunQueryResponse.prototype.done = null; + /** + * RunQueryResponse stats. + * @member {google.firestore.v1.IResultSetStats|null|undefined} stats + * @memberof google.firestore.v1.RunQueryResponse + * @instance + */ + RunQueryResponse.prototype.stats = null; + // OneOf field names bound to virtual getters and setters var $oneOfFields; @@ -11873,6 +11919,11 @@ message.skippedResults = object.skippedResults | 0; if (object.done != null) message.done = Boolean(object.done); + if (object.stats != null) { + if (typeof object.stats !== "object") + throw TypeError(".google.firestore.v1.RunQueryResponse.stats: object expected"); + message.stats = $root.google.firestore.v1.ResultSetStats.fromObject(object.stats); + } return message; }; @@ -11900,6 +11951,7 @@ } object.readTime = null; object.skippedResults = 0; + object.stats = null; } if (message.document != null && message.hasOwnProperty("document")) object.document = $root.google.firestore.v1.Document.toObject(message.document, options); @@ -11914,6 +11966,8 @@ if (options.oneofs) object.continuationSelector = "done"; } + if (message.stats != null && message.hasOwnProperty("stats")) + object.stats = $root.google.firestore.v1.ResultSetStats.toObject(message.stats, options); return object; }; @@ -11957,6 +12011,7 @@ * @property {Uint8Array|null} [transaction] RunAggregationQueryRequest transaction * @property {google.firestore.v1.ITransactionOptions|null} [newTransaction] RunAggregationQueryRequest newTransaction * @property {google.protobuf.ITimestamp|null} [readTime] RunAggregationQueryRequest readTime + * @property {google.firestore.v1.QueryMode|null} [mode] RunAggregationQueryRequest mode */ /** @@ -12014,6 +12069,14 @@ */ RunAggregationQueryRequest.prototype.readTime = null; + /** + * RunAggregationQueryRequest mode. + * @member {google.firestore.v1.QueryMode} mode + * @memberof google.firestore.v1.RunAggregationQueryRequest + * @instance + */ + RunAggregationQueryRequest.prototype.mode = 0; + // OneOf field names bound to virtual getters and setters var $oneOfFields; @@ -12073,6 +12136,26 @@ throw TypeError(".google.firestore.v1.RunAggregationQueryRequest.readTime: object expected"); message.readTime = $root.google.protobuf.Timestamp.fromObject(object.readTime); } + switch (object.mode) { + default: + if (typeof object.mode === "number") { + message.mode = object.mode; + break; + } + break; + case "NORMAL": + case 0: + message.mode = 0; + break; + case "PLAN": + case 1: + message.mode = 1; + break; + case "PROFILE": + case 2: + message.mode = 2; + break; + } return message; }; @@ -12089,8 +12172,10 @@ if (!options) options = {}; var object = {}; - if (options.defaults) + if (options.defaults) { object.parent = ""; + object.mode = options.enums === String ? "NORMAL" : 0; + } if (message.parent != null && message.hasOwnProperty("parent")) object.parent = message.parent; if (message.structuredAggregationQuery != null && message.hasOwnProperty("structuredAggregationQuery")) { @@ -12113,6 +12198,8 @@ if (options.oneofs) object.consistencySelector = "readTime"; } + if (message.mode != null && message.hasOwnProperty("mode")) + object.mode = options.enums === String ? $root.google.firestore.v1.QueryMode[message.mode] === undefined ? message.mode : $root.google.firestore.v1.QueryMode[message.mode] : message.mode; return object; }; @@ -12154,6 +12241,7 @@ * @property {google.firestore.v1.IAggregationResult|null} [result] RunAggregationQueryResponse result * @property {Uint8Array|null} [transaction] RunAggregationQueryResponse transaction * @property {google.protobuf.ITimestamp|null} [readTime] RunAggregationQueryResponse readTime + * @property {google.firestore.v1.IResultSetStats|null} [stats] RunAggregationQueryResponse stats */ /** @@ -12195,6 +12283,14 @@ */ RunAggregationQueryResponse.prototype.readTime = null; + /** + * RunAggregationQueryResponse stats. + * @member {google.firestore.v1.IResultSetStats|null|undefined} stats + * @memberof google.firestore.v1.RunAggregationQueryResponse + * @instance + */ + RunAggregationQueryResponse.prototype.stats = null; + /** * Creates a RunAggregationQueryResponse message from a plain object. Also converts values to their respective internal types. * @function fromObject @@ -12222,6 +12318,11 @@ throw TypeError(".google.firestore.v1.RunAggregationQueryResponse.readTime: object expected"); message.readTime = $root.google.protobuf.Timestamp.fromObject(object.readTime); } + if (object.stats != null) { + if (typeof object.stats !== "object") + throw TypeError(".google.firestore.v1.RunAggregationQueryResponse.stats: object expected"); + message.stats = $root.google.firestore.v1.ResultSetStats.fromObject(object.stats); + } return message; }; @@ -12248,6 +12349,7 @@ object.transaction = $util.newBuffer(object.transaction); } object.readTime = null; + object.stats = null; } if (message.result != null && message.hasOwnProperty("result")) object.result = $root.google.firestore.v1.AggregationResult.toObject(message.result, options); @@ -12255,6 +12357,8 @@ object.transaction = options.bytes === String ? $util.base64.encode(message.transaction, 0, message.transaction.length) : options.bytes === Array ? Array.prototype.slice.call(message.transaction) : message.transaction; if (message.readTime != null && message.hasOwnProperty("readTime")) object.readTime = $root.google.protobuf.Timestamp.toObject(message.readTime, options); + if (message.stats != null && message.hasOwnProperty("stats")) + object.stats = $root.google.firestore.v1.ResultSetStats.toObject(message.stats, options); return object; }; @@ -14612,6 +14716,242 @@ return BatchWriteResponse; })(); + /** + * QueryMode enum. + * @name google.firestore.v1.QueryMode + * @enum {string} + * @property {string} NORMAL=NORMAL NORMAL value + * @property {string} PLAN=PLAN PLAN value + * @property {string} PROFILE=PROFILE PROFILE value + */ + v1.QueryMode = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "NORMAL"] = "NORMAL"; + values[valuesById[1] = "PLAN"] = "PLAN"; + values[valuesById[2] = "PROFILE"] = "PROFILE"; + return values; + })(); + + v1.QueryPlan = (function() { + + /** + * Properties of a QueryPlan. + * @memberof google.firestore.v1 + * @interface IQueryPlan + * @property {google.protobuf.IStruct|null} [planInfo] QueryPlan planInfo + */ + + /** + * Constructs a new QueryPlan. + * @memberof google.firestore.v1 + * @classdesc Represents a QueryPlan. + * @implements IQueryPlan + * @constructor + * @param {google.firestore.v1.IQueryPlan=} [properties] Properties to set + */ + function QueryPlan(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * QueryPlan planInfo. + * @member {google.protobuf.IStruct|null|undefined} planInfo + * @memberof google.firestore.v1.QueryPlan + * @instance + */ + QueryPlan.prototype.planInfo = null; + + /** + * Creates a QueryPlan message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof google.firestore.v1.QueryPlan + * @static + * @param {Object.} object Plain object + * @returns {google.firestore.v1.QueryPlan} QueryPlan + */ + QueryPlan.fromObject = function fromObject(object) { + if (object instanceof $root.google.firestore.v1.QueryPlan) + return object; + var message = new $root.google.firestore.v1.QueryPlan(); + if (object.planInfo != null) { + if (typeof object.planInfo !== "object") + throw TypeError(".google.firestore.v1.QueryPlan.planInfo: object expected"); + message.planInfo = $root.google.protobuf.Struct.fromObject(object.planInfo); + } + return message; + }; + + /** + * Creates a plain object from a QueryPlan message. Also converts values to other types if specified. + * @function toObject + * @memberof google.firestore.v1.QueryPlan + * @static + * @param {google.firestore.v1.QueryPlan} message QueryPlan + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + QueryPlan.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + object.planInfo = null; + if (message.planInfo != null && message.hasOwnProperty("planInfo")) + object.planInfo = $root.google.protobuf.Struct.toObject(message.planInfo, options); + return object; + }; + + /** + * Converts this QueryPlan to JSON. + * @function toJSON + * @memberof google.firestore.v1.QueryPlan + * @instance + * @returns {Object.} JSON object + */ + QueryPlan.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for QueryPlan + * @function getTypeUrl + * @memberof google.firestore.v1.QueryPlan + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + QueryPlan.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/google.firestore.v1.QueryPlan"; + }; + + return QueryPlan; + })(); + + v1.ResultSetStats = (function() { + + /** + * Properties of a ResultSetStats. + * @memberof google.firestore.v1 + * @interface IResultSetStats + * @property {google.firestore.v1.IQueryPlan|null} [queryPlan] ResultSetStats queryPlan + * @property {google.protobuf.IStruct|null} [queryStats] ResultSetStats queryStats + */ + + /** + * Constructs a new ResultSetStats. + * @memberof google.firestore.v1 + * @classdesc Represents a ResultSetStats. + * @implements IResultSetStats + * @constructor + * @param {google.firestore.v1.IResultSetStats=} [properties] Properties to set + */ + function ResultSetStats(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * ResultSetStats queryPlan. + * @member {google.firestore.v1.IQueryPlan|null|undefined} queryPlan + * @memberof google.firestore.v1.ResultSetStats + * @instance + */ + ResultSetStats.prototype.queryPlan = null; + + /** + * ResultSetStats queryStats. + * @member {google.protobuf.IStruct|null|undefined} queryStats + * @memberof google.firestore.v1.ResultSetStats + * @instance + */ + ResultSetStats.prototype.queryStats = null; + + /** + * Creates a ResultSetStats message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof google.firestore.v1.ResultSetStats + * @static + * @param {Object.} object Plain object + * @returns {google.firestore.v1.ResultSetStats} ResultSetStats + */ + ResultSetStats.fromObject = function fromObject(object) { + if (object instanceof $root.google.firestore.v1.ResultSetStats) + return object; + var message = new $root.google.firestore.v1.ResultSetStats(); + if (object.queryPlan != null) { + if (typeof object.queryPlan !== "object") + throw TypeError(".google.firestore.v1.ResultSetStats.queryPlan: object expected"); + message.queryPlan = $root.google.firestore.v1.QueryPlan.fromObject(object.queryPlan); + } + if (object.queryStats != null) { + if (typeof object.queryStats !== "object") + throw TypeError(".google.firestore.v1.ResultSetStats.queryStats: object expected"); + message.queryStats = $root.google.protobuf.Struct.fromObject(object.queryStats); + } + return message; + }; + + /** + * Creates a plain object from a ResultSetStats message. Also converts values to other types if specified. + * @function toObject + * @memberof google.firestore.v1.ResultSetStats + * @static + * @param {google.firestore.v1.ResultSetStats} message ResultSetStats + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + ResultSetStats.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.queryPlan = null; + object.queryStats = null; + } + if (message.queryPlan != null && message.hasOwnProperty("queryPlan")) + object.queryPlan = $root.google.firestore.v1.QueryPlan.toObject(message.queryPlan, options); + if (message.queryStats != null && message.hasOwnProperty("queryStats")) + object.queryStats = $root.google.protobuf.Struct.toObject(message.queryStats, options); + return object; + }; + + /** + * Converts this ResultSetStats to JSON. + * @function toJSON + * @memberof google.firestore.v1.ResultSetStats + * @instance + * @returns {Object.} JSON object + */ + ResultSetStats.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for ResultSetStats + * @function getTypeUrl + * @memberof google.firestore.v1.ResultSetStats + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + ResultSetStats.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/google.firestore.v1.ResultSetStats"; + }; + + return ResultSetStats; + })(); + v1.StructuredQuery = (function() { /** @@ -20249,6 +20589,7 @@ * @interface IMethodSettings * @property {string|null} [selector] MethodSettings selector * @property {google.api.MethodSettings.ILongRunning|null} [longRunning] MethodSettings longRunning + * @property {Array.|null} [autoPopulatedFields] MethodSettings autoPopulatedFields */ /** @@ -20260,6 +20601,7 @@ * @param {google.api.IMethodSettings=} [properties] Properties to set */ function MethodSettings(properties) { + this.autoPopulatedFields = []; if (properties) for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -20282,6 +20624,14 @@ */ MethodSettings.prototype.longRunning = null; + /** + * MethodSettings autoPopulatedFields. + * @member {Array.} autoPopulatedFields + * @memberof google.api.MethodSettings + * @instance + */ + MethodSettings.prototype.autoPopulatedFields = $util.emptyArray; + /** * Creates a MethodSettings message from a plain object. Also converts values to their respective internal types. * @function fromObject @@ -20301,6 +20651,13 @@ throw TypeError(".google.api.MethodSettings.longRunning: object expected"); message.longRunning = $root.google.api.MethodSettings.LongRunning.fromObject(object.longRunning); } + if (object.autoPopulatedFields) { + if (!Array.isArray(object.autoPopulatedFields)) + throw TypeError(".google.api.MethodSettings.autoPopulatedFields: array expected"); + message.autoPopulatedFields = []; + for (var i = 0; i < object.autoPopulatedFields.length; ++i) + message.autoPopulatedFields[i] = String(object.autoPopulatedFields[i]); + } return message; }; @@ -20317,6 +20674,8 @@ if (!options) options = {}; var object = {}; + if (options.arrays || options.defaults) + object.autoPopulatedFields = []; if (options.defaults) { object.selector = ""; object.longRunning = null; @@ -20325,6 +20684,11 @@ object.selector = message.selector; if (message.longRunning != null && message.hasOwnProperty("longRunning")) object.longRunning = $root.google.api.MethodSettings.LongRunning.toObject(message.longRunning, options); + if (message.autoPopulatedFields && message.autoPopulatedFields.length) { + object.autoPopulatedFields = []; + for (var j = 0; j < message.autoPopulatedFields.length; ++j) + object.autoPopulatedFields[j] = message.autoPopulatedFields[j]; + } return object; }; @@ -20561,6 +20925,7 @@ * @property {string} IMMUTABLE=IMMUTABLE IMMUTABLE value * @property {string} UNORDERED_LIST=UNORDERED_LIST UNORDERED_LIST value * @property {string} NON_EMPTY_DEFAULT=NON_EMPTY_DEFAULT NON_EMPTY_DEFAULT value + * @property {string} IDENTIFIER=IDENTIFIER IDENTIFIER value */ api.FieldBehavior = (function() { var valuesById = {}, values = Object.create(valuesById); @@ -20572,6 +20937,7 @@ values[valuesById[5] = "IMMUTABLE"] = "IMMUTABLE"; values[valuesById[6] = "UNORDERED_LIST"] = "UNORDERED_LIST"; values[valuesById[7] = "NON_EMPTY_DEFAULT"] = "NON_EMPTY_DEFAULT"; + values[valuesById[8] = "IDENTIFIER"] = "IDENTIFIER"; return values; })(); diff --git a/dev/protos/firestore_v1beta1_proto_api.d.ts b/dev/protos/firestore_v1beta1_proto_api.d.ts index 6bb394bc5..9014804a8 100644 --- a/dev/protos/firestore_v1beta1_proto_api.d.ts +++ b/dev/protos/firestore_v1beta1_proto_api.d.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -7467,6 +7467,9 @@ export namespace google { /** MethodSettings longRunning */ longRunning?: (google.api.MethodSettings.ILongRunning|null); + + /** MethodSettings autoPopulatedFields */ + autoPopulatedFields?: (string[]|null); } /** Represents a MethodSettings. */ @@ -7484,6 +7487,9 @@ export namespace google { /** MethodSettings longRunning. */ public longRunning?: (google.api.MethodSettings.ILongRunning|null); + /** MethodSettings autoPopulatedFields. */ + public autoPopulatedFields: string[]; + /** * Creates a MethodSettings message from a plain object. Also converts values to their respective internal types. * @param object Plain object @@ -7592,7 +7598,7 @@ export namespace google { /** FieldBehavior enum. */ type FieldBehavior = - "FIELD_BEHAVIOR_UNSPECIFIED"| "OPTIONAL"| "REQUIRED"| "OUTPUT_ONLY"| "INPUT_ONLY"| "IMMUTABLE"| "UNORDERED_LIST"| "NON_EMPTY_DEFAULT"; + "FIELD_BEHAVIOR_UNSPECIFIED"| "OPTIONAL"| "REQUIRED"| "OUTPUT_ONLY"| "INPUT_ONLY"| "IMMUTABLE"| "UNORDERED_LIST"| "NON_EMPTY_DEFAULT"| "IDENTIFIER"; /** LaunchStage enum. */ type LaunchStage = diff --git a/dev/protos/firestore_v1beta1_proto_api.js b/dev/protos/firestore_v1beta1_proto_api.js index bd47626ef..e3fa056fa 100644 --- a/dev/protos/firestore_v1beta1_proto_api.js +++ b/dev/protos/firestore_v1beta1_proto_api.js @@ -1,5 +1,5 @@ /*! - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -3477,6 +3477,10 @@ case 7: message[".google.api.fieldBehavior"][i] = 7; break; + case "IDENTIFIER": + case 8: + message[".google.api.fieldBehavior"][i] = 8; + break; } } if (object[".google.api.resourceReference"] != null) { @@ -17960,6 +17964,7 @@ * @interface IMethodSettings * @property {string|null} [selector] MethodSettings selector * @property {google.api.MethodSettings.ILongRunning|null} [longRunning] MethodSettings longRunning + * @property {Array.|null} [autoPopulatedFields] MethodSettings autoPopulatedFields */ /** @@ -17971,6 +17976,7 @@ * @param {google.api.IMethodSettings=} [properties] Properties to set */ function MethodSettings(properties) { + this.autoPopulatedFields = []; if (properties) for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -17993,6 +17999,14 @@ */ MethodSettings.prototype.longRunning = null; + /** + * MethodSettings autoPopulatedFields. + * @member {Array.} autoPopulatedFields + * @memberof google.api.MethodSettings + * @instance + */ + MethodSettings.prototype.autoPopulatedFields = $util.emptyArray; + /** * Creates a MethodSettings message from a plain object. Also converts values to their respective internal types. * @function fromObject @@ -18012,6 +18026,13 @@ throw TypeError(".google.api.MethodSettings.longRunning: object expected"); message.longRunning = $root.google.api.MethodSettings.LongRunning.fromObject(object.longRunning); } + if (object.autoPopulatedFields) { + if (!Array.isArray(object.autoPopulatedFields)) + throw TypeError(".google.api.MethodSettings.autoPopulatedFields: array expected"); + message.autoPopulatedFields = []; + for (var i = 0; i < object.autoPopulatedFields.length; ++i) + message.autoPopulatedFields[i] = String(object.autoPopulatedFields[i]); + } return message; }; @@ -18028,6 +18049,8 @@ if (!options) options = {}; var object = {}; + if (options.arrays || options.defaults) + object.autoPopulatedFields = []; if (options.defaults) { object.selector = ""; object.longRunning = null; @@ -18036,6 +18059,11 @@ object.selector = message.selector; if (message.longRunning != null && message.hasOwnProperty("longRunning")) object.longRunning = $root.google.api.MethodSettings.LongRunning.toObject(message.longRunning, options); + if (message.autoPopulatedFields && message.autoPopulatedFields.length) { + object.autoPopulatedFields = []; + for (var j = 0; j < message.autoPopulatedFields.length; ++j) + object.autoPopulatedFields[j] = message.autoPopulatedFields[j]; + } return object; }; @@ -18272,6 +18300,7 @@ * @property {string} IMMUTABLE=IMMUTABLE IMMUTABLE value * @property {string} UNORDERED_LIST=UNORDERED_LIST UNORDERED_LIST value * @property {string} NON_EMPTY_DEFAULT=NON_EMPTY_DEFAULT NON_EMPTY_DEFAULT value + * @property {string} IDENTIFIER=IDENTIFIER IDENTIFIER value */ api.FieldBehavior = (function() { var valuesById = {}, values = Object.create(valuesById); @@ -18283,6 +18312,7 @@ values[valuesById[5] = "IMMUTABLE"] = "IMMUTABLE"; values[valuesById[6] = "UNORDERED_LIST"] = "UNORDERED_LIST"; values[valuesById[7] = "NON_EMPTY_DEFAULT"] = "NON_EMPTY_DEFAULT"; + values[valuesById[8] = "IDENTIFIER"] = "IDENTIFIER"; return values; })(); diff --git a/dev/protos/google/api/client.proto b/dev/protos/google/api/client.proto index 6d01954ee..39bdde82a 100644 --- a/dev/protos/google/api/client.proto +++ b/dev/protos/google/api/client.proto @@ -349,6 +349,19 @@ message MethodSettings { // total_poll_timeout: // seconds: 54000 # 90 minutes LongRunning long_running = 2; + + // List of top-level fields of the request message, that should be + // automatically populated by the client libraries based on their + // (google.api.field_info).format. Currently supported format: UUID4. + // + // Example of a YAML configuration: + // + // publishing: + // method_settings: + // - selector: google.example.v1.ExampleService.CreateExample + // auto_populated_fields: + // - request_id + repeated string auto_populated_fields = 3; } // The organization for which the client libraries are being published. diff --git a/dev/protos/google/api/field_behavior.proto b/dev/protos/google/api/field_behavior.proto index 1a3a2f2fb..344cb0b1f 100644 --- a/dev/protos/google/api/field_behavior.proto +++ b/dev/protos/google/api/field_behavior.proto @@ -87,4 +87,18 @@ enum FieldBehavior { // a non-empty value will be returned. The user will not be aware of what // non-empty value to expect. NON_EMPTY_DEFAULT = 7; + + // Denotes that the field in a resource (a message annotated with + // google.api.resource) is used in the resource name to uniquely identify the + // resource. For AIP-compliant APIs, this should only be applied to the + // `name` field on the resource. + // + // This behavior should not be applied to references to other resources within + // the message. + // + // The identifier field of resources often have different field behavior + // depending on the request it is embedded in (e.g. for Create methods name + // is optional and unused, while for Update methods it is required). Instead + // of method-specific annotations, only `IDENTIFIER` is required. + IDENTIFIER = 8; } diff --git a/dev/protos/google/firestore/admin/v1/database.proto b/dev/protos/google/firestore/admin/v1/database.proto index 5d9b762a1..fbf4bd754 100644 --- a/dev/protos/google/firestore/admin/v1/database.proto +++ b/dev/protos/google/firestore/admin/v1/database.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. @@ -18,6 +18,8 @@ package google.firestore.admin.v1; import "google/api/field_behavior.proto"; import "google/api/resource.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; option csharp_namespace = "Google.Cloud.Firestore.Admin.V1"; option go_package = "cloud.google.com/go/firestore/apiv1/admin/adminpb;adminpb"; @@ -29,8 +31,6 @@ option php_namespace = "Google\\Cloud\\Firestore\\Admin\\V1"; option ruby_package = "Google::Cloud::Firestore::Admin::V1"; // A Cloud Firestore Database. -// Currently only one database is allowed per cloud project; this database -// must have a `database_id` of '(default)'. message Database { option (google.api.resource) = { type: "firestore.googleapis.com/Database" @@ -78,6 +78,26 @@ message Database { OPTIMISTIC_WITH_ENTITY_GROUPS = 3; } + // Point In Time Recovery feature enablement. + enum PointInTimeRecoveryEnablement { + // Not used. + POINT_IN_TIME_RECOVERY_ENABLEMENT_UNSPECIFIED = 0; + + // Reads are supported on selected versions of the data from within the past + // 7 days: + // + // * Reads against any timestamp within the past hour + // * Reads against 1-minute snapshots beyond 1 hour and within 7 days + // + // `version_retention_period` and `earliest_version_time` can be + // used to determine the supported versions. + POINT_IN_TIME_RECOVERY_ENABLED = 1; + + // Reads are supported on any version of the data from within the past 1 + // hour. + POINT_IN_TIME_RECOVERY_DISABLED = 2; + } + // The type of App Engine integration mode. enum AppEngineIntegrationMode { // Not used. @@ -89,16 +109,44 @@ message Database { // the database. ENABLED = 1; - // Appengine has no affect on the ability of this database to serve + // App Engine has no effect on the ability of this database to serve // requests. + // + // This is the default setting for databases created with the Firestore API. DISABLED = 2; } + // The delete protection state of the database. + enum DeleteProtectionState { + // The default value. Delete protection type is not specified + DELETE_PROTECTION_STATE_UNSPECIFIED = 0; + + // Delete protection is disabled + DELETE_PROTECTION_DISABLED = 1; + + // Delete protection is enabled + DELETE_PROTECTION_ENABLED = 2; + } + // The resource name of the Database. // Format: `projects/{project}/databases/{database}` string name = 1; - // The location of the database. Available databases are listed at + // Output only. The system-generated UUID4 for this Database. + string uid = 3 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The timestamp at which this database was created. Databases + // created before 2016 do not populate create_time. + google.protobuf.Timestamp create_time = 5 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The timestamp at which this database was most recently + // updated. Note this only includes updates to the database resource and not + // data contained by the database. + google.protobuf.Timestamp update_time = 6 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // The location of the database. Available locations are listed at // https://cloud.google.com/firestore/docs/locations. string location_id = 9; @@ -110,18 +158,48 @@ message Database { // The concurrency control mode to use for this database. ConcurrencyMode concurrency_mode = 15; + // Output only. The period during which past versions of data are retained in + // the database. + // + // Any [read][google.firestore.v1.GetDocumentRequest.read_time] + // or [query][google.firestore.v1.ListDocumentsRequest.read_time] can specify + // a `read_time` within this window, and will read the state of the database + // at that time. + // + // If the PITR feature is enabled, the retention period is 7 days. Otherwise, + // the retention period is 1 hour. + google.protobuf.Duration version_retention_period = 17 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The earliest timestamp at which older versions of the data can + // be read from the database. See [version_retention_period] above; this field + // is populated with `now - version_retention_period`. + // + // This value is continuously updated, and becomes stale the moment it is + // queried. If you are using this value to recover data, make sure to account + // for the time from the moment when the value is queried to the moment when + // you initiate the recovery. + google.protobuf.Timestamp earliest_version_time = 18 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Whether to enable the PITR feature on this database. + PointInTimeRecoveryEnablement point_in_time_recovery_enablement = 21; + // The App Engine integration mode to use for this database. AppEngineIntegrationMode app_engine_integration_mode = 19; - // Output only. The key_prefix for this database. This key_prefix is used, in combination - // with the project id ("~") to construct the - // application id that is returned from the Cloud Datastore APIs in Google App - // Engine first generation runtimes. + // Output only. The key_prefix for this database. This key_prefix is used, in + // combination with the project id ("~") to construct + // the application id that is returned from the Cloud Datastore APIs in Google + // App Engine first generation runtimes. // // This value may be empty in which case the appid to use for URL-encoded keys // is the project_id (eg: foo instead of v~foo). string key_prefix = 20 [(google.api.field_behavior) = OUTPUT_ONLY]; + // State of delete protection for the database. + DeleteProtectionState delete_protection_state = 22; + // This checksum is computed by the server based on the value of other // fields, and may be sent on update and delete requests to ensure the // client has an up-to-date value before proceeding. diff --git a/dev/protos/google/firestore/admin/v1/firestore_admin.proto b/dev/protos/google/firestore/admin/v1/firestore_admin.proto index e1faa4dcf..53f134a1b 100644 --- a/dev/protos/google/firestore/admin/v1/firestore_admin.proto +++ b/dev/protos/google/firestore/admin/v1/firestore_admin.proto @@ -23,9 +23,11 @@ import "google/api/resource.proto"; import "google/firestore/admin/v1/database.proto"; import "google/firestore/admin/v1/field.proto"; import "google/firestore/admin/v1/index.proto"; +import "google/firestore/admin/v1/operation.proto"; import "google/longrunning/operations.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; +import "google/protobuf/timestamp.proto"; option csharp_namespace = "Google.Cloud.Firestore.Admin.V1"; option go_package = "cloud.google.com/go/firestore/apiv1/admin/adminpb;adminpb"; @@ -162,7 +164,8 @@ service FirestoreAdmin { // only supports listing fields that have been explicitly overridden. To issue // this query, call // [FirestoreAdmin.ListFields][google.firestore.admin.v1.FirestoreAdmin.ListFields] - // with the filter set to `indexConfig.usesAncestorConfig:false` . + // with the filter set to `indexConfig.usesAncestorConfig:false or + // `ttlConfig:*`. rpc ListFields(ListFieldsRequest) returns (ListFieldsResponse) { option (google.api.http) = { get: "/v1/{parent=projects/*/databases/*/collectionGroups/*}/fields" @@ -255,6 +258,19 @@ service FirestoreAdmin { metadata_type: "UpdateDatabaseMetadata" }; } + + // Deletes a database. + rpc DeleteDatabase(DeleteDatabaseRequest) + returns (google.longrunning.Operation) { + option (google.api.http) = { + delete: "/v1/{name=projects/*/databases/*}" + }; + option (google.api.method_signature) = "name"; + option (google.longrunning.operation_info) = { + response_type: "Database" + metadata_type: "DeleteDatabaseMetadata" + }; + } } // A request to list the Firestore Databases in all locations for a project. @@ -287,7 +303,11 @@ message CreateDatabaseRequest { // Required. The ID to use for the database, which will become the final // component of the database's resource name. // - // The value must be set to "(default)". + // This value should be 4-63 characters. Valid characters are /[a-z][0-9]-/ + // with first character a letter and the last a letter or a number. Must not + // be UUID-like /[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}/. + // + // "(default)" database id is also valid. string database_id = 3 [(google.api.field_behavior) = REQUIRED]; } @@ -298,6 +318,17 @@ message CreateDatabaseMetadata {} message ListDatabasesResponse { // The databases in the project. repeated Database databases = 1; + + // In the event that data about individual databases cannot be listed they + // will be recorded here. + // + // An example entry might be: projects/some_project/locations/some_location + // This can happen if the Cloud Region that the Database resides in is + // currently unavailable. In this case we can't fetch all the details about + // the database. You may be able to get a more detailed error message + // (or possibly fetch the resource) by sending a 'Get' request for the + // resource or a 'List' request for the specific location. + repeated string unreachable = 3; } // The request for @@ -326,6 +357,27 @@ message UpdateDatabaseRequest { // Metadata related to the update database operation. message UpdateDatabaseMetadata {} +// The request for +// [FirestoreAdmin.DeleteDatabase][google.firestore.admin.v1.FirestoreAdmin.DeleteDatabase]. +message DeleteDatabaseRequest { + // Required. A name of the form + // `projects/{project_id}/databases/{database_id}` + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "firestore.googleapis.com/Database" + } + ]; + + // The current etag of the Database. + // If an etag is provided and does not match the current etag of the database, + // deletion will be blocked and a FAILED_PRECONDITION error will be returned. + string etag = 3; +} + +// Metadata related to the delete database operation. +message DeleteDatabaseMetadata {} + // The request for // [FirestoreAdmin.CreateIndex][google.firestore.admin.v1.FirestoreAdmin.CreateIndex]. message CreateIndexRequest { @@ -485,6 +537,23 @@ message ExportDocumentsRequest { // If the URI is a bucket (without a namespace path), a prefix will be // generated based on the start time. string output_uri_prefix = 3; + + // An empty list represents all namespaces. This is the preferred + // usage for databases that don't use namespaces. + // + // An empty string element represents the default namespace. This should be + // used if the database has data in non-default namespaces, but doesn't want + // to include them. Each namespace in this list must be unique. + repeated string namespace_ids = 4; + + // The timestamp that corresponds to the version of the database to be + // exported. The timestamp must be in the past, rounded to the minute and not + // older than + // [earliestVersionTime][google.firestore.admin.v1.Database.earliest_version_time]. + // If specified, then the exported documents will represent a consistent view + // of the database at the provided time. Otherwise, there are no guarantees + // about the consistency of the exported documents. + google.protobuf.Timestamp snapshot_time = 5; } // The request for @@ -509,4 +578,12 @@ message ImportDocumentsRequest { // See: // [google.firestore.admin.v1.ExportDocumentsResponse.output_uri_prefix][google.firestore.admin.v1.ExportDocumentsResponse.output_uri_prefix]. string input_uri_prefix = 3; + + // An empty list represents all namespaces. This is the preferred + // usage for databases that don't use namespaces. + // + // An empty string element represents the default namespace. This should be + // used if the database has data in non-default namespaces, but doesn't want + // to include them. Each namespace in this list must be unique. + repeated string namespace_ids = 4; } diff --git a/dev/protos/google/firestore/admin/v1/location.proto b/dev/protos/google/firestore/admin/v1/location.proto index abf836d92..71f4247ae 100644 --- a/dev/protos/google/firestore/admin/v1/location.proto +++ b/dev/protos/google/firestore/admin/v1/location.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. @@ -25,7 +25,6 @@ option objc_class_prefix = "GCFS"; option php_namespace = "Google\\Cloud\\Firestore\\Admin\\V1"; option ruby_package = "Google::Cloud::Firestore::Admin::V1"; -// The metadata message for [google.cloud.location.Location.metadata][google.cloud.location.Location.metadata]. -message LocationMetadata { - -} +// The metadata message for +// [google.cloud.location.Location.metadata][google.cloud.location.Location.metadata]. +message LocationMetadata {} diff --git a/dev/protos/google/firestore/admin/v1/operation.proto b/dev/protos/google/firestore/admin/v1/operation.proto index 6b0562293..31f63af37 100644 --- a/dev/protos/google/firestore/admin/v1/operation.proto +++ b/dev/protos/google/firestore/admin/v1/operation.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,6 +16,7 @@ syntax = "proto3"; package google.firestore.admin.v1; +import "google/api/resource.proto"; import "google/firestore/admin/v1/index.proto"; import "google/protobuf/timestamp.proto"; @@ -28,7 +29,8 @@ option objc_class_prefix = "GCFS"; option php_namespace = "Google\\Cloud\\Firestore\\Admin\\V1"; option ruby_package = "Google::Cloud::Firestore::Admin::V1"; -// Metadata for [google.longrunning.Operation][google.longrunning.Operation] results from +// Metadata for [google.longrunning.Operation][google.longrunning.Operation] +// results from // [FirestoreAdmin.CreateIndex][google.firestore.admin.v1.FirestoreAdmin.CreateIndex]. message IndexOperationMetadata { // The time this operation started. @@ -52,7 +54,8 @@ message IndexOperationMetadata { Progress progress_bytes = 6; } -// Metadata for [google.longrunning.Operation][google.longrunning.Operation] results from +// Metadata for [google.longrunning.Operation][google.longrunning.Operation] +// results from // [FirestoreAdmin.UpdateField][google.firestore.admin.v1.FirestoreAdmin.UpdateField]. message FieldOperationMetadata { // Information about an index configuration change. @@ -76,7 +79,7 @@ message FieldOperationMetadata { Index index = 2; } - // Information about an TTL configuration change. + // Information about a TTL configuration change. message TtlConfigDelta { // Specifies how the TTL config is changing. enum ChangeType { @@ -105,8 +108,9 @@ message FieldOperationMetadata { // `projects/{project_id}/databases/{database_id}/collectionGroups/{collection_id}/fields/{field_path}` string field = 3; - // A list of [IndexConfigDelta][google.firestore.admin.v1.FieldOperationMetadata.IndexConfigDelta], which describe the intent of this - // operation. + // A list of + // [IndexConfigDelta][google.firestore.admin.v1.FieldOperationMetadata.IndexConfigDelta], + // which describe the intent of this operation. repeated IndexConfigDelta index_config_deltas = 4; // The state of the operation. @@ -122,7 +126,8 @@ message FieldOperationMetadata { TtlConfigDelta ttl_config_delta = 8; } -// Metadata for [google.longrunning.Operation][google.longrunning.Operation] results from +// Metadata for [google.longrunning.Operation][google.longrunning.Operation] +// results from // [FirestoreAdmin.ExportDocuments][google.firestore.admin.v1.FirestoreAdmin.ExportDocuments]. message ExportDocumentsMetadata { // The time this operation started. @@ -144,11 +149,20 @@ message ExportDocumentsMetadata { // Which collection ids are being exported. repeated string collection_ids = 6; - // Where the entities are being exported to. + // Where the documents are being exported to. string output_uri_prefix = 7; + + // Which namespace ids are being exported. + repeated string namespace_ids = 8; + + // The timestamp that corresponds to the version of the database that is being + // exported. If unspecified, there are no guarantees about the consistency of + // the documents being exported. + google.protobuf.Timestamp snapshot_time = 9; } -// Metadata for [google.longrunning.Operation][google.longrunning.Operation] results from +// Metadata for [google.longrunning.Operation][google.longrunning.Operation] +// results from // [FirestoreAdmin.ImportDocuments][google.firestore.admin.v1.FirestoreAdmin.ImportDocuments]. message ImportDocumentsMetadata { // The time this operation started. @@ -172,9 +186,13 @@ message ImportDocumentsMetadata { // The location of the documents being imported. string input_uri_prefix = 7; + + // Which namespace ids are being imported. + repeated string namespace_ids = 8; } -// Returned in the [google.longrunning.Operation][google.longrunning.Operation] response field. +// Returned in the [google.longrunning.Operation][google.longrunning.Operation] +// response field. message ExportDocumentsResponse { // Location of the output files. This can be used to begin an import // into Cloud Firestore (this project or another project) after the operation @@ -212,8 +230,8 @@ enum OperationState { } // Describes the progress of the operation. -// Unit of work is generic and must be interpreted based on where [Progress][google.firestore.admin.v1.Progress] -// is used. +// Unit of work is generic and must be interpreted based on where +// [Progress][google.firestore.admin.v1.Progress] is used. message Progress { // The amount of work estimated. int64 estimated_work = 1; diff --git a/dev/protos/google/firestore/v1/firestore.proto b/dev/protos/google/firestore/v1/firestore.proto index a4447d167..cc38cfb4a 100644 --- a/dev/protos/google/firestore/v1/firestore.proto +++ b/dev/protos/google/firestore/v1/firestore.proto @@ -23,6 +23,7 @@ import "google/firestore/v1/aggregation_result.proto"; import "google/firestore/v1/common.proto"; import "google/firestore/v1/document.proto"; import "google/firestore/v1/query.proto"; +import "google/firestore/v1/query_profile.proto"; import "google/firestore/v1/write.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/timestamp.proto"; @@ -576,6 +577,11 @@ message RunQueryRequest { // minute timestamp within the past 7 days. google.protobuf.Timestamp read_time = 7; } + + // Optional. The mode in which the query request is processed. This field is + // optional, and when not provided, it defaults to `NORMAL` mode where no + // additional statistics will be returned with the query results. + QueryMode mode = 9 [(google.api.field_behavior) = OPTIONAL]; } // The response for @@ -612,6 +618,13 @@ message RunQueryResponse { // documents will be returned. bool done = 6; } + + // Query plan and execution statistics. Note that the returned stats are + // subject to change as Firestore evolves. + // + // This is only present when the request specifies a mode other than `NORMAL` + // and is sent only once with the last response in the stream. + ResultSetStats stats = 7; } // The request for @@ -651,6 +664,11 @@ message RunAggregationQueryRequest { // minute timestamp within the past 7 days. google.protobuf.Timestamp read_time = 6; } + + // Optional. The mode in which the query request is processed. This field is + // optional, and when not provided, it defaults to `NORMAL` mode where no + // additional statistics will be returned with the query results. + QueryMode mode = 7 [(google.api.field_behavior) = OPTIONAL]; } // The response for @@ -676,6 +694,13 @@ message RunAggregationQueryResponse { // `result` will be sent, and this represents the time at which the query // was run. google.protobuf.Timestamp read_time = 3; + + // Query plan and execution statistics. Note that the returned stats are + // subject to change as Firestore evolves. + // + // This is only present when the request specifies a mode other than `NORMAL` + // and is sent only once with the last response in the stream. + ResultSetStats stats = 6; } // The request for @@ -761,7 +786,7 @@ message PartitionQueryResponse { // * query, start_at B // // An empty result may indicate that the query has too few results to be - // partitioned. + // partitioned, or that the query is not yet supported for partitioning. repeated Cursor partitions = 1; // A page token that may be used to request an additional set of results, up @@ -944,6 +969,21 @@ message Target { // The target ID that identifies the target on the stream. Must be a positive // number and non-zero. + // + // If `target_id` is 0 (or unspecified), the server will assign an ID for this + // target and return that in a `TargetChange::ADD` event. Once a target with + // `target_id=0` is added, all subsequent targets must also have + // `target_id=0`. If an `AddTarget` request with `target_id != 0` is + // sent to the server after a target with `target_id=0` is added, the server + // will immediately send a response with a `TargetChange::Remove` event. + // + // Note that if the client sends multiple `AddTarget` requests + // without an ID, the order of IDs returned in `TargetChage.target_ids` are + // undefined. Therefore, clients should provide a target ID instead of relying + // on the server to assign one. + // + // If `target_id` is non-zero, there must not be an existing active target on + // this stream with the same ID. int32 target_id = 5; // If the target should be removed once it is current and consistent. diff --git a/dev/protos/google/firestore/v1/query.proto b/dev/protos/google/firestore/v1/query.proto index 4a4919634..b7d01c24e 100644 --- a/dev/protos/google/firestore/v1/query.proto +++ b/dev/protos/google/firestore/v1/query.proto @@ -236,11 +236,12 @@ message StructuredQuery { // A reference to a field in a document, ex: `stats.operations`. message FieldReference { - // The relative path of the document being referenced. + // A reference to a field in a document. // // Requires: // - // * Conform to [document field name][google.firestore.v1.Document.fields] + // * MUST be a dot-delimited (`.`) string of segments, where each segment + // conforms to [document field name][google.firestore.v1.Document.fields] // limitations. string field_path = 2; } diff --git a/dev/protos/google/firestore/v1/query_profile.proto b/dev/protos/google/firestore/v1/query_profile.proto new file mode 100644 index 000000000..371aa1ef2 --- /dev/null +++ b/dev/protos/google/firestore/v1/query_profile.proto @@ -0,0 +1,76 @@ +// 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; + +import "google/protobuf/struct.proto"; + +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 = "QueryProfileProto"; +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"; + +// Specification of the Firestore Query Profile fields. + +// The mode in which the query request must be processed. +enum QueryMode { + // The default mode. Only the query results are returned. + NORMAL = 0; + + // This mode returns only the query plan, without any results or execution + // statistics information. + PLAN = 1; + + // This mode returns both the query plan and the execution statistics along + // with the results. + PROFILE = 2; +} + +// Plan for the query. +message QueryPlan { + // Planning phase information for the query. It will include: + // + // { + // "indexes_used": [ + // {"query_scope": "Collection", "properties": "(foo ASC, __name__ ASC)"}, + // {"query_scope": "Collection", "properties": "(bar ASC, __name__ ASC)"} + // ] + // } + google.protobuf.Struct plan_info = 1; +} + +// Planning and execution statistics for the query. +message ResultSetStats { + // Plan for the query. + QueryPlan query_plan = 1; + + // Aggregated statistics from the execution of the query. + // + // This will only be present when the request specifies `PROFILE` mode. + // For example, a query will return the statistics including: + // + // { + // "results_returned": "20", + // "documents_scanned": "20", + // "indexes_entries_scanned": "10050", + // "total_execution_time": "100.7 msecs" + // } + google.protobuf.Struct query_stats = 2; +} diff --git a/dev/protos/google/protobuf/descriptor.proto b/dev/protos/google/protobuf/descriptor.proto index 5f148224c..aaa542452 100644 --- a/dev/protos/google/protobuf/descriptor.proto +++ b/dev/protos/google/protobuf/descriptor.proto @@ -57,6 +57,37 @@ message FileDescriptorSet { repeated FileDescriptorProto file = 1; } +// The full set of known editions. +enum Edition { + // A placeholder for an unknown edition value. + EDITION_UNKNOWN = 0; + + // Legacy syntax "editions". These pre-date editions, but behave much like + // distinct editions. These can't be used to specify the edition of proto + // files, but feature definitions must supply proto2/proto3 defaults for + // backwards compatibility. + EDITION_PROTO2 = 998; + EDITION_PROTO3 = 999; + + // Editions that have been released. The specific values are arbitrary and + // should not be depended on, but they will always be time-ordered for easy + // comparison. + EDITION_2023 = 1000; + + // Placeholder editions for testing feature resolution. These should not be + // used or relyed on outside of tests. + EDITION_1_TEST_ONLY = 1; + EDITION_2_TEST_ONLY = 2; + EDITION_99997_TEST_ONLY = 99997; + EDITION_99998_TEST_ONLY = 99998; + EDITION_99999_TEST_ONLY = 99999; + + // Placeholder for specifying unbounded edition support. This should only + // ever be used by plugins that can expect to never require any changes to + // support a new edition. + EDITION_MAX = 0x7FFFFFFF; +} + // Describes a complete .proto file. message FileDescriptorProto { optional string name = 1; // file name, relative to root of source tree @@ -90,8 +121,8 @@ message FileDescriptorProto { // If `edition` is present, this value must be "editions". optional string syntax = 12; - // The edition of the proto file, which is an opaque string. - optional string edition = 13; + // The edition of the proto file. + optional Edition edition = 14; } // Describes a message type. @@ -174,9 +205,10 @@ message ExtensionRangeOptions { } // The verification state of the range. - // TODO(b/278783756): flip the default to DECLARATION once all empty ranges + // TODO: flip the default to DECLARATION once all empty ranges // are marked as UNVERIFIED. - optional VerificationState verification = 3 [default = UNVERIFIED]; + optional VerificationState verification = 3 + [default = UNVERIFIED, retention = RETENTION_SOURCE]; // Clients can define custom options in extensions of this message. See above. extensions 1000 to max; @@ -201,9 +233,10 @@ message FieldDescriptorProto { TYPE_BOOL = 8; TYPE_STRING = 9; // Tag-delimited aggregate. - // Group type is deprecated and not supported in proto3. However, Proto3 + // Group type is deprecated and not supported after google.protobuf. However, Proto3 // implementations should still be able to parse the group wire format and - // treat group fields as unknown fields. + // treat group fields as unknown fields. In Editions, the group wire format + // can be enabled via the `message_encoding` feature. TYPE_GROUP = 10; TYPE_MESSAGE = 11; // Length-delimited aggregate. @@ -220,8 +253,11 @@ message FieldDescriptorProto { enum Label { // 0 is reserved for errors LABEL_OPTIONAL = 1; - LABEL_REQUIRED = 2; LABEL_REPEATED = 3; + // The required label is only allowed in google.protobuf. In proto3 and Editions + // it's explicitly prohibited. In Editions, the `field_presence` feature + // can be used to get this behavior. + LABEL_REQUIRED = 2; } optional string name = 1; @@ -264,12 +300,12 @@ message FieldDescriptorProto { // If true, this is a proto3 "optional". When a proto3 field is optional, it // tracks presence regardless of field type. // - // When proto3_optional is true, this field must be belong to a oneof to - // signal to old proto3 clients that presence is tracked for this field. This - // oneof is known as a "synthetic" oneof, and this field must be its sole - // member (each proto3 optional field gets its own synthetic oneof). Synthetic - // oneofs exist in the descriptor only, and do not generate any API. Synthetic - // oneofs must be ordered after all "real" oneofs. + // When proto3_optional is true, this field must belong to a oneof to signal + // to old proto3 clients that presence is tracked for this field. This oneof + // is known as a "synthetic" oneof, and this field must be its sole member + // (each proto3 optional field gets its own synthetic oneof). Synthetic oneofs + // exist in the descriptor only, and do not generate any API. Synthetic oneofs + // must be ordered after all "real" oneofs. // // For message fields, proto3_optional doesn't create any semantic change, // since non-repeated message fields always track presence. However it still @@ -448,7 +484,7 @@ message FileOptions { optional bool cc_generic_services = 16 [default = false]; optional bool java_generic_services = 17 [default = false]; optional bool py_generic_services = 18 [default = false]; - optional bool php_generic_services = 42 [default = false]; + reserved 42; // removed php_generic_services // Is this file deprecated? // Depending on the target platform, this can emit Deprecated annotations @@ -540,10 +576,6 @@ message MessageOptions { reserved 4, 5, 6; - // NOTE: Do not set the option in .proto files. Always use the maps syntax - // instead. The option should only be implicitly set by the proto compiler - // parser. - // // Whether the message is an automatically generated map entry type for the // maps field. // @@ -561,6 +593,10 @@ message MessageOptions { // use a native map in the target language to hold the keys and values. // The reflection APIs in such implementations still need to work as // if the field is a repeated message field. + // + // NOTE: Do not set the option in .proto files. Always use the maps syntax + // instead. The option should only be implicitly set by the proto compiler + // parser. optional bool map_entry = 7; reserved 8; // javalite_serializable @@ -574,7 +610,7 @@ message MessageOptions { // This should only be used as a temporary measure against broken builds due // to the change in behavior for JSON field name conflicts. // - // TODO(b/261750190) This is legacy behavior we plan to remove once downstream + // TODO This is legacy behavior we plan to remove once downstream // teams have had time to migrate. optional bool deprecated_legacy_json_field_conflicts = 11 [deprecated = true]; @@ -614,7 +650,9 @@ message FieldOptions { // a more efficient representation on the wire. Rather than repeatedly // writing the tag and type for each element, the entire array is encoded as // a single length-delimited blob. In proto3, only explicit setting it to - // false will avoid using packed encoding. + // false will avoid using packed encoding. This option is prohibited in + // Editions, but the `repeated_field_encoding` feature can be used to control + // the behavior. optional bool packed = 2; // The jstype option determines the JavaScript type used for values of the @@ -657,19 +695,11 @@ message FieldOptions { // call from multiple threads concurrently, while non-const methods continue // to require exclusive access. // - // Note that implementations may choose not to check required fields within - // a lazy sub-message. That is, calling IsInitialized() on the outer message - // may return true even if the inner message has missing required fields. - // This is necessary because otherwise the inner message would have to be - // parsed in order to perform the check, defeating the purpose of lazy - // parsing. An implementation which chooses not to check required fields - // must be consistent about it. That is, for any particular sub-message, the - // implementation must either *always* check its required fields, or *never* - // check its required fields, regardless of whether or not the message has - // been parsed. - // - // As of May 2022, lazy verifies the contents of the byte stream during - // parsing. An invalid byte stream will cause the overall parsing to fail. + // Note that lazy message fields are still eagerly verified to check + // ill-formed wireformat or missing required fields. Calling IsInitialized() + // on the outer message would fail if the inner message has missing required + // fields. Failed verification would result in parsing failure (except when + // uninitialized messages are acceptable). optional bool lazy = 5 [default = false]; // unverified_lazy does no correctness checks on the byte stream. This should @@ -721,7 +751,7 @@ message FieldOptions { repeated OptionTargetType targets = 19; message EditionDefault { - optional string edition = 1; + optional Edition edition = 3; optional string value = 2; // Textproto value. } repeated EditionDefault edition_defaults = 20; @@ -768,7 +798,7 @@ message EnumOptions { // and strips underscored from the fields before comparison in proto3 only. // The new behavior takes `json_name` into account and applies to proto2 as // well. - // TODO(b/261750190) Remove this legacy behavior once downstream teams have + // TODO Remove this legacy behavior once downstream teams have // had time to migrate. optional bool deprecated_legacy_json_field_conflicts = 6 [deprecated = true]; @@ -892,7 +922,7 @@ message UninterpretedOption { // =================================================================== // Features -// TODO(b/274655146) Enums in C++ gencode (and potentially other languages) are +// TODO Enums in C++ gencode (and potentially other languages) are // not well scoped. This means that each of the feature enums below can clash // with each other. The short names we've chosen maximize call-site // readability, but leave us very open to this scenario. A future feature will @@ -909,7 +939,9 @@ message FeatureSet { retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FIELD, targets = TARGET_TYPE_FILE, - edition_defaults = { edition: "2023", value: "EXPLICIT" } + edition_defaults = { edition: EDITION_PROTO2, value: "EXPLICIT" }, + edition_defaults = { edition: EDITION_PROTO3, value: "IMPLICIT" }, + edition_defaults = { edition: EDITION_2023, value: "EXPLICIT" } ]; enum EnumType { @@ -921,7 +953,8 @@ message FeatureSet { retention = RETENTION_RUNTIME, targets = TARGET_TYPE_ENUM, targets = TARGET_TYPE_FILE, - edition_defaults = { edition: "2023", value: "OPEN" } + edition_defaults = { edition: EDITION_PROTO2, value: "CLOSED" }, + edition_defaults = { edition: EDITION_PROTO3, value: "OPEN" } ]; enum RepeatedFieldEncoding { @@ -933,20 +966,21 @@ message FeatureSet { retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FIELD, targets = TARGET_TYPE_FILE, - edition_defaults = { edition: "2023", value: "PACKED" } + edition_defaults = { edition: EDITION_PROTO2, value: "EXPANDED" }, + edition_defaults = { edition: EDITION_PROTO3, value: "PACKED" } ]; - enum StringFieldValidation { - STRING_FIELD_VALIDATION_UNKNOWN = 0; - MANDATORY = 1; - HINT = 2; + enum Utf8Validation { + UTF8_VALIDATION_UNKNOWN = 0; + VERIFY = 2; NONE = 3; } - optional StringFieldValidation string_field_validation = 4 [ + optional Utf8Validation utf8_validation = 4 [ retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FIELD, targets = TARGET_TYPE_FILE, - edition_defaults = { edition: "2023", value: "MANDATORY" } + edition_defaults = { edition: EDITION_PROTO2, value: "NONE" }, + edition_defaults = { edition: EDITION_PROTO3, value: "VERIFY" } ]; enum MessageEncoding { @@ -958,7 +992,7 @@ message FeatureSet { retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FIELD, targets = TARGET_TYPE_FILE, - edition_defaults = { edition: "2023", value: "LENGTH_PREFIXED" } + edition_defaults = { edition: EDITION_PROTO2, value: "LENGTH_PREFIXED" } ]; enum JsonFormat { @@ -971,10 +1005,11 @@ message FeatureSet { targets = TARGET_TYPE_MESSAGE, targets = TARGET_TYPE_ENUM, targets = TARGET_TYPE_FILE, - edition_defaults = { edition: "2023", value: "ALLOW" } + edition_defaults = { edition: EDITION_PROTO2, value: "LEGACY_BEST_EFFORT" }, + edition_defaults = { edition: EDITION_PROTO3, value: "ALLOW" } ]; - optional FeatureSet raw_features = 999 [targets = TARGET_TYPE_UNKNOWN]; + reserved 999; extensions 1000; // for Protobuf C++ extensions 1001; // for Protobuf Java @@ -982,6 +1017,30 @@ message FeatureSet { extensions 9995 to 9999; // For internal testing } +// A compiled specification for the defaults of a set of features. These +// messages are generated from FeatureSet extensions and can be used to seed +// feature resolution. The resolution with this object becomes a simple search +// for the closest matching edition, followed by proto merges. +message FeatureSetDefaults { + // A map from every known edition with a unique set of defaults to its + // defaults. Not all editions may be contained here. For a given edition, + // the defaults at the closest matching edition ordered at or before it should + // be used. This field must be in strict ascending order by edition. + message FeatureSetEditionDefault { + optional Edition edition = 3; + optional FeatureSet features = 2; + } + repeated FeatureSetEditionDefault defaults = 1; + + // The minimum supported edition (inclusive) when this was constructed. + // Editions before this will not have defaults. + optional Edition minimum_edition = 4; + + // The maximum known edition (inclusive) when this was constructed. Editions + // after this will not have reliable defaults. + optional Edition maximum_edition = 5; +} + // =================================================================== // Optional source code info @@ -1037,7 +1096,7 @@ message SourceCodeInfo { // location. // // Each element is a field number or an index. They form a path from - // the root FileDescriptorProto to the place where the definition occurs. + // the root FileDescriptorProto to the place where the definition appears. // For example, this path: // [ 4, 3, 2, 7, 1 ] // refers to: diff --git a/dev/protos/update.sh b/dev/protos/update.sh index d97f127b0..79be9ea91 100755 --- a/dev/protos/update.sh +++ b/dev/protos/update.sh @@ -18,7 +18,7 @@ set -euo pipefail IFS=$'\n\t' echo "Running update.sh" - +echo $(npm --version) # Variables PROTOS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" WORK_DIR=`mktemp -d` @@ -34,8 +34,8 @@ function cleanup { trap cleanup EXIT # Capture location of pbjs / pbts before we pushd. -PBJS="$(npm bin)/pbjs" -PBTS="$(npm bin)/pbts" +PBJS="$(npm root)/.bin/pbjs" +PBTS="$(npm root)/.bin/pbts" # Enter work dir pushd "$WORK_DIR" @@ -137,4 +137,6 @@ perl -pi -e 's/number\|Long/number\|string/g' firestore_v1beta1_proto_api.js "${PROTOS_DIR}/google/protobuf/*.proto" "${PROTOS_DIR}/google/type/*.proto" \ "${PROTOS_DIR}/google/rpc/*.proto" "${PROTOS_DIR}/google/api/*.proto" -node ../../scripts/license.js *.d.ts *.js ../../build/src/v1beta1/*.d.ts ../../build/src/v1/*.d.ts +echo "Finished running update.sh" + +node ../../scripts/license.js ../../build ../protos diff --git a/dev/protos/v1.json b/dev/protos/v1.json index 863777736..db0e96384 100644 --- a/dev/protos/v1.json +++ b/dev/protos/v1.json @@ -1829,6 +1829,13 @@ "readTime": { "type": "google.protobuf.Timestamp", "id": 7 + }, + "mode": { + "type": "QueryMode", + "id": 9, + "options": { + "(google.api.field_behavior)": "OPTIONAL" + } } } }, @@ -1860,6 +1867,10 @@ "done": { "type": "bool", "id": 6 + }, + "stats": { + "type": "ResultSetStats", + "id": 7 } } }, @@ -1901,6 +1912,13 @@ "readTime": { "type": "google.protobuf.Timestamp", "id": 6 + }, + "mode": { + "type": "QueryMode", + "id": 7, + "options": { + "(google.api.field_behavior)": "OPTIONAL" + } } } }, @@ -1917,6 +1935,10 @@ "readTime": { "type": "google.protobuf.Timestamp", "id": 3 + }, + "stats": { + "type": "ResultSetStats", + "id": 6 } } }, @@ -2283,6 +2305,33 @@ } } }, + "QueryMode": { + "values": { + "NORMAL": 0, + "PLAN": 1, + "PROFILE": 2 + } + }, + "QueryPlan": { + "fields": { + "planInfo": { + "type": "google.protobuf.Struct", + "id": 1 + } + } + }, + "ResultSetStats": { + "fields": { + "queryPlan": { + "type": "QueryPlan", + "id": 1 + }, + "queryStats": { + "type": "google.protobuf.Struct", + "id": 2 + } + } + }, "StructuredQuery": { "fields": { "select": { @@ -3093,6 +3142,11 @@ "longRunning": { "type": "LongRunning", "id": 2 + }, + "autoPopulatedFields": { + "rule": "repeated", + "type": "string", + "id": 3 } }, "nested": { @@ -3152,7 +3206,8 @@ "INPUT_ONLY": 4, "IMMUTABLE": 5, "UNORDERED_LIST": 6, - "NON_EMPTY_DEFAULT": 7 + "NON_EMPTY_DEFAULT": 7, + "IDENTIFIER": 8 } }, "LaunchStage": { diff --git a/dev/protos/v1beta1.json b/dev/protos/v1beta1.json index e2c143932..9eb2f449b 100644 --- a/dev/protos/v1beta1.json +++ b/dev/protos/v1beta1.json @@ -2838,6 +2838,11 @@ "longRunning": { "type": "LongRunning", "id": 2 + }, + "autoPopulatedFields": { + "rule": "repeated", + "type": "string", + "id": 3 } }, "nested": { @@ -2897,7 +2902,8 @@ "INPUT_ONLY": 4, "IMMUTABLE": 5, "UNORDERED_LIST": 6, - "NON_EMPTY_DEFAULT": 7 + "NON_EMPTY_DEFAULT": 7, + "IDENTIFIER": 8 } }, "LaunchStage": { diff --git a/dev/src/v1/firestore_admin_client.ts b/dev/src/v1/firestore_admin_client.ts index 722049b57..657ab2484 100644 --- a/dev/src/v1/firestore_admin_client.ts +++ b/dev/src/v1/firestore_admin_client.ts @@ -123,8 +123,7 @@ export class FirestoreAdminClient { * API remote host. * @param {gax.ClientConfig} [options.clientConfig] - Client configuration override. * Follows the structure of {@link gapicConfig}. - * @param {boolean | "rest"} [options.fallback] - Use HTTP fallback mode. - * Pass "rest" to use HTTP/1.1 REST API instead of gRPC. + * @param {boolean} [options.fallback] - Use HTTP/1.1 REST mode. * For more information, please check the * {@link https://github.com/googleapis/gax-nodejs/blob/main/client-libraries.md#http11-rest-api-mode documentation}. * @param {gax} [gaxInstance]: loaded instance of `google-gax`. Useful if you @@ -132,7 +131,7 @@ export class FirestoreAdminClient { * HTTP implementation. Load only fallback version and pass it to the constructor: * ``` * const gax = require('google-gax/build/src/fallback'); // avoids loading google-gax with gRPC - * const client = new FirestoreAdminClient({fallback: 'rest'}, gax); + * const client = new FirestoreAdminClient({fallback: true}, gax); * ``` */ constructor( @@ -202,7 +201,7 @@ export class FirestoreAdminClient { } if (!opts.fallback) { clientHeader.push(`grpc/${this._gaxGrpc.grpcVersion}`); - } else if (opts.fallback === 'rest') { + } else { clientHeader.push(`rest/${this._gaxGrpc.grpcVersion}`); } if (opts.libName && opts.libVersion) { @@ -256,7 +255,7 @@ export class FirestoreAdminClient { auth: this.auth, grpc: 'grpc' in this._gaxGrpc ? this._gaxGrpc.grpc : undefined, }; - if (opts.fallback === 'rest') { + if (opts.fallback) { lroOptions.protoJson = protoFilesRoot; lroOptions.httpRules = [ { @@ -317,6 +316,12 @@ export class FirestoreAdminClient { const updateDatabaseMetadata = protoFilesRoot.lookup( '.google.firestore.admin.v1.UpdateDatabaseMetadata' ) as gax.protobuf.Type; + const deleteDatabaseResponse = protoFilesRoot.lookup( + '.google.firestore.admin.v1.Database' + ) as gax.protobuf.Type; + const deleteDatabaseMetadata = protoFilesRoot.lookup( + '.google.firestore.admin.v1.DeleteDatabaseMetadata' + ) as gax.protobuf.Type; this.descriptors.longrunning = { createIndex: new this._gaxModule.LongrunningDescriptor( @@ -349,6 +354,11 @@ export class FirestoreAdminClient { updateDatabaseResponse.decode.bind(updateDatabaseResponse), updateDatabaseMetadata.decode.bind(updateDatabaseMetadata) ), + deleteDatabase: new this._gaxModule.LongrunningDescriptor( + this.operationsClient, + deleteDatabaseResponse.decode.bind(deleteDatabaseResponse), + deleteDatabaseMetadata.decode.bind(deleteDatabaseMetadata) + ), }; // Put together the default options sent with requests. @@ -414,6 +424,7 @@ export class FirestoreAdminClient { 'getDatabase', 'listDatabases', 'updateDatabase', + 'deleteDatabase', ]; for (const methodName of firestoreAdminStubMethods) { const callPromise = this.firestoreAdminStub.then( @@ -1251,6 +1262,21 @@ export class FirestoreAdminClient { * guidelines: https://cloud.google.com/storage/docs/naming. * If the URI is a bucket (without a namespace path), a prefix will be * generated based on the start time. + * @param {string[]} request.namespaceIds + * An empty list represents all namespaces. This is the preferred + * usage for databases that don't use namespaces. + * + * An empty string element represents the default namespace. This should be + * used if the database has data in non-default namespaces, but doesn't want + * to include them. Each namespace in this list must be unique. + * @param {google.protobuf.Timestamp} request.snapshotTime + * The timestamp that corresponds to the version of the database to be + * exported. The timestamp must be in the past, rounded to the minute and not + * older than + * {@link protos.google.firestore.admin.v1.Database.earliest_version_time|earliestVersionTime}. + * If specified, then the exported documents will represent a consistent view + * of the database at the provided time. Otherwise, there are no guarantees + * about the consistency of the exported documents. * @param {object} [options] * Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details. * @returns {Promise} - The promise which resolves to an array. @@ -1401,6 +1427,13 @@ export class FirestoreAdminClient { * an export that has completed successfully. * See: * {@link protos.google.firestore.admin.v1.ExportDocumentsResponse.output_uri_prefix|google.firestore.admin.v1.ExportDocumentsResponse.output_uri_prefix}. + * @param {string[]} request.namespaceIds + * An empty list represents all namespaces. This is the preferred + * usage for databases that don't use namespaces. + * + * An empty string element represents the default namespace. This should be + * used if the database has data in non-default namespaces, but doesn't want + * to include them. Each namespace in this list must be unique. * @param {object} [options] * Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details. * @returns {Promise} - The promise which resolves to an array. @@ -1544,7 +1577,11 @@ export class FirestoreAdminClient { * Required. The ID to use for the database, which will become the final * component of the database's resource name. * - * The value must be set to "(default)". + * This value should be 4-63 characters. Valid characters are /{@link protos.0-9|a-z}-/ + * with first character a letter and the last a letter or a number. Must not + * be UUID-like /[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}/. + * + * "(default)" database id is also valid. * @param {object} [options] * Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details. * @returns {Promise} - The promise which resolves to an array. @@ -1812,6 +1849,147 @@ export class FirestoreAdminClient { protos.google.firestore.admin.v1.UpdateDatabaseMetadata >; } + /** + * Deletes a database. + * + * @param {Object} request + * The request object that will be sent. + * @param {string} request.name + * Required. A name of the form + * `projects/{project_id}/databases/{database_id}` + * @param {string} request.etag + * The current etag of the Database. + * If an etag is provided and does not match the current etag of the database, + * deletion will be blocked and a FAILED_PRECONDITION error will be returned. + * @param {object} [options] + * Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details. + * @returns {Promise} - The promise which resolves to an array. + * The first element of the array is an object representing + * a long running operation. Its `promise()` method returns a promise + * you can `await` for. + * Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#long-running-operations | documentation } + * for more details and examples. + * @example include:samples/generated/v1/firestore_admin.delete_database.js + * region_tag:firestore_v1_generated_FirestoreAdmin_DeleteDatabase_async + */ + deleteDatabase( + request?: protos.google.firestore.admin.v1.IDeleteDatabaseRequest, + options?: CallOptions + ): Promise< + [ + LROperation< + protos.google.firestore.admin.v1.IDatabase, + protos.google.firestore.admin.v1.IDeleteDatabaseMetadata + >, + protos.google.longrunning.IOperation | undefined, + {} | undefined, + ] + >; + deleteDatabase( + request: protos.google.firestore.admin.v1.IDeleteDatabaseRequest, + options: CallOptions, + callback: Callback< + LROperation< + protos.google.firestore.admin.v1.IDatabase, + protos.google.firestore.admin.v1.IDeleteDatabaseMetadata + >, + protos.google.longrunning.IOperation | null | undefined, + {} | null | undefined + > + ): void; + deleteDatabase( + request: protos.google.firestore.admin.v1.IDeleteDatabaseRequest, + callback: Callback< + LROperation< + protos.google.firestore.admin.v1.IDatabase, + protos.google.firestore.admin.v1.IDeleteDatabaseMetadata + >, + protos.google.longrunning.IOperation | null | undefined, + {} | null | undefined + > + ): void; + deleteDatabase( + request?: protos.google.firestore.admin.v1.IDeleteDatabaseRequest, + optionsOrCallback?: + | CallOptions + | Callback< + LROperation< + protos.google.firestore.admin.v1.IDatabase, + protos.google.firestore.admin.v1.IDeleteDatabaseMetadata + >, + protos.google.longrunning.IOperation | null | undefined, + {} | null | undefined + >, + callback?: Callback< + LROperation< + protos.google.firestore.admin.v1.IDatabase, + protos.google.firestore.admin.v1.IDeleteDatabaseMetadata + >, + protos.google.longrunning.IOperation | null | undefined, + {} | null | undefined + > + ): Promise< + [ + LROperation< + protos.google.firestore.admin.v1.IDatabase, + protos.google.firestore.admin.v1.IDeleteDatabaseMetadata + >, + protos.google.longrunning.IOperation | undefined, + {} | undefined, + ] + > | void { + request = request || {}; + let options: CallOptions; + if (typeof optionsOrCallback === 'function' && callback === undefined) { + callback = optionsOrCallback; + options = {}; + } else { + options = optionsOrCallback as CallOptions; + } + options = options || {}; + options.otherArgs = options.otherArgs || {}; + options.otherArgs.headers = options.otherArgs.headers || {}; + options.otherArgs.headers['x-goog-request-params'] = + this._gaxModule.routingHeader.fromParams({ + name: request.name ?? '', + }); + this.initialize(); + return this.innerApiCalls.deleteDatabase(request, options, callback); + } + /** + * Check the status of the long running operation returned by `deleteDatabase()`. + * @param {String} name + * The operation name that will be passed. + * @returns {Promise} - The promise which resolves to an object. + * The decoded operation object has result and metadata field to get information from. + * Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#long-running-operations | documentation } + * for more details and examples. + * @example include:samples/generated/v1/firestore_admin.delete_database.js + * region_tag:firestore_v1_generated_FirestoreAdmin_DeleteDatabase_async + */ + async checkDeleteDatabaseProgress( + name: string + ): Promise< + LROperation< + protos.google.firestore.admin.v1.Database, + protos.google.firestore.admin.v1.DeleteDatabaseMetadata + > + > { + const request = + new this._gaxModule.operationsProtos.google.longrunning.GetOperationRequest( + {name} + ); + const [operation] = await this.operationsClient.getOperation(request); + const decodeOperation = new this._gaxModule.Operation( + operation, + this.descriptors.longrunning.deleteDatabase, + this._gaxModule.createDefaultBackoffSettings() + ); + return decodeOperation as LROperation< + protos.google.firestore.admin.v1.Database, + protos.google.firestore.admin.v1.DeleteDatabaseMetadata + >; + } /** * Lists composite indexes. * @@ -2015,7 +2193,8 @@ export class FirestoreAdminClient { * only supports listing fields that have been explicitly overridden. To issue * this query, call * {@link protos.google.firestore.admin.v1.FirestoreAdmin.ListFields|FirestoreAdmin.ListFields} - * with the filter set to `indexConfig.usesAncestorConfig:false` . + * with the filter set to `indexConfig.usesAncestorConfig:false or + * `ttlConfig:*`. * * @param {Object} request * The request object that will be sent. diff --git a/dev/src/v1/firestore_admin_client_config.json b/dev/src/v1/firestore_admin_client_config.json index ee5860dbc..e434c90c7 100644 --- a/dev/src/v1/firestore_admin_client_config.json +++ b/dev/src/v1/firestore_admin_client_config.json @@ -85,6 +85,10 @@ "UpdateDatabase": { "retry_codes_name": "non_idempotent", "retry_params_name": "default" + }, + "DeleteDatabase": { + "retry_codes_name": "non_idempotent", + "retry_params_name": "default" } } } diff --git a/dev/src/v1/firestore_client.ts b/dev/src/v1/firestore_client.ts index 86b9f080d..d63ecb8c7 100644 --- a/dev/src/v1/firestore_client.ts +++ b/dev/src/v1/firestore_client.ts @@ -1,4 +1,4 @@ -// Copyright 2023 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -99,8 +99,7 @@ export class FirestoreClient { * API remote host. * @param {gax.ClientConfig} [options.clientConfig] - Client configuration override. * Follows the structure of {@link gapicConfig}. - * @param {boolean | "rest"} [options.fallback] - Use HTTP fallback mode. - * Pass "rest" to use HTTP/1.1 REST API instead of gRPC. + * @param {boolean} [options.fallback] - Use HTTP/1.1 REST mode. * For more information, please check the * {@link https://github.com/googleapis/gax-nodejs/blob/main/client-libraries.md#http11-rest-api-mode documentation}. * @param {gax} [gaxInstance]: loaded instance of `google-gax`. Useful if you @@ -108,7 +107,7 @@ export class FirestoreClient { * HTTP implementation. Load only fallback version and pass it to the constructor: * ``` * const gax = require('google-gax/build/src/fallback'); // avoids loading google-gax with gRPC - * const client = new FirestoreClient({fallback: 'rest'}, gax); + * const client = new FirestoreClient({fallback: true}, gax); * ``` */ constructor( @@ -178,7 +177,7 @@ export class FirestoreClient { } if (!opts.fallback) { clientHeader.push(`grpc/${this._gaxGrpc.grpcVersion}`); - } else if (opts.fallback === 'rest') { + } else { clientHeader.push(`rest/${this._gaxGrpc.grpcVersion}`); } if (opts.libName && opts.libVersion) { @@ -213,23 +212,23 @@ export class FirestoreClient { this.descriptors.stream = { batchGetDocuments: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.SERVER_STREAMING, - opts.fallback === 'rest' + !!opts.fallback ), runQuery: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.SERVER_STREAMING, - opts.fallback === 'rest' + !!opts.fallback ), runAggregationQuery: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.SERVER_STREAMING, - opts.fallback === 'rest' + !!opts.fallback ), write: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.BIDI_STREAMING, - opts.fallback === 'rest' + !!opts.fallback ), listen: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.BIDI_STREAMING, - opts.fallback === 'rest' + !!opts.fallback ), }; @@ -1231,6 +1230,10 @@ export class FirestoreClient { * This must be a microsecond precision timestamp within the past one hour, * or if Point-in-Time Recovery is enabled, can additionally be a whole * minute timestamp within the past 7 days. + * @param {google.firestore.v1.QueryMode} [request.mode] + * Optional. The mode in which the query request is processed. This field is + * optional, and when not provided, it defaults to `NORMAL` mode where no + * additional statistics will be returned with the query results. * @param {object} [options] * Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details. * @returns {Stream} @@ -1297,6 +1300,10 @@ export class FirestoreClient { * This must be a microsecond precision timestamp within the past one hour, * or if Point-in-Time Recovery is enabled, can additionally be a whole * minute timestamp within the past 7 days. + * @param {google.firestore.v1.QueryMode} [request.mode] + * Optional. The mode in which the query request is processed. This field is + * optional, and when not provided, it defaults to `NORMAL` mode where no + * additional statistics will be returned with the query results. * @param {object} [options] * Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details. * @returns {Stream} diff --git a/dev/src/v1/firestore_proto_list.json b/dev/src/v1/firestore_proto_list.json index 5c2cd76e2..318c636c1 100644 --- a/dev/src/v1/firestore_proto_list.json +++ b/dev/src/v1/firestore_proto_list.json @@ -5,5 +5,6 @@ "../../protos/google/firestore/v1/document.proto", "../../protos/google/firestore/v1/firestore.proto", "../../protos/google/firestore/v1/query.proto", + "../../protos/google/firestore/v1/query_profile.proto", "../../protos/google/firestore/v1/write.proto" ] diff --git a/dev/src/v1beta1/firestore_client.ts b/dev/src/v1beta1/firestore_client.ts index 11a617337..7e62cb603 100644 --- a/dev/src/v1beta1/firestore_client.ts +++ b/dev/src/v1beta1/firestore_client.ts @@ -1,4 +1,4 @@ -// Copyright 2023 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -99,8 +99,7 @@ export class FirestoreClient { * API remote host. * @param {gax.ClientConfig} [options.clientConfig] - Client configuration override. * Follows the structure of {@link gapicConfig}. - * @param {boolean | "rest"} [options.fallback] - Use HTTP fallback mode. - * Pass "rest" to use HTTP/1.1 REST API instead of gRPC. + * @param {boolean} [options.fallback] - Use HTTP/1.1 REST mode. * For more information, please check the * {@link https://github.com/googleapis/gax-nodejs/blob/main/client-libraries.md#http11-rest-api-mode documentation}. * @param {gax} [gaxInstance]: loaded instance of `google-gax`. Useful if you @@ -108,7 +107,7 @@ export class FirestoreClient { * HTTP implementation. Load only fallback version and pass it to the constructor: * ``` * const gax = require('google-gax/build/src/fallback'); // avoids loading google-gax with gRPC - * const client = new FirestoreClient({fallback: 'rest'}, gax); + * const client = new FirestoreClient({fallback: true}, gax); * ``` */ constructor( @@ -174,7 +173,7 @@ export class FirestoreClient { } if (!opts.fallback) { clientHeader.push(`grpc/${this._gaxGrpc.grpcVersion}`); - } else if (opts.fallback === 'rest') { + } else { clientHeader.push(`rest/${this._gaxGrpc.grpcVersion}`); } if (opts.libName && opts.libVersion) { @@ -209,19 +208,19 @@ export class FirestoreClient { this.descriptors.stream = { batchGetDocuments: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.SERVER_STREAMING, - opts.fallback === 'rest' + !!opts.fallback ), runQuery: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.SERVER_STREAMING, - opts.fallback === 'rest' + !!opts.fallback ), write: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.BIDI_STREAMING, - opts.fallback === 'rest' + !!opts.fallback ), listen: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.BIDI_STREAMING, - opts.fallback === 'rest' + !!opts.fallback ), }; diff --git a/dev/test/gapic_firestore_admin_v1.ts b/dev/test/gapic_firestore_admin_v1.ts index 904c246a6..2aa2dc3be 100644 --- a/dev/test/gapic_firestore_admin_v1.ts +++ b/dev/test/gapic_firestore_admin_v1.ts @@ -2070,6 +2070,200 @@ describe('v1.FirestoreAdminClient', () => { }); }); + describe('deleteDatabase', () => { + it('invokes deleteDatabase without error', async () => { + const client = new firestoreadminModule.FirestoreAdminClient({ + credentials: {client_email: 'bogus', private_key: 'bogus'}, + projectId: 'bogus', + }); + client.initialize(); + const request = generateSampleMessage( + new protos.google.firestore.admin.v1.DeleteDatabaseRequest() + ); + const defaultValue1 = getTypeDefaultValue( + '.google.firestore.admin.v1.DeleteDatabaseRequest', + ['name'] + ); + request.name = defaultValue1; + const expectedHeaderRequestParams = `name=${defaultValue1}`; + const expectedResponse = generateSampleMessage( + new protos.google.longrunning.Operation() + ); + client.innerApiCalls.deleteDatabase = + stubLongRunningCall(expectedResponse); + const [operation] = await client.deleteDatabase(request); + const [response] = await operation.promise(); + assert.deepStrictEqual(response, expectedResponse); + const actualRequest = ( + client.innerApiCalls.deleteDatabase as SinonStub + ).getCall(0).args[0]; + assert.deepStrictEqual(actualRequest, request); + const actualHeaderRequestParams = ( + client.innerApiCalls.deleteDatabase as SinonStub + ).getCall(0).args[1].otherArgs.headers['x-goog-request-params']; + assert(actualHeaderRequestParams.includes(expectedHeaderRequestParams)); + }); + + it('invokes deleteDatabase without error using callback', async () => { + const client = new firestoreadminModule.FirestoreAdminClient({ + credentials: {client_email: 'bogus', private_key: 'bogus'}, + projectId: 'bogus', + }); + client.initialize(); + const request = generateSampleMessage( + new protos.google.firestore.admin.v1.DeleteDatabaseRequest() + ); + const defaultValue1 = getTypeDefaultValue( + '.google.firestore.admin.v1.DeleteDatabaseRequest', + ['name'] + ); + request.name = defaultValue1; + const expectedHeaderRequestParams = `name=${defaultValue1}`; + const expectedResponse = generateSampleMessage( + new protos.google.longrunning.Operation() + ); + client.innerApiCalls.deleteDatabase = + stubLongRunningCallWithCallback(expectedResponse); + const promise = new Promise((resolve, reject) => { + client.deleteDatabase( + request, + ( + err?: Error | null, + result?: LROperation< + protos.google.firestore.admin.v1.IDatabase, + protos.google.firestore.admin.v1.IDeleteDatabaseMetadata + > | null + ) => { + if (err) { + reject(err); + } else { + resolve(result); + } + } + ); + }); + const operation = (await promise) as LROperation< + protos.google.firestore.admin.v1.IDatabase, + protos.google.firestore.admin.v1.IDeleteDatabaseMetadata + >; + const [response] = await operation.promise(); + assert.deepStrictEqual(response, expectedResponse); + const actualRequest = ( + client.innerApiCalls.deleteDatabase as SinonStub + ).getCall(0).args[0]; + assert.deepStrictEqual(actualRequest, request); + const actualHeaderRequestParams = ( + client.innerApiCalls.deleteDatabase as SinonStub + ).getCall(0).args[1].otherArgs.headers['x-goog-request-params']; + assert(actualHeaderRequestParams.includes(expectedHeaderRequestParams)); + }); + + it('invokes deleteDatabase with call error', async () => { + const client = new firestoreadminModule.FirestoreAdminClient({ + credentials: {client_email: 'bogus', private_key: 'bogus'}, + projectId: 'bogus', + }); + client.initialize(); + const request = generateSampleMessage( + new protos.google.firestore.admin.v1.DeleteDatabaseRequest() + ); + const defaultValue1 = getTypeDefaultValue( + '.google.firestore.admin.v1.DeleteDatabaseRequest', + ['name'] + ); + request.name = defaultValue1; + const expectedHeaderRequestParams = `name=${defaultValue1}`; + const expectedError = new Error('expected'); + client.innerApiCalls.deleteDatabase = stubLongRunningCall( + undefined, + expectedError + ); + await assert.rejects(client.deleteDatabase(request), expectedError); + const actualRequest = ( + client.innerApiCalls.deleteDatabase as SinonStub + ).getCall(0).args[0]; + assert.deepStrictEqual(actualRequest, request); + const actualHeaderRequestParams = ( + client.innerApiCalls.deleteDatabase as SinonStub + ).getCall(0).args[1].otherArgs.headers['x-goog-request-params']; + assert(actualHeaderRequestParams.includes(expectedHeaderRequestParams)); + }); + + it('invokes deleteDatabase with LRO error', async () => { + const client = new firestoreadminModule.FirestoreAdminClient({ + credentials: {client_email: 'bogus', private_key: 'bogus'}, + projectId: 'bogus', + }); + client.initialize(); + const request = generateSampleMessage( + new protos.google.firestore.admin.v1.DeleteDatabaseRequest() + ); + const defaultValue1 = getTypeDefaultValue( + '.google.firestore.admin.v1.DeleteDatabaseRequest', + ['name'] + ); + request.name = defaultValue1; + const expectedHeaderRequestParams = `name=${defaultValue1}`; + const expectedError = new Error('expected'); + client.innerApiCalls.deleteDatabase = stubLongRunningCall( + undefined, + undefined, + expectedError + ); + const [operation] = await client.deleteDatabase(request); + await assert.rejects(operation.promise(), expectedError); + const actualRequest = ( + client.innerApiCalls.deleteDatabase as SinonStub + ).getCall(0).args[0]; + assert.deepStrictEqual(actualRequest, request); + const actualHeaderRequestParams = ( + client.innerApiCalls.deleteDatabase as SinonStub + ).getCall(0).args[1].otherArgs.headers['x-goog-request-params']; + assert(actualHeaderRequestParams.includes(expectedHeaderRequestParams)); + }); + + it('invokes checkDeleteDatabaseProgress without error', async () => { + const client = new firestoreadminModule.FirestoreAdminClient({ + credentials: {client_email: 'bogus', private_key: 'bogus'}, + projectId: 'bogus', + }); + client.initialize(); + const expectedResponse = generateSampleMessage( + new operationsProtos.google.longrunning.Operation() + ); + expectedResponse.name = 'test'; + expectedResponse.response = {type_url: 'url', value: Buffer.from('')}; + expectedResponse.metadata = {type_url: 'url', value: Buffer.from('')}; + + client.operationsClient.getOperation = stubSimpleCall(expectedResponse); + const decodedOperation = await client.checkDeleteDatabaseProgress( + expectedResponse.name + ); + assert.deepStrictEqual(decodedOperation.name, expectedResponse.name); + assert(decodedOperation.metadata); + assert((client.operationsClient.getOperation as SinonStub).getCall(0)); + }); + + it('invokes checkDeleteDatabaseProgress with error', async () => { + const client = new firestoreadminModule.FirestoreAdminClient({ + credentials: {client_email: 'bogus', private_key: 'bogus'}, + projectId: 'bogus', + }); + client.initialize(); + const expectedError = new Error('expected'); + + client.operationsClient.getOperation = stubSimpleCall( + undefined, + expectedError + ); + await assert.rejects( + client.checkDeleteDatabaseProgress(''), + expectedError + ); + assert((client.operationsClient.getOperation as SinonStub).getCall(0)); + }); + }); + describe('listIndexes', () => { it('invokes listIndexes without error', async () => { const client = new firestoreadminModule.FirestoreAdminClient({ diff --git a/dev/test/gapic_firestore_v1.ts b/dev/test/gapic_firestore_v1.ts index d7d34d909..7a1e7e1c5 100644 --- a/dev/test/gapic_firestore_v1.ts +++ b/dev/test/gapic_firestore_v1.ts @@ -1,4 +1,4 @@ -// Copyright 2023 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -1420,7 +1420,9 @@ describe('v1.FirestoreClient', () => { request.database = defaultValue1; const expectedError = new Error('The client has already been closed.'); client.close(); - const stream = client.batchGetDocuments(request); + const stream = client.batchGetDocuments(request, { + retryRequestOptions: {noResponseRetries: 0}, + }); const promise = new Promise((resolve, reject) => { stream.on( 'data', @@ -1539,7 +1541,9 @@ describe('v1.FirestoreClient', () => { request.parent = defaultValue1; const expectedError = new Error('The client has already been closed.'); client.close(); - const stream = client.runQuery(request); + const stream = client.runQuery(request, { + retryRequestOptions: {noResponseRetries: 0}, + }); const promise = new Promise((resolve, reject) => { stream.on( 'data', @@ -1663,7 +1667,9 @@ describe('v1.FirestoreClient', () => { request.parent = defaultValue1; const expectedError = new Error('The client has already been closed.'); client.close(); - const stream = client.runAggregationQuery(request); + const stream = client.runAggregationQuery(request, { + retryRequestOptions: {noResponseRetries: 0}, + }); const promise = new Promise((resolve, reject) => { stream.on( 'data', diff --git a/dev/test/gapic_firestore_v1beta1.ts b/dev/test/gapic_firestore_v1beta1.ts index 89d3a2257..031ff228c 100644 --- a/dev/test/gapic_firestore_v1beta1.ts +++ b/dev/test/gapic_firestore_v1beta1.ts @@ -1,4 +1,4 @@ -// Copyright 2023 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -1426,7 +1426,9 @@ describe('v1beta1.FirestoreClient', () => { request.database = defaultValue1; const expectedError = new Error('The client has already been closed.'); client.close(); - const stream = client.batchGetDocuments(request); + const stream = client.batchGetDocuments(request, { + retryRequestOptions: {noResponseRetries: 0}, + }); const promise = new Promise((resolve, reject) => { stream.on( 'data', @@ -1547,7 +1549,9 @@ describe('v1beta1.FirestoreClient', () => { request.parent = defaultValue1; const expectedError = new Error('The client has already been closed.'); client.close(); - const stream = client.runQuery(request); + const stream = client.runQuery(request, { + retryRequestOptions: {noResponseRetries: 0}, + }); const promise = new Promise((resolve, reject) => { stream.on( 'data', diff --git a/owlbot.py b/owlbot.py index f6c1b2c9a..c1d2a1819 100644 --- a/owlbot.py +++ b/owlbot.py @@ -181,7 +181,7 @@ os.system('rm -rf dev/system-test/fixtures dev/system-test/install.ts') os.chdir("dev") - node.compile_protos_hermetic() + node.compile_protos() os.chdir("protos") os.unlink('protos.js') os.unlink('protos.d.ts') @@ -223,7 +223,11 @@ s.copy(templates, excludes=[".eslintrc.json", ".kokoro/**/*", ".github/CODEOWNERS"]) -node.fix() # fix formatting - # Remove generated samples from veneer library: shell.run(('rm', '-rf', 'dev/samples/generated'), hide_output = False) + +shell.run(('node', 'scripts/license.js', 'dev/protos'), hide_output = False) + +node.fix() # fix formatting + + diff --git a/scripts/license.js b/scripts/license.js index d6f913429..96d2fdb6a 100755 --- a/scripts/license.js +++ b/scripts/license.js @@ -15,9 +15,10 @@ */ const fs = require('fs'); +const path = require('path'); const LICENSE_HEADER = `/*! - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +34,28 @@ const LICENSE_HEADER = `/*! */ `; -for (const file of process.argv.slice(2)) { - const content = fs.readFileSync(file, 'utf-8'); - fs.writeFileSync(file, `${LICENSE_HEADER}\n${content.trim()}\n`); +function addLicenses(dirNameReads) { + for (const dirNameRead of dirNameReads) { + iterateThroughFiles(dirNameRead); + } } +function iterateThroughFiles(dirNameRead) { + console.log(dirNameRead); + if (fs.existsSync(path.join(dirNameRead))) { + const files = fs.readdirSync(dirNameRead); + files.forEach(file => { + const fileName = file.toString(); + const readName = path.join(dirNameRead, fileName); + if (fs.statSync(readName).isDirectory()) { + iterateThroughFiles(readName); + } else { + const content = fs.readFileSync(readName, 'utf-8'); + if (!content.includes('Copyright') && !fileName.endsWith('.json')) { + fs.writeFileSync(readName, `${LICENSE_HEADER}\n${content.trim()}\n`); + } + } + }); + } +} + +addLicenses(process.argv.slice(2)); From a29d34a58bf57363df48ad5cc1ef206256215cc9 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 10 Jan 2024 16:43:59 -0500 Subject: [PATCH 16/30] docs: tweak Count API docs (#1974) --- dev/src/reference.ts | 11 ++++++----- types/firestore.d.ts | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/dev/src/reference.ts b/dev/src/reference.ts index 012634c8f..dd9af2e58 100644 --- a/dev/src/reference.ts +++ b/dev/src/reference.ts @@ -1839,8 +1839,8 @@ export class Query< * * Using the returned query to count the documents is efficient because only * the final count, not the documents' data, is downloaded. The returned - * query can even count the documents if the result set would be - * prohibitively large to download entirely (e.g. thousands of documents). + * query can count the documents in cases where the result set is + * prohibitively large to download entirely (thousands of documents). * * @return a query that counts the documents in the result set of this * query. The count can be retrieved from `snapshot.data().count`, where @@ -1861,13 +1861,14 @@ export class Query< * Returns a query that can perform the given aggregations. * * The returned query, when executed, calculates the specified aggregations - * over the documents in the result set of this query, without actually + * over the documents in the result set of this query without actually * downloading the documents. * * Using the returned query to perform aggregations is efficient because only * the final aggregation values, not the documents' data, is downloaded. The - * returned query can even perform aggregations of the documents if the result set - * would be prohibitively large to download entirely (e.g. thousands of documents). + * returned query can perform aggregations of the documents count the + * documents in cases where the result set is prohibitively large to download + * entirely (thousands of documents). * * @param aggregateSpec An `AggregateSpec` object that specifies the aggregates * to perform over the result set. The AggregateSpec specifies aliases for each diff --git a/types/firestore.d.ts b/types/firestore.d.ts index 8d19fd5cf..9f4c2928c 100644 --- a/types/firestore.d.ts +++ b/types/firestore.d.ts @@ -1910,8 +1910,8 @@ declare namespace FirebaseFirestore { * * Using the returned query to count the documents is efficient because only * the final count, not the documents' data, is downloaded. The returned - * query can even count the documents if the result set would be - * prohibitively large to download entirely (e.g. thousands of documents). + * query can count the documents in cases where the result set is + * prohibitively large to download entirely (thousands of documents). * * @return a query that counts the documents in the result set of this * query. The count can be retrieved from `snapshot.data().count`, where @@ -1928,13 +1928,14 @@ declare namespace FirebaseFirestore { * Returns a query that can perform the given aggregations. * * The returned query, when executed, calculates the specified aggregations - * over the documents in the result set of this query, without actually + * over the documents in the result set of this query without actually * downloading the documents. * * Using the returned query to perform aggregations is efficient because only * the final aggregation values, not the documents' data, is downloaded. The - * returned query can even perform aggregations of the documents if the result set - * would be prohibitively large to download entirely (e.g. thousands of documents). + * returned query can perform aggregations of the documents in cases where + * the result set is prohibitively large to download entirely (thousands of + * documents). * * @param aggregateSpec An `AggregateSpec` object that specifies the aggregates * to perform over the result set. The AggregateSpec specifies aliases for each From 7578c69fc4d973b5d4f8a3f7ffae5fa82318be04 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Thu, 11 Jan 2024 00:24:21 +0100 Subject: [PATCH 17/30] chore(deps): update dependency c8 to v9 (#1969) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 04fcec5b1..c4637339b 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "@types/node": "^20.0.0", "@types/sinon": "^17.0.0", "@types/through2": "^2.0.34", - "c8": "^8.0.0", + "c8": "^9.0.0", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "codecov": "^3.6.1", From 22ac90f0a043e493f89834604bd249f69c5dc036 Mon Sep 17 00:00:00 2001 From: Mark Duckworth <1124037+MarkDuckworth@users.noreply.github.com> Date: Fri, 12 Jan 2024 13:45:08 -0700 Subject: [PATCH 18/30] ci: Trampoline image to Node 14 (#1975) --- .kokoro/common.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.kokoro/common.cfg b/.kokoro/common.cfg index 1a8cb95e4..fe78759b7 100644 --- a/.kokoro/common.cfg +++ b/.kokoro/common.cfg @@ -16,7 +16,7 @@ build_file: "nodejs-firestore/.kokoro/trampoline.sh" # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/node:10-user" + value: "gcr.io/cloud-devrel-kokoro-resources/node:14-user" } env_vars: { key: "TRAMPOLINE_BUILD_FILE" From d142413a11c42f3a50e8450f6f8847f3c22c4f93 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 06:51:43 -0700 Subject: [PATCH 19/30] docs: Fix formatting due to unclosed backtick (#1977) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: Fix formatting due to unclosed backtick PiperOrigin-RevId: 597942027 Source-Link: https://github.com/googleapis/googleapis/commit/6c31cc0071ccca8af83964afc4178ede73d6cbd3 Source-Link: https://github.com/googleapis/googleapis-gen/commit/4a99b8dc90136d3f29ccf40f7d9dea3b6f26dbb8 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNGE5OWI4ZGM5MDEzNmQzZjI5Y2NmNDBmN2Q5ZGVhM2I2ZjI2ZGJiOCJ9 * πŸ¦‰ Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot --- dev/protos/google/firestore/admin/v1/firestore_admin.proto | 2 +- dev/protos/google/protobuf/descriptor.proto | 1 + dev/src/v1/firestore_admin_client.ts | 4 ++-- dev/test/gapic_firestore_admin_v1.ts | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/dev/protos/google/firestore/admin/v1/firestore_admin.proto b/dev/protos/google/firestore/admin/v1/firestore_admin.proto index 53f134a1b..07cc764b5 100644 --- a/dev/protos/google/firestore/admin/v1/firestore_admin.proto +++ b/dev/protos/google/firestore/admin/v1/firestore_admin.proto @@ -164,7 +164,7 @@ service FirestoreAdmin { // only supports listing fields that have been explicitly overridden. To issue // this query, call // [FirestoreAdmin.ListFields][google.firestore.admin.v1.FirestoreAdmin.ListFields] - // with the filter set to `indexConfig.usesAncestorConfig:false or + // with the filter set to `indexConfig.usesAncestorConfig:false` or // `ttlConfig:*`. rpc ListFields(ListFieldsRequest) returns (ListFieldsResponse) { option (google.api.http) = { diff --git a/dev/protos/google/protobuf/descriptor.proto b/dev/protos/google/protobuf/descriptor.proto index aaa542452..1dbeaa0c5 100644 --- a/dev/protos/google/protobuf/descriptor.proto +++ b/dev/protos/google/protobuf/descriptor.proto @@ -73,6 +73,7 @@ enum Edition { // should not be depended on, but they will always be time-ordered for easy // comparison. EDITION_2023 = 1000; + EDITION_2024 = 1001; // Placeholder editions for testing feature resolution. These should not be // used or relyed on outside of tests. diff --git a/dev/src/v1/firestore_admin_client.ts b/dev/src/v1/firestore_admin_client.ts index 657ab2484..bf764ca29 100644 --- a/dev/src/v1/firestore_admin_client.ts +++ b/dev/src/v1/firestore_admin_client.ts @@ -1,4 +1,4 @@ -// Copyright 2023 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -2193,7 +2193,7 @@ export class FirestoreAdminClient { * only supports listing fields that have been explicitly overridden. To issue * this query, call * {@link protos.google.firestore.admin.v1.FirestoreAdmin.ListFields|FirestoreAdmin.ListFields} - * with the filter set to `indexConfig.usesAncestorConfig:false or + * with the filter set to `indexConfig.usesAncestorConfig:false` or * `ttlConfig:*`. * * @param {Object} request diff --git a/dev/test/gapic_firestore_admin_v1.ts b/dev/test/gapic_firestore_admin_v1.ts index 2aa2dc3be..25d6c28f2 100644 --- a/dev/test/gapic_firestore_admin_v1.ts +++ b/dev/test/gapic_firestore_admin_v1.ts @@ -1,4 +1,4 @@ -// Copyright 2023 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From 283e9c474eacb2af019f29cf57eb5dbc56a48cd7 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 14:18:17 +0000 Subject: [PATCH 20/30] Revert "chore: remove compile protos from post processor" (#1951) Revert "chore: remove compile protos from post processor (#1899)" This reverts commit 73248044166b6ba2dd102862f8cd2c4829e868db. Source-Link: https://togithub.com/googleapis/synthtool/commit/2f0fea69b6ae56dba965ea8817750793862b3b2d Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-nodejs:latest@sha256:37f2f91dea317c75ebf4e19880aa8f10b2228b8d07859c0f384dbcf660735ba2 --- .github/.OwlBot.lock.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 638efabfb..ed31848f6 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -13,5 +13,5 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-nodejs:latest - digest: sha256:e92044720ab3cb6984a70b0c6001081204375959ba3599ef6c42dd99a7783a67 -# created: 2023-11-10T00:24:05.581078808Z + digest: sha256:37f2f91dea317c75ebf4e19880aa8f10b2228b8d07859c0f384dbcf660735ba2 +# created: 2023-11-28T20:22:21.420619474Z From 1baa84d68264abe48b0ae498045ee8cc01fa26d5 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:18:50 -0700 Subject: [PATCH 21/30] chore(main): release 7.2.0 (#1963) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- CHANGELOG.md | 13 +++++++++++++ package.json | 2 +- samples/package.json | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 854fe4ac9..4c267ad85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,19 @@ [1]: https://www.npmjs.com/package/@google-cloud/firestore?activeTab=versions +## [7.2.0](https://github.com/googleapis/nodejs-firestore/compare/v7.1.1...v7.2.0) (2024-01-16) + + +### Features + +* Add new types QueryMode, QueryPlan, ResultSetStats ([#1911](https://github.com/googleapis/nodejs-firestore/issues/1911)) ([8f77b68](https://github.com/googleapis/nodejs-firestore/commit/8f77b68bf70b04a66e982067aa246de0596e4ea2)) +* Expose databaseId and projectId getter on Firestore class ([#1937](https://github.com/googleapis/nodejs-firestore/issues/1937)) ([d9c867f](https://github.com/googleapis/nodejs-firestore/commit/d9c867fc5048748e5af378fd8465303224668781)), closes [#1936](https://github.com/googleapis/nodejs-firestore/issues/1936) + + +### Bug Fixes + +* Make transaction rollback best effort. ([#1967](https://github.com/googleapis/nodejs-firestore/issues/1967)) ([1d76546](https://github.com/googleapis/nodejs-firestore/commit/1d76546a781527ef5b9085fc9f189c024355ae9f)) + ## [7.1.1](https://github.com/googleapis/nodejs-firestore/compare/v7.1.0...v7.1.1) (2023-11-20) diff --git a/package.json b/package.json index c4637339b..f84a3ee14 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@google-cloud/firestore", "description": "Firestore Client Library for Node.js", - "version": "7.1.1", + "version": "7.2.0", "license": "Apache-2.0", "author": "Google Inc.", "engines": { diff --git a/samples/package.json b/samples/package.json index a0f594283..34674f273 100644 --- a/samples/package.json +++ b/samples/package.json @@ -11,7 +11,7 @@ "test": "mocha --timeout 600000" }, "dependencies": { - "@google-cloud/firestore": "^7.1.1" + "@google-cloud/firestore": "^7.2.0" }, "devDependencies": { "chai": "^4.2.0", From 62926d2092d3214981663ec8e429b0dcce341dd4 Mon Sep 17 00:00:00 2001 From: Mark Duckworth <1124037+MarkDuckworth@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:26:15 -0700 Subject: [PATCH 22/30] ci: update trampoline image to Node 14 in docs.cfg (#1980) --- .kokoro/release/docs.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.kokoro/release/docs.cfg b/.kokoro/release/docs.cfg index 0b3df1a68..373997814 100644 --- a/.kokoro/release/docs.cfg +++ b/.kokoro/release/docs.cfg @@ -11,7 +11,7 @@ before_action { # doc publications use a Python image. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/node:10-user" + value: "gcr.io/cloud-devrel-kokoro-resources/node:14-user" } # Download trampoline resources. From 2b839b12526acb9ae2a5b3917126a5b0a68e386e Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Mon, 22 Jan 2024 11:43:58 -0500 Subject: [PATCH 23/30] test: update conformance tests to commit 3264aa6a --- .../query-cursor-startafter-docsnap.json | 59 +++++++++++++++++++ .../query-invalid-operator.json | 4 +- .../set-arrayunion-merge.json | 48 +++++++++++++++ 3 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 dev/conformance/conformance-tests/query-cursor-startafter-docsnap.json create mode 100644 dev/conformance/conformance-tests/set-arrayunion-merge.json diff --git a/dev/conformance/conformance-tests/query-cursor-startafter-docsnap.json b/dev/conformance/conformance-tests/query-cursor-startafter-docsnap.json new file mode 100644 index 000000000..9cc49e3a2 --- /dev/null +++ b/dev/conformance/conformance-tests/query-cursor-startafter-docsnap.json @@ -0,0 +1,59 @@ +{ + "tests": [ + { + "description": "query: StartAfter with document snapshot", + "comment": "Cursor methods are allowed to use document snapshots with start_after. It should result in the document's data in the query.", + "query": { + "collPath": "projects/projectID/databases/(default)/documents/C", + "clauses": [ + { + "orderBy": { + "path": { + "field": [ + "a" + ] + }, + "direction": "asc" + } + }, + { + "startAt": { + "jsonValues": [ + "{\"a\": \"b\"}" + ] + } + } + ], + "query": { + "from": [ + { + "collectionId": "C" + } + ], + "orderBy": [ + { + "field": { + "fieldPath": "a" + }, + "direction": "ASCENDING" + } + ], + "startAt": { + "values": [ + { + "mapValue": { + "fields": { + "a": { + "stringValue": "b" + } + } + } + } + ], + "before": true + } + } + } + } + ] +} diff --git a/dev/conformance/conformance-tests/query-invalid-operator.json b/dev/conformance/conformance-tests/query-invalid-operator.json index 70f28424d..0acfeae67 100644 --- a/dev/conformance/conformance-tests/query-invalid-operator.json +++ b/dev/conformance/conformance-tests/query-invalid-operator.json @@ -2,7 +2,7 @@ "tests": [ { "description": "query: invalid operator in Where clause", - "comment": "The ! operator is not supported.", + "comment": "The |~| operator is not supported.", "query": { "collPath": "projects/projectID/databases/(default)/documents/C", "clauses": [ @@ -13,7 +13,7 @@ "a" ] }, - "op": "!", + "op": "|~|", "jsonValue": "4" } } diff --git a/dev/conformance/conformance-tests/set-arrayunion-merge.json b/dev/conformance/conformance-tests/set-arrayunion-merge.json new file mode 100644 index 000000000..46c2fbfb3 --- /dev/null +++ b/dev/conformance/conformance-tests/set-arrayunion-merge.json @@ -0,0 +1,48 @@ +{ + "tests": [ + { + "description": "set: merge ArrayUnion field", + "comment": "An ArrayUnion value can occur at any depth. In this case,\nthe transform applies to the field path \"b.c\". \"a\" is left alone and remains in the object.", + "set": { + "option": { + "all": true + }, + "docRefPath": "projects/projectID/databases/(default)/documents/C/d", + "jsonData": "{\"a\": 1, \"b\": {\"c\": [\"ArrayUnion\", \"foo\", \"bar\"]}}", + "request": { + "database": "projects/projectID/databases/(default)", + "writes": [ + { + "update": { + "name": "projects/projectID/databases/(default)/documents/C/d", + "fields": { + "a": { + "integerValue": "1" + } + } + }, + "updateMask": { + "fieldPaths": ["a"] + }, + "updateTransforms": [ + { + "fieldPath": "b.c", + "appendMissingElements": { + "values": [ + { + "stringValue": "foo" + }, + { + "stringValue": "bar" + } + ] + } + } + ] + } + ] + } + } + } + ] +} From ecfb8fe29af482b3e7786b8ed0b5690693f0ec57 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Mon, 22 Jan 2024 18:00:26 +0100 Subject: [PATCH 24/30] chore(deps): update dependency gapic-tools to ^0.3.0 (#1981) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [gapic-tools](https://togithub.com/googleapis/gax-nodejs) ([source](https://togithub.com/googleapis/gax-nodejs/tree/HEAD/gapic-tools)) | [`^0.2.0` -> `^0.3.0`](https://renovatebot.com/diffs/npm/gapic-tools/0.2.0/0.3.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/gapic-tools/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/gapic-tools/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/gapic-tools/0.2.0/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/gapic-tools/0.2.0/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
googleapis/gax-nodejs (gapic-tools) ### [`v0.3.0`](https://togithub.com/googleapis/gax-nodejs/releases/tag/gapic-tools-v0.3.0): gapic-tools: v0.3.0 [Compare Source](https://togithub.com/googleapis/gax-nodejs/compare/gapic-tools-v0.2.0...gapic-tools-v0.3.0)
0.3.0 #### [0.3.0](https://togithub.com/googleapis/gax-nodejs/compare/v0.2.0...v0.3.0) (2024-01-16) ##### Features - add ESM tools in gax ([#​1459](https://togithub.com/googleapis/gax-nodejs/issues/1459)) ([0fb1cf9](https://togithub.com/googleapis/gax-nodejs/commit/0fb1cf9acd32dc1ae03a33279eca9449a7d3fca7)) ##### Bug Fixes - **deps:** update dependency google-proto-files to v4 ([#​1490](https://togithub.com/googleapis/gax-nodejs/issues/1490)) ([4748c9f](https://togithub.com/googleapis/gax-nodejs/commit/4748c9fc3a8cfe31e5abb3e35a6ee0d9a6f0e560)) - **deps:** update dependency protobufjs-cli to v1.1.2 ([#​1495](https://togithub.com/googleapis/gax-nodejs/issues/1495)) ([762591e](https://togithub.com/googleapis/gax-nodejs/commit/762591ed28801e5311ab737b04185781a41752e6)) - make gapic-tools depend on gax-nodejs ([#​1480](https://togithub.com/googleapis/gax-nodejs/issues/1480)) ([d0f410d](https://togithub.com/googleapis/gax-nodejs/commit/d0f410d2e08f393f2661c8c92568a0b518fddf99)) - release new version of gapic-tools ([#​1483](https://togithub.com/googleapis/gax-nodejs/issues/1483)) ([e4f5482](https://togithub.com/googleapis/gax-nodejs/commit/e4f548254bfce3daa3b02ae81764bb3394fc4f23)) - update google-gax and google-proto-files ([#​1533](https://togithub.com/googleapis/gax-nodejs/issues/1533)) ([4897bc6](https://togithub.com/googleapis/gax-nodejs/commit/4897bc60db5e22427e533805d528f434e0cc8e40))
--- ### Configuration πŸ“… **Schedule**: Branch creation - "after 9am and before 3pm" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. β™» **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. πŸ”• **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/googleapis/nodejs-firestore). --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f84a3ee14..2776b1019 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "codecov": "^3.6.1", "duplexify": "^4.0.0", "extend": "^3.0.2", - "gapic-tools": "^0.2.0", + "gapic-tools": "^0.3.0", "gts": "^5.0.1", "jsdoc": "^4.0.0", "jsdoc-fresh": "^3.0.0", From 133f4da892b453a4c4fd1324154eed907ad4663a Mon Sep 17 00:00:00 2001 From: Abhishek Mathur Date: Tue, 23 Jan 2024 00:53:41 +0530 Subject: [PATCH 25/30] fix: Remove incorrect,unreachable and unused code (#1983) * Prevent Serialization of firestore The toJSON function spelling name is incorrect therefore this method is not triggered when using JSON.stringify on firestore objects * Remove unused code This code is never invoked, the spelling of toJSON is also wrong ( it is toJson here) Also the Firestore Class toJSON method works well now for protecting the private keys of firestore instance --------- Co-authored-by: Mila <107142260+milaGGL@users.noreply.github.com> --- dev/src/index.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/dev/src/index.ts b/dev/src/index.ts index a50d10f46..3932b2137 100644 --- a/dev/src/index.ts +++ b/dev/src/index.ts @@ -745,13 +745,6 @@ export class Firestore implements firestore.Firestore { } this._settings = settings; - this._settings.toJson = function () { - const temp = Object.assign({}, this); - if (temp.credentials) { - temp.credentials = {private_key: '***', client_email: '***'}; - } - return temp; - }; this._serializer = new Serializer(this); } From cd6bc8690e1c9c560c9a386c127c43af8fa349b7 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Tue, 23 Jan 2024 18:02:14 -0500 Subject: [PATCH 26/30] docs: FirestoreDataConverter doc and test improvements (#1988) --- dev/test/types.ts | 132 +++++++++++++++++++++++++++++++------------ types/firestore.d.ts | 25 +++++++- 2 files changed, 119 insertions(+), 38 deletions(-) diff --git a/dev/test/types.ts b/dev/test/types.ts index 2240cdaf9..98296980a 100644 --- a/dev/test/types.ts +++ b/dev/test/types.ts @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {expect} from 'chai'; import { QueryDocumentSnapshot, DocumentReference, @@ -22,6 +21,7 @@ import { FirestoreDataConverter, SetOptions, } from '@google-cloud/firestore'; + describe('FirestoreTypeConverter', () => { it('converter has the minimal typing information', () => { interface MyModelType { @@ -39,17 +39,13 @@ describe('FirestoreTypeConverter', () => { }; }, }; - // The intent of the function below is to test TypeScript compile and not execute. - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async function _(docRef: DocumentReference): Promise { + neverCall>(async docRef => { const newDocRef = docRef.withConverter(converter); await newDocRef.set({stringProperty: 'foo', numberProperty: 42}); await newDocRef.update({a: 'newFoo', b: 43}); const snapshot = await newDocRef.get(); - const data: MyModelType = snapshot.data()!; - expect(data.stringProperty).to.equal('newFoo'); - expect(data.numberProperty).to.equal(43); - } + return snapshot.data()!; + }); }); it('converter has the minimal typing information plus return types', () => { @@ -68,17 +64,13 @@ describe('FirestoreTypeConverter', () => { }; }, }; - // The intent of the function below is to test TypeScript compile and not execute. - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async function _(docRef: DocumentReference): Promise { + neverCall>(async docRef => { const newDocRef = docRef.withConverter(converter); await newDocRef.set({stringProperty: 'foo', numberProperty: 42}); await newDocRef.update({a: 'newFoo', b: 43}); const snapshot = await newDocRef.get(); - const data: MyModelType = snapshot.data()!; - expect(data.stringProperty).to.equal('newFoo'); - expect(data.numberProperty).to.equal(43); - } + return snapshot.data()!; + }); }); it("has the additional 'merge' version of toFirestore()", () => { @@ -113,17 +105,13 @@ describe('FirestoreTypeConverter', () => { }; }, }; - // The intent of the function below is to test TypeScript compile and not execute. - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async function _(docRef: DocumentReference): Promise { + neverCall>(async docRef => { const newDocRef = docRef.withConverter(converter); await newDocRef.set({stringProperty: 'foo', numberProperty: 42}); await newDocRef.update({a: 'newFoo', b: 43}); const snapshot = await newDocRef.get(); - const data: MyModelType = snapshot.data()!; - expect(data.stringProperty).to.equal('newFoo'); - expect(data.numberProperty).to.equal(43); - } + return snapshot.data()!; + }); }); it('converter is explicitly typed as FirestoreDataConverter', () => { @@ -142,17 +130,13 @@ describe('FirestoreTypeConverter', () => { }; }, }; - // The intent of the function below is to test TypeScript compile and not execute. - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async function _(docRef: DocumentReference): Promise { + neverCall>(async docRef => { const newDocRef = docRef.withConverter(converter); await newDocRef.set({stringProperty: 'foo', numberProperty: 42}); await newDocRef.update({a: 'newFoo', b: 43}); const snapshot = await newDocRef.get(); - const data: MyModelType = snapshot.data()!; - expect(data.stringProperty).to.equal('newFoo'); - expect(data.numberProperty).to.equal(43); - } + return snapshot.data()!; + }); }); it('converter is explicitly typed as FirestoreDataConverter', () => { @@ -175,16 +159,92 @@ describe('FirestoreTypeConverter', () => { }; }, }; - // The intent of the function below is to test TypeScript compile and not execute. - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async function _(docRef: DocumentReference): Promise { + neverCall>(async docRef => { const newDocRef = docRef.withConverter(converter); await newDocRef.set({stringProperty: 'foo', numberProperty: 42}); await newDocRef.update({a: 'newFoo', b: 43}); const snapshot = await newDocRef.get(); - const data: MyModelType = snapshot.data()!; - expect(data.stringProperty).to.equal('newFoo'); - expect(data.numberProperty).to.equal(43); - } + return snapshot.data()!; + }); }); + + it('DocumentReference.set() fails to compile if AppModelType argument is missing properties', () => + neverCall(async docRef => { + const converter = fakeConverter<{foo: string}, {}>(); + const docRefWithConverter = docRef.withConverter(converter); + // @ts-expect-error The `foo` property declared in AppModelType is missing. + await docRefWithConverter.set({}); + })); + + it('DocumentReference.set() fails to compile if AppModelType argument contains undeclared properties', () => + neverCall(async docRef => { + const converter = fakeConverter<{foo: string}, {bar: number}>(); + const docRefWithConverter = docRef.withConverter(converter); + // @ts-expect-error The `bar` property is not declared in AppModelType. + await docRefWithConverter.set({foo: 'foo', bar: 42}); + })); + + it('DocumentReference.set() fails to compile if AppModelType argument contains a property with an incorrect type', () => + neverCall(async docRef => { + const converter = fakeConverter<{foo: string}, {foo: number}>(); + const docRefWithConverter = docRef.withConverter(converter); + // @ts-expect-error The `foo` property is declared as `string` in + // AppModelType, but a `number` is specified. + await docRefWithConverter.set({foo: 42}); + })); + + it('DocumentReference.update() successfully compiles even if DbModelType argument is missing properties', () => + neverCall(async docRef => { + const converter = fakeConverter<{foo: string}, {bar: number}>(); + const docRefWithConverter = docRef.withConverter(converter); + await docRefWithConverter.update({}); + })); + + it('DocumentReference.update() fails to compile if DbModelType argument contains undeclared properties', () => + neverCall(async docRef => { + const converter = fakeConverter<{foo: string}, {bar: number}>(); + const docRefWithConverter = docRef.withConverter(converter); + // @ts-expect-error The `foo` property is not declared in DbModelType. + await docRefWithConverter.update({foo: 'foo', bar: 42}); + })); + + it('DocumentReference.update() fails to compile if DbModelType argument contains a property with an incorrect type', () => + neverCall(async docRef => { + const converter = fakeConverter<{foo: string}, {foo: number}>(); + const docRefWithConverter = docRef.withConverter(converter); + // @ts-expect-error The `foo` property is declared as `number` in + // DbModelType, but a `string` is specified. + await docRefWithConverter.update({foo: 'foo'}); + })); + + it('DocumentReference.get() returns AppModelType', () => + neverCall>(async docRef => { + const converter = fakeConverter<{foo: string}, {bar: number}>(); + const docRefWithConverter = docRef.withConverter(converter); + const snapshot = await docRefWithConverter.get(); + return snapshot.data()!; + })); }); + +/** + * Does nothing; however, this function can be useful in tests that only check + * the compile-time behavior of the TypeScript compiler. For example, a test + * that ensures that a certain statement successfully compiles could pass the + * code block to this function to exercise the compiler but the code will not + * actually be executed at runtime. + */ +function neverCall(_: (docRef: DocumentReference) => T): void { + _; // Trick eslint into thinking that `_` is used. +} + +/** + * Does nothing; this function does not actually exist but is merely _declared_ + * to exist. This facilitates creating variables typed as FirestoreDataConverter + * with the given type parameters at compile time. This can be useful in tests + * that only check compile-time behavior of the TypeScript compiler but don't + * actually get executed at runtime. + */ +declare function fakeConverter< + AppModelType, + DbModelType extends DocumentData, +>(): FirestoreDataConverter; diff --git a/types/firestore.d.ts b/types/firestore.d.ts index 9f4c2928c..eaa0d85bd 100644 --- a/types/firestore.d.ts +++ b/types/firestore.d.ts @@ -150,6 +150,23 @@ declare namespace FirebaseFirestore { * Using the converter allows you to specify generic type arguments when * storing and retrieving objects from Firestore. * + * In this context, an "AppModel" is a class that is used in an application to + * package together related information and functionality. Such a class could, + * for example, have properties with complex, nested data types, properties + * used for memoization, properties of types not supported by Firestore (such + * as `symbol` and `bigint`), and helper functions that perform compound + * operations. Such classes are not suitable and/or possible to store into a + * Firestore database. Instead, instances of such classes need to be converted + * to "plain old JavaScript objects" (POJOs) with exclusively primitive + * properties, potentially nested inside other POJOs or arrays of POJOs. In + * this context, this type is referred to as the "DbModel" and would be an + * object suitable for persisting into Firestore. For convenience, + * applications can implement `FirestoreDataConverter` and register the + * converter with Firestore objects, such as `DocumentReference` or `Query`, + * to automatically convert `AppModel` to `DbModel` when storing into + * Firestore, and convert `DbModel` to `AppModel` when retrieving from + * Firestore. + * * @example * * Simple Example @@ -353,8 +370,12 @@ declare namespace FirebaseFirestore { * `snapshot.data()`. * * Generally, the data returned from `snapshot.data()` can be cast to - * `DbModelType`; however, this is not guaranteed as writes to the database - * may have occurred without a type converter enforcing this specific layout. + * `DbModelType`; however, this is not guaranteed because Firestore does not + * enforce a schema on the database. For example, writes from a previous + * version of the application or writes from another client that did not use + * a type converter could have written data with different properties and/or + * property types. The implementation will need to choose whether to + * gracefully recover from non-conforming data or throw an error. */ fromFirestore(snapshot: QueryDocumentSnapshot): AppModelType; } From 99d60a6f87b70c942ac2bd9464cc6d64323f9dfb Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Tue, 23 Jan 2024 19:03:43 -0500 Subject: [PATCH 27/30] fix: Allow an explicit MustExist precondition for update (#1985) --- ....json => update-exists-false-precond.json} | 6 +-- .../update-exists-true-precond.json | 38 +++++++++++++++ ...=> update-paths-exists-false-precond.json} | 6 +-- .../update-paths-exists-true-precond.json | 47 +++++++++++++++++++ dev/src/write-batch.ts | 24 +++++----- dev/test/document.ts | 23 ++++++++- 6 files changed, 126 insertions(+), 18 deletions(-) rename dev/conformance/conformance-tests/{update-exists-precond.json => update-exists-false-precond.json} (70%) create mode 100644 dev/conformance/conformance-tests/update-exists-true-precond.json rename dev/conformance/conformance-tests/{update-paths-exists-precond.json => update-paths-exists-false-precond.json} (76%) create mode 100644 dev/conformance/conformance-tests/update-paths-exists-true-precond.json diff --git a/dev/conformance/conformance-tests/update-exists-precond.json b/dev/conformance/conformance-tests/update-exists-false-precond.json similarity index 70% rename from dev/conformance/conformance-tests/update-exists-precond.json rename to dev/conformance/conformance-tests/update-exists-false-precond.json index bdbe274b4..031fd1af3 100644 --- a/dev/conformance/conformance-tests/update-exists-precond.json +++ b/dev/conformance/conformance-tests/update-exists-false-precond.json @@ -1,12 +1,12 @@ { "tests": [ { - "description": "update: Exists precondition is invalid", - "comment": "The Update method does not support an explicit exists precondition.", + "description": "update: Exists=false precondition is invalid", + "comment": "The Update method does not support an explicit exists=false precondition.", "update": { "docRefPath": "projects/projectID/databases/(default)/documents/C/d", "precondition": { - "exists": true + "exists": false }, "jsonData": "{\"a\": 1}", "isError": true diff --git a/dev/conformance/conformance-tests/update-exists-true-precond.json b/dev/conformance/conformance-tests/update-exists-true-precond.json new file mode 100644 index 000000000..f1aa800e4 --- /dev/null +++ b/dev/conformance/conformance-tests/update-exists-true-precond.json @@ -0,0 +1,38 @@ +{ + "tests": [ + { + "description": "update: Exists=true precondition is valid", + "comment": "The Update method supports an explicit exists=true precondition.", + "update": { + "docRefPath": "projects/projectID/databases/(default)/documents/C/d", + "precondition": { + "exists": true + }, + "jsonData": "{\"a\": 1}", + "request": { + "database": "projects/projectID/databases/(default)", + "writes": [ + { + "update": { + "name": "projects/projectID/databases/(default)/documents/C/d", + "fields": { + "a": { + "integerValue": "1" + } + } + }, + "updateMask": { + "fieldPaths": [ + "a" + ] + }, + "currentDocument": { + "exists": true + } + } + ] + } + } + } + ] +} diff --git a/dev/conformance/conformance-tests/update-paths-exists-precond.json b/dev/conformance/conformance-tests/update-paths-exists-false-precond.json similarity index 76% rename from dev/conformance/conformance-tests/update-paths-exists-precond.json rename to dev/conformance/conformance-tests/update-paths-exists-false-precond.json index d495db033..f122b5307 100644 --- a/dev/conformance/conformance-tests/update-paths-exists-precond.json +++ b/dev/conformance/conformance-tests/update-paths-exists-false-precond.json @@ -1,12 +1,12 @@ { "tests": [ { - "description": "update-paths: Exists precondition is invalid", - "comment": "The Update method does not support an explicit exists precondition.", + "description": "update-paths: Exists=false precondition is invalid", + "comment": "The Update method does not support an explicit exists=false precondition.", "updatePaths": { "docRefPath": "projects/projectID/databases/(default)/documents/C/d", "precondition": { - "exists": true + "exists": false }, "fieldPaths": [ { diff --git a/dev/conformance/conformance-tests/update-paths-exists-true-precond.json b/dev/conformance/conformance-tests/update-paths-exists-true-precond.json new file mode 100644 index 000000000..39d61c99a --- /dev/null +++ b/dev/conformance/conformance-tests/update-paths-exists-true-precond.json @@ -0,0 +1,47 @@ +{ + "tests": [ + { + "description": "update-paths: Exists=true precondition is valid", + "comment": "The Update method supports an explicit exists=true precondition.", + "updatePaths": { + "docRefPath": "projects/projectID/databases/(default)/documents/C/d", + "precondition": { + "exists": true + }, + "fieldPaths": [ + { + "field": [ + "a" + ] + } + ], + "jsonValues": [ + "1" + ], + "request": { + "database": "projects/projectID/databases/(default)", + "writes": [ + { + "update": { + "name": "projects/projectID/databases/(default)/documents/C/d", + "fields": { + "a": { + "integerValue": "1" + } + } + }, + "updateMask": { + "fieldPaths": [ + "a" + ] + }, + "currentDocument": { + "exists": true + } + } + ] + } + } + } + ] +} diff --git a/dev/src/write-batch.ts b/dev/src/write-batch.ts index 33536a275..7cce55e04 100644 --- a/dev/src/write-batch.ts +++ b/dev/src/write-batch.ts @@ -660,12 +660,12 @@ export class WriteBatch implements firestore.WriteBatch { * @internal * @param arg The argument name or argument index (for varargs methods). * @param value The object to validate - * @param allowExists Whether to allow the 'exists' preconditions. + * @param options Options describing other things for this function to validate. */ function validatePrecondition( arg: string | number, value: unknown, - allowExists: boolean + options?: {allowedExistsValues?: boolean[]} ): void { if (typeof value !== 'object' || value === null) { throw new Error('Input is not an object.'); @@ -677,20 +677,22 @@ function validatePrecondition( if (precondition.exists !== undefined) { ++conditions; - if (!allowExists) { + if (typeof precondition.exists !== 'boolean') { throw new Error( `${invalidArgumentMessage( arg, 'precondition' - )} "exists" is not an allowed precondition.` + )} "exists" is not a boolean.'` ); } - if (typeof precondition.exists !== 'boolean') { + if ( + options?.allowedExistsValues && + options.allowedExistsValues.indexOf(precondition.exists) < 0 + ) { throw new Error( - `${invalidArgumentMessage( - arg, - 'precondition' - )} "exists" is not a boolean.'` + `${invalidArgumentMessage(arg, 'precondition')} ` + + `"exists" is not allowed to have the value ${precondition.exists} ` + + `(allowed values: ${options.allowedExistsValues.join(', ')})` ); } } @@ -733,7 +735,7 @@ function validateUpdatePrecondition( options?: RequiredArgumentOptions ): asserts value is {lastUpdateTime?: Timestamp} { if (!validateOptional(value, options)) { - validatePrecondition(arg, value, /* allowExists= */ false); + validatePrecondition(arg, value, {allowedExistsValues: [true]}); } } @@ -753,7 +755,7 @@ function validateDeletePrecondition( options?: RequiredArgumentOptions ): void { if (!validateOptional(value, options)) { - validatePrecondition(arg, value, /* allowExists= */ true); + validatePrecondition(arg, value); } } diff --git a/dev/test/document.ts b/dev/test/document.ts index f37bf0050..bc140aeef 100644 --- a/dev/test/document.ts +++ b/dev/test/document.ts @@ -1629,6 +1629,27 @@ describe('update document', () => { }); }); + it('allows explicitly specifying {exists:true} precondition', () => { + const overrides: ApiOverride = { + commit: request => { + requestEquals( + request, + update({ + document: document('documentId', 'foo', 'bar'), + mask: updateMask('foo'), + }) + ); + return response(writeResult(1)); + }, + }; + + return createInstance(overrides).then(firestore => { + return firestore + .doc('collectionId/documentId') + .update('foo', 'bar', {exists: true}); + }); + }); + it('returns update time', () => { const overrides: ApiOverride = { commit: request => { @@ -2069,7 +2090,7 @@ describe('update document', () => { it("doesn't accept argument after precondition", () => { expect(() => { firestore.doc('collectionId/documentId').update('foo', 'bar', { - exists: true, + exists: false, }); }).to.throw(INVALID_ARGUMENTS_TO_UPDATE); From 98e668bb9ce7feb090f65fc61d92af433bc23094 Mon Sep 17 00:00:00 2001 From: Mark Duckworth <1124037+MarkDuckworth@users.noreply.github.com> Date: Thu, 25 Jan 2024 09:46:12 -0700 Subject: [PATCH 28/30] fix: Fix redaction of credentials in Firestore settings (#1989) * Revert "fix: Remove incorrect,unreachable and unused code (#1983)" This reverts commit 133f4da892b453a4c4fd1324154eed907ad4663a. * fix: Fix redaction of credentials in Firestore settings. --- dev/src/index.ts | 7 +++++++ dev/test/index.ts | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/dev/src/index.ts b/dev/src/index.ts index 3932b2137..82fd88abe 100644 --- a/dev/src/index.ts +++ b/dev/src/index.ts @@ -745,6 +745,13 @@ export class Firestore implements firestore.Firestore { } this._settings = settings; + this._settings.toJSON = function () { + const temp = Object.assign({}, this); + if (temp.credentials) { + temp.credentials = {private_key: '***', client_email: '***'}; + } + return temp; + }; this._serializer = new Serializer(this); } diff --git a/dev/test/index.ts b/dev/test/index.ts index 12df809ec..5e1713376 100644 --- a/dev/test/index.ts +++ b/dev/test/index.ts @@ -1372,3 +1372,38 @@ describe('getAll() method', () => { }); }); }); + +describe('toJSON', () => { + it('Serializing Firestore settings redacts credentials', () => { + const firestore = new Firestore.Firestore({ + projectId: 'myProjectId', + credentials: {client_email: 'foo@bar', private_key: 'asdf1234'}, + }); + + const serializedSettings = JSON.stringify(firestore._settings); + + // Instead of validating the serialized string for redacted credentials, + // parse the settings and check the credential values. + const parsedSettings = JSON.parse(serializedSettings); + expect(parsedSettings.credentials.client_email).to.equal('***'); + expect(parsedSettings.credentials.private_key).to.equal('***'); + }); + + it('Serializing Firestore instance', () => { + const firestore = new Firestore.Firestore({ + projectId: 'myProjectId', + credentials: {client_email: 'foo@bar', private_key: 'asdf1234'}, + }); + + const serializedFirestore = JSON.stringify(firestore); + + // Instead of validating the serialized string, + // parse the JSON back to an object and check the properties. + const expectedParsedFirestore = { + projectId: 'myProjectId', + }; + + const parsedFirestore = JSON.parse(serializedFirestore); + expect(parsedFirestore).to.deep.equal(expectedParsedFirestore); + }); +}); From b4f7d6015b50f1da6afeae0b4215be416596cc69 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Wed, 31 Jan 2024 13:28:51 -0700 Subject: [PATCH 29/30] fix: remove types QueryMode, QueryPlan, ResultSetStats (#1982) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Expose the undeliverable_first_gen_event.proto PiperOrigin-RevId: 600597791 Source-Link: https://github.com/googleapis/googleapis/commit/fc15738ed0e542d482283f6e39f0e5d9279c1f4a Source-Link: https://github.com/googleapis/googleapis-gen/commit/1939fcafe56e2ca08767d94509fa62631934b2a0 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMTkzOWZjYWZlNTZlMmNhMDg3NjdkOTQ1MDlmYTYyNjMxOTM0YjJhMCJ9 fix: improve retry logic for streaming API calls build: update typescript generator version to 4.3.0 The streaming API call retry logic has changed, which in some rare cases may require code changes. Please feel free to reach out to us in the issues if you experience new problems with retrying streaming calls after this update. PiperOrigin-RevId: 599622271 Source-Link: https://github.com/googleapis/googleapis/commit/6239c217f083277d7a43c8bee55969654c3b2fee Source-Link: https://github.com/googleapis/googleapis-gen/commit/da13d8222d3ba33734501999864458640f1405ae Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiZGExM2Q4MjIyZDNiYTMzNzM0NTAxOTk5ODY0NDU4NjQwZjE0MDVhZSJ9 chore: Add FindNearest API to the preview branch docs: Improve the documentation on Document.fields PiperOrigin-RevId: 599602467 Source-Link: https://github.com/googleapis/googleapis/commit/d32bd9795d2620d327f1fd21477c53e828ab5a86 Source-Link: https://github.com/googleapis/googleapis-gen/commit/0545ffc488b82d3a4771118c923d64cd0b759953 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMDU0NWZmYzQ4OGI4MmQzYTQ3NzExMThjOTIzZDY0Y2QwYjc1OTk1MyJ9 * πŸ¦‰ Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * πŸ¦‰ Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * fix!: remove types QueryMode, QueryPlan, ResultSetStats fix!: remove QueryMode field from RunQueryRequest fix!: remove ResultSetStats field from RunQueryResponse fix!: remove QueryMode field from RunAggregationQueryRequest fix!: remove ResultSetStats field from RunAggregationQueryResponse PiperOrigin-RevId: 601486523 Source-Link: https://github.com/googleapis/googleapis/commit/a8b027a5c70951da414e3f6b3131cc0ed7886d48 Source-Link: https://github.com/googleapis/googleapis-gen/commit/b9b571f58541c0ace1ff94bf22af3d063eebfb7c Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiYjliNTcxZjU4NTQxYzBhY2UxZmY5NGJmMjJhZjNkMDYzZWViZmI3YyJ9 * πŸ¦‰ Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot --- dev/protos/google/firestore/v1/document.proto | 24 +++--- .../google/firestore/v1/firestore.proto | 25 ------- dev/protos/google/firestore/v1/query.proto | 8 ++ .../undeliverable_first_gen_event.proto | 75 +++++++++++++++++++ dev/protos/google/protobuf/descriptor.proto | 2 + dev/src/v1/firestore_client.ts | 23 +++--- dev/src/v1/firestore_proto_list.json | 1 - dev/src/v1beta1/firestore_client.ts | 12 ++- dev/src/v1beta1/firestore_proto_list.json | 1 + dev/src/v1beta1/gapic_metadata.json | 2 +- dev/test/gapic_firestore_v1.ts | 18 ++++- dev/test/gapic_firestore_v1beta1.ts | 12 ++- 12 files changed, 142 insertions(+), 61 deletions(-) create mode 100644 dev/protos/google/firestore/v1beta1/undeliverable_first_gen_event.proto diff --git a/dev/protos/google/firestore/v1/document.proto b/dev/protos/google/firestore/v1/document.proto index 795200498..a8764a1a2 100644 --- a/dev/protos/google/firestore/v1/document.proto +++ b/dev/protos/google/firestore/v1/document.proto @@ -41,23 +41,23 @@ message Document { // // The map keys represent field names. // - // A simple field name contains only characters `a` to `z`, `A` to `Z`, - // `0` to `9`, or `_`, and must not start with `0` to `9`. For example, - // `foo_bar_17`. - // // Field names matching the regular expression `__.*__` are reserved. Reserved - // field names are forbidden except in certain documented contexts. The map - // keys, represented as UTF-8, must not exceed 1,500 bytes and cannot be + // field names are forbidden except in certain documented contexts. The field + // names, represented as UTF-8, must not exceed 1,500 bytes and cannot be // empty. // // Field paths may be used in other contexts to refer to structured fields - // defined here. For `map_value`, the field path is represented by the simple - // or quoted field names of the containing fields, delimited by `.`. For - // example, the structured field - // `"foo" : { map_value: { "x&y" : { string_value: "hello" }}}` would be - // represented by the field path `foo.x&y`. + // defined here. For `map_value`, the field path is represented by a + // dot-delimited (`.`) string of segments. Each segment is either a simple + // field name (defined below) or a quoted field name. For example, the + // structured field `"foo" : { map_value: { "x&y" : { string_value: "hello" + // }}}` would be represented by the field path `` foo.`x&y` ``. + // + // A simple field name contains only characters `a` to `z`, `A` to `Z`, + // `0` to `9`, or `_`, and must not start with `0` to `9`. For example, + // `foo_bar_17`. // - // Within a field path, a quoted field name starts and ends with `` ` `` and + // A quoted field name starts and ends with `` ` `` and // may contain any character. Some characters, including `` ` ``, must be // escaped using a `\`. For example, `` `x&y` `` represents `x&y` and // `` `bak\`tik` `` represents `` bak`tik ``. diff --git a/dev/protos/google/firestore/v1/firestore.proto b/dev/protos/google/firestore/v1/firestore.proto index cc38cfb4a..3b843eed5 100644 --- a/dev/protos/google/firestore/v1/firestore.proto +++ b/dev/protos/google/firestore/v1/firestore.proto @@ -23,7 +23,6 @@ import "google/firestore/v1/aggregation_result.proto"; import "google/firestore/v1/common.proto"; import "google/firestore/v1/document.proto"; import "google/firestore/v1/query.proto"; -import "google/firestore/v1/query_profile.proto"; import "google/firestore/v1/write.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/timestamp.proto"; @@ -577,11 +576,6 @@ message RunQueryRequest { // minute timestamp within the past 7 days. google.protobuf.Timestamp read_time = 7; } - - // Optional. The mode in which the query request is processed. This field is - // optional, and when not provided, it defaults to `NORMAL` mode where no - // additional statistics will be returned with the query results. - QueryMode mode = 9 [(google.api.field_behavior) = OPTIONAL]; } // The response for @@ -618,13 +612,6 @@ message RunQueryResponse { // documents will be returned. bool done = 6; } - - // Query plan and execution statistics. Note that the returned stats are - // subject to change as Firestore evolves. - // - // This is only present when the request specifies a mode other than `NORMAL` - // and is sent only once with the last response in the stream. - ResultSetStats stats = 7; } // The request for @@ -664,11 +651,6 @@ message RunAggregationQueryRequest { // minute timestamp within the past 7 days. google.protobuf.Timestamp read_time = 6; } - - // Optional. The mode in which the query request is processed. This field is - // optional, and when not provided, it defaults to `NORMAL` mode where no - // additional statistics will be returned with the query results. - QueryMode mode = 7 [(google.api.field_behavior) = OPTIONAL]; } // The response for @@ -694,13 +676,6 @@ message RunAggregationQueryResponse { // `result` will be sent, and this represents the time at which the query // was run. google.protobuf.Timestamp read_time = 3; - - // Query plan and execution statistics. Note that the returned stats are - // subject to change as Firestore evolves. - // - // This is only present when the request specifies a mode other than `NORMAL` - // and is sent only once with the last response in the stream. - ResultSetStats stats = 6; } // The request for diff --git a/dev/protos/google/firestore/v1/query.proto b/dev/protos/google/firestore/v1/query.proto index b7d01c24e..09eefa241 100644 --- a/dev/protos/google/firestore/v1/query.proto +++ b/dev/protos/google/firestore/v1/query.proto @@ -30,6 +30,14 @@ option php_namespace = "Google\\Cloud\\Firestore\\V1"; option ruby_package = "Google::Cloud::Firestore::V1"; // A Firestore query. +// +// The query stages are executed in the following order: +// 1. from +// 2. where +// 3. select +// 4. order_by + start_at + end_at +// 5. offset +// 6. limit message StructuredQuery { // A selection of a collection, such as `messages as m1`. message CollectionSelector { diff --git a/dev/protos/google/firestore/v1beta1/undeliverable_first_gen_event.proto b/dev/protos/google/firestore/v1beta1/undeliverable_first_gen_event.proto new file mode 100644 index 000000000..fd3bf1f49 --- /dev/null +++ b/dev/protos/google/firestore/v1beta1/undeliverable_first_gen_event.proto @@ -0,0 +1,75 @@ +// 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.v1beta1; + +import "google/protobuf/timestamp.proto"; + +option csharp_namespace = "Google.Cloud.Firestore.V1Beta1"; +option go_package = "cloud.google.com/go/firestore/apiv1beta1/firestorepb;firestorepb"; +option java_multiple_files = true; +option java_outer_classname = "UndeliverableFirstGenEventProto"; +option java_package = "com.google.firestore.v1beta1"; +option php_namespace = "Google\\Cloud\\Firestore\\V1beta1"; +option ruby_package = "Google::Cloud::Firestore::V1beta1"; + +// A message signifying an event that cannot be delivered to Cloud Functions +// from Firestore using [Cloud Firestore triggers 1st +// gen](https://cloud.google.com/functions/docs/calling/cloud-firestore) +message UndeliverableFirstGenEvent { + // Reason for events being undeliverable. + enum Reason { + // Unspecified. + REASON_UNSPECIFIED = 0; + + // Exceeding maximum event size limit + EXCEEDING_SIZE_LIMIT = 1; + } + + // Document change type. + enum DocumentChangeType { + // Unspecified. + DOCUMENT_CHANGE_TYPE_UNSPECIFIED = 0; + + // Represent creation operation. + CREATE = 1; + + // Represent delete operation. + DELETE = 2; + + // Represent update operation. + UPDATE = 3; + } + + // Error message for events being undeliverable. + string message = 1; + + // Reason for events being undeliverable. + Reason reason = 2; + + // The resource name of the changed document, in the format of + // `projects/{projectId}/databases/{databaseId}/documents/{document_path}`. + string document_name = 3; + + // The type of the document change. + DocumentChangeType document_change_type = 4; + + // The names of the functions that were supposed to be triggered. + repeated string function_name = 5; + + // The commit time of triggered write operation. + google.protobuf.Timestamp triggered_time = 6; +} diff --git a/dev/protos/google/protobuf/descriptor.proto b/dev/protos/google/protobuf/descriptor.proto index 1dbeaa0c5..8f619e85c 100644 --- a/dev/protos/google/protobuf/descriptor.proto +++ b/dev/protos/google/protobuf/descriptor.proto @@ -1014,8 +1014,10 @@ message FeatureSet { extensions 1000; // for Protobuf C++ extensions 1001; // for Protobuf Java + extensions 1002; // for Protobuf Go extensions 9995 to 9999; // For internal testing + extensions 10000; // for https://github.com/bufbuild/protobuf-es } // A compiled specification for the defaults of a set of features. These diff --git a/dev/src/v1/firestore_client.ts b/dev/src/v1/firestore_client.ts index d63ecb8c7..e8cafd5c1 100644 --- a/dev/src/v1/firestore_client.ts +++ b/dev/src/v1/firestore_client.ts @@ -212,23 +212,28 @@ export class FirestoreClient { this.descriptors.stream = { batchGetDocuments: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.SERVER_STREAMING, - !!opts.fallback + !!opts.fallback, + /* gaxStreamingRetries: */ true ), runQuery: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.SERVER_STREAMING, - !!opts.fallback + !!opts.fallback, + /* gaxStreamingRetries: */ true ), runAggregationQuery: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.SERVER_STREAMING, - !!opts.fallback + !!opts.fallback, + /* gaxStreamingRetries: */ true ), write: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.BIDI_STREAMING, - !!opts.fallback + !!opts.fallback, + /* gaxStreamingRetries: */ true ), listen: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.BIDI_STREAMING, - !!opts.fallback + !!opts.fallback, + /* gaxStreamingRetries: */ true ), }; @@ -1230,10 +1235,6 @@ export class FirestoreClient { * This must be a microsecond precision timestamp within the past one hour, * or if Point-in-Time Recovery is enabled, can additionally be a whole * minute timestamp within the past 7 days. - * @param {google.firestore.v1.QueryMode} [request.mode] - * Optional. The mode in which the query request is processed. This field is - * optional, and when not provided, it defaults to `NORMAL` mode where no - * additional statistics will be returned with the query results. * @param {object} [options] * Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details. * @returns {Stream} @@ -1300,10 +1301,6 @@ export class FirestoreClient { * This must be a microsecond precision timestamp within the past one hour, * or if Point-in-Time Recovery is enabled, can additionally be a whole * minute timestamp within the past 7 days. - * @param {google.firestore.v1.QueryMode} [request.mode] - * Optional. The mode in which the query request is processed. This field is - * optional, and when not provided, it defaults to `NORMAL` mode where no - * additional statistics will be returned with the query results. * @param {object} [options] * Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details. * @returns {Stream} diff --git a/dev/src/v1/firestore_proto_list.json b/dev/src/v1/firestore_proto_list.json index 318c636c1..5c2cd76e2 100644 --- a/dev/src/v1/firestore_proto_list.json +++ b/dev/src/v1/firestore_proto_list.json @@ -5,6 +5,5 @@ "../../protos/google/firestore/v1/document.proto", "../../protos/google/firestore/v1/firestore.proto", "../../protos/google/firestore/v1/query.proto", - "../../protos/google/firestore/v1/query_profile.proto", "../../protos/google/firestore/v1/write.proto" ] diff --git a/dev/src/v1beta1/firestore_client.ts b/dev/src/v1beta1/firestore_client.ts index 7e62cb603..dcc1233ce 100644 --- a/dev/src/v1beta1/firestore_client.ts +++ b/dev/src/v1beta1/firestore_client.ts @@ -208,19 +208,23 @@ export class FirestoreClient { this.descriptors.stream = { batchGetDocuments: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.SERVER_STREAMING, - !!opts.fallback + !!opts.fallback, + /* gaxStreamingRetries: */ true ), runQuery: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.SERVER_STREAMING, - !!opts.fallback + !!opts.fallback, + /* gaxStreamingRetries: */ true ), write: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.BIDI_STREAMING, - !!opts.fallback + !!opts.fallback, + /* gaxStreamingRetries: */ true ), listen: new this._gaxModule.StreamDescriptor( this._gaxModule.StreamType.BIDI_STREAMING, - !!opts.fallback + !!opts.fallback, + /* gaxStreamingRetries: */ true ), }; diff --git a/dev/src/v1beta1/firestore_proto_list.json b/dev/src/v1beta1/firestore_proto_list.json index fbbd4aecc..735b79559 100644 --- a/dev/src/v1beta1/firestore_proto_list.json +++ b/dev/src/v1beta1/firestore_proto_list.json @@ -3,5 +3,6 @@ "../../protos/google/firestore/v1beta1/document.proto", "../../protos/google/firestore/v1beta1/firestore.proto", "../../protos/google/firestore/v1beta1/query.proto", + "../../protos/google/firestore/v1beta1/undeliverable_first_gen_event.proto", "../../protos/google/firestore/v1beta1/write.proto" ] diff --git a/dev/src/v1beta1/gapic_metadata.json b/dev/src/v1beta1/gapic_metadata.json index 45483c761..90ed69dcf 100644 --- a/dev/src/v1beta1/gapic_metadata.json +++ b/dev/src/v1beta1/gapic_metadata.json @@ -3,7 +3,7 @@ "comment": "This file maps proto services/RPCs to the corresponding library clients/methods", "language": "typescript", "protoPackage": "google.firestore.v1beta1", - "libraryPackage": "firestore", + "libraryPackage": "@google-cloud/firestore", "services": { "Firestore": { "clients": { diff --git a/dev/test/gapic_firestore_v1.ts b/dev/test/gapic_firestore_v1.ts index 7a1e7e1c5..c4cff606b 100644 --- a/dev/test/gapic_firestore_v1.ts +++ b/dev/test/gapic_firestore_v1.ts @@ -1421,7 +1421,11 @@ describe('v1.FirestoreClient', () => { const expectedError = new Error('The client has already been closed.'); client.close(); const stream = client.batchGetDocuments(request, { - retryRequestOptions: {noResponseRetries: 0}, + retry: { + shouldRetryFn: () => { + return false; + }, + }, }); const promise = new Promise((resolve, reject) => { stream.on( @@ -1542,7 +1546,11 @@ describe('v1.FirestoreClient', () => { const expectedError = new Error('The client has already been closed.'); client.close(); const stream = client.runQuery(request, { - retryRequestOptions: {noResponseRetries: 0}, + retry: { + shouldRetryFn: () => { + return false; + }, + }, }); const promise = new Promise((resolve, reject) => { stream.on( @@ -1668,7 +1676,11 @@ describe('v1.FirestoreClient', () => { const expectedError = new Error('The client has already been closed.'); client.close(); const stream = client.runAggregationQuery(request, { - retryRequestOptions: {noResponseRetries: 0}, + retry: { + shouldRetryFn: () => { + return false; + }, + }, }); const promise = new Promise((resolve, reject) => { stream.on( diff --git a/dev/test/gapic_firestore_v1beta1.ts b/dev/test/gapic_firestore_v1beta1.ts index 031ff228c..6e6668011 100644 --- a/dev/test/gapic_firestore_v1beta1.ts +++ b/dev/test/gapic_firestore_v1beta1.ts @@ -1427,7 +1427,11 @@ describe('v1beta1.FirestoreClient', () => { const expectedError = new Error('The client has already been closed.'); client.close(); const stream = client.batchGetDocuments(request, { - retryRequestOptions: {noResponseRetries: 0}, + retry: { + shouldRetryFn: () => { + return false; + }, + }, }); const promise = new Promise((resolve, reject) => { stream.on( @@ -1550,7 +1554,11 @@ describe('v1beta1.FirestoreClient', () => { const expectedError = new Error('The client has already been closed.'); client.close(); const stream = client.runQuery(request, { - retryRequestOptions: {noResponseRetries: 0}, + retry: { + shouldRetryFn: () => { + return false; + }, + }, }); const promise = new Promise((resolve, reject) => { stream.on( From 78edf804075e48033dc3755c62af33cd643c2aa4 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Wed, 31 Jan 2024 16:01:25 -0700 Subject: [PATCH 30/30] chore(main): release 7.3.0 (#1987) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- CHANGELOG.md | 15 +++++++++++++++ package.json | 2 +- samples/package.json | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c267ad85..a23d3950a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ [1]: https://www.npmjs.com/package/@google-cloud/firestore?activeTab=versions +## [7.3.0](https://github.com/googleapis/nodejs-firestore/compare/v7.2.0...v7.3.0) (2024-01-31) + + +### Features + +* Expose the undeliverable_first_gen_event.proto ([b4f7d60](https://github.com/googleapis/nodejs-firestore/commit/b4f7d6015b50f1da6afeae0b4215be416596cc69)) + + +### Bug Fixes + +* Allow an explicit MustExist precondition for update ([#1985](https://github.com/googleapis/nodejs-firestore/issues/1985)) ([99d60a6](https://github.com/googleapis/nodejs-firestore/commit/99d60a6f87b70c942ac2bd9464cc6d64323f9dfb)) +* Fix redaction of credentials in Firestore settings ([#1989](https://github.com/googleapis/nodejs-firestore/issues/1989)) ([98e668b](https://github.com/googleapis/nodejs-firestore/commit/98e668bb9ce7feb090f65fc61d92af433bc23094)) +* Improve retry logic for streaming API calls ([b4f7d60](https://github.com/googleapis/nodejs-firestore/commit/b4f7d6015b50f1da6afeae0b4215be416596cc69)) +* Removed unsupported QueryMode, QueryPlan, and ResultSetStats protos ([b4f7d60](https://github.com/googleapis/nodejs-firestore/commit/b4f7d6015b50f1da6afeae0b4215be416596cc69)) + ## [7.2.0](https://github.com/googleapis/nodejs-firestore/compare/v7.1.1...v7.2.0) (2024-01-16) diff --git a/package.json b/package.json index 2776b1019..dec585131 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@google-cloud/firestore", "description": "Firestore Client Library for Node.js", - "version": "7.2.0", + "version": "7.3.0", "license": "Apache-2.0", "author": "Google Inc.", "engines": { diff --git a/samples/package.json b/samples/package.json index 34674f273..121dc3435 100644 --- a/samples/package.json +++ b/samples/package.json @@ -11,7 +11,7 @@ "test": "mocha --timeout 600000" }, "dependencies": { - "@google-cloud/firestore": "^7.2.0" + "@google-cloud/firestore": "^7.3.0" }, "devDependencies": { "chai": "^4.2.0",