diff --git a/src/bucket.ts b/src/bucket.ts index c6d3903ea..f6aef95d5 100644 --- a/src/bucket.ts +++ b/src/bucket.ts @@ -1048,6 +1048,27 @@ class Bucket extends ServiceObject { this.instancePreconditionOpts = options?.preconditionOpts; } + /** + * The bucket's Cloud Storage URI (`gs://`) + * + * @example + * ```ts + * const {Storage} = require('@google-cloud/storage'); + * const storage = new Storage(); + * const bucket = storage.bucket('my-bucket'); + * + * // `gs://my-bucket` + * const href = bucket.cloudStorageURI.href; + * ``` + */ + get cloudStorageURI(): URL { + const uri = new URL('gs://'); + + uri.host = this.name; + + return uri; + } + addLifecycleRule( rule: LifecycleRule, options?: AddLifecycleRuleOptions @@ -4149,7 +4170,7 @@ paginator.extend(Bucket, 'getFiles'); * that a callback is omitted. */ promisifyAll(Bucket, { - exclude: ['request', 'file', 'notification'], + exclude: ['cloudStorageURI', 'request', 'file', 'notification'], }); /** diff --git a/src/file.ts b/src/file.ts index 329b43b37..9f82142d9 100644 --- a/src/file.ts +++ b/src/file.ts @@ -883,6 +883,28 @@ class File extends ServiceObject { this.instancePreconditionOpts = options?.preconditionOpts; } + /** + * The object's Cloud Storage URI (`gs://`) + * + * @example + * ```ts + * const {Storage} = require('@google-cloud/storage'); + * const storage = new Storage(); + * const bucket = storage.bucket('my-bucket'); + * const file = bucket.file('image.png'); + * + * // `gs://my-bucket/image.png` + * const href = file.cloudStorageURI.href; + * ``` + */ + get cloudStorageURI(): URL { + const uri = this.bucket.cloudStorageURI; + + uri.pathname = this.name; + + return uri; + } + /** * A helper method for determining if a request should be retried based on preconditions. * This should only be used for methods where the idempotency is determined by @@ -2415,7 +2437,7 @@ class File extends ServiceObject { * @param {boolean} [config.virtualHostedStyle=false] Use virtual hosted-style * URLs ('https://mybucket.storage.googleapis.com/...') instead of path-style * ('https://storage.googleapis.com/mybucket/...'). Virtual hosted-style URLs - * should generally be preferred instaed of path-style URL. + * should generally be preferred instead of path-style URL. * Currently defaults to `false` for path-style, although this may change in a * future major-version release. * @param {string} [config.bucketBoundHostname] The bucket-bound hostname to return in @@ -3855,6 +3877,7 @@ class File extends ServiceObject { */ promisifyAll(File, { exclude: [ + 'cloudStorageURI', 'publicUrl', 'request', 'save', diff --git a/test/bucket.ts b/test/bucket.ts index 2b9cf040a..eedd90825 100644 --- a/test/bucket.ts +++ b/test/bucket.ts @@ -113,6 +113,7 @@ const fakePromisify = { promisified = true; assert.deepStrictEqual(options.exclude, [ + 'cloudStorageURI', 'request', 'file', 'notification', @@ -443,6 +444,15 @@ describe('Bucket', () => { }); }); + describe('cloudStorageURI', () => { + it('should return the appropriate `gs://` URI', () => { + const bucket = new Bucket(STORAGE, BUCKET_NAME); + + assert(bucket.cloudStorageURI instanceof URL); + assert.equal(bucket.cloudStorageURI.host, BUCKET_NAME); + }); + }); + describe('addLifecycleRule', () => { beforeEach(() => { bucket.getMetadata = (callback: GetBucketMetadataCallback) => { diff --git a/test/file.ts b/test/file.ts index 171b88020..18893a2e2 100644 --- a/test/file.ts +++ b/test/file.ts @@ -92,6 +92,7 @@ const fakePromisify = { promisified = true; assert.deepStrictEqual(options.exclude, [ + 'cloudStorageURI', 'publicUrl', 'request', 'save', @@ -471,6 +472,16 @@ describe('File', () => { }); }); + describe('cloudStorageURI', () => { + it('should return the appropriate `gs://` URI', () => { + const file = new File(BUCKET, FILE_NAME); + + assert(file.cloudStorageURI instanceof URL); + assert.equal(file.cloudStorageURI.host, BUCKET.name); + assert.equal(file.cloudStorageURI.pathname, `/${FILE_NAME}`); + }); + }); + describe('copy', () => { it('should throw if no destination is provided', () => { assert.throws(() => {