Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Clients should support the universe domain option #1365

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion protos/protos.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion protos/protos.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 28 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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(
Expand All @@ -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
Expand Down
18 changes: 14 additions & 4 deletions test/gapic-mocks/get-initialized-datastore-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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;
}
3 changes: 2 additions & 1 deletion test/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand Down
102 changes: 102 additions & 0 deletions test/service-path.ts
Original file line number Diff line number Diff line change
@@ -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;
});
});
Loading