Skip to content

Commit

Permalink
feat: add ability to create a File object from URL (#2432)
Browse files Browse the repository at this point in the history
* feat: add ability to create a File object from URL

* fixes

* linter fixes
  • Loading branch information
ddelgrosso1 committed Apr 15, 2024
1 parent 6e81e05 commit 1b71fcc
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 0 deletions.
32 changes: 32 additions & 0 deletions src/file.ts
Expand Up @@ -479,6 +479,9 @@ export class RequestError extends Error {
}

const SEVEN_DAYS = 7 * 24 * 60 * 60;
const GS_UTIL_URL_REGEX = /(gs):\/\/([a-z0-9_.-]+)\/(.+)/g;
const HTTPS_PUBLIC_URL_REGEX =
/(https):\/\/(storage\.googleapis\.com)\/([a-z0-9_.-]+)\/(.+)/g;

export enum FileExceptionMessages {
EXPIRATION_TIME_NA = 'An expiration time is not available.',
Expand Down Expand Up @@ -2358,6 +2361,35 @@ class File extends ServiceObject<File, FileMetadata> {
return this;
}

/**
* Gets a reference to a Cloud Storage {@link File} file from the provided URL in string format.
* @param {string} publicUrlOrGsUrl the URL as a string. Must be of the format gs://bucket/file
* or https://storage.googleapis.com/bucket/file.
* @param {Storage} storageInstance an instance of a Storage object.
* @param {FileOptions} [options] Configuration options
* @returns {File}
*/
static from(
publicUrlOrGsUrl: string,
storageInstance: Storage,
options?: FileOptions
): File {
const gsMatches = [...publicUrlOrGsUrl.matchAll(GS_UTIL_URL_REGEX)];
const httpsMatches = [...publicUrlOrGsUrl.matchAll(HTTPS_PUBLIC_URL_REGEX)];

if (gsMatches.length > 0) {
const bucket = new Bucket(storageInstance, gsMatches[0][1]);
return new File(bucket, gsMatches[0][2], options);
} else if (httpsMatches.length > 0) {
const bucket = new Bucket(storageInstance, httpsMatches[0][2]);
return new File(bucket, httpsMatches[0][3], options);
} else {
throw new Error(
'URL string must be of format gs://bucket/file or https://storage.googleapis.com/bucket/file'
);
}
}

get(options?: GetFileOptions): Promise<GetResponse<File>>;
get(callback: InstanceResponseCallback<File>): void;
get(options: GetFileOptions, callback: InstanceResponseCallback<File>): void;
Expand Down
45 changes: 45 additions & 0 deletions test/file.ts
Expand Up @@ -5168,4 +5168,49 @@ describe('File', () => {
file.setUserProject(userProject);
});
});

describe('from', () => {
it('should create a File object from a gs:// formatted URL', () => {
const gsUrl = 'gs://mybucket/myfile';
const result = File.from(gsUrl, STORAGE);

assert(result);
assert(result.bucket.name, 'mybucket');
assert(result.name, 'myfile');
});

it('should create a File object from a gs:// formatted URL including a folder', () => {
const gsUrl = 'gs://mybucket/myfolder/myfile';
const result = File.from(gsUrl, STORAGE);

assert(result);
assert(result.bucket.name, 'mybucket');
assert(result.name, 'myfolder/myfile');
});

it('should create a File object from a https:// formatted URL', () => {
const httpsUrl = 'https://storage.googleapis.com/mybucket/myfile';
const result = File.from(httpsUrl, STORAGE);

assert(result);
assert(result.bucket.name, 'mybucket');
assert(result.name, 'myfile');
});

it('should create a File object from a https:// formatted URL including a folder', () => {
const httpsUrl =
'https://storage.googleapis.com/mybucket/myfolder/myfile';
const result = File.from(httpsUrl, STORAGE);

assert(result);
assert(result.bucket.name, 'mybucket');
assert(result.name, 'myfolder/myfile');
});

it('should throw an error when invoked with an incorrectly formatted URL', () => {
const invalidUrl = 'https://storage.com/mybucket/myfile';

assert.throws(() => File.from(invalidUrl, STORAGE));
});
});
});

0 comments on commit 1b71fcc

Please sign in to comment.