From 83ede789d3591a5cffa9c421efef9612083cf9bf Mon Sep 17 00:00:00 2001 From: Praveen Kumar Singh Date: Wed, 29 May 2019 14:55:52 +0530 Subject: [PATCH 1/2] refactor(typescript): noImplicityAny for src row file --- src/cluster.ts | 2 +- src/index.ts | 102 ++++++ src/row.ts | 687 +++++++++++++++++++++++----------------- system-test/bigtable.ts | 2 +- 4 files changed, 506 insertions(+), 287 deletions(-) diff --git a/src/cluster.ts b/src/cluster.ts index 93a6da827..35196153e 100644 --- a/src/cluster.ts +++ b/src/cluster.ts @@ -15,8 +15,8 @@ */ import {promisifyAll} from '@google-cloud/promisify'; -import {ServiceError} from '@grpc/grpc-js'; import {CallOptions, Operation as GaxOperation} from 'google-gax'; +import {ServiceError} from 'grpc'; import {google as btTypes} from '../proto/bigtable'; diff --git a/src/index.ts b/src/index.ts index de282f799..6d9aa1a8d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -50,6 +50,17 @@ import {AppProfile} from './app-profile'; import {Cluster} from './cluster'; import {Instance} from './instance'; + +import {google as btTypes} from '../proto/bigtable'; + +import {ServiceError} from 'grpc'; + +import {Row} from './row'; +import {PartialFailureError} from '@google-cloud/common/build/src/util'; +import {IMutation} from './mutation'; + + + const retryRequest = require('retry-request'); const streamEvents = require('stream-events'); @@ -57,6 +68,97 @@ const PKG = require('../../package.json'); const v2 = require('./v2'); const {grpc} = new gax.GrpcClient(); + + +type RequestCallback = + R extends void ? NormalCallback: FullCallback; +type NormalCallback = (err: ServiceError|null, response?: T|null) => void; +type FullCallback = + (err: ServiceError|null, response?: T|null, apiResponse?: R|null) => void; + +type ApiResponse = + R extends void ? NormalResponse: FullResponse; +type NormalResponse = [T]; +type FullResponse = [T, R]; +interface OptionInterface { + gaxOptions?: gax.CallOptions; +} + + +export type IOperation = btTypes.longrunning.IOperation; + +export interface CreateRowOptions extends OptionInterface { + entry?: Entry; +} + + + +export interface Rule { + age?: {}; + versions?: number; + intersect?: boolean; + union?: boolean; +} + +export interface GetRowOptions extends OptionInterface { + decode?: boolean; + filter?: Array<{}>; +} + +export type EmptyResponse = ApiResponse; + +export interface FilterRowConfigOptions { + onMatch: IMutation[]|null; + onNoMatch?: IMutation[]; + gaxOptions?: gax.CallOptions; +} + + +export interface GetTableRowsOptions extends OptionInterface {} +export type ExistsCallback = RequestCallback; +export type ExistsResponse = ApiResponse; + +export interface MutateTableRowsOptions extends OptionInterface { + rawMutation?: boolean; +} +export type CreateRowCallback = + RequestCallback; +export type CreateRowResponse = + ApiResponse; +export type CreateRulesCallback = + RequestCallback; +export type CreateRulesResponse = + ApiResponse; +export type DeleteRowCallback = RequestCallback; +export type DeleteRowResponse = ApiResponse; +export type DeleteRowCellsCallback = RequestCallback; +export type DeleteRowCellsResponse = ApiResponse; +export type SaveRowCallback = RequestCallback; +export type SaveRowResponse = ApiResponse; +export type IncrementRowCallback = + RequestCallback; +export type IncrementRowResponse = + ApiResponse; +export type GetRowMetadataCallback = RequestCallback; +export type GetRowMetadataResponse = ApiResponse; +export type GetRowCallback = RequestCallback; +export type GetRowResponse = ApiResponse; +export type FilterRowCallback = + RequestCallback; +export type FilterRowResponse = + ApiResponse; + +export type Data = Value|Value[]|Entry; +export type Value = string|number|boolean; + + +export interface Entry { + key?: string; + method?: string; + data?: Data; +} + + /** * @typedef {object} ClientConfig * @property {string} [apiEndpoint] Override the default API endpoint used diff --git a/src/row.ts b/src/row.ts index 60dd3c3e7..20849e3d1 100644 --- a/src/row.ts +++ b/src/row.ts @@ -20,14 +20,21 @@ import arrify = require('arrify'); const dotProp = require('dot-prop'); import * as is from 'is'; import {Filter} from './filter'; -import {Mutation} from './mutation'; +import {Mutation, IMutation} from './mutation'; +import {Bigtable, CreateRowOptions, CreateRowCallback, CreateRowResponse, CreateRulesCallback, CreateRulesResponse, MutateTableRowsOptions, DeleteRowCallback, EmptyResponse, DeleteRowResponse, DeleteRowCellsCallback, DeleteRowCellsResponse, ExistsCallback, ExistsResponse, Entry, SaveRowCallback, SaveRowResponse, IncrementRowCallback, IncrementRowResponse, GetRowOptions, GetRowMetadataCallback, GetRowMetadataResponse, GetRowCallback, GetTableRowsOptions, GetRowResponse, FilterRowConfigOptions, FilterRowCallback, FilterRowResponse} from '.'; +import {Table} from './table'; +import {google} from '../proto/bigtable'; +import {CallOptions} from 'google-gax'; +import {Chunk, Qualifier} from './chunktransformer'; +import {Family} from './family'; + /** * @private */ export class RowError extends Error { code: number; - constructor(row) { + constructor(row: string) { super(); this.name = 'RowError'; this.message = `Unknown row: ${row}.`; @@ -35,6 +42,31 @@ export class RowError extends Error { } } +export interface RowRule extends google.bigtable.v2.IReadModifyWriteRule { + column?: string; + append?: boolean; + increment?: number|Long; +} + +export interface RowData { + // tslint:disable-next-line no-any + data?: any; + key?: string|Buffer; +} + +export interface UserOptions { + decode?: boolean; + encoding?: string; +} + +export interface UnformattedFamily { + name: string; + columns: [{ + qualifier: string; + cells: Chunk[]; + }]; +} + /** * Create a Row object to interact with your table rows. * @@ -50,11 +82,11 @@ export class RowError extends Error { * const row = table.row('gwashington'); */ export class Row { - bigtable; - table; - id; - data; - constructor(table, key) { + bigtable: Bigtable; + table: Table; + id: string; + data: google.bigtable.v2.IRow; + constructor(table: Table, key: string) { this.bigtable = table.bigtable; this.table = table; this.id = key; @@ -91,23 +123,25 @@ export class Row { * // } * // } */ - static formatChunks_(chunks, options) { + static formatChunks_(chunks: Chunk[], options: UserOptions) { + // tslint:disable-next-line no-any const rows: any[] = []; - let familyName; - let qualifierName; + let familyName: string|null; + let qualifierName: string|null; options = options || {}; - chunks.reduce((row, chunk) => { - let family; - let qualifier; + chunks.reduce((row: RowData, chunk: Chunk) => { + let family!: Family; + let qualifier!: Qualifier|Qualifier[]; - row.data = row.data || {}; + row.data = row.data! || {}; if (chunk.rowKey) { - row.key = Mutation.convertFromBytes(chunk.rowKey, { + row.key = Mutation.convertFromBytes(chunk.rowKey as string, { userOptions: options, - }); + }) as string | + Buffer; } if (chunk.familyName) { @@ -119,18 +153,24 @@ export class Row { } if (chunk.qualifier) { - qualifierName = Mutation.convertFromBytes(chunk.qualifier.value, { - userOptions: options, - }); + qualifierName = + Mutation.convertFromBytes(chunk.qualifier.value as string, { + userOptions: options, + }) as string; } if (family && qualifierName) { - qualifier = family[qualifierName] = family[qualifierName] || []; + // tslint:disable-next-line no-any + qualifier = (family as any)[qualifierName] = + // tslint:disable-next-line no-any + (family as any)[qualifierName] || []; } if (qualifier && chunk.value) { - qualifier.push({ - value: Mutation.convertFromBytes(chunk.value, {userOptions: options}), + (qualifier as Qualifier[]).push({ + value: Mutation.convertFromBytes( + chunk.value, {userOptions: options}) as string | + Buffer, labels: chunk.labels, timestamp: chunk.timestampMicros, size: chunk.valueSize, @@ -188,17 +228,22 @@ export class Row { * // } * // } */ - static formatFamilies_(families, options?) { + static formatFamilies_( + families: google.bigtable.v2.IFamily[], options?: UserOptions) { const data = {}; options = options || {}; families.forEach(family => { - const familyData = (data[family.name] = {}); - family.columns.forEach(column => { + // tslint:disable-next-line no-any + const familyData = ((data as any)[family.name!] = {}); + (family as {} as UnformattedFamily).columns.forEach(column => { const qualifier = Mutation.convertFromBytes(column.qualifier); - familyData[qualifier as any] = column.cells.map(cell => { + // tslint:disable-next-line no-any + (familyData as any)[qualifier as any] = column.cells.map(cell => { let value = cell.value; - if (options.decode !== false) { - value = Mutation.convertFromBytes(value, {isPossibleNumber: true}); + if (options!.decode !== false) { + value = Mutation.convertFromBytes( + value!, {isPossibleNumber: true}) as string | + Buffer; } return { value, @@ -228,28 +273,35 @@ export class Row { * @example include:samples/document-snippets/row.js * region_tag:bigtable_create_row */ - create(options, callback) { - if (is.function(options)) - { - callback = options; - options = {}; - } - - const entry = { - key: this.id, - data: options.entry, - method: Mutation.methods.INSERT, - }; - this.data = {}; + create(options?: CreateRowOptions): Promise; + create(callback: CreateRowCallback): void; + create(options: CreateRowOptions, callback: CreateRowCallback): void; + create( + optionsOrCallback?: CreateRowOptions|CreateRowCallback, + callback?: CreateRowCallback): void|Promise { + const options = + typeof optionsOrCallback === 'object' ? optionsOrCallback : {}; + callback = + typeof optionsOrCallback === 'function' ? optionsOrCallback : callback; + + const entry = { + key: this.id, + data: options.entry, + method: Mutation.methods.INSERT, + }; + this.data = {}; - this.table.mutate(entry, options.gaxOptions, (err, apiResponse) => { - if (err) { - callback(err, null, apiResponse); - return; - } + this.table.mutate( + entry, options.gaxOptions as MutateTableRowsOptions, + // tslint:disable-next-line no-any + (err, apiResponse: any) => { + if (err) { + callback!(err, null, apiResponse); + return; + } - callback(null, this, apiResponse); - }); + callback!(null, this, apiResponse); + }); } /** @@ -270,49 +322,59 @@ export class Row { * @example include:samples/document-snippets/row.js * region_tag:bigtable_create_rules */ - createRules(rules, gaxOptions, callback) { - if (is.fn(gaxOptions)) { - callback = gaxOptions; - gaxOptions = {}; + createRules(rules: RowRule|RowRule[], gaxOptions?: CallOptions): + Promise; + createRules(rules: RowRule|RowRule[], callback: CreateRulesCallback): void; + createRules( + rules: RowRule|RowRule[], gaxOptions: CallOptions, + callback: CreateRulesCallback): void; + createRules( + rules: RowRule|RowRule[], + gaxOptionsOrcallback?: CallOptions|CreateRulesCallback, + callback?: CreateRulesCallback): void|Promise { + const gaxOptions = + typeof gaxOptionsOrcallback === 'object' ? gaxOptionsOrcallback : {}; + callback = typeof gaxOptionsOrcallback === 'function' ? + gaxOptionsOrcallback : + callback; + + if (!rules || (rules as RowRule[]).length === 0) { + throw new Error('At least one rule must be provided.'); + } + + rules = arrify(rules).map(rule => { + const column = Mutation.parseColumnName(rule.column!); + const ruleData = { + familyName: column.family, + columnQualifier: Mutation.convertToBytes(column.qualifier!), + } as google.bigtable.v2.IReadModifyWriteRule; + + if (rule.append) { + ruleData.appendValue = + Mutation.convertToBytes(rule.append) as Uint8Array; } - - if (!rules || rules.length === 0) { - throw new Error('At least one rule must be provided.'); + if (rule.increment) { + ruleData.incrementAmount = rule.increment; } - rules = arrify(rules).map(rule => { - const column = Mutation.parseColumnName(rule.column); - const ruleData: any = { - familyName: column.family, - columnQualifier: Mutation.convertToBytes(column.qualifier!), - }; - - if (rule.append) { - ruleData.appendValue = Mutation.convertToBytes(rule.append); - } - - if (rule.increment) { - ruleData.incrementAmount = rule.increment; - } - - return ruleData; - }); + return ruleData; + }); - const reqOpts = { - tableName: this.table.name, - appProfileId: this.bigtable.appProfileId, - rowKey: Mutation.convertToBytes(this.id), - rules, - }; - this.data = {}; - this.bigtable.request( - { - client: 'BigtableClient', - method: 'readModifyWriteRow', - reqOpts, - gaxOpts: gaxOptions, - }, - callback); + const reqOpts = { + tableName: this.table.name, + appProfileId: this.bigtable.appProfileId, + rowKey: Mutation.convertToBytes(this.id), + rules, + }; + this.data = {}; + this.bigtable.request( + { + client: 'BigtableClient', + method: 'readModifyWriteRow', + reqOpts, + gaxOpts: gaxOptions, + }, + callback); } /** @@ -328,18 +390,25 @@ export class Row { * @example include:samples/document-snippets/row.js * region_tag:bigtable_delete_all_cells */ - delete(gaxOptions, callback) { - if (is.fn(gaxOptions)) { - callback = gaxOptions; - gaxOptions = {}; - } - - const mutation = { - key: this.id, - method: Mutation.methods.DELETE, - }; - this.data = {}; - this.table.mutate(mutation, gaxOptions, callback); + delete(gaxOptions?: CallOptions): Promise; + delete(callback: DeleteRowCallback): void; + delete(gaxOptions: CallOptions, callback: DeleteRowCallback): void; + delete( + gaxOptionsOrcallback?: CallOptions|DeleteRowCallback, + callback?: DeleteRowCallback): void|Promise { + const gaxOptions = + typeof gaxOptionsOrcallback === 'object' ? gaxOptionsOrcallback : {}; + callback = typeof gaxOptionsOrcallback === 'function' ? + gaxOptionsOrcallback : + callback; + + const mutation = { + key: this.id, + method: Mutation.methods.DELETE, + }; + this.data = {}; + this.table.mutate( + mutation, gaxOptions as MutateTableRowsOptions, callback!); } /** @@ -356,19 +425,30 @@ export class Row { * @example include:samples/document-snippets/row.js * region_tag:bigtable_delete_particular_cells */ - deleteCells(columns, gaxOptions, callback) { - if (is.fn(gaxOptions)) { - callback = gaxOptions; - gaxOptions = {}; - } - - const mutation = { - key: this.id, - data: arrify(columns), - method: Mutation.methods.DELETE, - }; - this.data = {}; - this.table.mutate(mutation, gaxOptions, callback); + deleteCells(columns: string[], gaxOptions?: CallOptions): + Promise; + deleteCells(columns: string[], callback: DeleteRowCellsCallback): void; + deleteCells( + columns: string[], gaxOptions: CallOptions, + callback: DeleteRowCellsCallback): void; + deleteCells( + columns: string[], + gaxOptionsOrcallback?: CallOptions|DeleteRowCellsCallback, + callback?: DeleteRowCellsCallback): void|Promise { + const gaxOptions = + typeof gaxOptionsOrcallback === 'object' ? gaxOptionsOrcallback : {}; + callback = typeof gaxOptionsOrcallback === 'function' ? + gaxOptionsOrcallback : + callback; + + const mutation = { + key: this.id, + data: arrify(columns), + method: Mutation.methods.DELETE, + }; + this.data = {}; + this.table.mutate( + mutation, gaxOptions as MutateTableRowsOptions, callback!); } /** @@ -384,25 +464,31 @@ export class Row { * @example include:samples/document-snippets/row.js * region_tag:bigtable_row_exists */ - exists(gaxOptions, callback) { - if (is.fn(gaxOptions)) { - callback = gaxOptions; - gaxOptions = {}; - } - - this.getMetadata(gaxOptions, err => { - if (err) { - if (err instanceof RowError) { - callback(null, false); - return; - } - - callback(err); + exists(gaxOptions?: CallOptions): Promise; + exists(callback: ExistsCallback): void; + exists(gaxOptions: CallOptions, callback: ExistsCallback): void; + exists( + gaxOptionsOrcallback?: CallOptions|ExistsCallback, + callback?: ExistsCallback): void|Promise { + const gaxOptions = + typeof gaxOptionsOrcallback === 'object' ? gaxOptionsOrcallback : {}; + callback = typeof gaxOptionsOrcallback === 'function' ? + gaxOptionsOrcallback : + callback; + + this.getMetadata(gaxOptions as GetRowOptions, err => { + if (err) { + if (err instanceof RowError) { + callback!(null, false); return; } - callback(null, true); - }); + callback!(err); + return; + } + + callback!(null, true); + }); } /** @@ -426,36 +512,45 @@ export class Row { * @example include:samples/document-snippets/row.js * region_tag:bigtable_row_filter */ - filter(filter, config, callback) { - const reqOpts = { - tableName: this.table.name, - appProfileId: this.bigtable.appProfileId, - rowKey: Mutation.convertToBytes(this.id), - predicateFilter: Filter.parse(filter), - trueMutations: createFlatMutationsList(config.onMatch), - falseMutations: createFlatMutationsList(config.onNoMatch), - }; - this.data = {}; - this.bigtable.request( - { - client: 'BigtableClient', - method: 'checkAndMutateRow', - reqOpts, - gaxOpts: config.gaxOptions, - }, - (err, apiResponse) => { - if (err) { - callback(err, null, apiResponse); - return; - } - - callback(null, apiResponse.predicateMatched, apiResponse); - }); - - function createFlatMutationsList(entries) { - entries = arrify(entries).map(entry => Mutation.parse(entry).mutations); - return entries.reduce((a, b) => a.concat(b), []); - } + filter(filter: Filter, config: FilterRowConfigOptions): + Promise; + filter( + filter: Filter, config: FilterRowConfigOptions, + callback: FilterRowCallback): void; + filter( + filter: Filter, config: FilterRowConfigOptions, + callback?: FilterRowCallback): void|Promise { + const reqOpts = { + tableName: this.table.name, + appProfileId: this.bigtable.appProfileId, + rowKey: Mutation.convertToBytes(this.id), + predicateFilter: Filter.parse(filter), + trueMutations: createFlatMutationsList(config.onMatch!), + falseMutations: createFlatMutationsList(config.onNoMatch!), + }; + this.data = {}; + this.bigtable.request( + { + client: 'BigtableClient', + method: 'checkAndMutateRow', + reqOpts, + gaxOpts: config.gaxOptions, + }, + (err: Error, + apiResponse: google.bigtable.v2.CheckAndMutateRowResponse) => { + if (err) { + callback!(err, null, apiResponse); + return; + } + + callback!(null, apiResponse.predicateMatched, apiResponse); + }); + + function createFlatMutationsList(entries: IMutation[]) { + entries = arrify(entries).map( + entry => Mutation.parse(entry as Mutation).mutations) as []; + return entries.reduce((a, b) => a.concat(b as []), []); + } } /** @@ -475,75 +570,83 @@ export class Row { * @example include:samples/document-snippets/row.js * region_tag:bigtable_get_row */ - get(columns, options, callback?) { - if (!is.array(columns)) { - callback = options; - options = columns; - columns = []; - } - - if (is.function(options)) - { - callback = options; - options = {}; + get(columns: string[], options: GetRowOptions, + callback: GetRowCallback): void; + get(options?: GetRowOptions): Promise; + get(columns: string[], options?: GetRowOptions): Promise; + get(callback: GetRowCallback): void; + get(options: GetRowOptions, callback: GetRowCallback): void; + get(columns: string[], callback: GetRowCallback): void; + get(columnsOrOpts?: string[]|GetRowOptions|GetRowCallback, + optionsOrCallback?: GetRowOptions|GetRowCallback, + callback?: GetRowCallback): void|Promise { + let columns = (is.array(columnsOrOpts) ? columnsOrOpts : []) as string[]; + const options = + (!is.array(columnsOrOpts) ? + columnsOrOpts : + typeof optionsOrCallback === 'object' ? optionsOrCallback : {}) as + GetRowOptions; + callback = typeof columnsOrOpts === 'function' ? + columnsOrOpts : + typeof optionsOrCallback === 'function' ? optionsOrCallback : callback; + + let filter!: Array<{}>; + columns = arrify(columns); + + // if there is column filter + if (columns.length) { + const filters = columns.map(Mutation.parseColumnName).map(column => { + // tslint:disable-next-line no-any + const colmFilters: any = [{family: column.family}]; + if (column.qualifier) { + colmFilters.push({column: column.qualifier}); } + return colmFilters; + }); - let filter; - columns = arrify(columns); - - // if there is column filter - if (columns.length) { - const filters = columns.map(Mutation.parseColumnName).map(column => { - const colmFilters: any = [{family: column.family}]; - if (column.qualifier) { - colmFilters.push({column: column.qualifier}); - } - return colmFilters; - }); - - // if there is more then one filter, make it type inteleave filter - if (filters.length > 1) { - filter = [ - { - interleave: filters, - }, - ]; - } else { - filter = filters[0]; - } - } + // if there is more then one filter, make it type inteleave filter + if (filters.length > 1) { + filter = [ + { + interleave: filters, + }, + ]; + } else { + filter = filters[0]; + } + } - // if there is also a second option.filter append to filter array - if (options.filter) { - filter = arrify(filter).concat(options.filter); - } + // if there is also a second option.filter append to filter array + if (options.filter) { + filter = arrify(filter).concat(options.filter); + } - const getRowsOptions = Object.assign({}, options, { - keys: [this.id], - filter, - }); + const getRowsOptions = Object.assign({}, options, { + keys: [this.id], + filter, + }); - this.table.getRows(getRowsOptions, (err, rows) => { - if (err) { - callback(err); - return; - } + this.table.getRows(getRowsOptions as GetTableRowsOptions, (err, rows) => { + if (err) { + callback!(err); + return; + } - const row = rows[0]; + const row = rows![0]; - if (!row) { - err = new RowError(this.id); - callback(err); - return; - } + if (!row) { + err = new RowError(this.id); + callback!(err); + return; + } - this.data = row.data; + this.data = row.data; - // If the user specifies column names, we'll return back the row data - // we received. Otherwise, we'll return the row "this" in a typical - // GrpcServiceObject#get fashion. - callback(null, columns.length ? row.data : this); - }); + // If the user specifies column names, we'll return back the row data + // we received. Otherwise, we'll return the row "this" in a typical + // GrpcServiceObject#get fashion. + callback!(null, (columns.length ? row.data : this) as Row); + }); } /** @@ -562,21 +665,25 @@ export class Row { * @example include:samples/document-snippets/row.js * region_tag:bigtable_get_row_meta */ - getMetadata(options, callback) { - if (is.function(options)) - { - callback = options; - options = {}; - } - - this.get(options, (err, row) => { - if (err) { - callback(err); - return; - } + getMetadata(options?: GetRowOptions): Promise; + getMetadata(callback: GetRowMetadataCallback): void; + getMetadata(options: GetRowOptions, callback: GetRowMetadataCallback): void; + getMetadata( + optionsOrCallback?: GetRowOptions|GetRowMetadataCallback, + callback?: GetRowMetadataCallback): void|Promise { + const options = + typeof optionsOrCallback === 'object' ? optionsOrCallback : {}; + callback = + typeof optionsOrCallback === 'function' ? optionsOrCallback : callback; + // tslint:disable-next-line no-any + this.get(options, (err, row: any) => { + if (err) { + callback!(err); + return; + } - callback(null, row.metadata); - }); + callback!(null, row.metadata); + }); } /** @@ -596,46 +703,49 @@ export class Row { * @example include:samples/document-snippets/row.js * region_tag:bigtable_row_increment */ - increment(column, value, gaxOptions, callback) { - // increment('column', callback) - if (is.function(value)) - { - callback = value; - value = 1; - gaxOptions = {}; - } - - // increment('column', value, callback) - if (is.function(gaxOptions)) - { - callback = gaxOptions; - gaxOptions = {}; - } - - // increment('column', { gaxOptions }, callback) - if (is.object(value)) { - callback = gaxOptions; - gaxOptions = value; - value = 1; - } - - const reqOpts = { - column, - increment: value, - }; - - this.createRules(reqOpts, gaxOptions, (err, resp) => { - if (err) { - callback(err, null, resp); - return; - } - - const data = Row.formatFamilies_(resp.row.families); - const value = - dotProp.get(data, column.replace(':', '.'))[0].value; - - callback(null, value, resp); - }); + increment(column: string, gaxOptions?: CallOptions): + Promise; + increment(column: string, value: number, gaxOptions?: CallOptions): + Promise; + increment( + column: string, gaxOptions: CallOptions, + callback: IncrementRowCallback): void; + increment(column: string, callback: IncrementRowCallback): void; + increment(column: string, value: number, callback: IncrementRowCallback): + void; + increment( + column: string, value: number, gaxOptions: CallOptions, + callback: IncrementRowCallback): void; + increment( + column: string, valueOrOptsOrCb?: number|CallOptions|IncrementRowCallback, + gaxOptionsOrcallback?: CallOptions|IncrementRowCallback, + callback?: IncrementRowCallback): void|Promise { + const value = typeof valueOrOptsOrCb === 'number' ? valueOrOptsOrCb : 1; + const gaxOptions = typeof valueOrOptsOrCb === 'object' ? + valueOrOptsOrCb : + typeof gaxOptionsOrcallback === 'object' ? gaxOptionsOrcallback : {}; + callback = typeof valueOrOptsOrCb === 'function' ? + valueOrOptsOrCb : + typeof gaxOptionsOrcallback === 'function' ? gaxOptionsOrcallback : + callback; + + + const reqOpts = { + column, + increment: value, + } as RowRule; + + this.createRules(reqOpts, gaxOptions, (err, resp) => { + if (err) { + callback!(err, null, resp); + return; + } + + const data = Row.formatFamilies_(resp!.row!.families!); + const value = dotProp.get(data, column.replace(':', '.'))[0].value; + + callback!(null, value, resp); + }); } /** @@ -653,19 +763,26 @@ export class Row { * @example include:samples/document-snippets/row.js * region_tag:bigtable_row_save */ - save(entry, gaxOptions, callback) { - if (is.fn(gaxOptions)) { - callback = gaxOptions; - gaxOptions = {}; - } - - const mutation = { - key: this.id, - data: entry, - method: Mutation.methods.INSERT, - }; - this.data = {}; - this.table.mutate(mutation, gaxOptions, callback); + save(entry: Entry, gaxOptions?: CallOptions): Promise; + save(entry: Entry, callback: SaveRowCallback): void; + save(entry: Entry, gaxOptions: CallOptions, callback: SaveRowCallback): void; + save( + entry: Entry, gaxOptionsOrcallback?: CallOptions|SaveRowCallback, + callback?: SaveRowCallback): void|Promise { + const gaxOptions = + typeof gaxOptionsOrcallback === 'object' ? gaxOptionsOrcallback : {}; + callback = typeof gaxOptionsOrcallback === 'function' ? + gaxOptionsOrcallback : + callback; + + const mutation = { + key: this.id, + data: entry, + method: Mutation.methods.INSERT, + }; + this.data = {}; + this.table.mutate( + mutation, gaxOptions as MutateTableRowsOptions, callback!); } } diff --git a/system-test/bigtable.ts b/system-test/bigtable.ts index ea8f02de5..0c09455d9 100644 --- a/system-test/bigtable.ts +++ b/system-test/bigtable.ts @@ -15,7 +15,7 @@ */ import * as assert from 'assert'; -import Q = require('p-queue'); +const Q = require('p-queue'); import * as uuid from 'uuid'; import {Bigtable} from '../src'; From 0d0cc06b349625577b1a53624201c6a4c3bb988f Mon Sep 17 00:00:00 2001 From: Praveen Kumar Singh Date: Wed, 29 May 2019 18:15:32 +0530 Subject: [PATCH 2/2] refactor(typescript): noImplicitAny for row file in test folder --- test/row.ts | 240 +++++++++++++++++++++++++++++----------------------- 1 file changed, 132 insertions(+), 108 deletions(-) diff --git a/test/row.ts b/test/row.ts index dc4dea289..900e72263 100644 --- a/test/row.ts +++ b/test/row.ts @@ -17,15 +17,24 @@ import * as promisify from '@google-cloud/promisify'; import * as assert from 'assert'; import * as proxyquire from 'proxyquire'; +import * as sn from 'sinon'; + +import {google} from '../proto/bigtable'; +import {CreateRowOptions, Entry, FilterRowConfigOptions, GetRowCallback, GetRowOptions} from '../src'; +import {Chunk} from '../src/chunktransformer'; +import {Family} from '../src/family'; +import {Filter} from '../src/filter'; +// const sn = require('sinon'); +import {Bytes, Data, Mutation} from '../src/mutation.js'; +import * as RowTypes from '../src/row'; +import {Table} from '../src/table'; -const sn = require('sinon'); -import {Mutation} from '../src/mutation.js'; const sinon = sn.createSandbox(); let promisified = false; const fakePromisify = Object.assign({}, promisify, { - promisifyAll(Class) { + promisifyAll(Class: typeof RowTypes.Row) { if (Class.name === 'Row') { promisified = true; } @@ -37,39 +46,41 @@ const CONVERTED_ROW_ID = 'my-converted-row'; const TABLE = { bigtable: {}, name: '/projects/project/instances/my-instance/tables/my-table', -}; +} as Table; -const FakeMutation = { +// tslint:disable-next-line no-any +const FakeMutation: any = { methods: Mutation.methods, - convertToBytes: sinon.spy(function(value) { + convertToBytes: sinon.spy(function(value: Buffer|Data) { if (value === ROW_ID) { return CONVERTED_ROW_ID; } return value; }), - convertFromBytes: sinon.spy(function(value) { + convertFromBytes: sinon.spy(function(value: string) { return value; }), - parseColumnName: sinon.spy(function(column) { + parseColumnName: sinon.spy(function(column: string) { return Mutation.parseColumnName(column); }), - parse: sinon.spy(function(entry) { + parse: sinon.spy(function(entry: Mutation) { return { mutations: entry, }; }), }; -const FakeFilter = { - parse: sinon.spy(function(filter) { +// tslint:disable-next-line no-any +const FakeFilter: any = { + parse: sinon.spy(function(filter: Filter) { return filter; }), }; describe('Bigtable/Row', function() { - let Row; - let RowError; - let row; + let Row: typeof RowTypes.Row; + let RowError: typeof RowTypes.RowError; + let row: RowTypes.Row; before(function() { const Fake = proxyquire('../src/row.js', { @@ -120,7 +131,7 @@ describe('Bigtable/Row', function() { beforeEach(function() { convert = FakeMutation.convertFromBytes; - FakeMutation.convertFromBytes = sinon.spy(function(val) { + FakeMutation.convertFromBytes = sinon.spy(function(val: string) { return val.replace('unconverted', 'converted'); }); }); @@ -151,8 +162,8 @@ describe('Bigtable/Row', function() { commitRow: true, }, ]; - - const rows = Row.formatChunks_(chunks); + // tslint:disable-next-line no-any + const rows = (Row as any).formatChunks_(chunks); assert.deepStrictEqual(rows, [ { @@ -196,8 +207,8 @@ describe('Bigtable/Row', function() { commitRow: true, }, ]; - - const rows = Row.formatChunks_(chunks); + // tslint:disable-next-line no-any + const rows = (Row as any).formatChunks_(chunks); assert.deepStrictEqual(rows, [ { @@ -237,8 +248,8 @@ describe('Bigtable/Row', function() { commitRow: true, }, ]; - - const rows = Row.formatChunks_(chunks); + // tslint:disable-next-line no-any + const rows = (Row as any).formatChunks_(chunks); assert.deepStrictEqual(rows, [ { @@ -283,8 +294,8 @@ describe('Bigtable/Row', function() { commitRow: true, }, ]; - - const rows = Row.formatChunks_(chunks); + // tslint:disable-next-line no-any + const rows = (Row as any).formatChunks_(chunks); assert.deepStrictEqual(rows, [ { @@ -316,10 +327,12 @@ describe('Bigtable/Row', function() { decode: false, }; - FakeMutation.convertFromBytes = sinon.spy(function(val, options) { - assert.deepStrictEqual(options, {userOptions: formatOptions}); - return val.replace('unconverted', 'converted'); - }); + // tslint:disable-next-line no-any + FakeMutation.convertFromBytes = + sinon.spy(function(val: string, options: any) { + assert.deepStrictEqual(options, {userOptions: formatOptions}); + return val.replace('unconverted', 'converted'); + }); const timestamp1 = 123; const timestamp2 = 345; @@ -349,7 +362,7 @@ describe('Bigtable/Row', function() { { commitRow: true, }, - ]; + ] as Chunk[]; const rows = Row.formatChunks_(chunks, formatOptions); @@ -380,7 +393,7 @@ describe('Bigtable/Row', function() { // 0 === row key // 1 === qualifier // 2 === value - const args = FakeMutation.convertFromBytes.getCall(2).args; + const args: string[] = FakeMutation.convertFromBytes.getCall(2).args; assert.deepStrictEqual(args[1], {userOptions: formatOptions}); }); @@ -408,7 +421,7 @@ describe('Bigtable/Row', function() { labels: ['label'], timestampMicros: 123, commitRow: true, - }, + } as {} as Chunk, ]; const rows = Row.formatChunks_(chunks, formatOptions); @@ -473,8 +486,8 @@ describe('Bigtable/Row', function() { commitRow: true, }, ]; - - const rows = Row.formatChunks_(chunks); + // tslint:disable-next-line no-any + const rows = (Row as any).formatChunks_(chunks); assert.deepStrictEqual(rows, [ { @@ -514,7 +527,7 @@ describe('Bigtable/Row', function() { ], }, ], - }, + } as {} as Family, ]; const formattedRowData = { @@ -571,7 +584,7 @@ describe('Bigtable/Row', function() { a: 'a', b: 'b', }, - }; + } as CreateRowOptions; row.table.mutate = function(entry) { assert.strictEqual(entry.data, options.entry); @@ -632,12 +645,13 @@ describe('Bigtable/Row', function() { column: 'a:b', append: 'c', increment: 1, - }, + } as {} as RowTypes.RowRule, ]; it('should throw if a rule is not provided', function() { assert.throws(function() { - row.createRules(); + // tslint:disable-next-line no-any + (row as any).createRules(); }, /At least one rule must be provided\./); }); @@ -768,31 +782,29 @@ describe('Bigtable/Row', function() { describe('exists', function() { it('should not require gaxOptions', function(done) { - row.getMetadata = function(gaxOptions) { + sinon.stub(row, 'getMetadata').callsFake((gaxOptions) => { assert.deepStrictEqual(gaxOptions, {}); done(); - }; + }); row.exists(assert.ifError); }); it('should pass gaxOptions to getMetadata', function(done) { const gaxOptions = {}; - - row.getMetadata = function(gaxOptions_) { + sinon.stub(row, 'getMetadata').callsFake((gaxOptions_) => { assert.strictEqual(gaxOptions_, gaxOptions); done(); - }; + }); row.exists(gaxOptions, assert.ifError); }); it('should return false if error is RowError', function(done) { const error = new RowError('Error.'); - - row.getMetadata = function(gaxOptions, callback) { + sinon.stub(row, 'getMetadata').callsFake((gaxOptions, callback) => { callback(error); - }; + }); row.exists(function(err, exists) { assert.ifError(err); @@ -803,10 +815,10 @@ describe('Bigtable/Row', function() { it('should return error if not RowError', function(done) { const error = new Error('Error.'); - - row.getMetadata = function(gaxOptions, callback) { + sinon.stub(row, 'getMetadata').callsFake((gaxOptions, callback) => { callback(error); - }; + }); + row.exists(function(err) { assert.strictEqual(err, error); @@ -815,9 +827,10 @@ describe('Bigtable/Row', function() { }); it('should return true if no error', function(done) { - row.getMetadata = function(gaxOptions, callback) { + sinon.stub(row, 'getMetadata').callsFake((gaxOptions, callback) => { callback(null, {}); - }; + }); + row.exists(function(err, exists) { assert.ifError(err); @@ -834,7 +847,7 @@ describe('Bigtable/Row', function() { data: { a: 'a', }, - }, + } as {} as google.bigtable.v2.IMutation, ]; const fakeMutations = { @@ -853,7 +866,7 @@ describe('Bigtable/Row', function() { it('should provide the proper request options', function(done) { const filter = { column: 'a', - }; + } as {} as Filter; const fakeParsedFilter = { column: 'b', @@ -902,7 +915,7 @@ describe('Bigtable/Row', function() { it('should accept gaxOptions', function(done) { const filter = { column: 'a', - }; + } as {} as Filter; const gaxOptions = {}; row.bigtable.request = function(config) { @@ -910,13 +923,14 @@ describe('Bigtable/Row', function() { done(); }; - row.filter(filter, {gaxOptions}, assert.ifError); + row.filter( + filter, {gaxOptions} as FilterRowConfigOptions, assert.ifError); }); it('should use an appProfileId', function(done) { const filter = { column: 'a', - }; + } as {} as Filter; const bigtableInstance = row.bigtable; bigtableInstance.appProfileId = 'app-profile-id-12345'; @@ -926,8 +940,8 @@ describe('Bigtable/Row', function() { config.reqOpts.appProfileId, bigtableInstance.appProfileId); done(); }; - - row.filter(filter, assert.ifError); + // tslint:disable-next-line no-any + (row as any).filter(filter, assert.ifError); }); it('should return an error to the callback', function(done) { @@ -937,13 +951,18 @@ describe('Bigtable/Row', function() { row.bigtable.request = function(config, callback) { callback(err, response); }; - - row.filter({}, mutations, function(err_, matched, apiResponse) { - assert.strictEqual(err, err_); - assert.strictEqual(matched, null); - assert.strictEqual(response, apiResponse); - done(); - }); + // tslint:disable-next-line no-any + (row as any) + .filter( + {}, mutations, + function( + err_: Error, matched: boolean, + apiResponse: google.bigtable.v2.CheckAndMutateRowResponse) { + assert.strictEqual(err, err_); + assert.strictEqual(matched, null); + assert.strictEqual(response, apiResponse); + done(); + }); }); it('should return a matched flag', function(done) { @@ -954,13 +973,18 @@ describe('Bigtable/Row', function() { row.bigtable.request = function(config, callback) { callback(null, response); }; - - row.filter({}, mutations, function(err, matched, apiResponse) { - assert.ifError(err); - assert(matched); - assert.strictEqual(response, apiResponse); - done(); - }); + // tslint:disable-next-line no-any + (row as any) + .filter( + {}, mutations, + function( + err: Error, matched: boolean, + apiResponse: google.bigtable.v2.CheckAndMutateRowResponse) { + assert.ifError(err); + assert(matched); + assert.strictEqual(response, apiResponse); + done(); + }); }); }); @@ -1059,7 +1083,7 @@ describe('Bigtable/Row', function() { it('should respect the options object', function(done) { const keys = ['a:b']; - + // tslint:disable-next-line no-any const options: any = { filter: [ { @@ -1099,7 +1123,7 @@ describe('Bigtable/Row', function() { it('should respect the options object with filter for multiple columns', function(done) { const keys = ['a:b', 'c:d']; - + // tslint:disable-next-line no-any const options: any = { filter: [ { @@ -1150,7 +1174,7 @@ describe('Bigtable/Row', function() { }); it('should respect filter in options object', function(done) { - const keys = []; + const keys: string[] = []; const options = { decode: false, @@ -1201,7 +1225,7 @@ describe('Bigtable/Row', function() { row.get(function(err, row_) { assert(err instanceof RowError); - assert.strictEqual(err.message, 'Unknown row: ' + row.id + '.'); + assert.strictEqual(err!.message, 'Unknown row: ' + row.id + '.'); assert.deepStrictEqual(row_, undefined); done(); }); @@ -1213,7 +1237,7 @@ describe('Bigtable/Row', function() { fakeRow.data = { a: 'a', b: 'b', - }; + } as google.bigtable.v2.IRow; row.table.getRows = function(r, callback) { callback(null, [fakeRow]); @@ -1233,13 +1257,13 @@ describe('Bigtable/Row', function() { fakeRow.data = { a: 'a', b: 'b', - }; + } as google.bigtable.v2.IRow; const keys = ['a', 'b']; row.data = { c: 'c', - }; + } as google.bigtable.v2.IRow; row.table.getRows = function(r, callback) { callback(null, [fakeRow]); @@ -1247,7 +1271,7 @@ describe('Bigtable/Row', function() { row.get(keys, function(err, data) { assert.ifError(err); - assert.deepStrictEqual(Object.keys(data), keys); + assert.deepStrictEqual(Object.keys(data!), keys); done(); }); }); @@ -1257,9 +1281,9 @@ describe('Bigtable/Row', function() { it('should return an error to the callback', function(done) { const error = new Error('err'); - row.get = function(options, callback) { + sinon.stub(row, 'get').callsFake((options, callback) => { callback(error); - }; + }); row.getMetadata(function(err, metadata) { assert.strictEqual(error, err); @@ -1274,11 +1298,11 @@ describe('Bigtable/Row', function() { b: 'b', }; - row.get = function(options, callback) { + sinon.stub(row, 'get').callsFake((options, callback) => { callback(null, row); - }; - - row.metadata = fakeMetadata; + }); + // tslint:disable-next-line no-any + (row as any).metadata = fakeMetadata; row.getMetadata(function(err, metadata) { assert.ifError(err); @@ -1293,12 +1317,12 @@ describe('Bigtable/Row', function() { decode: false, }; - row.get = function(options, callback) { + sinon.stub(row, 'get').callsFake((options, callback) => { assert.strictEqual(options, fakeOptions); callback(null, row); - }; - - row.metadata = fakeMetadata; + }); + // tslint:disable-next-line no-any + (row as any).metadata = fakeMetadata; row.getMetadata(fakeOptions, function(err, metadata) { assert.ifError(err); @@ -1310,7 +1334,7 @@ describe('Bigtable/Row', function() { describe('increment', function() { const COLUMN_NAME = 'a:b'; - let formatFamiliesSpy; + let formatFamiliesSpy: sn.SinonStub; beforeEach(function() { formatFamiliesSpy = sinon.stub(Row, 'formatFamilies_').returns({ @@ -1329,12 +1353,12 @@ describe('Bigtable/Row', function() { }); it('should provide the proper request options', function(done) { - row.createRules = function(reqOpts, gaxOptions) { - assert.strictEqual(reqOpts.column, COLUMN_NAME); - assert.strictEqual(reqOpts.increment, 1); + sinon.stub(row, 'createRules').callsFake((reqOpts, gaxOptions) => { + assert.strictEqual((reqOpts as RowTypes.RowRule).column, COLUMN_NAME); + assert.strictEqual((reqOpts as RowTypes.RowRule).increment, 1); assert.deepStrictEqual(gaxOptions, {}); done(); - }; + }); row.increment(COLUMN_NAME, assert.ifError); }); @@ -1342,10 +1366,10 @@ describe('Bigtable/Row', function() { it('should optionally accept an increment amount', function(done) { const increment = 10; - row.createRules = function(reqOpts) { - assert.strictEqual(reqOpts.increment, increment); + sinon.stub(row, 'createRules').callsFake((reqOpts) => { + assert.strictEqual((reqOpts as RowTypes.RowRule).increment, increment); done(); - }; + }); row.increment(COLUMN_NAME, increment, assert.ifError); }); @@ -1353,10 +1377,10 @@ describe('Bigtable/Row', function() { it('should accept gaxOptions', function(done) { const gaxOptions = {}; - row.createRules = function(reqOpts, gaxOptions_) { + sinon.stub(row, 'createRules').callsFake((reqOpts, gaxOptions_) => { assert.strictEqual(gaxOptions_, gaxOptions); done(); - }; + }); row.increment(COLUMN_NAME, gaxOptions, assert.ifError); }); @@ -1365,11 +1389,11 @@ describe('Bigtable/Row', function() { const increment = 10; const gaxOptions = {}; - row.createRules = function(reqOpts, gaxOptions_) { - assert.strictEqual(reqOpts.increment, increment); + sinon.stub(row, 'createRules').callsFake((reqOpts, gaxOptions_) => { + assert.strictEqual((reqOpts as RowTypes.RowRule).increment, increment); assert.strictEqual(gaxOptions_, gaxOptions); done(); - }; + }); row.increment(COLUMN_NAME, increment, gaxOptions, assert.ifError); }); @@ -1378,9 +1402,9 @@ describe('Bigtable/Row', function() { const error = new Error('err'); const response = {}; - row.createRules = function(r, gaxOptions, callback) { + sinon.stub(row, 'createRules').callsFake((r, gaxOptions, callback) => { callback(error, response); - }; + }); row.increment(COLUMN_NAME, function(err, value, apiResponse) { assert.strictEqual(err, error); @@ -1412,18 +1436,18 @@ describe('Bigtable/Row', function() { }, ], }, - }; + } as {} as google.bigtable.v2.IReadModifyWriteRowResponse; - row.createRules = function(r, gaxOptions, callback) { + sinon.stub(row, 'createRules').callsFake((r, gaxOptions, callback) => { callback(null, response); - }; + }); row.increment(COLUMN_NAME, function(err, value, apiResponse) { assert.ifError(err); assert.strictEqual(value, fakeValue); assert.strictEqual(apiResponse, response); assert.strictEqual(formatFamiliesSpy.callCount, 1); - assert(formatFamiliesSpy.calledWithExactly(response.row.families)); + assert(formatFamiliesSpy.calledWithExactly(response.row!.families)); done(); }); }); @@ -1434,7 +1458,7 @@ describe('Bigtable/Row', function() { a: { b: 'c', }, - }; + } as Entry; it('should insert an object', function(done) { row.table.mutate = function(entry, gaxOptions, callback) {