Skip to content
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
4 changes: 2 additions & 2 deletions src/google/auth/base.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as google from 'googleapis';
import * as google from 'googleapis'

export interface AuthClient {
getAuth(): google.Auth.GoogleAuth
getAuthHeadersClient(): google.Auth.GoogleAuth | google.Auth.OAuth2Client
}
1 change: 1 addition & 0 deletions src/google/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './base'
export * from './models'
export * from './oauth2'
export * from './service_account'
export * from './models'
65 changes: 65 additions & 0 deletions src/google/auth/oauth2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import * as google from 'googleapis';
import { authenticate } from '@google-cloud/local-auth'

import * as fs from 'fs';
import { AuthClient } from './base';

export class Oauth2GoogleAuthClient implements AuthClient {
private auth!: google.Auth.OAuth2Client;

private constructor(auth: google.Auth.OAuth2Client) {
this.auth = auth;
}

public static async fromFile(
secretFilePath: string,
credsFilePath: string,
scopes: string[],
): Promise<Oauth2GoogleAuthClient> {
const storedClient = await this.loadCredsIfExists(credsFilePath);
if (storedClient) {
return new Oauth2GoogleAuthClient(storedClient);
}

const newClient = await authenticate({
scopes: scopes,
keyfilePath: secretFilePath,
});

if (newClient.credentials) {
await this.storeCredentials(secretFilePath, credsFilePath, newClient);
}
return new Oauth2GoogleAuthClient(newClient);
}

private static async loadCredsIfExists(credsFilePath: string): Promise<google.Auth.OAuth2Client | null> {
try {
const content = await fs.promises.readFile(credsFilePath);
const credentials = JSON.parse(content.toString());
return google.google.auth.fromJSON(credentials) as google.Auth.OAuth2Client;
} catch (err) {
return null;
}
}

private static async storeCredentials(
secretFilePath: string,
credsFilePath: string,
client: google.Auth.OAuth2Client,
) {
const content = await fs.promises.readFile(secretFilePath);
const keys = JSON.parse(content.toString());
const key = keys.installed || keys.web;
const payload = JSON.stringify({
type: 'authorized_user',
client_id: key.client_id,
client_secret: key.client_secret,
refresh_token: client.credentials.refresh_token,
});
await fs.promises.writeFile(credsFilePath, payload);
}

public getAuthHeadersClient(): google.Auth.GoogleAuth | google.Auth.OAuth2Client {
return this.auth!;
}
}
2 changes: 1 addition & 1 deletion src/google/auth/service_account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class ServiceAccountGoogleAuthClient implements AuthClient {
return new ServiceAccountGoogleAuthClient(authClient);
}

public getAuth(): google.Auth.GoogleAuth {
public getAuthHeadersClient(): google.Auth.GoogleAuth | google.Auth.OAuth2Client {
return this.auth!;
}
}
13 changes: 6 additions & 7 deletions src/google/sheets/wrapper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { google, sheets_v4 } from 'googleapis';
import * as google from 'googleapis';
import axios, { AxiosInstance } from 'axios';
import { GoogleAuth } from 'google-auth-library';

import { AuthClient } from '../auth/base';
import {
Expand All @@ -20,13 +19,13 @@ import {
} from './models';

export class Wrapper {
private googleAuth: GoogleAuth;
private service: sheets_v4.Sheets;
private authClient: google.Auth.GoogleAuth | google.Auth.OAuth2Client;
private service: google.sheets_v4.Sheets;
private rawClient: AxiosInstance;

constructor(auth: AuthClient) {
this.googleAuth = auth.getAuth();
this.service = google.sheets({ version: 'v4', auth: this.googleAuth });
this.authClient = auth.getAuthHeadersClient();
this.service = google.google.sheets({ version: 'v4', auth: this.authClient });
this.rawClient = axios.create({ validateStatus: () => true });
}

Expand Down Expand Up @@ -284,7 +283,7 @@ export class Wrapper {
// This ensures the latest access token is used (and refreshed if needed).
const response = await this.rawClient.get(
url,
{ headers: await this.googleAuth.getRequestHeaders() },
{ headers: await this.authClient.getRequestHeaders() },
);
if (response.status !== 200) {
throw new Error(`Failed to query rows, status: ${response.status}`);
Expand Down
6 changes: 3 additions & 3 deletions tests/google/sheets/models.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe('RawQueryRowsResult', () => {
};

const expected: QueryRowsResult = { rows: [] };
const mockAuth = { getAuth: jest.fn() };
const mockAuth = { getAuthHeadersClient: jest.fn() };
const wrapper = new Wrapper(mockAuth);
const result = wrapper['toQueryRowsResult'](rawResult);
expect(result).toEqual(expected);
Expand Down Expand Up @@ -121,7 +121,7 @@ describe('RawQueryRowsResult', () => {
],
};

const mockAuth = { getAuth: jest.fn() };
const mockAuth = { getAuthHeadersClient: jest.fn() };
const wrapper = new Wrapper(mockAuth);
const result = wrapper['toQueryRowsResult'](rawResult);
expect(result).toEqual(expected);
Expand All @@ -147,7 +147,7 @@ describe('RawQueryRowsResult', () => {
},
};

const mockAuth = { getAuth: jest.fn() };
const mockAuth = { getAuthHeadersClient: jest.fn() };
const wrapper = new Wrapper(mockAuth);
expect(() => wrapper['toQueryRowsResult'](rawResult)).toThrow('Unsupported cell value type: something');
});
Expand Down
2 changes: 1 addition & 1 deletion tests/google/sheets/wrapper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('Wrapper', () => {
let mockSheetsService: jest.Mocked<sheets_v4.Sheets>;

beforeEach(() => {
mockAuth = { getAuth: jest.fn() };
mockAuth = { getAuthHeadersClient: jest.fn() };
wrapper = new Wrapper(mockAuth);
mockSheetsService = wrapper['service'] as unknown as jest.Mocked<sheets_v4.Sheets>;
});
Expand Down
Loading