Skip to content
Draft
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
61 changes: 51 additions & 10 deletions handwritten/pubsub/src/pubsub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@
import {paginator} from '@google-cloud/paginator';
import {replaceProjectIdToken} from '@google-cloud/projectify';
import * as extend from 'extend';
import {AuthClient, GoogleAuth, GoogleAuthOptions} from 'google-auth-library';
import {
AuthClient,
GoogleAuth,
GoogleAuthOptions,
JWT,
} from 'google-auth-library';
import * as gax from 'google-gax';
import {v1} from './';

Expand Down Expand Up @@ -127,10 +132,12 @@
>;

export type GetSubscriptionsCallback =
GetAllSubscriptionsCallback | GetTopicSubscriptionsCallback;
| GetAllSubscriptionsCallback

Check failure on line 135 in handwritten/pubsub/src/pubsub.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `|·GetAllSubscriptionsCallback⏎·` with `GetAllSubscriptionsCallback`
| GetTopicSubscriptionsCallback;

export type GetSubscriptionsResponse =
GetAllSubscriptionsResponse | GetTopicSubscriptionsResponse;
| GetAllSubscriptionsResponse

Check failure on line 139 in handwritten/pubsub/src/pubsub.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `|·GetAllSubscriptionsResponse⏎·` with `GetAllSubscriptionsResponse`
| GetTopicSubscriptionsResponse;

export type GetTopicsCallback = RequestCallback<
Topic,
Expand Down Expand Up @@ -187,7 +194,8 @@
}

export type PagedResponse<Item, Response> =
[Item[]] | [Item[], {} | null, Response];
| [Item[]]

Check failure on line 197 in handwritten/pubsub/src/pubsub.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `|·[Item[]]⏎·` with `[Item[]]`
| [Item[], {} | null, Response];

export type ObjectStream<O> = {
addListener(event: 'data', listener: (data: O) => void): ObjectStream<O>;
Expand All @@ -214,10 +222,13 @@
* https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application
* Application Default Credentials}, your project ID will be detected
* automatically.
* @property {string} [keyFilename] Full path to the a .json, .pem, or .p12 key
* downloaded from the Google Developers Console. If you provide a path to a
* JSON file, the `projectId` option above is not necessary. NOTE: .pem and
* .p12 require you to specify the `email` option as well.
* @property {string} [keyFilename] @deprecated The `keyFilename` option is
* deprecated. Please use the `auth` object constructor instead. For more
* details, see https://cloud.google.com/docs/authentication/external/externally-sourced-credentials.
* Full path to the a .json, .pem, or .p12 key downloaded from the Google
* Developers Console. If you provide a path to a JSON file, the `projectId`
* option above is not necessary. NOTE: .pem and .p12 require you to specify
* the `email` option as well.
* @property {string} [apiEndpoint] The `apiEndpoint` from options will set the
* host. If not set, the `PUBSUB_EMULATOR_HOST` environment variable from the
* gcloud SDK is honored. We also check the `CLOUD_API_ENDPOINT_OVERRIDES_PUBSUB`
Expand All @@ -226,7 +237,10 @@
* we will assume that it's an emulator and disable strict SSL checks.
* @property {string} [email] Account email address. Required when using a .pem
* or .p12 keyFilename.
* @property {object} [credentials] Credentials object.
* @property {object} [credentials] @deprecated The `credentials` option is
* deprecated. Please use the `auth` object constructor instead. For more
* details, see https://cloud.google.com/docs/authentication/external/externally-sourced-credentials.
* Credentials object.
* @property {string} [credentials.client_email]
* @property {string} [credentials.private_key]
* @property {boolean} [autoRetry=true] Automatically retry requests if the
Expand Down Expand Up @@ -332,7 +346,9 @@
this.isEmulator = false;
this.determineBaseUrl_();
this.api = {};
this.auth = new GoogleAuth(this.options as GoogleAuthOptions<AuthClient>);
this.auth =
this.options.auth ||
new GoogleAuth(this.options as GoogleAuthOptions<AuthClient>);
this.projectId = this.options.projectId || PROJECT_ID_PLACEHOLDER;
if (this.projectId !== PROJECT_ID_PLACEHOLDER) {
this.name = PubSub.formatName_(this.projectId);
Expand Down Expand Up @@ -1246,6 +1262,7 @@
this.options.projectId = projectId!;
}

this.options.auth ??= this.auth;
return this.options;
}

Expand All @@ -1259,6 +1276,18 @@
if (!this.schemaClient) {
const options = (await this.getClientConfig()) as gax.ClientOptions;
this.schemaClient = new v1.SchemaServiceClient(options);
if (
this.auth.cachedCredential &&
'useJWTAccessWithScope' in this.auth.cachedCredential
) {
const jwt = this.auth.cachedCredential as JWT;
const {defaultServicePath, useJWTAccessWithScope, defaultScopes} = jwt;
this.auth.setGapicJWTValues?.(jwt);
jwt.defaultServicePath = defaultServicePath ?? jwt.defaultServicePath;
jwt.useJWTAccessWithScope =
useJWTAccessWithScope ?? jwt.useJWTAccessWithScope;
jwt.defaultScopes = defaultScopes ?? jwt.defaultScopes;
}
}

return this.schemaClient!;
Expand Down Expand Up @@ -1308,6 +1337,18 @@
if (!gaxClient) {
// Lazily instantiate client.
gaxClient = new v1[config.client](options) as unknown as gax.ClientStub;
if (
this.auth.cachedCredential &&
'useJWTAccessWithScope' in this.auth.cachedCredential
) {
const jwt = this.auth.cachedCredential as JWT;
const {defaultServicePath, useJWTAccessWithScope, defaultScopes} = jwt;
this.auth.setGapicJWTValues?.(jwt);
jwt.defaultServicePath = defaultServicePath ?? jwt.defaultServicePath;
jwt.useJWTAccessWithScope =
useJWTAccessWithScope ?? jwt.useJWTAccessWithScope;
jwt.defaultScopes = defaultScopes ?? jwt.defaultScopes;
}
this.api[config.client] = gaxClient;
}

Expand Down
111 changes: 110 additions & 1 deletion handwritten/pubsub/test/pubsub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ describe('PubSub', () => {
'./snapshot': {Snapshot: FakeSnapshot},
'./subscription': {Subscription},
'./topic': {Topic: FakeTopic},
'./index': {v1:v1Override},
'./index': {v1: v1Override},
'./util': fakeUtil,
}).PubSub;
});
Expand Down Expand Up @@ -340,6 +340,19 @@ describe('PubSub', () => {
assert.strictEqual(pubsub.auth, fakeGoogleAuthInstance);
});

it('should reuse an existing GoogleAuth instance if provided in options.auth', () => {
const existingAuth = {} as gax.GoogleAuth;
let googleAuthCalled = false;
googleAuthOverride = () => {
googleAuthCalled = true;
return {};
};

const pubsub = new PubSub({auth: existingAuth});
assert.strictEqual(pubsub.auth, existingAuth);
assert.strictEqual(googleAuthCalled, false);
});

it('should localize the options provided', () => {
const expectedOptions = Object.assign({}, DEFAULT_OPTIONS, OPTIONS);

Expand Down Expand Up @@ -1553,12 +1566,64 @@ describe('PubSub', () => {
options: pubsubTypes.ClientConfig,
) {
assert.strictEqual(options, pubsub.options);
assert.strictEqual(options.auth, pubsub.auth);
return FAKE_CLIENT_INSTANCE;
};

const client = await pubsub.getClientAsync_(CONFIG);
assert.strictEqual(client, FAKE_CLIENT_INSTANCE);
});

it('should sync GAPIC JWT values on cachedCredential after instantiating client without overwriting defined properties', async () => {
pubsub.projectId = PROJECT_ID_PLACEHOLDER;
const fakeJwt: {
useJWTAccessWithScope?: boolean;
defaultServicePath?: string;
defaultScopes?: string[];
} = {
useJWTAccessWithScope: undefined,
defaultServicePath: 'custom.googleapis.com',
defaultScopes: undefined,
};
const setGapicJWTValuesSpy = sandbox.spy(function (
this: {
useJWTAccessWithScope?: boolean;
defaultServicePath?: string;
defaultScopes?: string[];
},
client: typeof fakeJwt,
) {
// Simulate older google-auth-library setGapicJWTValues using '=' instead of '??='
client.defaultServicePath = this.defaultServicePath;
client.useJWTAccessWithScope = this.useJWTAccessWithScope;
client.defaultScopes = this.defaultScopes;
});
sandbox.stub(pubsub, 'auth').value({
cachedCredential: null,
useJWTAccessWithScope: true,
defaultServicePath: 'pubsub.googleapis.com',
defaultScopes: ['https://www.googleapis.com/auth/pubsub'],
getProjectId: async function () {
// Simulate getProjectId() caching a JWT client before GAPIC constructor runs
this.cachedCredential = fakeJwt;
return PROJECT_ID;
},
setGapicJWTValues: setGapicJWTValuesSpy,
});

// tslint:disable-next-line only-arrow-functions no-any
v1ClientOverrides.FakeClient = function () {
return FAKE_CLIENT_INSTANCE;
};

await pubsub.getClientAsync_(CONFIG);
assert(setGapicJWTValuesSpy.calledOnceWithExactly(fakeJwt));
assert.strictEqual(fakeJwt.useJWTAccessWithScope, true);
assert.strictEqual(fakeJwt.defaultServicePath, 'custom.googleapis.com');
assert.deepStrictEqual(fakeJwt.defaultScopes, [
'https://www.googleapis.com/auth/pubsub',
]);
});
});

describe('getClient_', () => {
Expand Down Expand Up @@ -1886,5 +1951,49 @@ describe('PubSub', () => {
await pubsub.validateSchema!(ischema, {});
assert.ok(called);
});

it('syncs GAPIC JWT values on cachedCredential in getSchemaClient()', async () => {
pubsub.projectId = PROJECT_ID_PLACEHOLDER;
const fakeJwt: {
useJWTAccessWithScope?: boolean;
defaultServicePath?: string;
defaultScopes?: string[];
} = {
useJWTAccessWithScope: undefined,
defaultServicePath: undefined,
defaultScopes: ['https://www.googleapis.com/auth/custom'],
};
const setGapicJWTValuesSpy = sandbox.spy(function (
this: {
useJWTAccessWithScope?: boolean;
defaultServicePath?: string;
defaultScopes?: string[];
},
client: typeof fakeJwt,
) {
client.defaultServicePath = this.defaultServicePath;
client.useJWTAccessWithScope = this.useJWTAccessWithScope;
client.defaultScopes = this.defaultScopes;
});
sandbox.stub(pubsub, 'auth').value({
cachedCredential: null,
getProjectId: async function () {
this.cachedCredential = fakeJwt;
return PROJECT_ID;
},
setGapicJWTValues: setGapicJWTValuesSpy,
});

const client = await pubsub.getSchemaClient();
assert(setGapicJWTValuesSpy.calledOnceWithExactly(fakeJwt));
assert.strictEqual(fakeJwt.useJWTAccessWithScope, true);
assert.strictEqual(fakeJwt.defaultServicePath, 'pubsub.googleapis.com');
assert.deepStrictEqual(fakeJwt.defaultScopes, [
'https://www.googleapis.com/auth/custom',
]);
sandbox.stub(client!, 'close').resolves();
await pubsub.close();
sandbox.restore();
});
});
});
Loading