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: Support AuthClient for authClient #732

Merged
merged 5 commits into from
Feb 22, 2022
Merged
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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"duplexify": "^4.1.1",
"ent": "^2.2.0",
"extend": "^3.0.2",
"google-auth-library": "^7.9.2",
"google-auth-library": "^7.14.0",
"retry-request": "^4.2.2",
"teeny-request": "^7.0.0"
},
Expand Down
12 changes: 6 additions & 6 deletions src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import arrify = require('arrify');
import * as extend from 'extend';
import {GoogleAuth, GoogleAuthOptions} from 'google-auth-library';
import {AuthClient, GoogleAuth, GoogleAuthOptions} from 'google-auth-library';
import * as r from 'teeny-request';

import {Interceptor} from './service-object';
Expand Down Expand Up @@ -57,13 +57,13 @@ export interface ServiceConfig {
packageJson: PackageJson;

/**
* Reuse an existing GoogleAuth client instead of creating a new one.
* Reuse an existing `AuthClient` or `GoogleAuth` client instead of creating a new one.
*/
authClient?: GoogleAuth;
authClient?: AuthClient | GoogleAuth;
}

export interface ServiceOptions extends GoogleAuthOptions {
authClient?: GoogleAuth;
export interface ServiceOptions extends Omit<GoogleAuthOptions, 'authClient'> {
authClient?: AuthClient | GoogleAuth;
interceptors_?: Interceptor[];
email?: string;
token?: string;
Expand All @@ -81,7 +81,7 @@ export class Service {
private projectIdRequired: boolean;
providedUserAgent?: string;
makeAuthenticatedRequest: MakeAuthenticatedRequest;
authClient: GoogleAuth;
authClient: GoogleAuth<AuthClient>;
private getCredentials: {};
readonly apiEndpoint: string;
timeout?: number;
Expand Down
29 changes: 21 additions & 8 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import {replaceProjectIdToken} from '@google-cloud/projectify';
import * as ent from 'ent';
import * as extend from 'extend';
import {GoogleAuth, GoogleAuthOptions} from 'google-auth-library';
import {AuthClient, GoogleAuth, GoogleAuthOptions} from 'google-auth-library';
import {CredentialBody} from 'google-auth-library';
import * as r from 'teeny-request';
import * as retryRequest from 'retry-request';
Expand Down Expand Up @@ -111,7 +111,7 @@ export interface MakeAuthenticatedRequest {
getCredentials: (
callback: (err?: Error | null, credentials?: CredentialBody) => void
) => void;
authClient: GoogleAuth;
authClient: GoogleAuth<AuthClient>;
}

export interface Abortable {
Expand All @@ -125,7 +125,7 @@ export interface PackageJson {
}

export interface MakeAuthenticatedRequestFactoryConfig
extends GoogleAuthOptions {
extends Omit<GoogleAuthOptions, 'authClient'> {
/**
* Automatically retry requests if the response is related to rate limits or
* certain intermittent server errors. We will exponentially backoff
Expand Down Expand Up @@ -157,10 +157,10 @@ export interface MakeAuthenticatedRequestFactoryConfig
stream?: Duplexify;

/**
* A pre-instantiated GoogleAuth client that should be used.
* A pre-instantiated `AuthClient` or `GoogleAuth` client that should be used.
* A new will be created if this is not set.
*/
authClient?: GoogleAuth;
authClient?: AuthClient | GoogleAuth;
}

export interface MakeAuthenticatedRequestOptions {
Expand Down Expand Up @@ -591,8 +591,21 @@ export class Util {
if (googleAutoAuthConfig.projectId === '{{projectId}}') {
delete googleAutoAuthConfig.projectId;
}
const authClient =
googleAutoAuthConfig.authClient || new GoogleAuth(googleAutoAuthConfig);

let authClient: GoogleAuth<AuthClient>;

if (googleAutoAuthConfig.authClient instanceof GoogleAuth) {
// Use an existing `GoogleAuth`
authClient = googleAutoAuthConfig.authClient;
} else {
// Pass an `AuthClient` to `GoogleAuth`, if available
const config = {
...googleAutoAuthConfig,
authClient: googleAutoAuthConfig.authClient,
};

authClient = new GoogleAuth(config);
}

/**
* The returned function that will make an authenticated request.
Expand Down Expand Up @@ -659,7 +672,7 @@ export class Util {
// A projectId was required, but we don't have one.
// Re-use the "Could not load the default credentials error" if
// auto auth failed.
err = err || e;
err = err || (e as Error);
}
}

Expand Down
1 change: 1 addition & 0 deletions test/service-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import * as extend from 'extend';
import * as proxyquire from 'proxyquire';
import * as r from 'teeny-request';
import * as sinon from 'sinon';
import {AuthClient, OAuth2Client} from 'google-auth-library';

import {Service} from '../src';
import * as SO from '../src/service-object';
Expand Down
41 changes: 35 additions & 6 deletions test/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {describe, it, before, beforeEach, after} from 'mocha';
import * as extend from 'extend';
import * as proxyquire from 'proxyquire';
import {Request} from 'teeny-request';
import {AuthClient, GoogleAuth, OAuth2Client} from 'google-auth-library';

import {Interceptor} from '../src';
import {ServiceConfig, ServiceOptions} from '../src/service';
Expand Down Expand Up @@ -69,7 +70,7 @@ describe('Service', () => {
};

const OPTIONS = {
authClient: {getCredentials: () => {}},
authClient: new GoogleAuth(),
credentials: {},
keyFile: {},
email: 'email',
Expand Down Expand Up @@ -130,11 +131,39 @@ describe('Service', () => {
assert.strictEqual(service.authClient, OPTIONS.authClient);
});

it('should allow passing a custom GoogleAuth client', () => {
const authClient = {getCredentials: () => {}};
const cfg = Object.assign({}, {authClient}, CONFIG);
const service = new Service(cfg);
assert.strictEqual(service.authClient, authClient);
describe('`AuthClient` support', () => {
// Using a custom `AuthClient` to ensure any `AuthClient` would work
class CustomAuthClient extends AuthClient {
async getAccessToken() {
return {token: '', res: undefined};
}

async getRequestHeaders() {
return {};
}

request = OAuth2Client.prototype.request.bind(this);
}

it('should accept an `AuthClient` passed to config', async () => {
const authClient = new CustomAuthClient();
const serviceObject = new Service({...CONFIG, authClient});

// The custom `AuthClient` should be passed to `GoogleAuth` and used internally
const client = await serviceObject.authClient.getClient();

assert.strictEqual(client, authClient);
});

it('should accept an `AuthClient` passed to options', async () => {
const authClient = new CustomAuthClient();
const serviceObject = new Service(CONFIG, {authClient});

// The custom `AuthClient` should be passed to `GoogleAuth` and used internally
const client = await serviceObject.authClient.getClient();

assert.strictEqual(client, authClient);
});
});

it('should localize the baseUrl', () => {
Expand Down
39 changes: 37 additions & 2 deletions test/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ import {replaceProjectIdToken} from '@google-cloud/projectify';
import * as assert from 'assert';
import {describe, it, before, beforeEach, afterEach} from 'mocha';
import * as extend from 'extend';
import {GoogleAuth, GoogleAuthOptions} from 'google-auth-library';
import {
AuthClient,
GoogleAuth,
GoogleAuthOptions,
OAuth2Client,
} from 'google-auth-library';
import * as nock from 'nock';
import * as proxyquire from 'proxyquire';
import * as r from 'teeny-request';
Expand Down Expand Up @@ -113,6 +118,18 @@ describe('common/util', () => {
}

const fakeGoogleAuth = {
// Using a custom `AuthClient` to ensure any `AuthClient` would work
AuthClient: class CustomAuthClient extends AuthClient {
async getAccessToken() {
return {token: '', res: undefined};
}

async getRequestHeaders() {
return {};
}

request = OAuth2Client.prototype.request.bind(this);
},
GoogleAuth: class {
constructor(config?: GoogleAuthOptions) {
return new GoogleAuth(config);
Expand Down Expand Up @@ -590,7 +607,7 @@ describe('common/util', () => {
const fakeStream = new stream.Writable();
const error = new Error('Error.');
fakeStream.write = () => false;
dup.end = () => {};
dup.end = () => dup;

stub('handleResp', (err, res, body, callback) => {
callback(error);
Expand Down Expand Up @@ -705,6 +722,24 @@ describe('common/util', () => {
it('should create an authClient', done => {
const config = {test: true} as MakeAuthenticatedRequestFactoryConfig;

sandbox
.stub(fakeGoogleAuth, 'GoogleAuth')
.callsFake((config_: GoogleAuthOptions) => {
assert.deepStrictEqual(config_, {...config, authClient: undefined});
setImmediate(done);
return authClient;
});

util.makeAuthenticatedRequestFactory(config);
});

it('should pass an `AuthClient` to `GoogleAuth` when provided', done => {
const customAuthClient = new fakeGoogleAuth.AuthClient();

const config: MakeAuthenticatedRequestFactoryConfig = {
authClient: customAuthClient,
};

sandbox
.stub(fakeGoogleAuth, 'GoogleAuth')
.callsFake((config_: GoogleAuthOptions) => {
Expand Down