From ee8354eebfbbc8e80004057cffc08adba9d6b71e Mon Sep 17 00:00:00 2001 From: Denis DelGrosso <85250797+ddelgrosso1@users.noreply.github.com> Date: Mon, 24 Jul 2023 09:53:40 -0400 Subject: [PATCH] feat!: better typing for metadata (#2234) * test: cleanup kms tests to avoid setting incorrect keys (#2213) * chore(deps): update dependency c8 to v8 (#2221) * feat!: better typing for metadata * more metadata typing * fix merge problems * remove extend * fix merge conflicts --------- Co-authored-by: Mend Renovate --- conformance-test/libraryMethods.ts | 6 +- package.json | 4 +- src/acl.ts | 33 ++- src/bucket.ts | 249 ++++++++++++------ src/channel.ts | 10 +- src/file.ts | 146 +++++++---- src/hmacKey.ts | 38 +-- src/iam.ts | 16 +- src/nodejs-common/index.ts | 2 +- src/nodejs-common/service-object.ts | 77 +++--- src/notification.ts | 374 +++++++++++---------------- src/storage.ts | 34 +-- src/transfer-manager.ts | 2 +- system-test/storage.ts | 120 ++++----- test/acl.ts | 10 +- test/bucket.ts | 86 +++--- test/channel.ts | 3 +- test/file.ts | 15 +- test/hmacKey.ts | 6 +- test/index.ts | 11 +- test/nodejs-common/service-object.ts | 152 +++++++---- test/notification.ts | 86 +++--- test/transfer-manager.ts | 14 +- 23 files changed, 818 insertions(+), 676 deletions(-) diff --git a/conformance-test/libraryMethods.ts b/conformance-test/libraryMethods.ts index 825c47533..73a325118 100644 --- a/conformance-test/libraryMethods.ts +++ b/conformance-test/libraryMethods.ts @@ -110,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); @@ -474,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/package.json b/package.json index 7ea78b68e..262781d48 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "compressible": "^2.0.12", "duplexify": "^4.0.0", "ent": "^2.2.0", - "gaxios": "^5.0.0", + "gaxios": "^5.1.2", "google-auth-library": "^8.0.1", "mime": "^3.0.0", "mime-types": "^2.0.8", @@ -86,7 +86,7 @@ "@types/tmp": "0.2.3", "@types/uuid": "^8.0.0", "@types/yargs": "^17.0.10", - "c8": "^7.0.0", + "c8": "^8.0.0", "form-data": "^4.0.0", "gts": "^3.1.0", "jsdoc": "^4.0.0", diff --git a/src/acl.ts b/src/acl.ts index ad67bbaa9..0e593f756 100644 --- a/src/acl.ts +++ b/src/acl.ts @@ -15,7 +15,7 @@ import { BodyResponseCallback, DecorateRequestOptions, - Metadata, + BaseMetadata, } from './nodejs-common'; import {promisifyAll} from '@google-cloud/promisify'; @@ -29,13 +29,13 @@ export interface AclOptions { export type GetAclResponse = [ AccessControlObject | AccessControlObject[], - Metadata + AclMetadata ]; export interface GetAclCallback { ( err: Error | null, acl?: AccessControlObject | AccessControlObject[] | null, - apiResponse?: Metadata + apiResponse?: AclMetadata ): void; } export interface GetAclOptions { @@ -50,12 +50,12 @@ export interface UpdateAclOptions { generation?: number; userProject?: string; } -export type UpdateAclResponse = [AccessControlObject, Metadata]; +export type UpdateAclResponse = [AccessControlObject, AclMetadata]; export interface UpdateAclCallback { ( err: Error | null, acl?: AccessControlObject | null, - apiResponse?: Metadata + apiResponse?: AclMetadata ): void; } @@ -65,17 +65,17 @@ export interface AddAclOptions { generation?: number; userProject?: string; } -export type AddAclResponse = [AccessControlObject, Metadata]; +export type AddAclResponse = [AccessControlObject, AclMetadata]; export interface AddAclCallback { ( err: Error | null, acl?: AccessControlObject | null, - apiResponse?: Metadata + apiResponse?: AclMetadata ): void; } -export type RemoveAclResponse = [Metadata]; +export type RemoveAclResponse = [AclMetadata]; export interface RemoveAclCallback { - (err: Error | null, apiResponse?: Metadata): void; + (err: Error | null, apiResponse?: AclMetadata): void; } export interface RemoveAclOptions { entity: string; @@ -94,6 +94,21 @@ export interface AccessControlObject { projectTeam: string; } +export interface AclMetadata extends BaseMetadata { + bucket?: string; + domain?: string; + entity?: string; + entityId?: string; + generation?: string; + object?: string; + projectTeam?: { + projectNumber?: string; + team?: 'editors' | 'owners' | 'viewers'; + }; + role?: 'OWNER' | 'READER' | 'WRITER' | 'FULL_CONTROL'; + [key: string]: unknown; +} + /** * Attach functionality to a {@link Storage.acl} instance. This will add an * object for each role group (owners, readers, and writers), with each object diff --git a/src/bucket.ts b/src/bucket.ts index 36b8b90d5..14b081842 100644 --- a/src/bucket.ts +++ b/src/bucket.ts @@ -19,34 +19,34 @@ 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 fs from 'fs'; import * as http from 'http'; import * as mime from 'mime-types'; import * as path from 'path'; -import pLimit = require('p-limit'); +import * as pLimit from 'p-limit'; 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, @@ -64,7 +64,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; @@ -78,19 +78,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; } @@ -112,6 +112,24 @@ 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 LifecycleCondition { age?: number; @@ -158,10 +176,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; @@ -171,10 +189,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 { @@ -189,21 +207,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 @@ -216,7 +234,7 @@ export interface DeleteFilesCallback { (err: Error | Error[] | null, apiResponse?: object): void; } -export type DeleteLabelsResponse = [Metadata]; +export type DeleteLabelsResponse = [unknown]; export type DeleteLabelsCallback = SetLabelsCallback; @@ -224,16 +242,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; @@ -249,29 +267,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[] | null; + 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 | null; + }; + 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; } @@ -306,16 +386,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; } @@ -345,17 +425,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; @@ -365,10 +445,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 { @@ -379,10 +459,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 @@ -696,8 +776,7 @@ export enum BucketExceptionMessages { * const bucket = storage.bucket('albums'); * ``` */ -class Bucket extends ServiceObject { - metadata: Metadata; +class Bucket extends ServiceObject { name: string; /** @@ -737,10 +816,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) { @@ -1385,23 +1464,19 @@ class Bucket extends ServiceObject { // 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(rules), - }, + lifecycle: {rule: currentLifecycleRules!.concat(rules)}, }, options as AddLifecycleRuleOptions, callback! @@ -1575,7 +1650,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; @@ -2337,9 +2414,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) { @@ -2704,7 +2784,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) { @@ -2715,7 +2795,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; @@ -2801,13 +2881,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 || {}); } ); } @@ -2895,11 +2975,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); } @@ -3268,7 +3350,7 @@ class Bucket extends ServiceObject { // so acl must explicitly be nullified. const metadata = {...options.metadata, acl: null}; - this.setMetadata(metadata, query, err => { + this.setMetadata(metadata, query, (err: Error | null | undefined) => { if (err) { callback!(err); } @@ -3492,7 +3574,7 @@ class Bucket extends ServiceObject { ); } - request(reqOpts: DecorateRequestOptions): Promise<[ResponseBody, Metadata]>; + request(reqOpts: DecorateRequestOptions): Promise; request( reqOpts: DecorateRequestOptions, callback: BodyResponseCallback @@ -3508,7 +3590,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 = {...reqOpts.qs, userProject: this.userProject}; } @@ -3598,25 +3680,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 +3782,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 f0dff46dd..ec4db1a35 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 fa22b8600..d949106a0 100644 --- a/src/file.ts +++ b/src/file.ts @@ -17,7 +17,6 @@ import { DecorateRequestOptions, GetConfig, Interceptor, - Metadata, MetadataCallback, ServiceObject, SetMetadataResponse, @@ -41,7 +40,7 @@ import { Storage, } from './storage'; import {AvailableServiceObjectMethods, Bucket} from './bucket'; -import {Acl} from './acl'; +import {Acl, AclMetadata} from './acl'; import { GetSignedUrlResponse, SigningError, @@ -71,8 +70,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'; @@ -82,7 +83,7 @@ export interface GetExpirationDateCallback { ( err: Error | null, expirationDate?: Date | null, - apiResponse?: Metadata + apiResponse?: unknown ): void; } @@ -151,20 +152,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 { @@ -182,10 +183,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 = @@ -199,7 +200,7 @@ export type PredefinedAcl = export interface CreateResumableUploadOptions { chunkSize?: number; highWaterMark?: number; - metadata?: Metadata; + metadata?: FileMetadata; origin?: string; offset?: number; predefinedAcl?: PredefinedAcl; @@ -225,13 +226,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; @@ -241,19 +242,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; } @@ -310,17 +311,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]; @@ -340,10 +341,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 { @@ -374,12 +375,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; @@ -387,7 +388,46 @@ export interface SetStorageClassOptions { } export interface SetStorageClassCallback { - (err?: Error | null, apiResponse?: Metadata): void; + (err?: Error | null, apiResponse?: unknown): void; +} + +export interface FileMetadata extends BaseMetadata { + acl?: AclMetadata[] | null; + 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 { @@ -422,7 +462,7 @@ export enum FileExceptionMessages { * * @class */ -class File extends ServiceObject { +class File extends ServiceObject { acl: Acl; crc32cGenerator: CRC32CValidatorGenerator; bucket: Bucket; @@ -430,7 +470,6 @@ class File extends ServiceObject { kmsKeyName?: string; userProject?: string; signer?: URLSigner; - metadata: Metadata; name: string; generation?: number; @@ -1426,11 +1465,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); }); @@ -1438,7 +1477,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} = {}; @@ -1493,7 +1532,7 @@ class File extends ServiceObject { } pipeline( - rawResponseStream, + rawResponseStream as Readable, ...(transformStreams as [Transform]), throughStream, onComplete @@ -1845,27 +1884,27 @@ class File extends ServiceObject { options.metadata ??= {}; 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; @@ -2226,7 +2265,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; @@ -3430,7 +3469,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 @@ -3446,7 +3485,7 @@ class File extends ServiceObject { request( reqOpts: DecorateRequestOptions, callback?: BodyResponseCallback - ): void | Promise<[ResponseBody, Metadata]> { + ): void | Promise { return this.parent.request.call(this, reqOpts, callback!); } @@ -3644,25 +3683,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 33f11431a..4621fef5e 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 1f27622d7..fc65aeac0 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 f90c514ba..293105b95 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 c2d906f02..bab980134 100644 --- a/src/nodejs-common/service-object.ts +++ b/src/nodejs-common/service-object.ts @@ -26,7 +26,7 @@ import { util, } from './util'; -export type RequestResponse = [Metadata, r.Response]; +export type RequestResponse = [unknown, r.Response]; export interface ServiceObjectParent { interceptors: Interceptor[]; @@ -44,12 +44,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; @@ -113,10 +111,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; @@ -128,16 +126,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. @@ -150,8 +156,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; @@ -180,7 +186,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). @@ -248,7 +254,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; @@ -405,10 +411,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); }); } @@ -420,16 +426,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 = @@ -478,23 +484,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' && @@ -576,7 +582,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 aff9d315c..c24bf0c39 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 ce6dc1800..3996132bb 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; } @@ -1155,7 +1155,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; @@ -1275,8 +1275,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; }); @@ -1398,7 +1398,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 6c616ea53..3b2fa8fff 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 e6d279c89..3cf0b3950 100644 --- a/system-test/storage.ts +++ b/system-test/storage.ts @@ -18,7 +18,7 @@ import * as crypto from 'crypto'; import * as fs from 'fs'; import fetch from 'node-fetch'; import * as FormData from 'form-data'; -import pLimit = require('p-limit'); +import * as pLimit from 'p-limit'; import {promisify} from 'util'; import * as path from 'path'; import * as tmp from 'tmp'; @@ -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.setMetadata({labels: {a: 'b'}}); - assert(types.includes(bucket.metadata.locationType)); + assert(types.includes(bucket.metadata.locationType!)); }); }); @@ -1193,8 +1192,8 @@ describe('storage', () => { 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', @@ -1208,7 +1207,7 @@ 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({ @@ -1231,7 +1230,7 @@ describe('storage', () => { }, }); assert.strictEqual( - bucket.metadata.lifecycle.rule.length, + bucket.metadata.lifecycle!.rule!.length, numExistingRules + 2 ); }); @@ -1247,7 +1246,7 @@ describe('storage', () => { }); assert( - bucket.metadata.lifecycle.rule.some( + bucket.metadata.lifecycle!.rule!.some( (rule: LifecycleRule) => typeof rule.action === 'object' && rule.action.type === 'Delete' && @@ -1269,7 +1268,7 @@ describe('storage', () => { }); assert( - bucket.metadata.lifecycle.rule.some( + bucket.metadata.lifecycle!.rule!.some( (rule: LifecycleRule) => typeof rule.action === 'object' && rule.action.type === 'Delete' && @@ -1287,7 +1286,7 @@ describe('storage', () => { 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', @@ -1312,7 +1311,7 @@ describe('storage', () => { }); assert( - bucket.metadata.lifecycle.rule.some( + bucket.metadata.lifecycle!.rule!.some( (rule: LifecycleRule) => typeof rule.action === 'object' && rule.action.type === 'Delete' && @@ -1336,7 +1335,7 @@ describe('storage', () => { }); assert( - bucket.metadata.lifecycle.rule.some( + bucket.metadata.lifecycle!.rule!.some( (rule: LifecycleRule) => typeof rule.action === 'object' && rule.action.type === 'Delete' && @@ -1411,7 +1410,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 () => { @@ -1434,7 +1433,7 @@ describe('storage', () => { }); await bucket.getMetadata(); assert.strictEqual( - bucket.metadata.retentionPolicy.retentionPeriod, + bucket.metadata!.retentionPolicy!.retentionPeriod, `${RETENTION_DURATION_SECONDS}` ); }); @@ -1445,7 +1444,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}` ); }); @@ -1456,7 +1455,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) => { @@ -1471,7 +1470,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}` ); @@ -1621,7 +1620,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 @@ -2134,7 +2133,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), @@ -2529,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, '' ); @@ -2549,7 +2548,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, '' ); @@ -2587,6 +2586,9 @@ describe('storage', () => { before(async () => { bucket = storage.bucket(generateName(), {kmsKeyName}); await bucket.create(); + }); + + beforeEach(async () => { await bucket.setMetadata({ encryption: { defaultKmsKeyName: kmsKeyName, @@ -2594,7 +2596,7 @@ describe('storage', () => { }); }); - after(async () => { + afterEach(async () => { await bucket.setMetadata({ encryption: null, }); @@ -2606,7 +2608,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); }); @@ -2626,24 +2631,13 @@ describe('storage', () => { it('should insert an object that inherits the kms key name', async () => { const file = bucket.file('kms-encrypted-file'); const [metadata] = await bucket.getMetadata(); - const defaultKmsKeyName = metadata.encryption.defaultKmsKeyName; await file.save(FILE_CONTENTS, {resumable: false}); + const [fileMetadata] = await file.getMetadata(); - // Strip the project ID, as it could be the placeholder locally, - // but the real value upstream. - const projectIdRegExp = /^.+\/locations/; - const actualKmsKeyName = file.metadata.kmsKeyName.replace( - projectIdRegExp, - '' - ); - let expectedKmsKeyName = defaultKmsKeyName.replace( - projectIdRegExp, - '' + assert.strictEqual( + fileMetadata.kmsKeyName, + `${metadata!.encryption!.defaultKmsKeyName}/cryptoKeyVersions/1` ); - - // Upstream attaches a version. - expectedKmsKeyName = `${expectedKmsKeyName}/cryptoKeyVersions/1`; - assert.strictEqual(actualKmsKeyName, expectedKmsKeyName); }); }); }); @@ -2669,10 +2663,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()]); }); @@ -3671,7 +3665,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 9339b6377..bee7fe2e4 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 407b7c83b..164cd99f2 100644 --- a/test/bucket.ts +++ b/test/bucket.ts @@ -13,8 +13,8 @@ // limitations under the License. import { + BaseMetadata, DecorateRequestOptions, - Metadata, ServiceObject, ServiceObjectConfig, util, @@ -34,6 +34,7 @@ import { File, SetFileMetadataOptions, FileOptions, + FileMetadata, } from '../src/file'; import {PromisifyAllOptions} from '@google-cloud/promisify'; import { @@ -44,6 +45,8 @@ import { GetBucketSignedUrlConfig, AvailableServiceObjectMethods, BucketExceptionMessages, + BucketMetadata, + LifecycleRule, } from '../src/bucket'; import {AddAclOptions} from '../src/acl'; import {Policy} from '../src/iam'; @@ -57,7 +60,7 @@ class FakeFile { bucket: Bucket; name: string; options: FileOptions; - metadata: {}; + metadata: FileMetadata; createWriteStream: Function; delete: Function; isSameFile = () => false; @@ -70,7 +73,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,8 +473,8 @@ describe('Bucket', () => { condition: {}, }; - bucket.setMetadata = (metadata: Metadata) => { - assert.deepStrictEqual(metadata.lifecycle.rule, [rule]); + bucket.setMetadata = (metadata: BucketMetadata) => { + assert.deepStrictEqual(metadata.lifecycle!.rule, [rule]); done(); }; @@ -488,8 +491,8 @@ describe('Bucket', () => { }, }; - bucket.setMetadata = (metadata: Metadata) => { - assert.deepStrictEqual(metadata.lifecycle.rule, [ + bucket.setMetadata = (metadata: BucketMetadata) => { + assert.deepStrictEqual(metadata.lifecycle?.rule, [ { action: { type: 'Delete', @@ -515,10 +518,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(); @@ -543,9 +546,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(); }; @@ -553,16 +556,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: {}, }; @@ -571,9 +574,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, ]); @@ -584,23 +587,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: {}, }, @@ -610,9 +613,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], @@ -1601,7 +1604,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, @@ -1616,8 +1619,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([]); }; @@ -1634,8 +1637,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([]); }; @@ -2434,7 +2440,7 @@ describe('Bucket', () => { it('should correctly call setMetadata', done => { const labels = {}; bucket.setMetadata = ( - metadata: Metadata, + metadata: BucketMetadata, _callbackOrOptions: {}, callback: Function ) => { @@ -2466,7 +2472,7 @@ describe('Bucket', () => { ) => { assert.deepStrictEqual(metadata, { retentionPolicy: { - retentionPeriod: duration, + retentionPeriod: `${duration}`, }, }); @@ -2503,7 +2509,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(); }; @@ -2512,7 +2518,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(); }; @@ -2522,7 +2528,7 @@ describe('Bucket', () => { it('should call setMetdata correctly', () => { bucket.setMetadata = ( - metadata: Metadata, + metadata: BucketMetadata, options: {}, callback: Function ) => { @@ -2584,7 +2590,7 @@ describe('Bucket', () => { }; beforeEach(() => { - bucket.file = (name: string, metadata: Metadata) => { + bucket.file = (name: string, metadata: FileMetadata) => { return new FakeFile(bucket, name, metadata); }; }); @@ -2920,7 +2926,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 e14d4ed1f..b671f0ae2 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 6b05f2efa..aa050254e 100644 --- a/test/file.ts +++ b/test/file.ts @@ -16,7 +16,6 @@ import { ApiError, BodyResponseCallback, DecorateRequestOptions, - Metadata, MetadataCallback, ServiceObject, ServiceObjectConfig, @@ -52,10 +51,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); @@ -3641,9 +3644,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 9ceb342a4..1f5b8b156 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 3b8b07c30..7b592abf4 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 fe540ca3d..b2c30c45b 100644 --- a/test/nodejs-common/service-object.ts +++ b/test/nodejs-common/service-object.ts @@ -62,12 +62,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 = { @@ -281,10 +283,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); }); @@ -294,9 +298,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); }); @@ -313,15 +319,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; @@ -356,9 +364,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); }); @@ -377,18 +387,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; @@ -430,10 +442,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); }); @@ -499,10 +513,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); } ); @@ -517,10 +534,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); } ); @@ -540,7 +560,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 = { @@ -548,7 +568,10 @@ describe('ServiceObject', () => { }; serviceObject.getMetadata = promisify( - (options: SO.GetMetadataOptions, callback: SO.MetadataCallback) => { + ( + options: SO.GetMetadataOptions, + callback: SO.MetadataCallback + ) => { callback(ERROR, METADATA); } ); @@ -632,11 +655,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(() => {}); }); @@ -646,9 +675,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); }); @@ -664,14 +695,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; @@ -693,18 +726,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; @@ -848,13 +883,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, () => {}); }); @@ -865,9 +906,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, () => {}); }); @@ -883,15 +926,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; @@ -912,18 +957,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; @@ -1068,7 +1115,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 e986f69db..184761289 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 eb32d3cf3..48b316122 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 { @@ -31,11 +36,12 @@ import * as assert from 'assert'; import * as path from 'path'; import * as stream from 'stream'; 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); @@ -56,7 +62,7 @@ class FakeFile { bucket: Bucket; name: string; options: FileOptions; - metadata: {}; + metadata: FileMetadata; createWriteStream: Function; isSameFile = () => false; constructor(bucket: Bucket, name: string, options?: FileOptions) { @@ -68,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');