Skip to content

Commit

Permalink
Merge pull request #31 from bdistin/eslint-typescript
Browse files Browse the repository at this point in the history
switch to eslint-typescript
  • Loading branch information
bdistin committed Apr 19, 2019
2 parents cd3d565 + eceaa3c commit 39d16b1
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 218 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.json
@@ -0,0 +1,3 @@
{
"extends": "klasa/eslint-ts"
}
10 changes: 6 additions & 4 deletions package.json
Expand Up @@ -5,17 +5,19 @@
"main": "dist/index.js",
"scripts": {
"prepublishOnly": "yarn build",
"build": "tsc",
"test:lint": "tslint '{src}/**/*.ts'",
"lint": "tslint --fix '{src}/**/*.ts'"
"build": "yarn tsc",
"test:lint": "yarn eslint --ext ts src",
"lint": "yarn eslint --fix --ext ts src"
},
"author": "bdistin",
"license": "MIT",
"devDependencies": {
"@types/node": "^11.9.5",
"@typescript-eslint/eslint-plugin": "^1.6.0",
"@typescript-eslint/parser": "^1.6.0",
"eslint": "^5.14.1",
"eslint-config-klasa": "dirigeants/klasa-lint",
"tslint": "^5.13.0"
"typescript": "^3.4.4"
},
"dependencies": {
"google-auth-library": "^3.1.0",
Expand Down
33 changes: 20 additions & 13 deletions src/GoogleSpreadsheet.ts
Expand Up @@ -3,13 +3,13 @@ import * as xml2js from 'xml2js';
import { JWT } from 'google-auth-library';

import * as http from 'http';
import * as querystring from 'querystring';
import { promisify } from 'util';

import SpreadsheetWorksheet from './SpreadsheetWorksheet';
import SpreadsheetCell from './SpreadsheetCell';
import SpreadsheetRow from './SpreadsheetRow';
import { forceArray, mergeDefault, deepClone, xmlSafeColumnName, xmlSafeValue } from './util';
import { URLSearchParams } from 'url';

const parser = new xml2js.Parser({
explicitArray: false,
Expand Down Expand Up @@ -45,7 +45,7 @@ export interface SpreadsheetInfo {
title: string;
updated: string;
author: string;
worksheets: Array<SpreadsheetWorksheet>;
worksheets: SpreadsheetWorksheet[];
}

export interface CellsQuery {
Expand Down Expand Up @@ -90,9 +90,9 @@ export default class GoogleSpreadsheet {
private options;

public info: SpreadsheetInfo;
public worksheets: Array<SpreadsheetWorksheet>;
public worksheets: SpreadsheetWorksheet[];

constructor(spreadsheetKey: string, authID, options) {
public constructor(spreadsheetKey: string, authID, options) {
this.options = options || {};
if (!spreadsheetKey) throw new Error('Spreadsheet key not provided');
this.spreadsheetKey = spreadsheetKey;
Expand Down Expand Up @@ -154,7 +154,8 @@ export default class GoogleSpreadsheet {
if (!this.isAuthActive) throw new Error(REQUIRE_AUTH_MESSAGE);

mergeDefault({
title: `Worksheet ${new Date()}`, // need a unique title
// need a unique title
title: `Worksheet ${new Date()}`,
rowCount: 50,
colCount: 20
}, options);
Expand All @@ -170,7 +171,7 @@ export default class GoogleSpreadsheet {
'</entry>'
].join('');

const { data } = await this.makeFeedRequest( ['worksheets', this.spreadsheetKey], 'POST', dataXML);
const { data } = await this.makeFeedRequest(['worksheets', this.spreadsheetKey], 'POST', dataXML);

const sheet = new SpreadsheetWorksheet(this, data);
this.worksheets = this.worksheets || [];
Expand All @@ -183,6 +184,7 @@ export default class GoogleSpreadsheet {
if (!this.isAuthActive) throw new Error(REQUIRE_AUTH_MESSAGE);
if (worksheet instanceof SpreadsheetWorksheet) return worksheet.del();
await this.makeFeedRequest(`${GOOGLE_FEED_URL}worksheets/${this.spreadsheetKey}/private/full/${worksheet}`, 'DELETE', null);
return undefined;
}

public async getRows(worksheetID: number, options: RowsQuery = {}): Promise<SpreadsheetRow[]> {
Expand All @@ -191,8 +193,11 @@ export default class GoogleSpreadsheet {

if (options.offset) query['start-index'] = options.offset;
if (options.limit) query['max-results'] = options.limit;
// eslint-disable-next-line dot-notation
if (options.orderBy) query['orderby'] = options.orderBy;
// eslint-disable-next-line dot-notation
if (options.reverse) query['reverse'] = 'true';
// eslint-disable-next-line dot-notation
if (options.query) query['sq'] = options.query;

const { data, xml } = await this.makeFeedRequest(['list', this.spreadsheetKey, worksheetID], 'GET', query);
Expand All @@ -205,10 +210,10 @@ export default class GoogleSpreadsheet {
// need to add the properties from the feed to the xml for the entries
const feedProps = deepClone(data.$);
delete feedProps['gd:etag'];
const feedPropsStr = feedProps.reduce((str, val, key) => `${str}${key}='${val}' `, '');
entriesXML = entriesXML.map((xml) => xml.replace('<entry ', `<entry ${feedPropsStr}`));
const feedPropsStr = feedProps.reduce((str, val, key): string => `${str}${key}='${val}' `, '');
entriesXML = entriesXML.map((_xml): string => _xml.replace('<entry ', `<entry ${feedPropsStr}`));

return forceArray(data.entry).map((entry, i) => new SpreadsheetRow(this, entry, entriesXML[i]));
return forceArray(data.entry).map((entry, i): SpreadsheetRow => new SpreadsheetRow(this, entry, entriesXML[i]));
}

public async addRow(worksheetID: number, rowData): Promise<SpreadsheetRow> {
Expand All @@ -229,10 +234,11 @@ export default class GoogleSpreadsheet {
public async getCells(worksheetID: number, options: CellsQuery = {}): Promise<SpreadsheetCell[]> {
const { data } = await this.makeFeedRequest(['cells', this.spreadsheetKey, worksheetID], 'GET', options);
if (!data) throw new Error('No response to getCells call');
return forceArray(data['entry']).map(entry => new SpreadsheetCell(this, this.spreadsheetKey, worksheetID, entry));
// eslint-disable-next-line dot-notation
return forceArray(data['entry']).map((entry): SpreadsheetCell => new SpreadsheetCell(this, this.spreadsheetKey, worksheetID, entry));
}

public async makeFeedRequest(urlParams: string | Array<string | number>, method: HTTP_METHODS, queryOrData: string | CellsQuery | APIRowQuery): Promise<{xml: string, data: any}> {
public async makeFeedRequest(urlParams: string | (string | number)[], method: HTTP_METHODS, queryOrData: string | CellsQuery | APIRowQuery): Promise<{xml: string, data: any}> {
let url;
const headers = {};
if (typeof urlParams === 'string') {
Expand All @@ -245,11 +251,12 @@ export default class GoogleSpreadsheet {
}

// auth
if (this.authMode === GoogleSpreadsheetAuthMode.jwt && this.googleAuth && this.googleAuth.expires > + new Date()) await this.renewJwtAuth();
if (this.authMode === GoogleSpreadsheetAuthMode.jwt && this.googleAuth && this.googleAuth.expires > Date.now()) await this.renewJwtAuth();

// request
headers['Gdata-Version'] = '3.0';

// eslint-disable-next-line dot-notation
if (this.googleAuth) headers['Authorization'] = this.googleAuth.type === 'Bearer' ? `Bearer ${this.googleAuth.value}` : `GoogleLogin auth=${this.googleAuth}`;

if (method === 'POST' || method === 'PUT') {
Expand All @@ -258,7 +265,7 @@ export default class GoogleSpreadsheet {
}

if (method === 'GET' && queryOrData) {
url += `?${querystring.stringify(queryOrData)}`
url += `?${new URLSearchParams(queryOrData as string | { [key: string]: string | string[] })}`
// replacements are needed for using structured queries on getRows
.replace(/%3E/g, '>')
.replace(/%3D/g, '=')
Expand Down
18 changes: 12 additions & 6 deletions src/SpreadsheetCell.ts
Expand Up @@ -19,7 +19,9 @@ export default class SpreadsheetCell {

public constructor(spreadsheet: GoogleSpreadsheet, spreadsheetKey: string, worksheetID: number, data) {
this.spreadsheet = spreadsheet;
// eslint-disable-next-line dot-notation
this.row = parseInt(data['gs:cell']['$']['row']);
// eslint-disable-next-line dot-notation
this.col = parseInt(data['gs:cell']['$']['col']);
this.batchID = `R${this.row}C${this.col}`;

Expand Down Expand Up @@ -58,23 +60,26 @@ export default class SpreadsheetCell {
` <batch:id>${this.batchID}</batch:id>`,
' <batch:operation type="update"/>',
` <id>${link}/${this.batchID}</id>`,
` <link rel="edit" type="application/atom+xml" href=\"${this.getEdit()}\"/>`,
` <link rel="edit" type="application/atom+xml" href="${this.getEdit()}"/>`,
` <gs:cell row="${this.row}" col="${this.col}" inputValue="${this.valueForSave}"/>`,
' </entry>'
].join('\n');
}

public updateValuesFromResponseData(data): void {
// formula value
const input_val = data['gs:cell']['$']['inputValue'];
// eslint-disable-next-line dot-notation
const inputVal = data['gs:cell']['$']['inputValue'];
// inputValue can be undefined so substr throws an error
// still unsure how this situation happens
this._formula = input_val && input_val.startsWith('=') ? input_val : undefined;
this._formula = inputVal && inputVal.startsWith('=') ? inputVal : undefined;

// numeric values
// eslint-disable-next-line dot-notation
this._numericValue = data['gs:cell']['$']['numericValue'] !== undefined ? parseFloat(data['gs:cell']['$']['numericValue']) : undefined;

// the main "value" - its always a string
// eslint-disable-next-line dot-notation
this._value = data['gs:cell']['_'] || '';
}

Expand All @@ -93,9 +98,9 @@ export default class SpreadsheetCell {
return;
}

const numeric_val = parseFloat(val);
if (!isNaN(numeric_val)) {
this._numericValue = numeric_val;
const numericVal = parseFloat(val);
if (!isNaN(numericVal)) {
this._numericValue = numericVal;
this._value = val.toString();
} else {
this._numericValue = undefined;
Expand Down Expand Up @@ -126,6 +131,7 @@ export default class SpreadsheetCell {
this._formula = val;
}

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
public get numericValue() {
return this._numericValue;
}
Expand Down
3 changes: 2 additions & 1 deletion src/SpreadsheetRow.ts
Expand Up @@ -7,14 +7,15 @@ export default class SpreadsheetRow extends Map {
private xml: string;
private links = new Map<string, string>();

constructor(spreadsheet: GoogleSpreadsheet, data: any, xml: string) {
public constructor(spreadsheet: GoogleSpreadsheet, data: any, xml: string) {
super();
this.spreadsheet = spreadsheet;
this.xml = xml;

for (const [key, value] of Object.entries(data)) {
if (key.startsWith('gsx:')) this.set(key === 'gsx:' ? key.substring(0, 3) : key.substring(4), typeof value === 'object' && Object.keys(value).length === 0 ? null : value);
else if (key === 'id') this.set(key, value);
// eslint-disable-next-line dot-notation
else if (value['_']) this.set(key, value['_']);
// @ts-ignore
else if (key === 'link') for (const link of forceArray(value)) this.links.set(link.$.rel, link.$.href);
Expand Down
9 changes: 5 additions & 4 deletions src/SpreadsheetWorksheet.ts
Expand Up @@ -19,7 +19,7 @@ export default class SpreadsheetWorksheet {
public rowCount: number;
public colCount: number;

constructor(spreadsheet: GoogleSpreadsheet, data) {
public constructor(spreadsheet: GoogleSpreadsheet, data) {
this.spreadsheet = spreadsheet;
this.url = data.id;
this.id = parseInt(data.id.substring(data.id.lastIndexOf('/') + 1));
Expand Down Expand Up @@ -81,7 +81,7 @@ export default class SpreadsheetWorksheet {

public async bulkUpdateCells(cells: SpreadsheetCell[]): Promise<void> {
const link = this.links.get('cells');
const entries = cells.map(cell => cell.getXML(link));
const entries = cells.map((cell): string => cell.getXML(link));

const dataXML = [
'<feed xmlns="http://www.w3.org/2005/Atom" xmlns:batch="http://schemas.google.com/gdata/batch" xmlns:gs="http://schemas.google.com/spreadsheets/2006">',
Expand All @@ -94,7 +94,8 @@ export default class SpreadsheetWorksheet {

// update all the cells
if (data.entry && data.entry.length) {
const cellsByBatchID = cells.reduce((acc, entry) => {
const cellsByBatchID = cells.reduce((acc, entry): any => {
// eslint-disable-next-line dot-notation
acc[entry['batchId']] = entry;
return acc;
}, {});
Expand All @@ -108,7 +109,7 @@ export default class SpreadsheetWorksheet {
}

public async setHeaderRow(values: string[]): Promise<void> {
if (!values) return;
if (!values) return undefined;
if (values.length > this.colCount) throw new Error(`Sheet is not large enough to fit ${values.length} columns. Resize the sheet first.`);

const cells = await this.getCells({
Expand Down
14 changes: 7 additions & 7 deletions src/util.ts
@@ -1,21 +1,21 @@
export const PRIMITIVE_TYPES = ['string', 'bigint', 'number', 'boolean'];

export const forceArray = <T>(val: T | T[]) => {
export const forceArray = <T>(val: T | T[]): T[] => {
if (Array.isArray(val)) return val;
if (!val) return [];
return [val];
};

export const xmlSafeValue = (val) => val === undefined || val === null ? '' : String(val).replace(/&/g, '&amp;')
export const xmlSafeValue = (val): string => val === undefined || val === null ? '' : String(val).replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/\n/g, '&#10;')
.replace(/\r/g, '&#13;');

export const xmlSafeColumnName = (val) => !val ? '' : String(val).replace(/[\s_]+/g, '').toLowerCase();
export const xmlSafeColumnName = (val): string => !val ? '' : String(val).replace(/[\s_]+/g, '').toLowerCase();

export const mergeDefault = (def, given) => {
export const mergeDefault = (def, given): any => {
if (!given) return deepClone(def);
for (const key in def) {
if (typeof given[key] === 'undefined') given[key] = deepClone(def[key]);
Expand All @@ -25,7 +25,7 @@ export const mergeDefault = (def, given) => {
return given;
};

export const deepClone = (source) => {
export const deepClone = (source): any => {
// Check if it's a primitive (with exception of function and null, which is typeof object)
if (source === null || isPrimitive(source)) return source;
if (Array.isArray(source)) {
Expand Down Expand Up @@ -53,6 +53,6 @@ export const deepClone = (source) => {
return source;
};

export const isObject = (input) => input && input.constructor === Object;
export const isObject = (input): boolean => input && input.constructor === Object;

export const isPrimitive = (value) => PRIMITIVE_TYPES.includes(typeof value);
export const isPrimitive = (value): boolean => PRIMITIVE_TYPES.includes(typeof value);
3 changes: 0 additions & 3 deletions tslint.json

This file was deleted.

0 comments on commit 39d16b1

Please sign in to comment.