diff --git a/package.json b/package.json index 779755da..b7dccb34 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,9 @@ "client library" ], "dependencies": { - "axios": "^0.18.0", "base64-js": "^1.3.0", "fast-text-encoding": "^1.0.0", + "gaxios": "^1.1.1", "gcp-metadata": "^0.9.3", "gtoken": "^2.3.2", "https-proxy-agent": "^2.2.1", diff --git a/src/auth/authclient.ts b/src/auth/authclient.ts index 8c722d01..f5408081 100644 --- a/src/auth/authclient.ts +++ b/src/auth/authclient.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import {AxiosPromise, AxiosRequestConfig} from 'axios'; import {EventEmitter} from 'events'; +import {GaxiosOptions, GaxiosPromise} from 'gaxios'; import {DefaultTransporter} from '../transporters'; @@ -30,9 +30,9 @@ export abstract class AuthClient extends EventEmitter { credentials: Credentials = {}; /** - * Provides an alternative Axios request implementation with auth credentials + * Provides an alternative Gaxios request implementation with auth credentials */ - abstract request(opts: AxiosRequestConfig): AxiosPromise; + abstract request(opts: GaxiosOptions): GaxiosPromise; /** * Sets the auth credentials. diff --git a/src/auth/computeclient.ts b/src/auth/computeclient.ts index 46b939de..61706d30 100644 --- a/src/auth/computeclient.ts +++ b/src/auth/computeclient.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import {AxiosError, AxiosPromise, AxiosRequestConfig, AxiosResponse} from 'axios'; +import {GaxiosError, GaxiosOptions, GaxiosPromise} from 'gaxios'; import * as gcpMetadata from 'gcp-metadata'; import * as messages from '../messages'; import {CredentialRequest, Credentials} from './credentials'; @@ -82,10 +82,10 @@ export class Compute extends OAuth2Client { return {tokens, res: null}; } - protected requestAsync(opts: AxiosRequestConfig, retry = false): - AxiosPromise { + protected requestAsync(opts: GaxiosOptions, retry = false): + GaxiosPromise { return super.requestAsync(opts, retry).catch(e => { - const res = (e as AxiosError).response; + const res = (e as GaxiosError).response; if (res && res.status) { let helpfulMessage = null; if (res.status === 403) { diff --git a/src/auth/googleauth.ts b/src/auth/googleauth.ts index 3a36cacf..4864e487 100644 --- a/src/auth/googleauth.ts +++ b/src/auth/googleauth.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import {AxiosRequestConfig, AxiosResponse} from 'axios'; import {exec} from 'child_process'; import * as fs from 'fs'; +import {GaxiosOptions, GaxiosResponse} from 'gaxios'; import * as gcpMetadata from 'gcp-metadata'; import * as os from 'os'; import * as path from 'path'; @@ -732,7 +732,7 @@ export class GoogleAuth { * @param opts Axios request options for the HTTP request. */ // tslint:disable-next-line no-any - async request(opts: AxiosRequestConfig): Promise> { + async request(opts: GaxiosOptions): Promise> { const client = await this.getClient(); return client.request(opts); } diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 3d70ddc7..4665bec1 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import {AxiosError, AxiosPromise, AxiosRequestConfig, AxiosResponse} from 'axios'; +import {GaxiosError, GaxiosOptions, GaxiosPromise, GaxiosResponse} from 'gaxios'; import * as querystring from 'querystring'; import * as stream from 'stream'; @@ -264,53 +264,53 @@ export interface GenerateAuthUrlOpts { } export interface GetTokenCallback { - (err: AxiosError|null, token?: Credentials|null, - res?: AxiosResponse|null): void; + (err: GaxiosError|null, token?: Credentials|null, + res?: GaxiosResponse|null): void; } export interface GetTokenResponse { tokens: Credentials; - res: AxiosResponse|null; + res: GaxiosResponse|null; } export interface GetAccessTokenCallback { - (err: AxiosError|null, token?: string|null, res?: AxiosResponse|null): void; + (err: GaxiosError|null, token?: string|null, res?: GaxiosResponse|null): void; } export interface GetAccessTokenResponse { token?: string|null; - res?: AxiosResponse|null; + res?: GaxiosResponse|null; } export interface RefreshAccessTokenCallback { - (err: AxiosError|null, credentials?: Credentials|null, - res?: AxiosResponse|null): void; + (err: GaxiosError|null, credentials?: Credentials|null, + res?: GaxiosResponse|null): void; } export interface RefreshAccessTokenResponse { credentials: Credentials; - res: AxiosResponse|null; + res: GaxiosResponse|null; } export interface RequestMetadataResponse { headers: Headers; - res?: AxiosResponse|null; + res?: GaxiosResponse|null; } export interface RequestMetadataCallback { - (err: AxiosError|null, headers?: Headers, - res?: AxiosResponse|null): void; + (err: GaxiosError|null, headers?: Headers, + res?: GaxiosResponse|null): void; } export interface GetFederatedSignonCertsCallback { - (err: AxiosError|null, certs?: Certificates, - response?: AxiosResponse|null): void; + (err: GaxiosError|null, certs?: Certificates, + response?: GaxiosResponse|null): void; } export interface FederatedSignonCertsResponse { certs: Certificates; format: CertificateFormat; - res?: AxiosResponse|null; + res?: GaxiosResponse|null; } export interface RevokeCredentialsResult { @@ -715,7 +715,7 @@ export class OAuth2Client extends AuthClient { r = await this.refreshToken(thisCreds.refresh_token); tokens = r.tokens; } catch (err) { - const e = err as AxiosError; + const e = err as GaxiosError; if (e.response && (e.response.status === 403 || e.response.status === 404)) { e.message = 'Could not refresh access token.'; @@ -747,14 +747,17 @@ export class OAuth2Client extends AuthClient { * @param token The existing token to be revoked. * @param callback Optional callback fn. */ - revokeToken(token: string): AxiosPromise; + revokeToken(token: string): GaxiosPromise; revokeToken( token: string, callback: BodyResponseCallback): void; revokeToken( token: string, callback?: BodyResponseCallback): - AxiosPromise|void { - const opts = {url: OAuth2Client.getRevokeTokenUrl(token), method: 'POST'}; + GaxiosPromise|void { + const opts: GaxiosOptions = { + url: OAuth2Client.getRevokeTokenUrl(token), + method: 'POST' + }; if (callback) { this.transporter.request(opts).then( r => callback(null, r), callback); @@ -768,11 +771,11 @@ export class OAuth2Client extends AuthClient { * Revokes access token and clears the credentials object * @param callback callback */ - revokeCredentials(): AxiosPromise; + revokeCredentials(): GaxiosPromise; revokeCredentials(callback: BodyResponseCallback): void; revokeCredentials(callback?: BodyResponseCallback): - AxiosPromise|void { + GaxiosPromise|void { if (callback) { this.revokeCredentialsAsync().then(res => callback(null, res), callback); } else { @@ -798,10 +801,10 @@ export class OAuth2Client extends AuthClient { * @param callback callback. * @return Request object */ - request(opts: AxiosRequestConfig): AxiosPromise; - request(opts: AxiosRequestConfig, callback: BodyResponseCallback): void; - request(opts: AxiosRequestConfig, callback?: BodyResponseCallback): - AxiosPromise|void { + request(opts: GaxiosOptions): GaxiosPromise; + request(opts: GaxiosOptions, callback: BodyResponseCallback): void; + request(opts: GaxiosOptions, callback?: BodyResponseCallback): + GaxiosPromise|void { if (callback) { this.requestAsync(opts).then(r => callback(null, r), e => { return callback(e, e.response); @@ -811,9 +814,9 @@ export class OAuth2Client extends AuthClient { } } - protected async requestAsync(opts: AxiosRequestConfig, retry = false): - Promise> { - let r2: AxiosResponse; + protected async requestAsync(opts: GaxiosOptions, retry = false): + Promise> { + let r2: GaxiosResponse; try { const r = await this.getRequestMetadataAsync(opts.url); if (r.headers && r.headers.Authorization) { @@ -826,7 +829,7 @@ export class OAuth2Client extends AuthClient { } r2 = await this.transporter.request(opts); } catch (e) { - const res = (e as AxiosError).response; + const res = (e as GaxiosError).response; if (res) { const statusCode = res.status; // Retry the request for metadata if the following criteria are true: @@ -944,7 +947,7 @@ export class OAuth2Client extends AuthClient { this.certificateCacheFormat === format) { return {certs: this.certificateCache, format}; } - let res: AxiosResponse; + let res: GaxiosResponse; let url: string; switch (format) { case CertificateFormat.PEM: diff --git a/src/transporters.ts b/src/transporters.ts index 632cb020..c2b9ae0a 100644 --- a/src/transporters.ts +++ b/src/transporters.ts @@ -14,44 +14,31 @@ * limitations under the License. */ -import axios, {AxiosError, AxiosPromise, AxiosRequestConfig, AxiosResponse} from 'axios'; +import {GaxiosError, GaxiosOptions, GaxiosPromise, GaxiosResponse, request} from 'gaxios'; import {isBrowser} from './isbrowser'; import {validate} from './options'; -// tslint:disable-next-line variable-name -const HttpsProxyAgent = require('https-proxy-agent'); - // tslint:disable-next-line no-var-requires const pkg = require('../../package.json'); const PRODUCT_NAME = 'google-api-nodejs-client'; export interface Transporter { - request(opts: AxiosRequestConfig): AxiosPromise; - request(opts: AxiosRequestConfig, callback?: BodyResponseCallback): - void; - request(opts: AxiosRequestConfig, callback?: BodyResponseCallback): - AxiosPromise|void; + request(opts: GaxiosOptions): GaxiosPromise; + request(opts: GaxiosOptions, callback?: BodyResponseCallback): void; + request(opts: GaxiosOptions, callback?: BodyResponseCallback): + GaxiosPromise|void; } export interface BodyResponseCallback { // The `body` object is a truly dynamic type. It must be `any`. - (err: Error|null, res?: AxiosResponse|null): void; + (err: Error|null, res?: GaxiosResponse|null): void; } -export interface RequestError extends AxiosError { +export interface RequestError extends GaxiosError { errors: Error[]; } -/** - * Axios will use XHR if it is available. In the case of Electron, - * since XHR is there it will try to use that. This leads to OPTIONS - * preflight requests which googleapis DOES NOT like. This line of - * code pins the adapter to ensure it uses node. - * https://github.com/google/google-api-nodejs-client/issues/1083 - */ -axios.defaults.adapter = require('axios/lib/adapters/http'); - export class DefaultTransporter { /** * Default user agent. @@ -60,10 +47,10 @@ export class DefaultTransporter { /** * Configures request options before making a request. - * @param opts AxiosRequestConfig options. + * @param opts GaxiosOptions options. * @return Configured options. */ - configure(opts: AxiosRequestConfig = {}): AxiosRequestConfig { + configure(opts: GaxiosOptions = {}): GaxiosOptions { opts.headers = opts.headers || {}; if (!isBrowser()) { // set transporter user agent if not in browser @@ -79,16 +66,15 @@ export class DefaultTransporter { } /** - * Makes a request using Axios with given options. - * @param opts AxiosRequestConfig options. - * @param callback optional callback that contains AxiosResponse object. - * @return AxiosPromise, assuming no callback is passed. + * Makes a request using Gaxios with given options. + * @param opts GaxiosOptions options. + * @param callback optional callback that contains GaxiosResponse object. + * @return GaxiosPromise, assuming no callback is passed. */ - request(opts: AxiosRequestConfig): AxiosPromise; - request(opts: AxiosRequestConfig, callback?: BodyResponseCallback): - void; - request(opts: AxiosRequestConfig, callback?: BodyResponseCallback): - AxiosPromise|void { + request(opts: GaxiosOptions): GaxiosPromise; + request(opts: GaxiosOptions, callback?: BodyResponseCallback): void; + request(opts: GaxiosOptions, callback?: BodyResponseCallback): + GaxiosPromise|void { // ensure the user isn't passing in request-style options opts = this.configure(opts); try { @@ -101,16 +87,8 @@ export class DefaultTransporter { } } - // If the user configured an `HTTPS_PROXY` environment variable, create - // a custom agent to proxy the request. - const proxy = process.env.HTTPS_PROXY || process.env.https_proxy; - if (proxy) { - opts.httpsAgent = new HttpsProxyAgent(proxy); - opts.proxy = false; - } - if (callback) { - axios(opts).then( + request(opts).then( r => { callback(null, r); }, @@ -118,7 +96,7 @@ export class DefaultTransporter { callback(this.processError(e)); }); } else { - return axios(opts).catch(e => { + return request(opts).catch(e => { throw this.processError(e); }); } @@ -127,7 +105,7 @@ export class DefaultTransporter { /** * Changes the error to include details from the body. */ - private processError(e: AxiosError): RequestError { + private processError(e: GaxiosError): RequestError { const res = e.response; const err = e as RequestError; const body = res ? res.data : null; diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index ceeeaf25..14f87e96 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -15,9 +15,9 @@ */ import * as assert from 'assert'; -import {AxiosError} from 'axios'; import * as crypto from 'crypto'; import * as fs from 'fs'; +import {GaxiosError} from 'gaxios'; import * as nock from 'nock'; import * as path from 'path'; import * as qs from 'querystring'; @@ -27,6 +27,7 @@ import * as url from 'url'; import {CodeChallengeMethod, OAuth2Client} from '../src'; import {LoginTicket} from '../src/auth/loginticket'; import * as messages from '../src/messages'; + const assertRejects = require('assert-rejects'); nock.disableNetConnect(); @@ -950,11 +951,12 @@ it('should not retry requests with streaming data', done => { const scope = nock('http://example.com').post('/').reply(401); client.credentials = { access_token: 'initial-access-token', - refresh_token: 'refresh-token-placeholder' + refresh_token: 'refresh-token-placeholder', + expiry_date: (new Date()).getTime() + 500000 }; client.request({method: 'POST', url: 'http://example.com', data: s}, err => { scope.done(); - const e = err as AxiosError; + const e = err as GaxiosError; assert(e); assert.strictEqual(e.response!.status, 401); done(); @@ -1000,7 +1002,7 @@ it('getToken should allow a code_verifier to be passed', async () => { assert(res.res); if (!res.res) return; const params = qs.parse(res.res.config.data); - assert(params.code_verifier === 'its_verified'); + assert.strictEqual(params.code_verifier, 'its_verified'); }); it('getToken should set redirect_uri if not provided in options', async () => { diff --git a/test/test.transporters.ts b/test/test.transporters.ts index 16b95896..5037c39d 100644 --- a/test/test.transporters.ts +++ b/test/test.transporters.ts @@ -15,7 +15,7 @@ */ import * as assert from 'assert'; -import {AxiosRequestConfig} from 'axios'; +import {GaxiosOptions} from 'gaxios'; import * as nock from 'nock'; import {DefaultTransporter, RequestError} from '../src/transporters'; @@ -31,16 +31,6 @@ nock.disableNetConnect(); const defaultUserAgentRE = 'google-api-nodejs-client/\\d+.\\d+.\\d+'; const transporter = new DefaultTransporter(); -it('should set default client user agent if none is set', async () => { - const url = 'http://example.com'; - const scope = nock(url).get('/').reply(200, {}); - const res = await transporter.request({url}); - assert.strictEqual(typeof res.config.adapter, 'function'); - assert.deepStrictEqual( - res.config.adapter, require('axios/lib/adapters/http')); - scope.done(); -}); - it('should set default adapter to node.js', () => { const opts = transporter.configure(); const re = new RegExp(defaultUserAgentRE); @@ -95,7 +85,7 @@ it('should return an error if you try to use request config options', done => { transporter.request( { uri: 'http://example.com/api', - } as AxiosRequestConfig, + } as GaxiosOptions, (error) => { assert.strictEqual(error!.message, expected); done(); @@ -109,8 +99,7 @@ it('should return an error if you try to use request config options with a promi `library is using Axios for requests. Please see https://github.com/axios/axios ` + `to learn more about the valid request options.`); const uri = 'http://example.com/api'; - assert.throws( - () => transporter.request({uri} as AxiosRequestConfig), expected); + assert.throws(() => transporter.request({uri} as GaxiosOptions), expected); }); it('should support invocation with async/await', async () => {