diff --git a/src/acl.ts b/src/acl.ts index c5ab20907..d73a8d2fd 100644 --- a/src/acl.ts +++ b/src/acl.ts @@ -20,6 +20,7 @@ import * as arrify from 'arrify'; import {promisifyAll} from '@google-cloud/promisify'; import * as extend from 'extend'; import * as is from 'is'; +import {DecorateRequestOptions, BodyResponseCallback} from '@google-cloud/common'; interface AclQuery { generation: number; @@ -757,7 +758,8 @@ class Acl extends AclRoleAccessorMethods { * @param {*} body Request body contents. * @param {function} callback Callback function. */ - request(reqOpts, callback) { + request(reqOpts: DecorateRequestOptions, callback: BodyResponseCallback): + void { reqOpts.uri = this.pathPrefix + reqOpts.uri; this.request_(reqOpts, callback); } diff --git a/src/bucket.ts b/src/bucket.ts index a86326051..7e4bdccee 100644 --- a/src/bucket.ts +++ b/src/bucket.ts @@ -18,7 +18,7 @@ import * as arrify from 'arrify'; import * as async from 'async'; -import {ExistsCallback, ServiceObject, Metadata, util, DeleteCallback, InstanceResponseCallback, GetConfig, GetMetadataCallback} from '@google-cloud/common'; +import {ExistsCallback, ServiceObject, Metadata, util, DeleteCallback, InstanceResponseCallback, GetConfig, GetMetadataCallback, DecorateRequestOptions, BodyResponseCallback} from '@google-cloud/common'; import {paginator} from '@google-cloud/paginator'; import {promisifyAll} from '@google-cloud/promisify'; import * as extend from 'extend'; @@ -26,7 +26,7 @@ import * as fs from 'fs'; import * as is from 'is'; import * as mime from 'mime-types'; import * as path from 'path'; -import * as snakeize from 'snakeize'; +const snakeize = require('snakeize'); import * as request from 'request'; import {Acl} from './acl'; @@ -54,6 +54,15 @@ interface BucketOptions { userProject?: string; } +/** + * @callback GetFilesCallback + * @param {?Error} err Request error, if any. + * @param {File[]} files Array of {@link File} instances. + */ +export interface GetFilesCallback { + (err: Error|null, files?: File[]): void; +} + /** * See a [Objects: * watchAll request @@ -128,7 +137,7 @@ export interface CombineOptions { * @param {object} apiResponse The full API response. */ export interface CombineCallback { - (err: Error|null, newFile: File|null, apiResponse: request.Response); + (err: Error|null, newFile: File|null, apiResponse: request.Response): void; } /** @@ -174,7 +183,7 @@ export type CreateChannelResponse = [Channel, request.Response]; * @param {object} apiResponse The full API response. */ export interface CreateChannelCallback { - (err: Error|null, channel: Channel|null, apiResponse: request.Response); + (err: Error|null, channel: Channel|null, apiResponse: request.Response): void; } /** @@ -214,7 +223,7 @@ export interface CreateNotificationOptions { */ export interface CreateNotificationCallback { (err: Error|null, notification: Notification|null, - apiResponse: request.Response); + apiResponse: request.Response): void; } /** @@ -245,7 +254,7 @@ export type DeleteBucketResponse = [request.Response]; * @param {object} apiResponse The full API response. */ export interface DeleteBucketCallback extends DeleteCallback { - (err: Error|null, apiResponse: request.Response); + (err: Error|null, apiResponse: request.Response): void; } /** @@ -265,7 +274,7 @@ export interface DeleteFilesOptions extends GetFilesOptions { * @param {object} [apiResponse] The full API response. */ export interface DeleteFilesCallback { - (err: Error|Error[]|null, apiResponse?: object); + (err: Error|Error[]|null, apiResponse?: object): void; } /** @@ -280,7 +289,7 @@ export type DeleteLabelsResponse = [request.Response]; * @param {object} apiResponse The full API response. */ export interface DeleteLabelsCallback { - (err: Error|null, apiResponse?: object); + (err: Error|null, apiResponse?: object): void; } /** @@ -295,7 +304,7 @@ export type DisableRequesterPaysResponse = [request.Response]; * @param {object} apiResponse The full API response. */ export interface DisableRequesterPaysCallback { - (err: Error|null, apiResponse?: object); + (err: Error|null, apiResponse?: object): void; } /** @@ -310,7 +319,7 @@ export type EnableRequesterPaysResponse = [request.Response]; * @param {object} apiResponse The full API response. */ export interface EnableRequesterPaysCallback { - (err: Error|null, apiResponse: request.Response); + (err: Error|null, apiResponse: request.Response): void; } /** @@ -360,7 +369,7 @@ export type GetBucketResponse = [Bucket, request.Response]; * @param {object} apiResponse The full API response. */ export interface GetBucketCallback extends InstanceResponseCallback { - (err: Error|null, bucket: Bucket|null, apiResponse: request.Response); + (err: Error|null, bucket: Bucket|null, apiResponse: request.Response): void; } /** @@ -384,7 +393,7 @@ export type GetLabelsResponse = [request.Response]; * @param {object} labels Object of labels currently set on this bucket. */ export interface GetLabelsCallback { - (err: Error|null, labels: object|null); + (err: Error|null, labels: object|null): void; } /** @@ -401,7 +410,8 @@ export type GetBucketMetadataResponse = [object, request.Response]; * @param {object} apiResponse The full API response. */ export interface GetBucketMetadataCallback extends GetMetadataCallback { - (err: Error|null, metadata: Metadata|null, apiResponse: request.Response); + (err: Error|null, metadata: Metadata|null, + apiResponse: request.Response): void; } /** @@ -431,7 +441,7 @@ export interface GetNotificationsOptions { */ export interface GetNotificationsCallback { (err: Error|null, notifications: Notification[]|null, - apiResponse: request.Response); + apiResponse: request.Response): void; } /** @@ -1201,7 +1211,7 @@ class Bucket extends ServiceObject { // Iterate through each file and attempt to delete it. async.eachLimit( - files, MAX_PARALLEL_LIMIT, deleteFile, err => { + files!, MAX_PARALLEL_LIMIT, deleteFile, err => { if (err || errors.length > 0) { callback!(err || errors); return; @@ -1551,11 +1561,6 @@ class Bucket extends ServiceObject { * @typedef {array} GetFilesResponse * @property {File[]} 0 Array of {@link File} instances. */ - /** - * @callback GetFilesCallback - * @param {?Error} err Request error, if any. - * @param {File[]} files Array of {@link File} instances. - */ /** * Get {@link File} objects for the files currently in the bucket. * @@ -1624,12 +1629,16 @@ class Bucket extends ServiceObject { * region_tag:storage_list_files_with_prefix * Example of listing files, filtered by a prefix: */ - getFiles(query: GetFilesOptions, callback?) { + getFiles(query?: GetFilesOptions): Promise<[File[]]>; + getFiles(query: GetFilesOptions, callback: GetFilesCallback): void; + getFiles(callback: GetFilesCallback): void; + getFiles( + queryOrCallback?: GetFilesOptions|GetFilesCallback, + callback?: GetFilesCallback): void|Promise<[File[]]> { + let query = typeof queryOrCallback === 'object' ? queryOrCallback : {}; if (!callback) { - callback = query; - query = {}; + callback = queryOrCallback as GetFilesCallback; } - query = extend({}, query); if (query.directory) { @@ -1644,7 +1653,8 @@ class Bucket extends ServiceObject { }, (err, resp) => { if (err) { - callback(err, null, null, resp); + // tslint:disable-next-line:no-any + (callback as any)(err, null, null, resp); return; } @@ -1671,8 +1681,8 @@ class Bucket extends ServiceObject { pageToken: resp.nextPageToken, }); } - - callback(null, files, nextQuery, resp); + // tslint:disable-next-line:no-any + (callback as any)(null, files, nextQuery, resp); }); } @@ -2145,13 +2155,15 @@ class Bucket extends ServiceObject { * @param {object} reqOpts - The request options. * @param {function} callback - The callback function. */ - request(reqOpts): Promise; - request(reqOpts, callback): void; - request(reqOpts, callback?): void|Promise { + request(reqOpts: DecorateRequestOptions): Promise; + request(reqOpts: DecorateRequestOptions, callback: BodyResponseCallback): + void; + request(reqOpts: DecorateRequestOptions, callback?: BodyResponseCallback): + void|Promise { if (this.userProject && (!reqOpts.qs || !reqOpts.qs.userProject)) { reqOpts.qs = extend(reqOpts.qs, {userProject: this.userProject}); } - return super.request(reqOpts, callback); + return super.request(reqOpts, callback!); } /** @@ -2691,7 +2703,7 @@ class Bucket extends ServiceObject { }; // Iterate through each file and make it public or private. - async.eachLimit(files, MAX_PARALLEL_LIMIT, processFile, err => { + async.eachLimit(files!, MAX_PARALLEL_LIMIT, processFile, err => { if (err || errors.length > 0) { callback(err || errors, updatedFiles); return; diff --git a/src/channel.ts b/src/channel.ts index fd5a72848..f04dec301 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -20,6 +20,16 @@ import {ServiceObject, util} from '@google-cloud/common'; import {promisifyAll} from '@google-cloud/promisify'; import * as request from 'request'; import {Storage} from '.'; +import {Response} from 'request'; + +/** + * @callback StopCallback + * @param {?Error} err Request error, if any. + * @param {object} apiResponse The full API response. + */ +export interface StopCallback { + (err: Error|null, apiResponse?: request.Response): void; +} /** * Create a channel object to interact with a Cloud Storage channel. @@ -66,11 +76,6 @@ class Channel extends ServiceObject { * @typedef {array} StopResponse * @property {object} 0 The full API response. */ - /** - * @callback StopCallback - * @param {?Error} err Request error, if any. - * @param {object} apiResponse The full API response. - */ /** * Stop this channel. * @@ -94,9 +99,10 @@ class Channel extends ServiceObject { * const apiResponse = data[0]; * }); */ - stop(callback?) { + stop(): Promise; + stop(callback: StopCallback): void; + stop(callback?: StopCallback): Promise|void { callback = callback || util.noop; - this.request( { method: 'POST', @@ -104,7 +110,7 @@ class Channel extends ServiceObject { json: this.metadata, }, (err, apiResponse) => { - callback(err, apiResponse); + callback!(err, apiResponse); }); } } diff --git a/src/file.ts b/src/file.ts index 6258de874..685110d05 100644 --- a/src/file.ts +++ b/src/file.ts @@ -16,7 +16,7 @@ 'use strict'; -import {ServiceObject, util} from '@google-cloud/common'; +import {ServiceObject, util, DecorateRequestOptions, BodyResponseCallback} from '@google-cloud/common'; import {promisifyAll} from '@google-cloud/promisify'; import compressible = require('compressible'); import concat = require('concat-stream'); @@ -24,14 +24,14 @@ import * as crypto from 'crypto'; import * as duplexify from 'duplexify'; import * as extend from 'extend'; import * as fs from 'fs'; -import * as hashStreamValidation from 'hash-stream-validation'; +const hashStreamValidation = require('hash-stream-validation'); import * as is from 'is'; import * as mime from 'mime'; import * as once from 'once'; import * as os from 'os'; const pumpify = require('pumpify'); import * as resumableUpload from 'gcs-resumable-upload'; -import {Stream, Duplex} from 'stream'; +import {Duplex, Writable} from 'stream'; import * as streamEvents from 'stream-events'; import * as through from 'through2'; import * as xdgBasedir from 'xdg-basedir'; @@ -42,6 +42,7 @@ import * as r from 'request'; import {Storage} from '.'; import {Bucket} from './bucket'; import {Acl} from './acl'; +import {ResponseBody} from '@google-cloud/common/build/src/util'; /** * Custom error type for errors related to creating a resumable upload. @@ -434,13 +435,14 @@ class File extends ServiceObject { * region_tag:storage_copy_file * Another example: */ - copy(destination: string|Bucket|File, callback: FileCallback); + copy(destination: string|Bucket|File, callback: FileCallback): void; copy( destination: string|Bucket|File, options: CopyOptions, - callback: FileCallback); + callback: FileCallback): void; copy( destination: string|Bucket|File, - optionsOrCallback: CopyOptions|FileCallback, callback?: FileCallback) { + optionsOrCallback: CopyOptions|FileCallback, + callback?: FileCallback): void { const noDestinationError = new Error('Destination file should have a name.'); @@ -458,9 +460,9 @@ class File extends ServiceObject { options = extend(true, {}, options); callback = callback || util.noop; - let destBucket; - let destName; - let newFile; + let destBucket: Bucket|File; + let destName: string; + let newFile: File; if (typeof destination === 'string') { const parsedDestination = GS_URL_REGEXP.exec(destination); @@ -495,9 +497,9 @@ class File extends ServiceObject { delete options.userProject; } - newFile = newFile || destBucket.file(destName); + newFile = newFile! || destBucket.file(destName); - const headers = {}; + const headers: {[index: string]: string|undefined} = {}; if (is.defined(this.encryptionKey)) { headers['x-goog-copy-source-encryption-algorithm'] = 'AES256'; @@ -653,7 +655,8 @@ class File extends ServiceObject { const rangeRequest = is.number(options.start) || is.number(options.end); const tailRequest = options.end! < 0; - let validateStream; // Created later, if necessary. + // tslint:disable-next-line:no-any + let validateStream: any; // Created later, if necessary. const throughStream = streamEvents(through()) as Duplex; let crc32c = true; @@ -722,7 +725,8 @@ class File extends ServiceObject { .on('response', res => { throughStream.emit('response', res); - util.handleResp(null, res, null, onResponse); + // tslint:disable-next-line:no-any + util.handleResp(null, res, null, onResponse as any); }) .resume(); @@ -735,44 +739,46 @@ class File extends ServiceObject { // which will return the bytes from the source without decompressing // gzip'd content. We then send it through decompressed, if // applicable, to the user. - const onResponse = (err, body, rawResponseStream) => { - if (err) { - // Get error message from the body. - rawResponseStream.pipe(concat(body => { - err.message = body.toString(); - throughStream.destroy(err); - })); - - return; - } - - const headers = rawResponseStream.toJSON().headers; - const isCompressed = headers['content-encoding'] === 'gzip'; + const onResponse = + (err: Error|null, body: ResponseBody, + rawResponseStream: r.Request) => { + if (err) { + // Get error message from the body. + rawResponseStream.pipe(concat(body => { + err.message = body.toString(); + throughStream.destroy(err); + })); - const shouldRunValidation = !rangeRequest && (crc32c || md5); + return; + } - const throughStreams = [] as Stream[]; + const headers = rawResponseStream.toJSON().headers; + const isCompressed = headers['content-encoding'] === 'gzip'; + const shouldRunValidation = !rangeRequest && (crc32c || md5); + const throughStreams: Writable[] = []; - if (shouldRunValidation) { - validateStream = hashStreamValidation({crc32c, md5}); - throughStreams.push(validateStream); - } + if (shouldRunValidation) { + validateStream = hashStreamValidation({crc32c, md5}); + throughStreams.push(validateStream); + } - if (isCompressed) { - throughStreams.push(zlib.createGunzip()); - } + if (isCompressed) { + throughStreams.push(zlib.createGunzip()); + } - if (throughStreams.length === 1) { - rawResponseStream = rawResponseStream.pipe(throughStreams[0]); - } else if (throughStreams.length > 1) { - rawResponseStream = - rawResponseStream.pipe(pumpify.obj(throughStreams)); - } + if (throughStreams.length === 1) { + rawResponseStream = + // tslint:disable-next-line:no-any + (rawResponseStream.pipe(throughStreams[0]) as any); + } else if (throughStreams.length > 1) { + rawResponseStream = + rawResponseStream.pipe(pumpify.obj(throughStreams)); + } - rawResponseStream.on('end', onComplete).pipe(throughStream, { - end: false - }); - }; + rawResponseStream.on('end', onComplete).pipe(throughStream, { + end: false + }); + }; // This is hooked to the `complete` event from the request stream. This is // our chance to validate the data and let the user know if anything went @@ -2260,14 +2266,15 @@ class File extends ServiceObject { * @param {object} reqOpts - The request options. * @param {function} callback - The callback function. */ - request(reqOpts): Promise; - request(reqOpts, callback): void; - request(reqOpts, callback?): void|Promise { + request(reqOpts: DecorateRequestOptions): Promise; + request(reqOpts: DecorateRequestOptions, callback: BodyResponseCallback): + void; + request(reqOpts: DecorateRequestOptions, callback?: BodyResponseCallback): + void|Promise { if (this.userProject && (!reqOpts.qs || !reqOpts.qs.userProject)) { reqOpts.qs = extend(reqOpts.qs, {userProject: this.userProject}); } - - return super.request(reqOpts, callback); + return super.request(reqOpts, callback!); } /** @@ -2581,11 +2588,10 @@ class File extends ServiceObject { }, options); - const reqOpts = { + const reqOpts: DecorateRequestOptions = { qs: { name: this.name, - // tslint:disable-next-line:no-any - } as any, + }, uri: `${STORAGE_UPLOAD_BASE_URL}/${this.bucket.name}/o`, }; @@ -2611,7 +2617,7 @@ class File extends ServiceObject { util.makeWritableStream(dup, { makeAuthenticatedRequest: reqOpts => { - this.request(reqOpts, (err, body, resp) => { + this.request(reqOpts as DecorateRequestOptions, (err, body, resp) => { if (err) { dup.destroy(err); return; diff --git a/src/notification.ts b/src/notification.ts index feded3782..6722a6c3c 100644 --- a/src/notification.ts +++ b/src/notification.ts @@ -16,11 +16,77 @@ 'use strict'; -import {ServiceObject, util} from '@google-cloud/common'; +import {ServiceObject, util, ApiError, GetMetadataCallback} from '@google-cloud/common'; import {promisifyAll} from '@google-cloud/promisify'; import * as is from 'is'; import * as request from 'request'; import {Bucket} from './bucket'; +import {ResponseBody} from '@google-cloud/common/build/src/util'; + +export interface DeleteNotificationOptions { + userProject?: string; +} + +export interface GetNotificationMetadataOptions { + userProject?: string; +} + +/** + * @typedef {array} GetNotificationMetadataResponse + * @property {object} 0 The notification metadata. + * @property {object} 1 The full API response. + */ +export type GetNotificationMetadataResponse = [ResponseBody, request.Response]; + +/** + * @callback GetNotificationMetadataCallback + * @param {?Error} err Request error, if any. + * @param {object} files The notification metadata. + * @param {object} apiResponse The full API response. + */ +export interface GetNotificationMetadataCallback { + (err: Error|null, metadata?: ResponseBody, + apiResponse?: request.Response): void; +} + +/** + * @typedef {array} GetNotificationResponse + * @property {Notification} 0 The {@link Notification} + * @property {object} 1 The full API response. + */ +export type GetNotificationResponse = [Notification, request.Response]; + +export interface GetNotificationOptions { + /** + * Automatically create the object if it does not exist. Default: `false`. + */ + autoCreate?: boolean; + + /** + * The ID of the project which will be billed for the request. + */ + userProject?: string; +} + +/** + * @callback GetNotificationCallback + * @param {?Error} err Request error, if any. + * @param {Notification} notification The {@link Notification}. + * @param {object} apiResponse The full API response. + */ +export interface GetNotificationCallback { + (err: Error|null, notification?: Notification|null, + apiResponse?: request.Response): void; +} + +/** + * @callback DeleteNotificationCallback + * @param {?Error} err Request error, if any. + * @param {object} apiResponse The full API response. + */ +export interface DeleteNotificationCallback { + (err: Error|null, apiResponse?: request.Response): void; +} /** * A Notification object is created from your {@link Bucket} object using @@ -133,11 +199,6 @@ class Notification extends ServiceObject { * @typedef {array} DeleteNotificationResponse * @property {object} 0 The full API response. */ - /** - * @callback DeleteNotificationCallback - * @param {?Error} err Request error, if any. - * @param {object} apiResponse The full API response. - */ /** * Permanently deletes a notification subscription. * @@ -168,12 +229,18 @@ class Notification extends ServiceObject { * region_tag:storage_delete_notification * Another example: */ - delete(options, callback?) { - if (is.fn(options)) { - callback = options; - options = {}; - } - + delete(options?: DeleteNotificationOptions): Promise<[request.Response]>; + delete( + options: DeleteNotificationOptions, + callback: DeleteNotificationCallback): void; + delete(callback: DeleteNotificationCallback): void; + delete( + optionsOrCallback?: DeleteNotificationOptions|DeleteNotificationCallback, + callback?: DeleteNotificationCallback): void|Promise<[request.Response]> { + const options = + typeof optionsOrCallback === 'object' ? optionsOrCallback : {}; + callback = + typeof optionsOrCallback === 'function' ? optionsOrCallback : callback; this.request( { method: 'DELETE', @@ -183,17 +250,6 @@ class Notification extends ServiceObject { callback || util.noop); } - /** - * @typedef {array} GetNotificationResponse - * @property {Notification} 0 The {@link Notification} - * @property {object} 1 The full API response. - */ - /** - * @callback GetNotificationCallback - * @param {?Error} err Request error, if any. - * @param {Notification} notification The {@link Notification}. - * @param {object} apiResponse The full API response. - */ /** * Get a notification and its metadata if it exists. * @@ -226,32 +282,39 @@ class Notification extends ServiceObject { * const apiResponse = data[1]; * }); */ - get(options, callback?) { - if (is.fn(options)) { - callback = options; - options = {}; - } + get(options?: GetNotificationOptions): Promise; + get(options: GetNotificationOptions, callback: GetNotificationCallback): void; + get(callback: GetNotificationCallback): void; + get(optionsOrCallback?: GetNotificationOptions|GetNotificationCallback, + callback?: GetNotificationCallback): + void|Promise { + const options = + typeof optionsOrCallback === 'object' ? optionsOrCallback : {}; + callback = + typeof optionsOrCallback === 'function' ? optionsOrCallback : callback; const autoCreate = options.autoCreate; delete options.autoCreate; - const onCreate = (err, notification, apiResponse) => { - if (err) { - if (err.code === 409) { - this.get(options, callback); - return; - } + const onCreate = + (err: ApiError|null, notification: Notification, + apiResponse: request.Response) => { + if (err) { + if (err.code === 409) { + this.get(options, callback!); + return; + } - callback(err, null, apiResponse); - return; - } + callback!(err, null, apiResponse); + return; + } - callback(null, notification, apiResponse); - }; + callback!(null, notification, apiResponse); + }; this.getMetadata(options, (err, metadata) => { if (err) { - if (err.code === 404 && autoCreate) { + if ((err as ApiError).code === 404 && autoCreate) { const args = [] as object[]; if (!is.empty(options)) { @@ -264,25 +327,14 @@ class Notification extends ServiceObject { return; } - callback(err, null, metadata); + callback!(err, null, metadata); return; } - callback(null, this, metadata); + callback!(null, this, metadata); }); } - /** - * @typedef {array} GetNotificationMetadataResponse - * @property {object} 0 The notification metadata. - * @property {object} 1 The full API response. - */ - /** - * @callback GetNotificationMetadataCallback - * @param {?Error} err Request error, if any. - * @param {object} files The notification metadata. - * @param {object} apiResponse The full API response. - */ /** * Get the notification's metadata. * @@ -314,12 +366,20 @@ class Notification extends ServiceObject { * region_tag:storage_notifications_get_metadata * Another example: */ - getMetadata(options, callback?) { - if (is.fn(options)) { - callback = options; - options = {}; - } - + getMetadata(options?: GetNotificationMetadataOptions): + Promise; + getMetadata( + options: GetNotificationMetadataOptions, + callback: GetMetadataCallback): void; + getMetadata(callback: GetMetadataCallback): void; + getMetadata( + optionsOrCallback?: GetNotificationMetadataOptions|GetMetadataCallback, + callback?: GetMetadataCallback): + void|Promise { + const options = + typeof optionsOrCallback === 'object' ? optionsOrCallback : {}; + callback = + typeof optionsOrCallback === 'function' ? optionsOrCallback : callback; this.request( { uri: '', @@ -327,13 +387,11 @@ class Notification extends ServiceObject { }, (err, resp) => { if (err) { - callback(err, null, resp); + callback!(err, null, resp); return; } - this.metadata = resp; - - callback(null, this.metadata, resp); + callback!(null, this.metadata, resp); }); } }