Skip to content
Closed
76 changes: 75 additions & 1 deletion handwritten/storage/src/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,21 +148,94 @@ export interface SignedPostPolicyV4Output {
url: string;
fields: PolicyFields;
}

export interface GetSignedUrlConfig
extends Pick<SignerGetSignedUrlConfig, 'host' | 'signingEndpoint'> {
/**
Comment thread
thiyaguk09 marked this conversation as resolved.
* The action to permit with the signed URL.
* - `'read'`: Allows downloading/viewing the file (HTTP GET).
* - `'write'`: Allows uploading/overwriting the file (HTTP PUT).
* - `'delete'`: Allows removing the file (HTTP DELETE).
* - `'resumable'`: Allows resumable uploads (HTTP POST).
* Note: When using `'resumable'`, the header `X-Goog-Resumable: start` must be sent in the client request.
*/
action: 'read' | 'write' | 'delete' | 'resumable';

/**
* The signing version to use.
* @default 'v2'
*/
version?: 'v2' | 'v4';

/**
* Determines the URL structure for accessing bucket resources.
* - `true`: Uses virtual hosted-style URLs (e.g., `https://mybucket.storage.googleapis.com/...`)
* - `false`: Uses path-style URLs (e.g., `https://storage.googleapis.com/mybucket/...`).
* Virtual hosted-style URLs are generally preferred.
* @default false
*/
virtualHostedStyle?: boolean;

/**
* The custom domain name (CNAME) mapped to this bucket (e.g., `"https://cdn.example.com"`).
*/
cname?: string;

/**
* The MD5 digest value in base64. If provided, the client request **must**
* include an identical `Content-MD5` HTTP header.
* If omitted, the client request must not include this header.
*/
contentMd5?: string;

/**
* The expected Content-Type of the file. If provided, the client request **must**
* include an identical `Content-Type` HTTP header.
* If omitted, the client request must not include this header.
*/
contentType?: string;

/**
* The expiration timestamp for the link. Any provided value is passed directly to `new Date()`.
* @throws {Error} If an expiration timestamp from the past is given.
* Note: `'v4'` signing supports a maximum duration of 7 days (604,800 seconds) from the creation time.
*/
expires: string | number | Date;

/**
* The timestamp when this link becomes usable. Any provided value is passed directly to `new Date()`.
* @default Date.now()
* Note: Only supported/applicable when `version` is set to `'v4'`.
*/
accessibleAt?: string | number | Date;

/**
* Canonical extension headers that the server will validate against the client's request.
* Requirements:
* - Header names must be prefixed with `x-goog-` and must be entirely lowercase.
* - Multi-valued headers passed as an array are converted into a comma-separated string (no spaces).
* The client must format them identically to prevent signature mismatches.
*/
extensionHeaders?: http.OutgoingHttpHeaders;

/**
* The filename to prompt the browser/user to save the file as upon access.
* Note: This option is ignored if `responseDisposition` is explicitly set.
*/
promptSaveAs?: string;

/**
* Maps to the `response-content-disposition` query parameter in the signed URL.
*/
responseDisposition?: string;

/**
* Maps to the `response-content-type` query parameter in the signed URL.
*/
responseType?: string;

/**
* Additional query parameters to include natively in the generated signed URL.
*/
queryParams?: Query;
}

Expand Down Expand Up @@ -3236,6 +3309,7 @@ class File extends ServiceObject<File, FileMetadata> {
contentMd5: cfg.contentMd5,
contentType: cfg.contentType,
host: cfg.host,
signingEndpoint: cfg.signingEndpoint,
};

if (cfg.cname) {
Expand Down
2 changes: 1 addition & 1 deletion handwritten/storage/src/signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type GoogleAuthLike = Pick<GoogleAuth, 'getCredentials' | 'sign'>;
* @deprecated Use {@link GoogleAuth} instead
*/
export interface AuthClient {
sign(blobToSign: string): Promise<string>;
sign(blobToSign: string, signingEndpoint?: string): Promise<string>;
getCredentials(): Promise<{
client_email?: string;
}>;
Expand Down
19 changes: 19 additions & 0 deletions handwritten/storage/test/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3794,11 +3794,30 @@ describe('File', () => {
contentType: config.contentType,
cname: CNAME,
virtualHostedStyle: true,
signingEndpoint: undefined,
});
done();
});
});

it('should pass signingEndpoint to URLSigner', done => {
const signingEndpoint = 'https://my-endpoint.com';
const config = {
...SIGNED_URL_CONFIG,
signingEndpoint,
};

file.getSignedUrl(config, (err: Error | null) => {
assert.ifError(err);
const getSignedUrlArgs = signerGetSignedUrlStub.getCall(0).args;
assert.strictEqual(
getSignedUrlArgs[0]['signingEndpoint'],
signingEndpoint
);
done();
});
});

it('should add "x-goog-resumable: start" header if action is resumable', done => {
SIGNED_URL_CONFIG.action = 'resumable';
SIGNED_URL_CONFIG.extensionHeaders = {
Expand Down
Loading