diff --git a/conformance-test/libraryMethods.ts b/conformance-test/libraryMethods.ts index eb09efb39a..73a325118b 100644 --- a/conformance-test/libraryMethods.ts +++ b/conformance-test/libraryMethods.ts @@ -42,7 +42,9 @@ export async function addLifecycleRuleInstancePrecondition( options: ConformanceTestOptions ) { await options.bucket!.addLifecycleRule({ - action: 'delete', + action: { + type: 'Delete', + }, condition: { age: 365 * 3, // Specified in days. }, @@ -53,7 +55,9 @@ export async function addLifecycleRule(options: ConformanceTestOptions) { if (options.preconditionRequired) { await options.bucket!.addLifecycleRule( { - action: 'delete', + action: { + type: 'Delete', + }, condition: { age: 365 * 3, // Specified in days. }, @@ -64,7 +68,9 @@ export async function addLifecycleRule(options: ConformanceTestOptions) { ); } else { await options.bucket!.addLifecycleRule({ - action: 'delete', + action: { + type: 'Delete', + }, condition: { age: 365 * 3, // Specified in days. }, @@ -104,7 +110,7 @@ export async function combine(options: ConformanceTestOptions) { await allFiles.save('allfiles contents'); if (options.preconditionRequired) { await options.bucket!.combine(sources, allFiles, { - ifGenerationMatch: allFiles.metadata.generation, + ifGenerationMatch: allFiles.metadata.generation!, }); } else { await options.bucket!.combine(sources, allFiles); @@ -468,7 +474,9 @@ export async function copy(options: ConformanceTestOptions) { if (options.preconditionRequired) { await options.file!.copy('a-different-file.png', { - preconditionOpts: {ifGenerationMatch: newFile.metadata.generation}, + preconditionOpts: { + ifGenerationMatch: newFile.metadata.generation!, + }, }); } else { await options.file!.copy('a-different-file.png'); diff --git a/src/acl.ts b/src/acl.ts index e405b8f548..0e593f7569 100644 --- a/src/acl.ts +++ b/src/acl.ts @@ -12,7 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {BodyResponseCallback, DecorateRequestOptions} from './nodejs-common'; +import { + BodyResponseCallback, + DecorateRequestOptions, + BaseMetadata, +} from './nodejs-common'; import {promisifyAll} from '@google-cloud/promisify'; export interface AclOptions { @@ -90,20 +94,18 @@ export interface AccessControlObject { projectTeam: string; } -export interface AclMetadata { +export interface AclMetadata extends BaseMetadata { bucket?: string; domain?: string; entity?: string; entityId?: string; - etag?: string; - id?: string; - kind?: string; + generation?: string; + object?: string; projectTeam?: { projectNumber?: string; team?: 'editors' | 'owners' | 'viewers'; }; - role?: 'OWNER' | 'READER' | 'WRITER'; - selfLink?: string; + role?: 'OWNER' | 'READER' | 'WRITER' | 'FULL_CONTROL'; [key: string]: unknown; } diff --git a/src/bucket.ts b/src/bucket.ts index 25227f9eae..2d29997965 100644 --- a/src/bucket.ts +++ b/src/bucket.ts @@ -19,13 +19,12 @@ import { DeleteCallback, ExistsCallback, GetConfig, - Metadata, MetadataCallback, - ResponseBody, ServiceObject, SetMetadataResponse, util, } from './nodejs-common'; +import {RequestResponse} from './nodejs-common/service-object'; import {paginator} from '@google-cloud/paginator'; import {promisifyAll} from '@google-cloud/promisify'; import * as extend from 'extend'; @@ -38,16 +37,17 @@ import {promisify} from 'util'; import retry = require('async-retry'); import {convertObjKeysToSnakeCase} from './util'; -import {Acl} from './acl'; +import {Acl, AclMetadata} from './acl'; import {Channel} from './channel'; import { File, FileOptions, CreateResumableUploadOptions, CreateWriteStreamOptions, + FileMetadata, } from './file'; import {Iam} from './iam'; -import {Notification} from './notification'; +import {Notification, NotificationMetadata} from './notification'; import { Storage, Cors, @@ -66,7 +66,7 @@ import { import {Readable} from 'stream'; import {CRC32CValidatorGenerator} from './crc32c'; import {URL} from 'url'; -import {SetMetadataOptions} from './nodejs-common/service-object'; +import {BaseMetadata, SetMetadataOptions} from './nodejs-common/service-object'; interface SourceObject { name: string; @@ -80,19 +80,19 @@ interface CreateNotificationQuery { interface MetadataOptions { predefinedAcl: string; userProject?: string; - ifGenerationMatch?: number; - ifGenerationNotMatch?: number; - ifMetagenerationMatch?: number; - ifMetagenerationNotMatch?: number; + ifGenerationMatch?: number | string; + ifGenerationNotMatch?: number | string; + ifMetagenerationMatch?: number | string; + ifMetagenerationNotMatch?: number | string; } -export type GetFilesResponse = [File[], {}, Metadata]; +export type GetFilesResponse = [File[], {}, unknown]; export interface GetFilesCallback { ( err: Error | null, files?: File[], nextQuery?: {}, - apiResponse?: Metadata + apiResponse?: unknown ): void; } @@ -110,11 +110,28 @@ export interface AddLifecycleRuleOptions extends PreconditionOptions { append?: boolean; } -export interface LifecycleRule { - action: {type: string; storageClass?: string} | string; - condition: {[key: string]: boolean | Date | number | string | string[]}; +export interface LifecycleAction { + type: 'Delete' | 'SetStorageClass' | 'AbortIncompleteMultipartUpload'; storageClass?: string; } +export interface LifecycleCondition { + age?: number; + createdBefore?: Date | string; + customTimeBefore?: Date | string; + daysSinceCustomTime?: number; + daysSinceNoncurrentTime?: number; + isLive?: boolean; + matchesPrefix?: string[]; + matchesSuffix?: string[]; + matchesStorageClass?: string[]; + noncurrentTimeBefore?: Date | string; + numNewerVersions?: number; +} + +export interface LifecycleRule { + action: LifecycleAction; + condition: LifecycleCondition; +} export interface EnableLoggingOptions extends PreconditionOptions { bucket?: string | Bucket; @@ -142,10 +159,10 @@ export interface CombineOptions extends PreconditionOptions { } export interface CombineCallback { - (err: Error | null, newFile: File | null, apiResponse: Metadata): void; + (err: Error | null, newFile: File | null, apiResponse: unknown): void; } -export type CombineResponse = [File, Metadata]; +export type CombineResponse = [File, unknown]; export interface CreateChannelConfig extends WatchAllOptions { address: string; @@ -155,10 +172,10 @@ export interface CreateChannelOptions { userProject?: string; } -export type CreateChannelResponse = [Channel, Metadata]; +export type CreateChannelResponse = [Channel, unknown]; export interface CreateChannelCallback { - (err: Error | null, channel: Channel | null, apiResponse: Metadata): void; + (err: Error | null, channel: Channel | null, apiResponse: unknown): void; } export interface CreateNotificationOptions { @@ -173,21 +190,21 @@ export interface CreateNotificationCallback { ( err: Error | null, notification: Notification | null, - apiResponse: Metadata + apiResponse: unknown ): void; } -export type CreateNotificationResponse = [Notification, Metadata]; +export type CreateNotificationResponse = [Notification, unknown]; export interface DeleteBucketOptions { ignoreNotFound?: boolean; userProject?: string; } -export type DeleteBucketResponse = [Metadata]; +export type DeleteBucketResponse = [unknown]; export interface DeleteBucketCallback extends DeleteCallback { - (err: Error | null, apiResponse: Metadata): void; + (err: Error | null, apiResponse: unknown): void; } export interface DeleteFilesOptions @@ -200,7 +217,7 @@ export interface DeleteFilesCallback { (err: Error | Error[] | null, apiResponse?: object): void; } -export type DeleteLabelsResponse = [Metadata]; +export type DeleteLabelsResponse = [unknown]; export type DeleteLabelsCallback = SetLabelsCallback; @@ -208,16 +225,16 @@ export type DeleteLabelsOptions = PreconditionOptions; export type DisableRequesterPaysOptions = PreconditionOptions; -export type DisableRequesterPaysResponse = [Metadata]; +export type DisableRequesterPaysResponse = [unknown]; export interface DisableRequesterPaysCallback { (err?: Error | null, apiResponse?: object): void; } -export type EnableRequesterPaysResponse = [Metadata]; +export type EnableRequesterPaysResponse = [unknown]; export interface EnableRequesterPaysCallback { - (err?: Error | null, apiResponse?: Metadata): void; + (err?: Error | null, apiResponse?: unknown): void; } export type EnableRequesterPaysOptions = PreconditionOptions; @@ -233,29 +250,91 @@ export interface GetBucketOptions extends GetConfig { userProject?: string; } -export type GetBucketResponse = [Bucket, Metadata]; +export type GetBucketResponse = [Bucket, unknown]; export interface GetBucketCallback { - (err: ApiError | null, bucket: Bucket | null, apiResponse: Metadata): void; + (err: ApiError | null, bucket: Bucket | null, apiResponse: unknown): void; } export interface GetLabelsOptions { userProject?: string; } -export type GetLabelsResponse = [Metadata]; +export type GetLabelsResponse = [unknown]; export interface GetLabelsCallback { (err: Error | null, labels: object | null): void; } -export type GetBucketMetadataResponse = [Metadata, Metadata]; +export interface BucketMetadata extends BaseMetadata { + acl?: AclMetadata[]; + autoclass?: { + enabled?: boolean; + toggleTime?: string; + }; + billing?: { + requesterPays?: boolean; + }; + cors?: Cors[]; + customPlacementConfig?: { + dataLocations?: string[]; + }; + defaultEventBasedHold?: boolean; + defaultObjectAcl?: AclMetadata[]; + encryption?: { + defaultKmsKeyName?: string; + } | null; + iamConfiguration?: { + publicAccessPrevention?: string; + uniformBucketLevelAccess?: { + enabled?: boolean; + lockedTime?: string; + }; + }; + labels?: { + [key: string]: string; + }; + lifecycle?: { + rule?: LifecycleRule[]; + } | null; + location?: string; + locationType?: string; + logging?: { + logBucket?: string; + logObjectPrefix?: string; + }; + metageneration?: string; + name?: string; + owner?: { + entity?: string; + entityId?: string; + }; + projectNumber?: string | number; + retentionPolicy?: { + effectiveTime?: string; + isLocked?: boolean; + retentionPeriod?: string | number; + } | null; + rpo?: string; + storageClass?: string; + timeCreated?: string; + updated?: string; + versioning?: { + enabled?: boolean; + }; + website?: { + mainPageSuffix?: string; + notFoundPage?: string; + }; +} + +export type GetBucketMetadataResponse = [BucketMetadata, unknown]; export interface GetBucketMetadataCallback { ( err: ApiError | null, - metadata: Metadata | null, - apiResponse: Metadata + metadata: BucketMetadata | null, + apiResponse: unknown ): void; } @@ -290,16 +369,16 @@ export interface GetNotificationsCallback { ( err: Error | null, notifications: Notification[] | null, - apiResponse: Metadata + apiResponse: unknown ): void; } -export type GetNotificationsResponse = [Notification[], Metadata]; +export type GetNotificationsResponse = [Notification[], unknown]; export interface MakeBucketPrivateOptions { includeFiles?: boolean; force?: boolean; - metadata?: Metadata; + metadata?: BucketMetadata; userProject?: string; preconditionOpts?: PreconditionOptions; } @@ -329,17 +408,17 @@ export interface SetBucketMetadataOptions extends PreconditionOptions { userProject?: string; } -export type SetBucketMetadataResponse = [Metadata]; +export type SetBucketMetadataResponse = [BucketMetadata]; export interface SetBucketMetadataCallback { - (err?: Error | null, metadata?: Metadata): void; + (err?: Error | null, metadata?: BucketMetadata): void; } export interface BucketLockCallback { - (err?: Error | null, apiResponse?: Metadata): void; + (err?: Error | null, apiResponse?: unknown): void; } -export type BucketLockResponse = [Metadata]; +export type BucketLockResponse = [unknown]; export interface Labels { [key: string]: string; @@ -349,10 +428,10 @@ export interface SetLabelsOptions extends PreconditionOptions { userProject?: string; } -export type SetLabelsResponse = [Metadata]; +export type SetLabelsResponse = [unknown]; export interface SetLabelsCallback { - (err?: Error | null, metadata?: Metadata): void; + (err?: Error | null, metadata?: unknown): void; } export interface SetBucketStorageClassOptions extends PreconditionOptions { @@ -363,10 +442,10 @@ export interface SetBucketStorageClassCallback { (err?: Error | null): void; } -export type UploadResponse = [File, Metadata]; +export type UploadResponse = [File, unknown]; export interface UploadCallback { - (err: Error | null, file?: File | null, apiResponse?: Metadata): void; + (err: Error | null, file?: File | null, apiResponse?: unknown): void; } export interface UploadOptions @@ -681,8 +760,7 @@ export enum BucketExceptionMessages { * const bucket = storage.bucket('albums'); * ``` */ -class Bucket extends ServiceObject { - metadata: Metadata; +class Bucket extends ServiceObject { name: string; /** @@ -722,10 +800,10 @@ class Bucket extends ServiceObject { const requestQueryObject: { userProject?: string; - ifGenerationMatch?: number; - ifGenerationNotMatch?: number; - ifMetagenerationMatch?: number; - ifMetagenerationNotMatch?: number; + ifGenerationMatch?: number | string; + ifGenerationNotMatch?: number | string; + ifMetagenerationMatch?: number | string; + ifMetagenerationNotMatch?: number | string; } = {}; if (options?.preconditionOpts?.ifGenerationMatch) { @@ -1344,68 +1422,45 @@ class Bucket extends ServiceObject { options = options || {}; const rules = Array.isArray(rule) ? rule : [rule]; - - const newLifecycleRules = rules.map(rule => { - if (typeof rule.action === 'object') { - // This is a raw-formatted rule object, the way the API expects. - // Just pass it through as-is. - return rule; + for (const curRule of rules) { + if (curRule.condition.createdBefore instanceof Date) { + curRule.condition.createdBefore = curRule.condition.createdBefore + .toISOString() + .replace(/T.+$/, ''); } - - const apiFormattedRule = {} as LifecycleRule; - - apiFormattedRule.condition = {}; - apiFormattedRule.action = { - type: rule.action.charAt(0).toUpperCase() + rule.action.slice(1), - }; - - if (rule.storageClass) { - apiFormattedRule.action.storageClass = rule.storageClass; + if (curRule.condition.customTimeBefore instanceof Date) { + curRule.condition.customTimeBefore = curRule.condition.customTimeBefore + .toISOString() + .replace(/T.+$/, ''); } - - for (const condition in rule.condition) { - if (rule.condition[condition] instanceof Date) { - apiFormattedRule.condition[condition] = ( - rule.condition[condition] as Date - ) + if (curRule.condition.noncurrentTimeBefore instanceof Date) { + curRule.condition.noncurrentTimeBefore = + curRule.condition.noncurrentTimeBefore .toISOString() .replace(/T.+$/, ''); - } else { - apiFormattedRule.condition[condition] = rule.condition[condition]; - } } - - return apiFormattedRule; - }); + } if (options.append === false) { - this.setMetadata( - {lifecycle: {rule: newLifecycleRules}}, - options, - callback! - ); + this.setMetadata({lifecycle: {rule: rules}}, options, callback!); return; } // The default behavior appends the previously-defined lifecycle rules with // the new ones just passed in by the user. - this.getMetadata((err: ApiError | null, metadata: Metadata) => { + this.getMetadata((err: ApiError | null, metadata: BucketMetadata) => { if (err) { callback!(err); return; } - const currentLifecycleRules = Array.isArray( - metadata.lifecycle && metadata.lifecycle.rule - ) - ? metadata.lifecycle && metadata.lifecycle.rule + const currentLifecycleRules = Array.isArray(metadata.lifecycle?.rule) + ? metadata.lifecycle?.rule : []; this.setMetadata( { - lifecycle: { - rule: currentLifecycleRules.concat(newLifecycleRules), - }, + lifecycle: {rule: currentLifecycleRules!.concat(rules)}, }, options as AddLifecycleRuleOptions, callback! @@ -1579,7 +1634,9 @@ class Bucket extends ServiceObject { } as SourceObject; if (source.metadata && source.metadata.generation) { - sourceObject.generation = source.metadata.generation; + sourceObject.generation = parseInt( + source.metadata.generation.toString() + ); } return sourceObject; @@ -2342,9 +2399,12 @@ class Bucket extends ServiceObject { ); } - const logBucket = config.bucket - ? (config.bucket as Bucket).id || config.bucket - : this.id; + let logBucket = this.id; + if (config.bucket && config.bucket instanceof Bucket) { + logBucket = config.bucket.id; + } else if (config.bucket && typeof config.bucket === 'string') { + logBucket = config.bucket; + } const options: PreconditionOptions = {}; if (config?.ifMetagenerationMatch) { @@ -2709,7 +2769,7 @@ class Bucket extends ServiceObject { } const itemsArray = resp.items ? resp.items : []; - const files = itemsArray.map((file: Metadata) => { + const files = itemsArray.map((file: FileMetadata) => { const options = {} as FileOptions; if (query.versions) { @@ -2720,7 +2780,7 @@ class Bucket extends ServiceObject { options.kmsKeyName = file.kmsKeyName; } - const fileInstance = this.file(file.name, options); + const fileInstance = this.file(file.name!, options); fileInstance.metadata = file; return fileInstance; @@ -2802,13 +2862,13 @@ class Bucket extends ServiceObject { this.getMetadata( options, - (err: ApiError | null, metadata: Metadata | null) => { + (err: ApiError | null, metadata: BucketMetadata | undefined) => { if (err) { callback!(err, null); return; } - callback!(null, metadata.labels || {}); + callback!(null, metadata?.labels || {}); } ); } @@ -2896,11 +2956,13 @@ class Bucket extends ServiceObject { return; } const itemsArray = resp.items ? resp.items : []; - const notifications = itemsArray.map((notification: Metadata) => { - const notificationInstance = this.notification(notification.id); - notificationInstance.metadata = notification; - return notificationInstance; - }); + const notifications = itemsArray.map( + (notification: NotificationMetadata) => { + const notificationInstance = this.notification(notification.id!); + notificationInstance.metadata = notification; + return notificationInstance; + } + ); callback!(null, notifications, resp); } @@ -3272,7 +3334,7 @@ class Bucket extends ServiceObject { // so acl must explicitly be nullified. const metadata = extend({}, options.metadata, {acl: null}); - this.setMetadata(metadata, query, err => { + this.setMetadata(metadata, query, (err: Error | null | undefined) => { if (err) { callback!(err); } @@ -3496,7 +3558,7 @@ class Bucket extends ServiceObject { ); } - request(reqOpts: DecorateRequestOptions): Promise<[ResponseBody, Metadata]>; + request(reqOpts: DecorateRequestOptions): Promise; request( reqOpts: DecorateRequestOptions, callback: BodyResponseCallback @@ -3512,7 +3574,7 @@ class Bucket extends ServiceObject { request( reqOpts: DecorateRequestOptions, callback?: BodyResponseCallback - ): void | Promise<[ResponseBody, Metadata]> { + ): void | Promise { if (this.userProject && (!reqOpts.qs || !reqOpts.qs.userProject)) { reqOpts.qs = extend(reqOpts.qs, {userProject: this.userProject}); } @@ -3598,25 +3660,28 @@ class Bucket extends ServiceObject { } setMetadata( - metadata: Metadata, + metadata: BucketMetadata, options?: SetMetadataOptions - ): Promise; - setMetadata(metadata: Metadata, callback: MetadataCallback): void; + ): Promise>; + setMetadata( + metadata: BucketMetadata, + callback: MetadataCallback + ): void; setMetadata( - metadata: Metadata, + metadata: BucketMetadata, options: SetMetadataOptions, - callback: MetadataCallback + callback: MetadataCallback ): void; setMetadata( - metadata: Metadata, - optionsOrCallback: SetMetadataOptions | MetadataCallback, - cb?: MetadataCallback - ): Promise | void { + metadata: BucketMetadata, + optionsOrCallback: SetMetadataOptions | MetadataCallback, + cb?: MetadataCallback + ): Promise> | void { const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {}; cb = typeof optionsOrCallback === 'function' - ? (optionsOrCallback as MetadataCallback) + ? (optionsOrCallback as MetadataCallback) : cb; this.disableAutoRetryConditionallyIdempotent_( @@ -3697,7 +3762,7 @@ class Bucket extends ServiceObject { this.setMetadata( { retentionPolicy: { - retentionPeriod: duration, + retentionPeriod: duration.toString(), }, }, options, diff --git a/src/channel.ts b/src/channel.ts index f0dff46ddc..ec4db1a35f 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {Metadata, ServiceObject, util} from './nodejs-common'; +import {BaseMetadata, ServiceObject, util} from './nodejs-common'; import {promisifyAll} from '@google-cloud/promisify'; import {Storage} from './storage'; export interface StopCallback { - (err: Error | null, apiResponse?: Metadata): void; + (err: Error | null, apiResponse?: unknown): void; } /** @@ -38,7 +38,7 @@ export interface StopCallback { * const channel = storage.channel('id', 'resource-id'); * ``` */ -class Channel extends ServiceObject { +class Channel extends ServiceObject { constructor(storage: Storage, id: string, resourceId: string) { const config = { parent: storage, @@ -60,7 +60,7 @@ class Channel extends ServiceObject { this.metadata.resourceId = resourceId; } - stop(): Promise; + stop(): Promise; stop(callback: StopCallback): void; /** * @typedef {array} StopResponse @@ -96,7 +96,7 @@ class Channel extends ServiceObject { * }); * ``` */ - stop(callback?: StopCallback): Promise | void { + stop(callback?: StopCallback): Promise | void { callback = callback || util.noop; this.request( { diff --git a/src/file.ts b/src/file.ts index 1237fd01d4..31b58b918c 100644 --- a/src/file.ts +++ b/src/file.ts @@ -17,7 +17,6 @@ import { DecorateRequestOptions, GetConfig, Interceptor, - Metadata, MetadataCallback, ServiceObject, SetMetadataResponse, @@ -42,7 +41,7 @@ import { Storage, } from './storage'; import {AvailableServiceObjectMethods, Bucket} from './bucket'; -import {Acl} from './acl'; +import {Acl, AclMetadata} from './acl'; import { GetSignedUrlResponse, SigningError, @@ -72,8 +71,10 @@ import {URL} from 'url'; import retry = require('async-retry'); import { + BaseMetadata, DeleteCallback, DeleteOptions, + RequestResponse, SetMetadataOptions, } from './nodejs-common/service-object'; import * as r from 'teeny-request'; @@ -83,7 +84,7 @@ export interface GetExpirationDateCallback { ( err: Error | null, expirationDate?: Date | null, - apiResponse?: Metadata + apiResponse?: unknown ): void; } @@ -152,20 +153,20 @@ export interface GetFileMetadataOptions { userProject?: string; } -export type GetFileMetadataResponse = [Metadata, Metadata]; +export type GetFileMetadataResponse = [FileMetadata, unknown]; export interface GetFileMetadataCallback { - (err: Error | null, metadata?: Metadata, apiResponse?: Metadata): void; + (err: Error | null, metadata?: FileMetadata, apiResponse?: unknown): void; } export interface GetFileOptions extends GetConfig { userProject?: string; } -export type GetFileResponse = [File, Metadata]; +export type GetFileResponse = [File, unknown]; export interface GetFileCallback { - (err: Error | null, file?: File, apiResponse?: Metadata): void; + (err: Error | null, file?: File, apiResponse?: unknown): void; } export interface FileExistsOptions { @@ -183,10 +184,10 @@ export interface DeleteFileOptions { userProject?: string; } -export type DeleteFileResponse = [Metadata]; +export type DeleteFileResponse = [unknown]; export interface DeleteFileCallback { - (err: Error | null, apiResponse?: Metadata): void; + (err: Error | null, apiResponse?: unknown): void; } export type PredefinedAcl = @@ -200,7 +201,7 @@ export type PredefinedAcl = export interface CreateResumableUploadOptions { chunkSize?: number; highWaterMark?: number; - metadata?: Metadata; + metadata?: FileMetadata; origin?: string; offset?: number; predefinedAcl?: PredefinedAcl; @@ -226,13 +227,13 @@ export interface CreateWriteStreamOptions extends CreateResumableUploadOptions { } export interface MakeFilePrivateOptions { - metadata?: Metadata; + metadata?: FileMetadata; strict?: boolean; userProject?: string; preconditionOpts?: PreconditionOptions; } -export type MakeFilePrivateResponse = [Metadata]; +export type MakeFilePrivateResponse = [unknown]; export type MakeFilePrivateCallback = SetFileMetadataCallback; @@ -242,19 +243,19 @@ export interface IsPublicCallback { export type IsPublicResponse = [boolean]; -export type MakeFilePublicResponse = [Metadata]; +export type MakeFilePublicResponse = [unknown]; export interface MakeFilePublicCallback { - (err?: Error | null, apiResponse?: Metadata): void; + (err?: Error | null, apiResponse?: unknown): void; } -export type MoveResponse = [Metadata]; +export type MoveResponse = [unknown]; export interface MoveCallback { ( err: Error | null, destinationFile?: File | null, - apiResponse?: Metadata + apiResponse?: unknown ): void; } @@ -311,17 +312,17 @@ export interface CopyOptions { contentType?: string; contentDisposition?: string; destinationKmsKeyName?: string; - metadata?: Metadata; + metadata?: FileMetadata; predefinedAcl?: string; token?: string; userProject?: string; preconditionOpts?: PreconditionOptions; } -export type CopyResponse = [File, Metadata]; +export type CopyResponse = [File, unknown]; export interface CopyCallback { - (err: Error | null, file?: File | null, apiResponse?: Metadata): void; + (err: Error | null, file?: File | null, apiResponse?: unknown): void; } export type DownloadResponse = [Buffer]; @@ -341,10 +342,10 @@ interface CopyQuery { userProject?: string; destinationKmsKeyName?: string; destinationPredefinedAcl?: string; - ifGenerationMatch?: number; - ifGenerationNotMatch?: number; - ifMetagenerationMatch?: number; - ifMetagenerationNotMatch?: number; + ifGenerationMatch?: number | string; + ifGenerationNotMatch?: number | string; + ifMetagenerationMatch?: number | string; + ifMetagenerationNotMatch?: number | string; } interface FileQuery { @@ -375,12 +376,12 @@ export interface SetFileMetadataOptions { } export interface SetFileMetadataCallback { - (err?: Error | null, apiResponse?: Metadata): void; + (err?: Error | null, apiResponse?: unknown): void; } -export type SetFileMetadataResponse = [Metadata]; +export type SetFileMetadataResponse = [unknown]; -export type SetStorageClassResponse = [Metadata]; +export type SetStorageClassResponse = [unknown]; export interface SetStorageClassOptions { userProject?: string; @@ -392,7 +393,46 @@ interface SetStorageClassRequest extends SetStorageClassOptions { } export interface SetStorageClassCallback { - (err?: Error | null, apiResponse?: Metadata): void; + (err?: Error | null, apiResponse?: unknown): void; +} + +export interface FileMetadata extends BaseMetadata { + acl?: AclMetadata[]; + bucket?: string; + cacheControl?: string; + componentCount?: number; + contentDisposition?: string; + contentEncoding?: string; + contentLanguage?: string; + contentType?: string; + crc32c?: string; + customerEncryption?: { + encryptionAlgorithm?: string; + keySha256?: string; + }; + customTime?: string; + eventBasedHold?: boolean | null; + generation?: string | number; + kmsKeyName?: string; + md5Hash?: string; + mediaLink?: string; + metadata?: { + [key: string]: string; + }; + metageneration?: string | number; + name?: string; + owner?: { + entity?: string; + entityId?: string; + }; + retentionExpirationTime?: string; + size?: string | number; + storageClass?: string; + temporaryHold?: boolean | null; + timeCreated?: string; + timeDeleted?: string; + timeStorageClassUpdated?: string; + updated?: string; } export class RequestError extends Error { @@ -427,7 +467,7 @@ export enum FileExceptionMessages { * * @class */ -class File extends ServiceObject { +class File extends ServiceObject { acl: Acl; crc32cGenerator: CRC32CValidatorGenerator; bucket: Bucket; @@ -435,7 +475,6 @@ class File extends ServiceObject { kmsKeyName?: string; userProject?: string; signer?: URLSigner; - metadata: Metadata; name: string; generation?: number; @@ -1432,11 +1471,11 @@ class File extends ServiceObject { const onResponse = ( err: Error | null, _body: ResponseBody, - rawResponseStream: Metadata + rawResponseStream: unknown ) => { if (err) { // Get error message from the body. - this.getBufferFromReadable(rawResponseStream).then(body => { + this.getBufferFromReadable(rawResponseStream as Readable).then(body => { err.message = body.toString('utf8'); throughStream.destroy(err); }); @@ -1444,7 +1483,7 @@ class File extends ServiceObject { return; } - const headers = rawResponseStream.toJSON().headers; + const headers = (rawResponseStream as ResponseBody).toJSON().headers; const isCompressed = headers['content-encoding'] === 'gzip'; const hashes: {crc32c?: string; md5?: string} = {}; @@ -1499,7 +1538,7 @@ class File extends ServiceObject { } pipeline( - rawResponseStream, + rawResponseStream as Readable, ...(transformStreams as [Transform]), throughStream, onComplete @@ -1851,27 +1890,27 @@ class File extends ServiceObject { options = extend(true, {metadata: {}}, options); if (options.contentType) { - options.metadata.contentType = options.contentType; + options!.metadata!.contentType = options.contentType; } if ( - !options.metadata.contentType || - options.metadata.contentType === 'auto' + !options!.metadata!.contentType || + options!.metadata!.contentType === 'auto' ) { const detectedContentType = mime.getType(this.name); if (detectedContentType) { - options.metadata.contentType = detectedContentType; + options!.metadata!.contentType = detectedContentType; } } let gzip = options.gzip; if (gzip === 'auto') { - gzip = compressible(options.metadata.contentType); + gzip = compressible(options!.metadata!.contentType || ''); } if (gzip) { - options.metadata.contentEncoding = 'gzip'; + options!.metadata!.contentEncoding = 'gzip'; } let crc32c = true; @@ -2232,7 +2271,7 @@ class File extends ServiceObject { callback?: GetExpirationDateCallback ): void | Promise { this.getMetadata( - (err: ApiError | null, metadata: Metadata, apiResponse: Metadata) => { + (err: ApiError | null, metadata: FileMetadata, apiResponse: unknown) => { if (err) { callback!(err, null, apiResponse); return; @@ -3439,7 +3478,7 @@ class File extends ServiceObject { this.move(destinationFile, options, callback); } - request(reqOpts: DecorateRequestOptions): Promise<[ResponseBody, Metadata]>; + request(reqOpts: DecorateRequestOptions): Promise; request( reqOpts: DecorateRequestOptions, callback: BodyResponseCallback @@ -3455,7 +3494,7 @@ class File extends ServiceObject { request( reqOpts: DecorateRequestOptions, callback?: BodyResponseCallback - ): void | Promise<[ResponseBody, Metadata]> { + ): void | Promise { return this.parent.request.call(this, reqOpts, callback!); } @@ -3653,25 +3692,28 @@ class File extends ServiceObject { } setMetadata( - metadata: Metadata, + metadata: FileMetadata, options?: SetMetadataOptions - ): Promise; - setMetadata(metadata: Metadata, callback: MetadataCallback): void; + ): Promise>; + setMetadata( + metadata: FileMetadata, + callback: MetadataCallback + ): void; setMetadata( - metadata: Metadata, + metadata: FileMetadata, options: SetMetadataOptions, - callback: MetadataCallback + callback: MetadataCallback ): void; setMetadata( - metadata: Metadata, - optionsOrCallback: SetMetadataOptions | MetadataCallback, - cb?: MetadataCallback - ): Promise | void { + metadata: FileMetadata, + optionsOrCallback: SetMetadataOptions | MetadataCallback, + cb?: MetadataCallback + ): Promise> | void { const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {}; cb = typeof optionsOrCallback === 'function' - ? (optionsOrCallback as MetadataCallback) + ? (optionsOrCallback as MetadataCallback) : cb; this.disableAutoRetryConditionallyIdempotent_( diff --git a/src/hmacKey.ts b/src/hmacKey.ts index 33f11431a9..4621fef5e2 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -13,13 +13,12 @@ // limitations under the License. import { - Metadata, ServiceObject, Methods, MetadataCallback, SetMetadataResponse, } from './nodejs-common'; -import {SetMetadataOptions} from './nodejs-common/service-object'; +import {BaseMetadata, SetMetadataOptions} from './nodejs-common/service-object'; import {IdempotencyStrategy, Storage} from './storage'; import {promisifyAll} from '@google-cloud/promisify'; @@ -27,10 +26,9 @@ export interface HmacKeyOptions { projectId?: string; } -export interface HmacKeyMetadata { - accessId: string; +export interface HmacKeyMetadata extends BaseMetadata { + accessId?: string; etag?: string; - id?: string; projectId?: string; serviceAccountEmail?: string; state?: string; @@ -51,10 +49,10 @@ export interface SetHmacKeyMetadata { } export interface HmacKeyMetadataCallback { - (err: Error | null, metadata?: HmacKeyMetadata, apiResponse?: Metadata): void; + (err: Error | null, metadata?: HmacKeyMetadata, apiResponse?: unknown): void; } -export type HmacKeyMetadataResponse = [HmacKeyMetadata, Metadata]; +export type HmacKeyMetadataResponse = [HmacKeyMetadata, unknown]; /** * The API-formatted resource description of the HMAC key. @@ -74,8 +72,7 @@ export type HmacKeyMetadataResponse = [HmacKeyMetadata, Metadata]; * * @class */ -export class HmacKey extends ServiceObject { - metadata: HmacKeyMetadata | undefined; +export class HmacKey extends ServiceObject { /** * A reference to the {@link Storage} associated with this {@link HmacKey} * instance. @@ -370,20 +367,23 @@ export class HmacKey extends ServiceObject { * @param {object} callback.apiResponse - The full API response. */ setMetadata( - metadata: Metadata, + metadata: HmacKeyMetadata, options?: SetMetadataOptions - ): Promise; - setMetadata(metadata: Metadata, callback: MetadataCallback): void; + ): Promise>; setMetadata( - metadata: Metadata, + metadata: HmacKeyMetadata, + callback: MetadataCallback + ): void; + setMetadata( + metadata: HmacKeyMetadata, options: SetMetadataOptions, - callback: MetadataCallback + callback: MetadataCallback ): void; setMetadata( - metadata: Metadata, - optionsOrCallback: SetMetadataOptions | MetadataCallback, - cb?: MetadataCallback - ): Promise | void { + metadata: HmacKeyMetadata, + optionsOrCallback: SetMetadataOptions | MetadataCallback, + cb?: MetadataCallback + ): Promise> | void { // ETag preconditions are not currently supported. Retries should be disabled if the idempotency strategy is not set to RetryAlways if ( this.storage.retryOptions.idempotencyStrategy !== @@ -395,7 +395,7 @@ export class HmacKey extends ServiceObject { typeof optionsOrCallback === 'object' ? optionsOrCallback : {}; cb = typeof optionsOrCallback === 'function' - ? (optionsOrCallback as MetadataCallback) + ? (optionsOrCallback as MetadataCallback) : cb; super diff --git a/src/iam.ts b/src/iam.ts index 1f27622d75..fc65aeac05 100644 --- a/src/iam.ts +++ b/src/iam.ts @@ -12,11 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { - BodyResponseCallback, - DecorateRequestOptions, - Metadata, -} from './nodejs-common'; +import {BodyResponseCallback, DecorateRequestOptions} from './nodejs-common'; import {promisifyAll} from '@google-cloud/promisify'; import {Bucket} from './bucket'; @@ -27,7 +23,7 @@ export interface GetPolicyOptions { requestedPolicyVersion?: number; } -export type GetPolicyResponse = [Policy, Metadata]; +export type GetPolicyResponse = [Policy, unknown]; /** * @callback GetPolicyCallback @@ -36,7 +32,7 @@ export type GetPolicyResponse = [Policy, Metadata]; * @param {object} apiResponse The full API response. */ export interface GetPolicyCallback { - (err?: Error | null, acl?: Policy, apiResponse?: Metadata): void; + (err?: Error | null, acl?: Policy, apiResponse?: unknown): void; } /** @@ -53,7 +49,7 @@ export interface SetPolicyOptions { * @property {object} 0 The policy. * @property {object} 1 The full API response. */ -export type SetPolicyResponse = [Policy, Metadata]; +export type SetPolicyResponse = [Policy, unknown]; /** * @callback SetPolicyCallback @@ -88,7 +84,7 @@ export interface Expr { * @property {object} 0 A subset of permissions that the caller is allowed. * @property {object} 1 The full API response. */ -export type TestIamPermissionsResponse = [{[key: string]: boolean}, Metadata]; +export type TestIamPermissionsResponse = [{[key: string]: boolean}, unknown]; /** * @callback TestIamPermissionsCallback @@ -100,7 +96,7 @@ export interface TestIamPermissionsCallback { ( err?: Error | null, acl?: {[key: string]: boolean} | null, - apiResponse?: Metadata + apiResponse?: unknown ): void; } diff --git a/src/nodejs-common/index.ts b/src/nodejs-common/index.ts index f90c514baa..293105b950 100644 --- a/src/nodejs-common/index.ts +++ b/src/nodejs-common/index.ts @@ -23,12 +23,12 @@ export { } from './service'; export { + BaseMetadata, DeleteCallback, ExistsCallback, GetConfig, InstanceResponseCallback, Interceptor, - Metadata, MetadataCallback, MetadataResponse, Methods, diff --git a/src/nodejs-common/service-object.ts b/src/nodejs-common/service-object.ts index afc5491c8c..78ef276da2 100644 --- a/src/nodejs-common/service-object.ts +++ b/src/nodejs-common/service-object.ts @@ -27,7 +27,7 @@ import { util, } from './util'; -export type RequestResponse = [Metadata, r.Response]; +export type RequestResponse = [unknown, r.Response]; export interface ServiceObjectParent { interceptors: Interceptor[]; @@ -45,12 +45,10 @@ export interface Interceptor { export type GetMetadataOptions = object; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type Metadata = any; -export type MetadataResponse = [Metadata, r.Response]; -export type MetadataCallback = ( +export type MetadataResponse = [K, r.Response]; +export type MetadataCallback = ( err: Error | null, - metadata?: Metadata, + metadata?: K, apiResponse?: r.Response ) => void; @@ -114,10 +112,10 @@ export interface CreateCallback { export type DeleteOptions = { ignoreNotFound?: boolean; - ifGenerationMatch?: number; - ifGenerationNotMatch?: number; - ifMetagenerationMatch?: number; - ifMetagenerationNotMatch?: number; + ifGenerationMatch?: number | string; + ifGenerationNotMatch?: number | string; + ifMetagenerationMatch?: number | string; + ifMetagenerationNotMatch?: number | string; } & object; export interface DeleteCallback { (err: Error | null, apiResponse?: r.Response): void; @@ -129,16 +127,24 @@ export interface GetConfig { */ autoCreate?: boolean; } -type GetOrCreateOptions = GetConfig & CreateOptions; +export type GetOrCreateOptions = GetConfig & CreateOptions; export type GetResponse = [T, r.Response]; export interface ResponseCallback { (err?: Error | null, apiResponse?: r.Response): void; } -export type SetMetadataResponse = [Metadata]; +export type SetMetadataResponse = [K]; export type SetMetadataOptions = object; +export interface BaseMetadata { + id?: string; + kind?: string; + etag?: string; + selfLink?: string; + [key: string]: unknown; +} + /** * ServiceObject is a base class, meant to be inherited from by a "service * object," like a BigQuery dataset or Storage bucket. @@ -151,8 +157,8 @@ export type SetMetadataOptions = object; * object requires specific behavior. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any -class ServiceObject extends EventEmitter { - metadata: Metadata; +class ServiceObject extends EventEmitter { + metadata: K; baseUrl?: string; parent: ServiceObjectParent; id?: string; @@ -181,7 +187,7 @@ class ServiceObject extends EventEmitter { */ constructor(config: ServiceObjectConfig) { super(); - this.metadata = {}; + this.metadata = {} as K; this.baseUrl = config.baseUrl; this.parent = config.parent; // Parent class. this.id = config.id; // Name or ID (e.g. dataset ID, bucket name, etc). @@ -249,7 +255,7 @@ class ServiceObject extends EventEmitter { // Wrap the callback to return *this* instance of the object, not the // newly-created one. // tslint: disable-next-line no-any - function onCreate(...args: [Error, ServiceObject]) { + function onCreate(...args: [Error, ServiceObject]) { const [err, instance] = args; if (!err) { self.metadata = instance.metadata; @@ -409,10 +415,10 @@ class ServiceObject extends EventEmitter { self.create(...args); return; } - callback!(err, null, metadata as r.Response); + callback!(err, null, metadata as unknown as r.Response); return; } - callback!(null, self as {} as T, metadata as r.Response); + callback!(null, self as {} as T, metadata as unknown as r.Response); }); } @@ -424,16 +430,16 @@ class ServiceObject extends EventEmitter { * @param {object} callback.metadata - The metadata for this object. * @param {object} callback.apiResponse - The full API response. */ - getMetadata(options?: GetMetadataOptions): Promise; - getMetadata(options: GetMetadataOptions, callback: MetadataCallback): void; - getMetadata(callback: MetadataCallback): void; + getMetadata(options?: GetMetadataOptions): Promise>; + getMetadata(options: GetMetadataOptions, callback: MetadataCallback): void; + getMetadata(callback: MetadataCallback): void; getMetadata( - optionsOrCallback: GetMetadataOptions | MetadataCallback, - cb?: MetadataCallback - ): Promise | void { + optionsOrCallback: GetMetadataOptions | MetadataCallback, + cb?: MetadataCallback + ): Promise> | void { const [options, callback] = util.maybeOptionsOrCallback< GetMetadataOptions, - MetadataCallback + MetadataCallback >(optionsOrCallback, cb); const methodConfig = @@ -484,23 +490,23 @@ class ServiceObject extends EventEmitter { * @param {object} callback.apiResponse - The full API response. */ setMetadata( - metadata: Metadata, + metadata: K, options?: SetMetadataOptions - ): Promise; - setMetadata(metadata: Metadata, callback: MetadataCallback): void; + ): Promise>; + setMetadata(metadata: K, callback: MetadataCallback): void; setMetadata( - metadata: Metadata, + metadata: K, options: SetMetadataOptions, - callback: MetadataCallback + callback: MetadataCallback ): void; setMetadata( - metadata: Metadata, - optionsOrCallback: SetMetadataOptions | MetadataCallback, - cb?: MetadataCallback - ): Promise | void { + metadata: K, + optionsOrCallback: SetMetadataOptions | MetadataCallback, + cb?: MetadataCallback + ): Promise> | void { const [options, callback] = util.maybeOptionsOrCallback< SetMetadataOptions, - MetadataCallback + MetadataCallback >(optionsOrCallback, cb); const methodConfig = (typeof this.methods.setMetadata === 'object' && @@ -582,7 +588,6 @@ class ServiceObject extends EventEmitter { if (reqOpts.shouldReturnStream) { return this.parent.requestStream(reqOpts); } - this.parent.request(reqOpts, callback!); } diff --git a/src/notification.ts b/src/notification.ts index aff9d315c8..c24bf0c397 100644 --- a/src/notification.ts +++ b/src/notification.ts @@ -12,13 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { - ApiError, - Metadata, - MetadataCallback, - ServiceObject, - util, -} from './nodejs-common'; +import {BaseMetadata, ServiceObject} from './nodejs-common'; import {ResponseBody} from './nodejs-common/util'; import {promisifyAll} from '@google-cloud/promisify'; @@ -37,7 +31,7 @@ export interface GetNotificationMetadataOptions { * @property {object} 0 The notification metadata. * @property {object} 1 The full API response. */ -export type GetNotificationMetadataResponse = [ResponseBody, Metadata]; +export type GetNotificationMetadataResponse = [ResponseBody, unknown]; /** * @callback GetNotificationMetadataCallback @@ -46,7 +40,7 @@ export type GetNotificationMetadataResponse = [ResponseBody, Metadata]; * @param {object} apiResponse The full API response. */ export interface GetNotificationMetadataCallback { - (err: Error | null, metadata?: ResponseBody, apiResponse?: Metadata): void; + (err: Error | null, metadata?: ResponseBody, apiResponse?: unknown): void; } /** @@ -54,7 +48,7 @@ export interface GetNotificationMetadataCallback { * @property {Notification} 0 The {@link Notification} * @property {object} 1 The full API response. */ -export type GetNotificationResponse = [Notification, Metadata]; +export type GetNotificationResponse = [Notification, unknown]; export interface GetNotificationOptions { /** @@ -78,7 +72,7 @@ export interface GetNotificationCallback { ( err: Error | null, notification?: Notification | null, - apiResponse?: Metadata + apiResponse?: unknown ): void; } @@ -88,7 +82,17 @@ export interface GetNotificationCallback { * @param {object} apiResponse The full API response. */ export interface DeleteNotificationCallback { - (err: Error | null, apiResponse?: Metadata): void; + (err: Error | null, apiResponse?: unknown): void; +} + +export interface NotificationMetadata extends BaseMetadata { + custom_attributes?: { + [key: string]: string; + }; + event_types?: string[]; + object_name_prefix?: string; + payload_format?: 'JSON_API_V1' | 'NONE'; + topic?: string; } /** @@ -122,8 +126,15 @@ export interface DeleteNotificationCallback { * const notification = myBucket.notification('1'); * ``` */ -class Notification extends ServiceObject { +class Notification extends ServiceObject { constructor(bucket: Bucket, id: string) { + const requestQueryObject: { + ifGenerationMatch?: number; + ifGenerationNotMatch?: number; + ifMetagenerationMatch?: number; + ifMetagenerationNotMatch?: number; + } = {}; + const methods = { /** * Creates a notification subscription for the bucket. @@ -169,6 +180,127 @@ class Notification extends ServiceObject { */ create: true, + /** + * @typedef {array} DeleteNotificationResponse + * @property {object} 0 The full API response. + */ + /** + * Permanently deletes a notification subscription. + * + * See {@link https://cloud.google.com/storage/docs/json_api/v1/notifications/delete| Notifications: delete API Documentation} + * + * @param {object} [options] Configuration options. + * @param {string} [options.userProject] The ID of the project which will be + * billed for the request. + * @param {DeleteNotificationCallback} [callback] Callback function. + * @returns {Promise} + * + * @example + * ``` + * const {Storage} = require('@google-cloud/storage'); + * const storage = new Storage(); + * const myBucket = storage.bucket('my-bucket'); + * const notification = myBucket.notification('1'); + * + * notification.delete(function(err, apiResponse) {}); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * notification.delete().then(function(data) { + * const apiResponse = data[0]; + * }); + * + * ``` + * @example include:samples/deleteNotification.js + * region_tag:storage_delete_bucket_notification + * Another example: + */ + delete: { + reqOpts: { + qs: requestQueryObject, + }, + }, + + /** + * Get a notification and its metadata if it exists. + * + * See {@link https://cloud.google.com/storage/docs/json_api/v1/notifications/get| Notifications: get API Documentation} + * + * @param {object} [options] Configuration options. + * See {@link Bucket#createNotification} for create options. + * @param {boolean} [options.autoCreate] Automatically create the object if + * it does not exist. Default: `false`. + * @param {string} [options.userProject] The ID of the project which will be + * billed for the request. + * @param {GetNotificationCallback} [callback] Callback function. + * @return {Promise} + * + * @example + * ``` + * const {Storage} = require('@google-cloud/storage'); + * const storage = new Storage(); + * const myBucket = storage.bucket('my-bucket'); + * const notification = myBucket.notification('1'); + * + * notification.get(function(err, notification, apiResponse) { + * // `notification.metadata` has been populated. + * }); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * notification.get().then(function(data) { + * const notification = data[0]; + * const apiResponse = data[1]; + * }); + * ``` + */ + get: { + reqOpts: { + qs: requestQueryObject, + }, + }, + + /** + * Get the notification's metadata. + * + * See {@link https://cloud.google.com/storage/docs/json_api/v1/notifications/get| Notifications: get API Documentation} + * + * @param {object} [options] Configuration options. + * @param {string} [options.userProject] The ID of the project which will be + * billed for the request. + * @param {GetNotificationMetadataCallback} [callback] Callback function. + * @returns {Promise} + * + * @example + * ``` + * const {Storage} = require('@google-cloud/storage'); + * const storage = new Storage(); + * const myBucket = storage.bucket('my-bucket'); + * const notification = myBucket.notification('1'); + * + * notification.getMetadata(function(err, metadata, apiResponse) {}); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * notification.getMetadata().then(function(data) { + * const metadata = data[0]; + * const apiResponse = data[1]; + * }); + * + * ``` + * @example include:samples/getMetadataNotifications.js + * region_tag:storage_print_pubsub_bucket_notification + * Another example: + */ + getMetadata: { + reqOpts: { + qs: requestQueryObject, + }, + }, + /** * @typedef {array} NotificationExistsResponse * @property {boolean} 0 Whether the notification exists or not. @@ -213,222 +345,6 @@ class Notification extends ServiceObject { methods, }); } - - delete(options?: DeleteNotificationOptions): Promise<[Metadata]>; - delete( - options: DeleteNotificationOptions, - callback: DeleteNotificationCallback - ): void; - delete(callback: DeleteNotificationCallback): void; - /** - * @typedef {array} DeleteNotificationResponse - * @property {object} 0 The full API response. - */ - /** - * Permanently deletes a notification subscription. - * - * See {@link https://cloud.google.com/storage/docs/json_api/v1/notifications/delete| Notifications: delete API Documentation} - * - * @param {object} [options] Configuration options. - * @param {string} [options.userProject] The ID of the project which will be - * billed for the request. - * @param {DeleteNotificationCallback} [callback] Callback function. - * @returns {Promise} - * - * @example - * ``` - * const {Storage} = require('@google-cloud/storage'); - * const storage = new Storage(); - * const myBucket = storage.bucket('my-bucket'); - * const notification = myBucket.notification('1'); - * - * notification.delete(function(err, apiResponse) {}); - * - * //- - * // If the callback is omitted, we'll return a Promise. - * //- - * notification.delete().then(function(data) { - * const apiResponse = data[0]; - * }); - * - * ``` - * @example include:samples/deleteNotification.js - * region_tag:storage_delete_bucket_notification - * Another example: - */ - delete( - optionsOrCallback?: DeleteNotificationOptions | DeleteNotificationCallback, - callback?: DeleteNotificationCallback - ): void | Promise<[Metadata]> { - const options = - typeof optionsOrCallback === 'object' ? optionsOrCallback : {}; - callback = - typeof optionsOrCallback === 'function' ? optionsOrCallback : callback; - this.request( - { - method: 'DELETE', - uri: '', - qs: options, - }, - callback || util.noop - ); - } - - get(options?: GetNotificationOptions): Promise; - get(options: GetNotificationOptions, callback: GetNotificationCallback): void; - get(callback: GetNotificationCallback): void; - /** - * Get a notification and its metadata if it exists. - * - * See {@link https://cloud.google.com/storage/docs/json_api/v1/notifications/get| Notifications: get API Documentation} - * - * @param {object} [options] Configuration options. - * See {@link Bucket#createNotification} for create options. - * @param {boolean} [options.autoCreate] Automatically create the object if - * it does not exist. Default: `false`. - * @param {string} [options.userProject] The ID of the project which will be - * billed for the request. - * @param {GetNotificationCallback} [callback] Callback function. - * @return {Promise} - * - * @example - * ``` - * const {Storage} = require('@google-cloud/storage'); - * const storage = new Storage(); - * const myBucket = storage.bucket('my-bucket'); - * const notification = myBucket.notification('1'); - * - * notification.get(function(err, notification, apiResponse) { - * // `notification.metadata` has been populated. - * }); - * - * //- - * // If the callback is omitted, we'll return a Promise. - * //- - * notification.get().then(function(data) { - * const notification = data[0]; - * const apiResponse = data[1]; - * }); - * ``` - */ - 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: ApiError | null, - notification: Notification, - apiResponse: Metadata - ) => { - if (err) { - if (err.code === 409) { - this.get(options, callback!); - return; - } - - callback!(err, null, apiResponse); - return; - } - - callback!(null, notification, apiResponse); - }; - - this.getMetadata(options, (err, metadata) => { - if (err) { - if ((err as ApiError).code === 404 && autoCreate) { - const args = [] as object[]; - - if (Object.keys(options).length > 0) { - args.push(options); - } - - args.push(onCreate); - - // eslint-disable-next-line - this.create.apply(this, args as any); - return; - } - - callback!(err, null, metadata); - return; - } - - callback!(null, this, metadata); - }); - } - - getMetadata( - options?: GetNotificationMetadataOptions - ): Promise; - getMetadata( - options: GetNotificationMetadataOptions, - callback: MetadataCallback - ): void; - getMetadata(callback: MetadataCallback): void; - /** - * Get the notification's metadata. - * - * See {@link https://cloud.google.com/storage/docs/json_api/v1/notifications/get| Notifications: get API Documentation} - * - * @param {object} [options] Configuration options. - * @param {string} [options.userProject] The ID of the project which will be - * billed for the request. - * @param {GetNotificationMetadataCallback} [callback] Callback function. - * @returns {Promise} - * - * @example - * ``` - * const {Storage} = require('@google-cloud/storage'); - * const storage = new Storage(); - * const myBucket = storage.bucket('my-bucket'); - * const notification = myBucket.notification('1'); - * - * notification.getMetadata(function(err, metadata, apiResponse) {}); - * - * //- - * // If the callback is omitted, we'll return a Promise. - * //- - * notification.getMetadata().then(function(data) { - * const metadata = data[0]; - * const apiResponse = data[1]; - * }); - * - * ``` - * @example include:samples/getMetadataNotifications.js - * region_tag:storage_print_pubsub_bucket_notification - * Another example: - */ - getMetadata( - optionsOrCallback?: GetNotificationMetadataOptions | MetadataCallback, - callback?: MetadataCallback - ): void | Promise { - const options = - typeof optionsOrCallback === 'object' ? optionsOrCallback : {}; - callback = - typeof optionsOrCallback === 'function' ? optionsOrCallback : callback; - this.request( - { - uri: '', - qs: options, - }, - (err, resp) => { - if (err) { - callback!(err, null, resp); - return; - } - this.metadata = resp; - callback!(null, this.metadata, resp); - } - ); - } } /*! Developer Documentation diff --git a/src/storage.ts b/src/storage.ts index ba77ee164c..8aa8e6b744 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {ApiError, Metadata, Service, ServiceOptions} from './nodejs-common'; +import {ApiError, Service, ServiceOptions} from './nodejs-common'; import {paginator} from '@google-cloud/paginator'; import {promisifyAll} from '@google-cloud/promisify'; import {Readable} from 'stream'; -import {Bucket} from './bucket'; +import {Bucket, BucketMetadata} from './bucket'; import {Channel} from './channel'; import {File} from './file'; import {normalize} from './util'; @@ -33,12 +33,12 @@ export interface GetServiceAccountOptions { export interface ServiceAccount { emailAddress?: string; } -export type GetServiceAccountResponse = [ServiceAccount, Metadata]; +export type GetServiceAccountResponse = [ServiceAccount, unknown]; export interface GetServiceAccountCallback { ( err: Error | null, serviceAccount?: ServiceAccount, - apiResponse?: Metadata + apiResponse?: unknown ): void; } @@ -64,10 +64,10 @@ export interface RetryOptions { } export interface PreconditionOptions { - ifGenerationMatch?: number; - ifGenerationNotMatch?: number; - ifMetagenerationMatch?: number; - ifMetagenerationNotMatch?: number; + ifGenerationMatch?: number | string; + ifGenerationNotMatch?: number | string; + ifMetagenerationMatch?: number | string; + ifMetagenerationNotMatch?: number | string; } export interface StorageOptions extends ServiceOptions { @@ -130,19 +130,19 @@ export interface CreateBucketRequest { versioning?: Versioning; } -export type CreateBucketResponse = [Bucket, Metadata]; +export type CreateBucketResponse = [Bucket, unknown]; export interface BucketCallback { - (err: Error | null, bucket?: Bucket | null, apiResponse?: Metadata): void; + (err: Error | null, bucket?: Bucket | null, apiResponse?: unknown): void; } -export type GetBucketsResponse = [Bucket[], {}, Metadata]; +export type GetBucketsResponse = [Bucket[], {}, unknown]; export interface GetBucketsCallback { ( err: Error | null, buckets: Bucket[], nextQuery?: {}, - apiResponse?: Metadata + apiResponse?: unknown ): void; } export interface GetBucketsRequest { @@ -192,7 +192,7 @@ export interface GetHmacKeysCallback { err: Error | null, hmacKeys: HmacKey[] | null, nextQuery?: {}, - apiResponse?: Metadata + apiResponse?: unknown ): void; } @@ -1156,7 +1156,7 @@ export class Storage extends Service { } const metadata = resp.metadata; - const hmacKey = this.hmacKey(metadata.accessId, { + const hmacKey = this.hmacKey(metadata.accessId!, { projectId: metadata.projectId, }); hmacKey.metadata = resp.metadata; @@ -1276,8 +1276,8 @@ export class Storage extends Service { } const itemsArray = resp.items ? resp.items : []; - const buckets = itemsArray.map((bucket: Metadata) => { - const bucketInstance = this.bucket(bucket.id); + const buckets = itemsArray.map((bucket: BucketMetadata) => { + const bucketInstance = this.bucket(bucket.id!); bucketInstance.metadata = bucket; return bucketInstance; }); @@ -1399,7 +1399,7 @@ export class Storage extends Service { const itemsArray = resp.items ? resp.items : []; const hmacKeys = itemsArray.map((hmacKey: HmacKeyMetadata) => { - const hmacKeyInstance = this.hmacKey(hmacKey.accessId, { + const hmacKeyInstance = this.hmacKey(hmacKey.accessId!, { projectId: hmacKey.projectId, }); hmacKeyInstance.metadata = hmacKey; diff --git a/src/transfer-manager.ts b/src/transfer-manager.ts index 58cd75c110..ec6d252f4a 100644 --- a/src/transfer-manager.ts +++ b/src/transfer-manager.ts @@ -322,7 +322,7 @@ export class TransferManager { : fileOrName; const fileInfo = await file.get(); - const size = parseInt(fileInfo[0].metadata.size); + const size = parseInt(fileInfo[0].metadata.size!.toString()); // If the file size does not meet the threshold download it as a single chunk. if (size < DOWNLOAD_IN_CHUNKS_FILE_SIZE_THRESHOLD) { limit = pLimit(1); diff --git a/system-test/storage.ts b/system-test/storage.ts index 4517272cc2..adfabe7ed8 100644 --- a/system-test/storage.ts +++ b/system-test/storage.ts @@ -425,10 +425,9 @@ describe('storage', () => { resumable: false, }); const [metadata] = await file.getMetadata(); - assert.strictEqual( - metadata.customerEncryption.encryptionAlgorithm, - 'AES256' - ); + const encyrptionAlgorithm = + metadata.customerEncryption?.encryptionAlgorithm; + assert.strictEqual(encyrptionAlgorithm, 'AES256'); }); it('should set custom encryption in a resumable upload', async () => { @@ -438,10 +437,9 @@ describe('storage', () => { resumable: true, }); const [metadata] = await file.getMetadata(); - assert.strictEqual( - metadata.customerEncryption.encryptionAlgorithm, - 'AES256' - ); + const encyrptionAlgorithm = + metadata.customerEncryption?.encryptionAlgorithm; + assert.strictEqual(encyrptionAlgorithm, 'AES256'); }); it('should make a file public during the upload', async () => { @@ -630,7 +628,7 @@ describe('storage', () => { ); const [bucketMetadata] = await bucket.getMetadata(); const publicAccessPreventionStatus = - bucketMetadata.iamConfiguration.publicAccessPrevention; + bucketMetadata!.iamConfiguration!.publicAccessPrevention; return assert.strictEqual( publicAccessPreventionStatus, PUBLIC_ACCESS_PREVENTION_ENFORCED @@ -679,7 +677,7 @@ describe('storage', () => { ); const [bucketMetadata] = await bucket.getMetadata(); const publicAccessPreventionStatus = - bucketMetadata.iamConfiguration.publicAccessPrevention; + bucketMetadata!.iamConfiguration!.publicAccessPrevention; return assert.strictEqual( publicAccessPreventionStatus, PUBLIC_ACCESS_PREVENTION_INHERITED @@ -704,7 +702,7 @@ describe('storage', () => { it('UBLA modification on PAP bucket does not affect pap setting', async () => { const [bucketMetadata] = await bucket.getMetadata(); const publicAccessPreventionStatus = - bucketMetadata.iamConfiguration.publicAccessPrevention; + bucketMetadata!.iamConfiguration!.publicAccessPrevention; await bucket.setMetadata({ iamConfiguration: { uniformBucketLevelAccess: { @@ -714,7 +712,7 @@ describe('storage', () => { }); const [updatedBucketMetadata] = await bucket.getMetadata(); return assert.strictEqual( - updatedBucketMetadata.iamConfiguration.publicAccessPrevention, + updatedBucketMetadata!.iamConfiguration!.publicAccessPrevention, publicAccessPreventionStatus ); }); @@ -729,14 +727,15 @@ describe('storage', () => { }); const [bucketMetadata] = await bucket.getMetadata(); const ublaSetting = - bucketMetadata.iamConfiguration.uniformBucketLevelAccess.enabled; + bucketMetadata!.iamConfiguration!.uniformBucketLevelAccess!.enabled; await setPublicAccessPrevention( bucket, PUBLIC_ACCESS_PREVENTION_INHERITED ); const [updatedBucketMetadata] = await bucket.getMetadata(); return assert.strictEqual( - updatedBucketMetadata.iamConfiguration.uniformBucketLevelAccess.enabled, + updatedBucketMetadata!.iamConfiguration!.uniformBucketLevelAccess! + .enabled, ublaSetting ); }); @@ -1061,33 +1060,33 @@ describe('storage', () => { }, }); let [metadata] = await bucket.getMetadata(); - const timestampEnabled = metadata.autoclass.toggleTime; - assert.strictEqual(metadata.autoclass.enabled, true); + const timestampEnabled = metadata!.autoclass!.toggleTime; + assert.strictEqual(metadata!.autoclass!.enabled, true); [metadata] = await bucket.setMetadata({ autoclass: { enabled: false, }, }); - const timestampDisabled = metadata.autoclass.toggleTime; - assert.strictEqual(metadata.autoclass.enabled, false); - assert.strictEqual(timestampDisabled > timestampEnabled, true); + const timestampDisabled = metadata!.autoclass!.toggleTime; + assert.strictEqual(metadata!.autoclass!.enabled, false); + assert.strictEqual(timestampDisabled! > timestampEnabled!, true); }); describe('locationType', () => { const types = ['multi-region', 'region', 'dual-region']; beforeEach(() => { - delete bucket.metadata; + bucket.metadata = {}; }); it('should be available from getting a bucket', async () => { const [metadata] = await bucket.getMetadata(); - assert(types.includes(metadata.locationType)); + assert(types.includes(metadata.locationType!)); }); it('should be available from creating a bucket', async () => { const [bucket] = await storage.createBucket(generateName()); - assert(types.includes(bucket.metadata.locationType)); + assert(types.includes(bucket.metadata.locationType!)); return bucket.delete(); }); @@ -1097,19 +1096,19 @@ describe('storage', () => { assert(buckets.length > 0); buckets.forEach(bucket => { - assert(types.includes(bucket.metadata.locationType)); + assert(types.includes(bucket.metadata.locationType!)); }); }); it('should be available from setting retention policy', async () => { await bucket.setRetentionPeriod(RETENTION_DURATION_SECONDS); - assert(types.includes(bucket.metadata.locationType)); + assert(types.includes(bucket.metadata.locationType!)); await bucket.removeRetentionPeriod(); }); it('should be available from updating a bucket', async () => { await bucket.setLabels({a: 'b'}); - assert(types.includes(bucket.metadata.locationType)); + assert(types.includes(bucket.metadata.locationType!)); }); }); @@ -1165,14 +1164,16 @@ describe('storage', () => { describe('bucket object lifecycle management', () => { it('should add a rule', async () => { await bucket.addLifecycleRule({ - action: 'delete', + action: { + type: 'Delete', + }, condition: { age: 30, isLive: true, }, }); - const rules = [].slice.call(bucket.metadata.lifecycle.rule); + const rules = [].slice.call(bucket.metadata.lifecycle?.rule); assert.deepStrictEqual(rules.pop(), { action: { type: 'Delete', @@ -1186,40 +1187,46 @@ describe('storage', () => { it('should append a new rule', async () => { const numExistingRules = - (bucket.metadata.lifecycle && bucket.metadata.lifecycle.rule.length) || + (bucket.metadata.lifecycle && bucket.metadata.lifecycle.rule!.length) || 0; await bucket.addLifecycleRule({ - action: 'delete', + action: { + type: 'Delete', + }, condition: { age: 30, isLive: true, }, }); await bucket.addLifecycleRule({ - action: 'setStorageClass', + action: { + type: 'SetStorageClass', + storageClass: 'coldline', + }, condition: { age: 60, isLive: true, }, - storageClass: 'coldline', }); assert.strictEqual( - bucket.metadata.lifecycle.rule.length, + bucket.metadata.lifecycle!.rule!.length, numExistingRules + 2 ); }); it('should add a prefix rule', async () => { await bucket.addLifecycleRule({ - action: 'delete', + action: { + type: 'Delete', + }, condition: { matchesPrefix: [TESTS_PREFIX], }, }); assert( - bucket.metadata.lifecycle.rule.some( + bucket.metadata.lifecycle!.rule!.some( (rule: LifecycleRule) => typeof rule.action === 'object' && rule.action.type === 'Delete' && @@ -1232,14 +1239,16 @@ describe('storage', () => { it('should add a suffix rule', async () => { await bucket.addLifecycleRule({ - action: 'delete', + action: { + type: 'Delete', + }, condition: { matchesSuffix: [TESTS_PREFIX, 'test_suffix'], }, }); assert( - bucket.metadata.lifecycle.rule.some( + bucket.metadata.lifecycle!.rule!.some( (rule: LifecycleRule) => typeof rule.action === 'object' && rule.action.type === 'Delete' && @@ -1250,12 +1259,14 @@ describe('storage', () => { it('should convert a rule with createdBefore to a date in string', async () => { await bucket.addLifecycleRule({ - action: 'delete', + action: { + type: 'Delete', + }, condition: { createdBefore: new Date('2018'), }, }); - const rules = [].slice.call(bucket.metadata.lifecycle.rule); + const rules = [].slice.call(bucket.metadata.lifecycle?.rule); assert.deepStrictEqual(rules.pop(), { action: { type: 'Delete', @@ -1270,7 +1281,9 @@ describe('storage', () => { const NONCURRENT_TIME_BEFORE = '2020-01-01'; await bucket.addLifecycleRule({ - action: 'delete', + action: { + type: 'Delete', + }, condition: { noncurrentTimeBefore: new Date(NONCURRENT_TIME_BEFORE), daysSinceNoncurrentTime: 100, @@ -1278,7 +1291,7 @@ describe('storage', () => { }); assert( - bucket.metadata.lifecycle.rule.some( + bucket.metadata.lifecycle!.rule!.some( (rule: LifecycleRule) => typeof rule.action === 'object' && rule.action.type === 'Delete' && @@ -1292,7 +1305,9 @@ describe('storage', () => { const CUSTOM_TIME_BEFORE = '2020-01-01'; await bucket.addLifecycleRule({ - action: 'delete', + action: { + type: 'Delete', + }, condition: { customTimeBefore: new Date(CUSTOM_TIME_BEFORE), daysSinceCustomTime: 100, @@ -1300,7 +1315,7 @@ describe('storage', () => { }); assert( - bucket.metadata.lifecycle.rule.some( + bucket.metadata.lifecycle!.rule!.some( (rule: LifecycleRule) => typeof rule.action === 'object' && rule.action.type === 'Delete' && @@ -1375,7 +1390,7 @@ describe('storage', () => { }, }); await bucket.getMetadata(); - assert.strictEqual(bucket.metadata.versioning.enabled, true); + assert.strictEqual(bucket.metadata!.versioning!.enabled, true); }); it('should by default create a bucket without versioning set', async () => { @@ -1398,7 +1413,7 @@ describe('storage', () => { }); await bucket.getMetadata(); assert.strictEqual( - bucket.metadata.retentionPolicy.retentionPeriod, + bucket.metadata!.retentionPolicy!.retentionPeriod, `${RETENTION_DURATION_SECONDS}` ); }); @@ -1409,7 +1424,7 @@ describe('storage', () => { await bucket.setRetentionPeriod(RETENTION_DURATION_SECONDS); await bucket.getMetadata(); assert.strictEqual( - bucket.metadata.retentionPolicy.retentionPeriod, + bucket.metadata!.retentionPolicy!.retentionPeriod, `${RETENTION_DURATION_SECONDS}` ); }); @@ -1420,7 +1435,7 @@ describe('storage', () => { await bucket.setRetentionPeriod(RETENTION_DURATION_SECONDS); await bucket.getMetadata(); - await bucket.lock(bucket.metadata.metageneration); + await bucket.lock(bucket.metadata!.metageneration!.toString()); await assert.rejects( bucket.setRetentionPeriod(RETENTION_DURATION_SECONDS / 2), (err: ApiError) => { @@ -1435,7 +1450,7 @@ describe('storage', () => { await bucket.setRetentionPeriod(RETENTION_DURATION_SECONDS); await bucket.getMetadata(); assert.strictEqual( - bucket.metadata.retentionPolicy.retentionPeriod, + bucket.metadata!.retentionPolicy!.retentionPeriod, `${RETENTION_DURATION_SECONDS}` ); @@ -1585,7 +1600,7 @@ describe('storage', () => { it('should have enabled requesterPays functionality', async () => { const [metadata] = await bucket.getMetadata(); - assert.strictEqual(metadata.billing.requesterPays, true); + assert.strictEqual(metadata.billing!.requesterPays, true); }); // These tests will verify that the requesterPays functionality works from @@ -2098,7 +2113,7 @@ describe('storage', () => { bucket.upload(FILES.big.path, (err: Error | null, file?: File | null) => { assert.ifError(err); - const fileSize = file!.metadata.size; + const fileSize = parseInt(file!.metadata.size!.toString()); const byteRange = { start: Math.floor((fileSize * 1) / 3), end: Math.floor((fileSize * 2) / 3), @@ -2493,7 +2508,7 @@ describe('storage', () => { // Strip the project ID, as it could be the placeholder locally, but // the real value upstream. const projectIdRegExp = /^.+\/locations/; - const actualKmsKeyName = metadata.kmsKeyName.replace( + const actualKmsKeyName = metadata!.kmsKeyName!.replace( projectIdRegExp, '' ); @@ -2513,7 +2528,7 @@ describe('storage', () => { // Strip the project ID, as it could be the placeholder locally, // but the real value upstream. const projectIdRegExp = /^.+\/locations/; - const actualKmsKeyName = metadata.kmsKeyName.replace( + const actualKmsKeyName = metadata!.kmsKeyName!.replace( projectIdRegExp, '' ); @@ -2573,7 +2588,10 @@ describe('storage', () => { // the real value upstream. const projectIdRegExp = /^.+\/locations/; const actualKmsKeyName = - metadata.encryption.defaultKmsKeyName.replace(projectIdRegExp, ''); + metadata!.encryption!.defaultKmsKeyName!.replace( + projectIdRegExp, + '' + ); const expectedKmsKeyName = kmsKeyName.replace(projectIdRegExp, ''); assert.strictEqual(actualKmsKeyName, expectedKmsKeyName); }); @@ -2598,7 +2616,7 @@ describe('storage', () => { assert.strictEqual( fileMetadata.kmsKeyName, - `${metadata.encryption.defaultKmsKeyName}/cryptoKeyVersions/1` + `${metadata!.encryption!.defaultKmsKeyName}/cryptoKeyVersions/1` ); }); }); @@ -2625,10 +2643,10 @@ describe('storage', () => { const [copiedFile] = await file.copy('CloudLogoCopy', copyOpts); const [metadata] = await copiedFile.getMetadata(); assert.strictEqual( - typeof metadata.metadata.originalProperty, + typeof metadata!.metadata!.originalProperty, 'undefined' ); - assert.strictEqual(metadata.metadata.newProperty, 'true'); + assert.strictEqual(metadata!.metadata!.newProperty, 'true'); await Promise.all([file.delete, copiedFile.delete()]); }); @@ -3627,7 +3645,7 @@ describe('storage', () => { const [metadata] = await file.getMetadata(); assert.equal(metadata.crc32c, expected); - assert(crc32c.validate(metadata.crc32c)); + assert(crc32c.validate(metadata.crc32c!)); } }); }); diff --git a/test/acl.ts b/test/acl.ts index 9339b6377a..bee7fe2e48 100644 --- a/test/acl.ts +++ b/test/acl.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {DecorateRequestOptions, Metadata, util} from '../src/nodejs-common'; +import {DecorateRequestOptions, util} from '../src/nodejs-common'; import * as assert from 'assert'; import {describe, it, before, beforeEach} from 'mocha'; import * as proxyquire from 'proxyquire'; @@ -147,7 +147,7 @@ describe('storage/acl', () => { acl.add( {entity: ENTITY, role: ROLE}, - (err: Error, acls: {}, apiResponse: Metadata) => { + (err: Error, acls: {}, apiResponse: unknown) => { assert.deepStrictEqual(resp, apiResponse); done(); } @@ -214,7 +214,7 @@ describe('storage/acl', () => { callback(null, resp); }; - acl.delete({entity: ENTITY}, (err: Error, apiResponse: Metadata) => { + acl.delete({entity: ENTITY}, (err: Error, apiResponse: unknown) => { assert.deepStrictEqual(resp, apiResponse); done(); }); @@ -351,7 +351,7 @@ describe('storage/acl', () => { callback(null, resp); }; - acl.get((err: Error, acls: Array<{}>, apiResponse: Metadata) => { + acl.get((err: Error, acls: Array<{}>, apiResponse: unknown) => { assert.deepStrictEqual(resp, apiResponse); done(); }); @@ -441,7 +441,7 @@ describe('storage/acl', () => { const config = {entity: ENTITY, role: ROLE}; acl.update( config, - (err: Error, acls: Array<{}>, apiResponse: Metadata) => { + (err: Error, acls: Array<{}>, apiResponse: unknown) => { assert.deepStrictEqual(resp, apiResponse); done(); } diff --git a/test/bucket.ts b/test/bucket.ts index 3951a1c525..d48cef3b83 100644 --- a/test/bucket.ts +++ b/test/bucket.ts @@ -13,8 +13,8 @@ // limitations under the License. import { + BaseMetadata, DecorateRequestOptions, - Metadata, ServiceObject, ServiceObjectConfig, util, @@ -35,6 +35,7 @@ import { File, SetFileMetadataOptions, FileOptions, + FileMetadata, } from '../src/file'; import {PromisifyAllOptions} from '@google-cloud/promisify'; import { @@ -45,6 +46,8 @@ import { GetBucketSignedUrlConfig, AvailableServiceObjectMethods, BucketExceptionMessages, + BucketMetadata, + LifecycleRule, } from '../src/bucket'; import {AddAclOptions} from '../src/acl'; import {Policy} from '../src/iam'; @@ -58,7 +61,7 @@ class FakeFile { bucket: Bucket; name: string; options: FileOptions; - metadata: {}; + metadata: FileMetadata; createWriteStream: Function; delete: Function; isSameFile = () => false; @@ -71,7 +74,7 @@ class FakeFile { this.metadata = {}; this.createWriteStream = (options: CreateWriteStreamOptions) => { - this.metadata = options.metadata; + this.metadata = options.metadata!; const ws = new stream.Writable(); ws.write = () => { ws.emit('complete'); @@ -160,7 +163,7 @@ class FakeIam { } } -class FakeServiceObject extends ServiceObject { +class FakeServiceObject extends ServiceObject { calledWith_: IArguments; constructor(config: ServiceObjectConfig) { super(config); @@ -470,54 +473,8 @@ describe('Bucket', () => { condition: {}, }; - bucket.setMetadata = (metadata: Metadata) => { - assert.deepStrictEqual(metadata.lifecycle.rule, [rule]); - done(); - }; - - bucket.addLifecycleRule(rule, assert.ifError); - }); - - it('should properly capitalize rule action', done => { - const rule = { - action: 'delete', - condition: {}, - }; - - bucket.setMetadata = (metadata: Metadata) => { - assert.deepStrictEqual(metadata.lifecycle.rule, [ - { - action: { - type: rule.action.charAt(0).toUpperCase() + rule.action.slice(1), - }, - condition: rule.condition, - }, - ]); - - done(); - }; - - bucket.addLifecycleRule(rule, assert.ifError); - }); - - it('should properly set the storage class', done => { - const rule = { - action: 'setStorageClass', - storageClass: 'storage class', - condition: {}, - }; - - bucket.setMetadata = (metadata: Metadata) => { - assert.deepStrictEqual(metadata.lifecycle.rule, [ - { - action: { - type: rule.action.charAt(0).toUpperCase() + rule.action.slice(1), - storageClass: rule.storageClass, - }, - condition: rule.condition, - }, - ]); - + bucket.setMetadata = (metadata: BucketMetadata) => { + assert.deepStrictEqual(metadata.lifecycle!.rule, [rule]); done(); }; @@ -526,17 +483,19 @@ describe('Bucket', () => { it('should properly set condition', done => { const rule = { - action: 'delete', + action: { + type: 'Delete', + }, condition: { age: 30, }, }; - bucket.setMetadata = (metadata: Metadata) => { - assert.deepStrictEqual(metadata.lifecycle.rule, [ + bucket.setMetadata = (metadata: BucketMetadata) => { + assert.deepStrictEqual(metadata.lifecycle?.rule, [ { action: { - type: rule.action.charAt(0).toUpperCase() + rule.action.slice(1), + type: 'Delete', }, condition: rule.condition, }, @@ -557,10 +516,10 @@ describe('Bucket', () => { }, }; - bucket.setMetadata = (metadata: Metadata) => { + bucket.setMetadata = (metadata: BucketMetadata) => { const expectedDateString = date.toISOString().replace(/T.+$/, ''); - const rule = metadata.lifecycle.rule[0]; + const rule = metadata!.lifecycle!.rule![0]; assert.strictEqual(rule.condition.createdBefore, expectedDateString); done(); @@ -585,9 +544,9 @@ describe('Bucket', () => { done(new Error('Metadata should not be refreshed.')); }; - bucket.setMetadata = (metadata: Metadata) => { - assert.strictEqual(metadata.lifecycle.rule.length, 1); - assert.deepStrictEqual(metadata.lifecycle.rule, [rule]); + bucket.setMetadata = (metadata: BucketMetadata) => { + assert.strictEqual(metadata!.lifecycle!.rule!.length, 1); + assert.deepStrictEqual(metadata.lifecycle?.rule, [rule]); done(); }; @@ -595,16 +554,16 @@ describe('Bucket', () => { }); it('should combine rule with existing rules by default', done => { - const existingRule = { + const existingRule: LifecycleRule = { action: { - type: 'type', + type: 'Delete', }, condition: {}, }; - const newRule = { + const newRule: LifecycleRule = { action: { - type: 'type', + type: 'Delete', }, condition: {}, }; @@ -613,9 +572,9 @@ describe('Bucket', () => { callback(null, {lifecycle: {rule: [existingRule]}}, {}); }; - bucket.setMetadata = (metadata: Metadata) => { - assert.strictEqual(metadata.lifecycle.rule.length, 2); - assert.deepStrictEqual(metadata.lifecycle.rule, [ + bucket.setMetadata = (metadata: BucketMetadata) => { + assert.strictEqual(metadata!.lifecycle!.rule!.length, 2); + assert.deepStrictEqual(metadata.lifecycle?.rule, [ existingRule, newRule, ]); @@ -626,23 +585,23 @@ describe('Bucket', () => { }); it('should accept multiple rules', done => { - const existingRule = { + const existingRule: LifecycleRule = { action: { - type: 'type', + type: 'Delete', }, condition: {}, }; - const newRules = [ + const newRules: LifecycleRule[] = [ { action: { - type: 'type', + type: 'Delete', }, condition: {}, }, { action: { - type: 'type2', + type: 'Delete', }, condition: {}, }, @@ -652,9 +611,9 @@ describe('Bucket', () => { callback(null, {lifecycle: {rule: [existingRule]}}, {}); }; - bucket.setMetadata = (metadata: Metadata) => { - assert.strictEqual(metadata.lifecycle.rule.length, 3); - assert.deepStrictEqual(metadata.lifecycle.rule, [ + bucket.setMetadata = (metadata: BucketMetadata) => { + assert.strictEqual(metadata!.lifecycle!.rule!.length, 3); + assert.deepStrictEqual(metadata.lifecycle?.rule, [ existingRule, newRules[0], newRules[1], @@ -1649,7 +1608,7 @@ describe('Bucket', () => { }); it('should update the logging metadata configuration', done => { - bucket.setMetadata = (metadata: Metadata) => { + bucket.setMetadata = (metadata: BucketMetadata) => { assert.deepStrictEqual(metadata.logging, { logBucket: bucket.id, logObjectPrefix: PREFIX, @@ -1664,8 +1623,8 @@ describe('Bucket', () => { it('should allow a custom bucket to be provided', done => { const bucketName = 'bucket-name'; - bucket.setMetadata = (metadata: Metadata) => { - assert.deepStrictEqual(metadata.logging.logBucket, bucketName); + bucket.setMetadata = (metadata: BucketMetadata) => { + assert.deepStrictEqual(metadata!.logging!.logBucket, bucketName); setImmediate(done); return Promise.resolve([]); }; @@ -1682,8 +1641,11 @@ describe('Bucket', () => { it('should accept a Bucket object', done => { const bucketForLogging = new Bucket(STORAGE, 'bucket-name'); - bucket.setMetadata = (metadata: Metadata) => { - assert.deepStrictEqual(metadata.logging.logBucket, bucketForLogging.id); + bucket.setMetadata = (metadata: BucketMetadata) => { + assert.deepStrictEqual( + metadata!.logging!.logBucket, + bucketForLogging.id + ); setImmediate(done); return Promise.resolve([]); }; @@ -2513,7 +2475,7 @@ describe('Bucket', () => { it('should correctly call setMetadata', done => { const labels = {}; bucket.setMetadata = ( - metadata: Metadata, + metadata: BucketMetadata, _callbackOrOptions: {}, callback: Function ) => { @@ -2545,7 +2507,7 @@ describe('Bucket', () => { ) => { assert.deepStrictEqual(metadata, { retentionPolicy: { - retentionPeriod: duration, + retentionPeriod: `${duration}`, }, }); @@ -2582,7 +2544,7 @@ describe('Bucket', () => { const CALLBACK = util.noop; it('should convert camelCase to snake_case', done => { - bucket.setMetadata = (metadata: Metadata) => { + bucket.setMetadata = (metadata: BucketMetadata) => { assert.strictEqual(metadata.storageClass, 'CAMEL_CASE'); done(); }; @@ -2591,7 +2553,7 @@ describe('Bucket', () => { }); it('should convert hyphenate to snake_case', done => { - bucket.setMetadata = (metadata: Metadata) => { + bucket.setMetadata = (metadata: BucketMetadata) => { assert.strictEqual(metadata.storageClass, 'HYPHENATED_CLASS'); done(); }; @@ -2601,7 +2563,7 @@ describe('Bucket', () => { it('should call setMetdata correctly', () => { bucket.setMetadata = ( - metadata: Metadata, + metadata: BucketMetadata, options: {}, callback: Function ) => { @@ -2663,7 +2625,7 @@ describe('Bucket', () => { }; beforeEach(() => { - bucket.file = (name: string, metadata: Metadata) => { + bucket.file = (name: string, metadata: FileMetadata) => { return new FakeFile(bucket, name, metadata); }; }); @@ -2999,7 +2961,7 @@ describe('Bucket', () => { ws.write = () => true; setImmediate(() => { assert.strictEqual( - options.metadata.contentType, + options!.metadata!.contentType, metadata.contentType ); done(); diff --git a/test/channel.ts b/test/channel.ts index e14d4ed1fb..b671f0ae26 100644 --- a/test/channel.ts +++ b/test/channel.ts @@ -17,6 +17,7 @@ */ import { + BaseMetadata, DecorateRequestOptions, ServiceObject, ServiceObjectConfig, @@ -34,7 +35,7 @@ const fakePromisify = { }, }; -class FakeServiceObject extends ServiceObject { +class FakeServiceObject extends ServiceObject { calledWith_: IArguments; constructor(config: ServiceObjectConfig) { super(config); diff --git a/test/file.ts b/test/file.ts index 14b493f229..d79e4b7ad0 100644 --- a/test/file.ts +++ b/test/file.ts @@ -16,7 +16,6 @@ import { ApiError, BodyResponseCallback, DecorateRequestOptions, - Metadata, MetadataCallback, ServiceObject, ServiceObjectConfig, @@ -53,10 +52,14 @@ import { STORAGE_POST_POLICY_BASE_URL, MoveOptions, FileExceptionMessages, + FileMetadata, } from '../src/file'; import {ExceptionMessages, IdempotencyStrategy} from '../src/storage'; import {formatAsUTCISO} from '../src/util'; -import {SetMetadataOptions} from '../src/nodejs-common/service-object'; +import { + BaseMetadata, + SetMetadataOptions, +} from '../src/nodejs-common/service-object'; class HTTPError extends Error { code: number; @@ -150,7 +153,7 @@ Object.assign(fakeResumableUpload, { }, }); -class FakeServiceObject extends ServiceObject { +class FakeServiceObject extends ServiceObject { calledWith_: IArguments; constructor(config: ServiceObjectConfig) { super(config); @@ -3683,9 +3686,9 @@ describe('File', () => { const apiResponse = {}; file.setMetadata = ( - metadata: Metadata, - optionsOrCallback: SetMetadataOptions | MetadataCallback, - cb: MetadataCallback + metadata: FileMetadata, + optionsOrCallback: SetMetadataOptions | MetadataCallback, + cb: MetadataCallback ) => { Promise.resolve([apiResponse]).then(resp => cb(null, ...resp)); }; diff --git a/test/hmacKey.ts b/test/hmacKey.ts index 9ceb342a4e..1f5b8b1566 100644 --- a/test/hmacKey.ts +++ b/test/hmacKey.ts @@ -16,8 +16,8 @@ import * as sinon from 'sinon'; import * as proxyquire from 'proxyquire'; import * as assert from 'assert'; import {describe, it, beforeEach, afterEach} from 'mocha'; -import {util, ServiceObject, Metadata} from '../src/nodejs-common'; -import {IdempotencyStrategy} from '../src'; +import {util, ServiceObject} from '../src/nodejs-common'; +import {HmacKeyMetadata, IdempotencyStrategy} from '../src'; const sandbox = sinon.createSandbox(); // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -98,7 +98,7 @@ describe('HmacKey', () => { }); it('should correctly call setMetadata', done => { - hmacKey.setMetadata = (metadata: Metadata, callback: Function) => { + hmacKey.setMetadata = (metadata: HmacKeyMetadata, callback: Function) => { assert.deepStrictEqual(metadata.accessId, ACCESS_ID); Promise.resolve([]).then(resp => callback(null, ...resp)); }; diff --git a/test/index.ts b/test/index.ts index 3b8b07c30f..7b592abf4a 100644 --- a/test/index.ts +++ b/test/index.ts @@ -15,7 +15,6 @@ import { ApiError, DecorateRequestOptions, - Metadata, Service, ServiceConfig, util, @@ -835,7 +834,7 @@ describe('Storage', () => { }; storage.createBucket( BUCKET_NAME, - (err: Error, bucket: Bucket, apiResponse: Metadata) => { + (err: Error, bucket: Bucket, apiResponse: unknown) => { assert.strictEqual(resp, apiResponse); done(); } @@ -1026,7 +1025,7 @@ describe('Storage', () => { storage.getBuckets( {}, - (err: Error, buckets: Bucket[], nextQuery: {}, resp: Metadata) => { + (err: Error, buckets: Bucket[], nextQuery: {}, resp: unknown) => { assert.strictEqual(err, error); assert.strictEqual(buckets, null); assert.strictEqual(nextQuery, null); @@ -1193,7 +1192,7 @@ describe('Storage', () => { storage.getHmacKeys( {}, - (err: Error, hmacKeys: HmacKey[], nextQuery: {}, resp: Metadata) => { + (err: Error, hmacKeys: HmacKey[], nextQuery: {}, resp: unknown) => { assert.strictEqual(err, error); assert.strictEqual(hmacKeys, null); assert.strictEqual(nextQuery, null); @@ -1244,7 +1243,7 @@ describe('Storage', () => { }); storage.getHmacKeys( - (err: Error, _hmacKeys: [], _nextQuery: {}, apiResponse: Metadata) => { + (err: Error, _hmacKeys: [], _nextQuery: {}, apiResponse: unknown) => { assert.ifError(err); assert.deepStrictEqual(resp, apiResponse); done(); @@ -1310,7 +1309,7 @@ describe('Storage', () => { it('should return the error and apiResponse', done => { storage.getServiceAccount( - (err: Error, serviceAccount: {}, apiResponse: Metadata) => { + (err: Error, serviceAccount: {}, apiResponse: unknown) => { assert.strictEqual(err, ERROR); assert.strictEqual(serviceAccount, null); assert.strictEqual(apiResponse, API_RESPONSE); diff --git a/test/nodejs-common/service-object.ts b/test/nodejs-common/service-object.ts index 518c08a5e9..c2be0f912f 100644 --- a/test/nodejs-common/service-object.ts +++ b/test/nodejs-common/service-object.ts @@ -63,12 +63,14 @@ interface InternalServiceObject { interceptors: SO.Interceptor[]; } -function asInternal(serviceObject: SO.ServiceObject) { +function asInternal( + serviceObject: SO.ServiceObject +) { return serviceObject as {} as InternalServiceObject; } describe('ServiceObject', () => { - let serviceObject: SO.ServiceObject; + let serviceObject: SO.ServiceObject; const sandbox = sinon.createSandbox(); const CONFIG = { @@ -293,10 +295,12 @@ describe('ServiceObject', () => { sandbox .stub(ServiceObject.prototype, 'request') .callsFake((reqOpts, callback) => { - assert.strictEqual(reqOpts.method, 'DELETE'); - assert.strictEqual(reqOpts.uri, ''); + const opts = reqOpts as r.OptionsWithUri; + const cb = callback as BodyResponseCallback; + assert.strictEqual(opts.method, 'DELETE'); + assert.strictEqual(opts.uri, ''); done(); - callback(null, null, {} as r.Response); + cb(null, null, {} as r.Response); }); serviceObject.delete(assert.ifError); }); @@ -306,9 +310,11 @@ describe('ServiceObject', () => { sandbox .stub(ServiceObject.prototype, 'request') .callsFake((reqOpts, callback) => { - assert.deepStrictEqual(reqOpts.qs, options); + const opts = reqOpts as r.OptionsWithUri; + const cb = callback as BodyResponseCallback; + assert.deepStrictEqual(opts.qs, options); done(); - callback(null, null, {} as r.Response); + cb(null, null, {} as r.Response); }); serviceObject.delete(options, assert.ifError); }); @@ -325,15 +331,17 @@ describe('ServiceObject', () => { sandbox .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts_, callback) => { + .callsFake((reqOpts, callback) => { + const opts = reqOpts as r.OptionsWithUri; + const cb = callback as BodyResponseCallback; assert.deepStrictEqual( serviceObject.methods.delete, cachedMethodConfig ); - assert.deepStrictEqual(reqOpts_.uri, 'v2'); - assert.deepStrictEqual(reqOpts_.method, 'PATCH'); + assert.deepStrictEqual(opts.uri, 'v2'); + assert.deepStrictEqual(opts.method, 'PATCH'); done(); - callback(null, null, null!); + cb(null, null, null!); }); const serviceObject = new ServiceObject(CONFIG) as FakeServiceObject; @@ -368,9 +376,11 @@ describe('ServiceObject', () => { sandbox .stub(ServiceObject.prototype, 'request') .callsFake((reqOpts, callback) => { - assert.strictEqual(reqOpts.qs.ignoreNotFound, undefined); + const opts = reqOpts as r.OptionsWithUri; + const cb = callback as BodyResponseCallback; + assert.strictEqual(opts.qs.ignoreNotFound, undefined); done(); - callback(null, null, {} as r.Response); + cb(null, null, {} as r.Response); }); serviceObject.delete(options, assert.ifError); }); @@ -389,18 +399,20 @@ describe('ServiceObject', () => { sandbox .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts_, callback) => { + .callsFake((reqOpts, callback) => { + const opts = reqOpts as r.OptionsWithUri; + const cb = callback as BodyResponseCallback; assert.deepStrictEqual( serviceObject.methods.delete, cachedMethodConfig ); - assert.deepStrictEqual(reqOpts_.qs, { + assert.deepStrictEqual(opts.qs, { defaultProperty: true, optionalProperty: true, thisPropertyWasOverridden: true, }); done(); - callback(null, null, null!); + cb(null, null, null!); }); const serviceObject = new ServiceObject(CONFIG) as FakeServiceObject; @@ -442,10 +454,12 @@ describe('ServiceObject', () => { const options = {queryOptionProperty: true}; sandbox .stub(ServiceObject.prototype, 'get') - .callsFake((options_, callback) => { - assert.deepStrictEqual(options_, options); + .callsFake((reqOpts, callback) => { + const opts = reqOpts as r.OptionsWithUri; + const cb = callback as BodyResponseCallback; + assert.deepStrictEqual(opts, options); done(); - callback(null, null, {} as r.Response); + cb(null, null, {} as r.Response); }); serviceObject.exists(options, assert.ifError); }); @@ -511,10 +525,13 @@ describe('ServiceObject', () => { it('should execute callback with error & metadata', done => { const error = new Error('Error.'); - const metadata = {} as SO.Metadata; + const metadata = {} as SO.BaseMetadata; serviceObject.getMetadata = promisify( - (options: SO.GetMetadataOptions, callback: SO.MetadataCallback) => { + ( + options: SO.GetMetadataOptions, + callback: SO.MetadataCallback + ) => { callback(error, metadata); } ); @@ -529,10 +546,13 @@ describe('ServiceObject', () => { }); it('should execute callback with instance & metadata', done => { - const metadata = {} as SO.Metadata; + const metadata = {} as SO.BaseMetadata; serviceObject.getMetadata = promisify( - (options: SO.GetMetadataOptions, callback: SO.MetadataCallback) => { + ( + options: SO.GetMetadataOptions, + callback: SO.MetadataCallback + ) => { callback(null, metadata); } ); @@ -552,7 +572,7 @@ describe('ServiceObject', () => { const ERROR = new ApiError('bad'); ERROR.code = 404; - const METADATA = {} as SO.Metadata; + const METADATA = {} as SO.BaseMetadata; beforeEach(() => { AUTO_CREATE_CONFIG = { @@ -560,7 +580,10 @@ describe('ServiceObject', () => { }; serviceObject.getMetadata = promisify( - (options: SO.GetMetadataOptions, callback: SO.MetadataCallback) => { + ( + options: SO.GetMetadataOptions, + callback: SO.MetadataCallback + ) => { callback(ERROR, METADATA); } ); @@ -644,11 +667,17 @@ describe('ServiceObject', () => { it('should make the correct request', done => { sandbox .stub(ServiceObject.prototype, 'request') - .callsFake(function (this: SO.ServiceObject, reqOpts, callback) { + .callsFake(function ( + this: SO.ServiceObject, + reqOpts, + callback + ) { + const opts = reqOpts as r.OptionsWithUri; + const cb = callback as BodyResponseCallback; assert.strictEqual(this, serviceObject); - assert.strictEqual(reqOpts.uri, ''); + assert.strictEqual(opts.uri, ''); done(); - callback(null, null, {} as r.Response); + cb(null, null, {} as r.Response); }); serviceObject.getMetadata(() => {}); }); @@ -658,9 +687,11 @@ describe('ServiceObject', () => { sandbox .stub(ServiceObject.prototype, 'request') .callsFake((reqOpts, callback) => { - assert.deepStrictEqual(reqOpts.qs, options); + const opts = reqOpts as r.OptionsWithUri; + const cb = callback as BodyResponseCallback; + assert.deepStrictEqual(opts.qs, options); done(); - callback(null, null, {} as r.Response); + cb(null, null, {} as r.Response); }); serviceObject.getMetadata(options, assert.ifError); }); @@ -676,14 +707,16 @@ describe('ServiceObject', () => { sandbox .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts_, callback) => { + .callsFake((reqOpts, callback) => { + const opts = reqOpts as r.OptionsWithUri; + const cb = callback as BodyResponseCallback; assert.deepStrictEqual( serviceObject.methods.getMetadata, cachedMethodConfig ); - assert.deepStrictEqual(reqOpts_.uri, 'v2'); + assert.deepStrictEqual(opts.uri, 'v2'); done(); - callback(null, null, null!); + cb(null, null, null!); }); const serviceObject = new ServiceObject(CONFIG) as FakeServiceObject; @@ -705,18 +738,20 @@ describe('ServiceObject', () => { sandbox .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts_, callback) => { + .callsFake((reqOpts, callback) => { + const opts = reqOpts as r.OptionsWithUri; + const cb = callback as BodyResponseCallback; assert.deepStrictEqual( serviceObject.methods.getMetadata, cachedMethodConfig ); - assert.deepStrictEqual(reqOpts_.qs, { + assert.deepStrictEqual(opts.qs, { defaultProperty: true, optionalProperty: true, thisPropertyWasOverridden: true, }); done(); - callback(null, null, null!); + cb(null, null, null!); }); const serviceObject = new ServiceObject(CONFIG) as FakeServiceObject; @@ -860,13 +895,19 @@ describe('ServiceObject', () => { const metadata = {metadataProperty: true}; sandbox .stub(ServiceObject.prototype, 'request') - .callsFake(function (this: SO.ServiceObject, reqOpts, callback) { + .callsFake(function ( + this: SO.ServiceObject, + reqOpts, + callback + ) { + const opts = reqOpts as r.OptionsWithUri; + const cb = callback as BodyResponseCallback; assert.strictEqual(this, serviceObject); - assert.strictEqual(reqOpts.method, 'PATCH'); - assert.strictEqual(reqOpts.uri, ''); - assert.deepStrictEqual(reqOpts.json, metadata); + assert.strictEqual(opts.method, 'PATCH'); + assert.strictEqual(opts.uri, ''); + assert.deepStrictEqual(opts.json, metadata); done(); - callback(null, null, {} as r.Response); + cb(null, null, {} as r.Response); }); serviceObject.setMetadata(metadata, () => {}); }); @@ -877,9 +918,11 @@ describe('ServiceObject', () => { sandbox .stub(ServiceObject.prototype, 'request') .callsFake((reqOpts, callback) => { - assert.deepStrictEqual(reqOpts.qs, options); + const opts = reqOpts as r.OptionsWithUri; + const cb = callback as BodyResponseCallback; + assert.deepStrictEqual(opts.qs, options); done(); - callback(null, null, {} as r.Response); + cb(null, null, {} as r.Response); }); serviceObject.setMetadata(metadata, options, () => {}); }); @@ -895,15 +938,17 @@ describe('ServiceObject', () => { sandbox .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts_, callback) => { + .callsFake((reqOpts, callback) => { + const opts = reqOpts as r.OptionsWithUri; + const cb = callback as BodyResponseCallback; assert.deepStrictEqual( serviceObject.methods.setMetadata, cachedMethodConfig ); - assert.deepStrictEqual(reqOpts_.uri, 'v2'); - assert.deepStrictEqual(reqOpts_.method, 'PUT'); + assert.deepStrictEqual(opts.uri, 'v2'); + assert.deepStrictEqual(opts.method, 'PUT'); done(); - callback(null, null, null!); + cb(null, null, null!); }); const serviceObject = new ServiceObject(CONFIG) as FakeServiceObject; @@ -924,18 +969,20 @@ describe('ServiceObject', () => { sandbox .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts_, callback) => { + .callsFake((reqOpts, callback) => { + const opts = reqOpts as r.OptionsWithUri; + const cb = callback as BodyResponseCallback; assert.deepStrictEqual( serviceObject.methods.setMetadata, cachedMethodConfig ); - assert.deepStrictEqual(reqOpts_.qs, { + assert.deepStrictEqual(opts.qs, { defaultProperty: true, optionalProperty: true, thisPropertyWasOverridden: true, }); done(); - callback(null, null, null!); + cb(null, null, null!); }); const serviceObject = new ServiceObject(CONFIG) as FakeServiceObject; @@ -1082,7 +1129,10 @@ describe('ServiceObject', () => { }); sandbox - .stub(parent.parent as SO.ServiceObject, 'request') + .stub( + parent.parent as SO.ServiceObject, + 'request' + ) .callsFake((reqOpts, callback) => { assert.deepStrictEqual( reqOpts.interceptors_![0].request({} as DecorateRequestOptions), diff --git a/test/notification.ts b/test/notification.ts index e986f69dbb..1847612890 100644 --- a/test/notification.ts +++ b/test/notification.ts @@ -13,6 +13,7 @@ // limitations under the License. import { + BaseMetadata, DecorateRequestOptions, ServiceObject, ServiceObjectConfig, @@ -24,7 +25,7 @@ import * as proxyquire from 'proxyquire'; import {Bucket} from '../src'; -class FakeServiceObject extends ServiceObject { +class FakeServiceObject extends ServiceObject { calledWith_: IArguments; constructor(config: ServiceObjectConfig) { super(config); @@ -51,6 +52,10 @@ describe('Notification', () => { const BUCKET = { createNotification: fakeUtil.noop, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + request(_reqOpts: DecorateRequestOptions, _callback: Function) { + return fakeUtil.noop(); + }, }; const ID = '123'; @@ -67,6 +72,7 @@ describe('Notification', () => { beforeEach(() => { BUCKET.createNotification = fakeUtil.noop = () => {}; + BUCKET.request = fakeUtil.noop = () => {}; notification = new Notification(BUCKET, ID); }); @@ -86,6 +92,21 @@ describe('Notification', () => { assert.deepStrictEqual(calledWith.methods, { create: true, + delete: { + reqOpts: { + qs: {}, + }, + }, + get: { + reqOpts: { + qs: {}, + }, + }, + getMetadata: { + reqOpts: { + qs: {}, + }, + }, exists: true, }); }); @@ -117,13 +138,13 @@ describe('Notification', () => { it('should make the correct request', done => { const options = {}; - notification.request = ( + BUCKET.request = ( reqOpts: DecorateRequestOptions, callback: Function ) => { assert.strictEqual(reqOpts.method, 'DELETE'); - assert.strictEqual(reqOpts.uri, ''); - assert.strictEqual(reqOpts.qs, options); + assert.strictEqual(reqOpts.uri, 'notificationConfigs/123'); + assert.deepStrictEqual(reqOpts.qs, options); callback(); // the done fn }; @@ -131,7 +152,7 @@ describe('Notification', () => { }); it('should optionally accept options', done => { - notification.request = ( + BUCKET.request = ( reqOpts: DecorateRequestOptions, callback: Function ) => { @@ -143,16 +164,14 @@ describe('Notification', () => { }); it('should optionally accept a callback', done => { - fakeUtil.noop = done; - - notification.request = ( - reqOpts: DecorateRequestOptions, + BUCKET.request = ( + _reqOpts: DecorateRequestOptions, callback: Function ) => { callback(); // the done fn }; - notification.delete(); + notification.delete(done); }); }); @@ -169,7 +188,7 @@ describe('Notification', () => { const options = {}; notification.getMetadata = (options_: {}) => { - assert.strictEqual(options_, options); + assert.deepStrictEqual(options_, options); done(); }; @@ -180,7 +199,7 @@ describe('Notification', () => { const error = new Error('Error.'); const metadata = {}; - notification.getMetadata = (options: {}, callback: Function) => { + notification.getMetadata = (_options: {}, callback: Function) => { callback(error, metadata); }; @@ -196,7 +215,7 @@ describe('Notification', () => { it('should execute callback with instance & metadata', done => { const metadata = {}; - notification.getMetadata = (options: {}, callback: Function) => { + notification.getMetadata = (_options: {}, callback: Function) => { callback(null, metadata); }; @@ -221,22 +240,25 @@ describe('Notification', () => { autoCreate: true, }; - notification.getMetadata = (options: {}, callback: Function) => { + notification.getMetadata = (_options: {}, callback: Function) => { callback(ERROR, METADATA); }; }); it('should pass config to create if it was provided', done => { - const config = Object.assign({}, AUTO_CREATE_CONFIG, { - maxResults: 5, - }); - - notification.create = (config_: {}) => { - assert.strictEqual(config_, config); + const config = Object.assign( + {}, + { + maxResults: 5, + } + ); + + notification.get = (config_: {}) => { + assert.deepStrictEqual(config_, config); done(); }; - notification.get(config, assert.ifError); + notification.get(config); }); it('should pass only a callback to create if no config', done => { @@ -296,9 +318,9 @@ describe('Notification', () => { it('should make the correct request', done => { const options = {}; - notification.request = (reqOpts: DecorateRequestOptions) => { - assert.strictEqual(reqOpts.uri, ''); - assert.strictEqual(reqOpts.qs, options); + BUCKET.request = (reqOpts: DecorateRequestOptions) => { + assert.strictEqual(reqOpts.uri, 'notificationConfigs/123'); + assert.deepStrictEqual(reqOpts.qs, options); done(); }; @@ -306,7 +328,7 @@ describe('Notification', () => { }); it('should optionally accept options', done => { - notification.request = (reqOpts: DecorateRequestOptions) => { + BUCKET.request = (reqOpts: DecorateRequestOptions) => { assert.deepStrictEqual(reqOpts.qs, {}); done(); }; @@ -318,16 +340,16 @@ describe('Notification', () => { const error = new Error('err'); const response = {}; - notification.request = ( - reqOpts: DecorateRequestOptions, + BUCKET.request = ( + _reqOpts: DecorateRequestOptions, callback: Function ) => { - callback(error, response); + callback(error, response, response); }; notification.getMetadata((err: Error, metadata: {}, resp: {}) => { assert.strictEqual(err, error); - assert.strictEqual(metadata, null); + assert.strictEqual(metadata, response); assert.strictEqual(resp, response); done(); }); @@ -336,11 +358,11 @@ describe('Notification', () => { it('should set and return the metadata', done => { const response = {}; - notification.request = ( - reqOpts: DecorateRequestOptions, + BUCKET.request = ( + _reqOpts: DecorateRequestOptions, callback: Function ) => { - callback(null, response); + callback(null, response, response); }; notification.getMetadata((err: Error, metadata: {}, resp: {}) => { diff --git a/test/transfer-manager.ts b/test/transfer-manager.ts index be9d56cd2e..b64b456fbd 100644 --- a/test/transfer-manager.ts +++ b/test/transfer-manager.ts @@ -15,7 +15,12 @@ */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import {ServiceObject, ServiceObjectConfig, util} from '../src/nodejs-common'; +import { + BaseMetadata, + ServiceObject, + ServiceObjectConfig, + util, +} from '../src/nodejs-common'; import * as pLimit from 'p-limit'; import * as proxyquire from 'proxyquire'; import { @@ -32,11 +37,12 @@ import * as path from 'path'; import * as stream from 'stream'; import * as extend from 'extend'; import * as fs from 'fs'; +import {FileMetadata} from '../src/file'; const fakeUtil = Object.assign({}, util); fakeUtil.noop = util.noop; -class FakeServiceObject extends ServiceObject { +class FakeServiceObject extends ServiceObject { calledWith_: IArguments; constructor(config: ServiceObjectConfig) { super(config); @@ -57,7 +63,7 @@ class FakeFile { bucket: Bucket; name: string; options: FileOptions; - metadata: {}; + metadata: FileMetadata; createWriteStream: Function; isSameFile = () => false; constructor(bucket: Bucket, name: string, options?: FileOptions) { @@ -69,7 +75,7 @@ class FakeFile { this.metadata = {}; this.createWriteStream = (options: CreateWriteStreamOptions) => { - this.metadata = options.metadata; + this.metadata = options.metadata!; const ws = new stream.Writable(); ws.write = () => { ws.emit('complete');