Skip to content

Commit

Permalink
fix(blob): provide custom errors for expired client tokens and pathna…
Browse files Browse the repository at this point in the history
…me mismatch (#777)

* fix(blob): provide custom error for client token pathname mismatch

Before this commit, you would not know why an upload or put failed when you
messed up the pathname value in upload() versus the one computed when generating
the client token

* fix(blob): Add specific client token expired error

Before this commit, it would be impossible to know that a request failed because
of an expired client token

* remove

* fix(blob): add BlobFileTooLargeError
  • Loading branch information
vvo authored Oct 14, 2024
1 parent 6e1863a commit d58f9de
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/modern-keys-brush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@vercel/blob": patch
---

fix(blob): provide custom errors for expired client tokens and pathname mismatch
4 changes: 2 additions & 2 deletions packages/blob/src/api.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
BlobStoreNotFoundError,
BlobStoreSuspendedError,
BlobUnknownError,
BlobContentTypeNotAllowed,
BlobContentTypeNotAllowedError,
requestApi,
} from './api';
import { BlobError } from './helpers';
Expand Down Expand Up @@ -107,7 +107,7 @@ describe('api', () => {
[
400,
'forbidden',
BlobContentTypeNotAllowed,
BlobContentTypeNotAllowedError,
'"contentType" text/plain is not allowed',
],
[500, 'not_found', BlobNotFoundError],
Expand Down
57 changes: 53 additions & 4 deletions packages/blob/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,29 @@ export class BlobAccessError extends BlobError {
}
}

export class BlobContentTypeNotAllowed extends BlobError {
export class BlobContentTypeNotAllowedError extends BlobError {
constructor(message: string) {
super(`Content type mismatch, ${message}`);
super(`Content type mismatch, ${message}.`);
}
}

export class BlobPathnameMismatchError extends BlobError {
constructor(message: string) {
super(
`Pathname mismatch, ${message}. Check the pathname used in upload() or put() matches the one from the client token.`,
);
}
}

export class BlobClientTokenExpiredError extends BlobError {
constructor() {
super('Client token has expired.');
}
}

export class BlobFileTooLargeError extends BlobError {
constructor(message: string) {
super(`File is too large, ${message}.`);
}
}

Expand Down Expand Up @@ -83,7 +103,10 @@ type BlobApiErrorCodes =
| 'not_allowed'
| 'service_unavailable'
| 'rate_limited'
| 'content_type_not_allowed';
| 'content_type_not_allowed'
| 'client_token_pathname_mismatch'
| 'client_token_expired'
| 'file_too_large';

export interface BlobApiError {
error?: { code?: BlobApiErrorCodes; message?: string };
Expand Down Expand Up @@ -166,6 +189,21 @@ async function getBlobError(
code = 'content_type_not_allowed';
}

if (
message?.includes('"pathname"') &&
message.includes('does not match the token payload')
) {
code = 'client_token_pathname_mismatch';
}

if (message === 'Token expired') {
code = 'client_token_expired';
}

if (message?.includes('the file length cannot be greater than')) {
code = 'file_too_large';
}

let error: BlobError;
switch (code) {
case 'store_suspended':
Expand All @@ -176,7 +214,18 @@ async function getBlobError(
break;
case 'content_type_not_allowed':
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- TS, be smarter
error = new BlobContentTypeNotAllowed(message!);
error = new BlobContentTypeNotAllowedError(message!);
break;
case 'client_token_pathname_mismatch':
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- TS, be smarter
error = new BlobPathnameMismatchError(message!);
break;
case 'client_token_expired':
error = new BlobClientTokenExpiredError();
break;
case 'file_too_large':
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- TS, be smarter
error = new BlobFileTooLargeError(message!);
break;
case 'not_found':
error = new BlobNotFoundError();
Expand Down
5 changes: 4 additions & 1 deletion packages/blob/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ export {
BlobServiceNotAvailable,
BlobRequestAbortedError,
BlobServiceRateLimited,
BlobContentTypeNotAllowed as BlobContentTypeNotAllowedError,
BlobContentTypeNotAllowedError,
BlobPathnameMismatchError,
BlobClientTokenExpiredError,
BlobFileTooLargeError,
} from './api';

// vercelBlob.put()
Expand Down

0 comments on commit d58f9de

Please sign in to comment.