-
-
Notifications
You must be signed in to change notification settings - Fork 668
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
api: Add new ApiError type and constructor-helper
Add a type to be used for API errors. Replace existing `makeApiError` helper with the slightly-more-rigorous `makeErrorFromApi`. In theory, one could make `ApiError` generic over `T: ApiResponseErrorData`, and refine errors to their appropriate types at runtime. However, this would be a lot of machinery (both type-level and runtime) for effectively no present benefit: we don't have a use yet for the additional data on any of the precise types. Based loosely on a series of commits by Boris Yankov in PR #3484.
- Loading branch information
1 parent
d196448
commit 988736b
Showing
3 changed files
with
52 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* @flow strict-local */ | ||
import type { ApiErrorCode, ApiResponseErrorData } from './transportTypes'; | ||
import * as logging from '../utils/logging'; | ||
|
||
/** Runtime class of custom API error types. */ | ||
export class ApiError extends Error { | ||
code: ApiErrorCode; | ||
data: $ReadOnly<{ ... }>; | ||
httpStatus: number; | ||
|
||
constructor(httpStatus: number, data: $ReadOnly<ApiResponseErrorData>) { | ||
// eslint-disable-next-line no-unused-vars | ||
const { result, code, msg, ...rest } = data; | ||
super(msg); | ||
this.data = rest; | ||
this.code = code; | ||
this.httpStatus = httpStatus; | ||
} | ||
} | ||
|
||
/** | ||
* Given a server response (allegedly) denoting an error, produce an Error to be | ||
* thrown. | ||
* | ||
* If the `data` argument is actually of the form expected from the server, the | ||
* returned error will be an {@link ApiError}; otherwise it will be a generic | ||
* Error. | ||
*/ | ||
export const makeErrorFromApi = (httpStatus: number, data: mixed): Error => { | ||
// Validate `data`, and construct the resultant error object. | ||
if (typeof data === 'object' && data !== null) { | ||
if (data.result === 'error' && typeof data.msg === 'string') { | ||
// If `code` is present, it must be a string. | ||
if (!('code' in data) || typeof data.code === 'string') { | ||
// Default to 'BAD_REQUEST' if `code` is not present. | ||
return new ApiError(httpStatus, { code: 'BAD_REQUEST', ...data }); | ||
} | ||
} | ||
} | ||
|
||
// Server has responded, but the response is not a valid error-object. | ||
// (This should never happen, even on old versions of the Zulip server.) | ||
logging.warn(`Bad response from server: ${JSON.stringify(data)}`); | ||
return new Error('Server responded with invalid message'); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters