diff --git a/protos/protos.d.ts b/protos/protos.d.ts index 4ca4f6b53..0a48bf135 100644 --- a/protos/protos.d.ts +++ b/protos/protos.d.ts @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/protos/protos.js b/protos/protos.js index a6b2b37db..88799e49a 100644 --- a/protos/protos.js +++ b/protos/protos.js @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/index.ts b/src/index.ts index 20cad99c4..ef2969be2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,7 +39,7 @@ import { import * as is from 'is'; import {Transform, pipeline} from 'stream'; -import {entity, Entities, Entity, EntityProto, ValueProto} from './entity'; +import {entity, Entities, Entity, ValueProto} from './entity'; import {AggregateField} from './aggregate'; import Key = entity.Key; export {Entity, Key, AggregateField}; @@ -125,6 +125,27 @@ const gapic = Object.freeze({ const urlSafeKey = new entity.URLSafeKey(); +/** + * Retrieves the domain to be used for the service path. + * + * This function retrieves the domain from DatastoreOptions passed in or via an + * environment variable. + * @param {string} [prefix] The prefix for the domain. + * @param {string} [suffix] The suffix for the domain. + * @param {DatastoreOptions} [opts] The gax client options + * @returns {string} The universe domain. + */ +function getDomain(prefix: string, suffix: string, opts?: DatastoreOptions) { + // From https://github.com/googleapis/nodejs-datastore/blob/5c0ddbca91c41e056443eb0b60449f3cdddd6e69/src/v1/datastore_client.ts#L127-L138 + // This code for universe domain was taken from the Gapic Layer. + // It is reused here to build the service path. + const universeDomainEnvVar = + typeof process === 'object' && typeof process.env === 'object' + ? process.env['GOOGLE_CLOUD_UNIVERSE_DOMAIN'] + : undefined; + return `${prefix}.${opts?.universeDomain ?? universeDomainEnvVar ?? suffix}`; +} + /** * Idiomatic class for interacting with Cloud Datastore. Uses the lower-level * {@link DatastoreClient} class under the hood. @@ -489,7 +510,9 @@ class Datastore extends DatastoreRequest { options.projectId = options.projectId || process.env.DATASTORE_PROJECT_ID; - this.defaultBaseUrl_ = 'datastore.googleapis.com'; + const prefixDefault = 'datastore'; + const suffixDefault = 'googleapis.com'; + this.defaultBaseUrl_ = `${prefixDefault}.${suffixDefault}`; this.determineBaseUrl_(options.apiEndpoint); const scopes: string[] = Array.from( @@ -504,7 +527,9 @@ class Datastore extends DatastoreRequest { libName: 'gccl', libVersion: require('../../package.json').version, scopes, - servicePath: this.baseUrl_, + servicePath: this.customEndpoint_ + ? this.baseUrl_ + : getDomain(prefixDefault, suffixDefault, options), port: typeof this.port_ === 'number' ? this.port_ : 443, }, options diff --git a/test/gapic-mocks/get-initialized-datastore-client.ts b/test/gapic-mocks/get-initialized-datastore-client.ts index bf3e4b652..2a5ded061 100644 --- a/test/gapic-mocks/get-initialized-datastore-client.ts +++ b/test/gapic-mocks/get-initialized-datastore-client.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {Datastore} from '../../src'; +import {Datastore, DatastoreOptions} from '../../src'; /** * This function gets a datastore client that has already been initialized @@ -22,15 +22,18 @@ import {Datastore} from '../../src'; * evaluate data that reaches the handwritten layer thereby testing the * handwritten layer in isolation. */ -export function getInitializedDatastoreClient(): Datastore { +export function getInitializedDatastoreClient( + opts?: DatastoreOptions +): Datastore { const clientName = 'DatastoreClient'; + const adminClientName = 'DatastoreAdminClient'; const PROJECT_ID = 'project-id'; const NAMESPACE = 'namespace'; const options = { projectId: PROJECT_ID, namespace: NAMESPACE, }; - const datastore = new Datastore(options); + const datastore = new Datastore(opts ? opts : options); // By default, datastore.clients_ is an empty map. // To mock out commit we need the map to contain the Gapic data client. // Normally a call to the data client through the datastore object would initialize it. @@ -39,6 +42,13 @@ export function getInitializedDatastoreClient(): Datastore { const gapic = Object.freeze({ v1: require('../../src/v1'), }); - datastore.clients_.set(clientName, new gapic.v1[clientName](options)); + datastore.clients_.set( + clientName, + new gapic.v1[clientName](datastore.options) + ); + datastore.clients_.set( + adminClientName, + new gapic.v1[adminClientName](datastore.options) + ); return datastore; } diff --git a/test/request.ts b/test/request.ts index 532966420..36a89f336 100644 --- a/test/request.ts +++ b/test/request.ts @@ -1886,7 +1886,8 @@ describe('Request', () => { }); describe('requestStream_', () => { - let GAX_STREAM: gax.CancellableStream; + let GAX_STREAM: gax.CancellableStream = + new PassThrough() as unknown as gax.CancellableStream; const CONFIG = {}; beforeEach(() => { diff --git a/test/service-path.ts b/test/service-path.ts new file mode 100644 index 000000000..d8af908fc --- /dev/null +++ b/test/service-path.ts @@ -0,0 +1,102 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {describe, it} from 'mocha'; +import * as assert from 'assert'; +import {DatastoreClient, DatastoreAdminClient} from '../src/v1'; +import {getInitializedDatastoreClient} from './gapic-mocks/get-initialized-datastore-client'; + +describe('Service Path', () => { + it('Setting universe domain should set the service path', async () => { + // Set the environment variable + process.env.GOOGLE_CLOUD_UNIVERSE_DOMAIN = 'otherDomain'; + + const universeDomain = 'someUniverseDomain'; // or your universe domain if not using emulator + const options = { + universeDomain, + }; + const datastore = getInitializedDatastoreClient(options); + assert.strictEqual( + ( + datastore.clients_.get( + 'DatastoreAdminClient' + ) as unknown as DatastoreAdminClient + )['_opts'].servicePath, + `datastore.${universeDomain}` + ); + assert.strictEqual( + (datastore.clients_.get('DatastoreClient') as unknown as DatastoreClient)[ + '_opts' + ].servicePath, + `datastore.${universeDomain}` + ); + + // Clean up the environment variable after the test + delete process.env.GOOGLE_CLOUD_UNIVERSE_DOMAIN; + }); + it('Setting universe domain and custom endpoint should set the service path to custom endpoint', async () => { + // Set the environment variable + process.env.GOOGLE_CLOUD_UNIVERSE_DOMAIN = 'otherDomain'; + + const universeDomain = 'someUniverseDomain'; // or your universe domain if not using emulator + const apiEndpoint = 'someApiEndpoint'; + const options = { + universeDomain, + apiEndpoint, + }; + const datastore = getInitializedDatastoreClient(options); + assert.strictEqual( + ( + datastore.clients_.get( + 'DatastoreAdminClient' + ) as unknown as DatastoreAdminClient + )['_opts'].servicePath, + 'someApiEndpoint' + ); + assert.strictEqual( + (datastore.clients_.get('DatastoreClient') as unknown as DatastoreClient)[ + '_opts' + ].servicePath, + 'someApiEndpoint' + ); + + // Clean up the environment variable after the test + delete process.env.GOOGLE_CLOUD_UNIVERSE_DOMAIN; + }); + it('Setting GOOGLE_CLOUD_UNIVERSE_DOMAIN should set the service path', async () => { + const universeDomain = 'someUniverseDomain'; // or your universe domain if not using emulator + + // Set the environment variable + process.env.GOOGLE_CLOUD_UNIVERSE_DOMAIN = universeDomain; + const datastore = getInitializedDatastoreClient(); // No options needed, it will pick up the env var + + assert.strictEqual( + ( + datastore.clients_.get( + 'DatastoreAdminClient' + ) as unknown as DatastoreAdminClient + )['_opts'].servicePath, + `datastore.${universeDomain}` + ); + assert.strictEqual( + (datastore.clients_.get('DatastoreClient') as unknown as DatastoreClient)[ + '_opts' + ].servicePath, + `datastore.${universeDomain}` + ); + + // Clean up the environment variable after the test + delete process.env.GOOGLE_CLOUD_UNIVERSE_DOMAIN; + }); +});