This repository has been archived by the owner on Jun 16, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add API response functions and ApiError
- Loading branch information
Ted Kesgar
committed
Sep 23, 2020
1 parent
fe4da03
commit 8c332cf
Showing
3 changed files
with
218 additions
and
0 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,138 @@ | ||
import { createApiSuccess, createApiFail, createApiError } from ".."; | ||
import { ApiCode, ApiError, ApiStatus } from "../api"; | ||
|
||
describe("createSuccess", () => { | ||
it("should return success object with null data by default", () => { | ||
const apiResponse = createApiSuccess(); | ||
|
||
expect(apiResponse).toEqual({ | ||
status: ApiStatus.Success, | ||
data: null, | ||
}); | ||
}); | ||
|
||
it("should return success object with data", () => { | ||
const apiResponse = createApiSuccess({ subaru: "shuba shuba shuba" }); | ||
|
||
expect(apiResponse).toEqual({ | ||
status: ApiStatus.Success, | ||
data: { subaru: "shuba shuba shuba" }, | ||
}); | ||
}); | ||
}); | ||
|
||
describe("createFail", () => { | ||
it("should return fail object with null data by default", () => { | ||
const apiResponse = createApiFail( | ||
"ILLEGAL_STATE", | ||
"Resource is in invalid state" | ||
); | ||
|
||
expect(apiResponse).toEqual({ | ||
status: ApiStatus.Fail, | ||
code: "ILLEGAL_STATE", | ||
message: "Resource is in invalid state", | ||
data: null, | ||
}); | ||
}); | ||
|
||
it("should return fail object with data", () => { | ||
const apiResponse = createApiFail( | ||
"ILLEGAL_STATE", | ||
"Resource is in invalid state", | ||
{ subaru: "shuba shuba shuba" } | ||
); | ||
|
||
expect(apiResponse).toEqual({ | ||
status: ApiStatus.Fail, | ||
code: "ILLEGAL_STATE", | ||
message: "Resource is in invalid state", | ||
data: { subaru: "shuba shuba shuba" }, | ||
}); | ||
}); | ||
}); | ||
|
||
describe("createError", () => { | ||
it("should return error object with null data by default", () => { | ||
const apiResponse = createApiError( | ||
"UPSTREAM_ERROR", | ||
"Upstream resource returns error" | ||
); | ||
|
||
expect(apiResponse).toEqual({ | ||
status: ApiStatus.Error, | ||
code: "UPSTREAM_ERROR", | ||
message: "Upstream resource returns error", | ||
data: null, | ||
}); | ||
}); | ||
|
||
it("should return error object with data", () => { | ||
const apiResponse = createApiError( | ||
"UPSTREAM_ERROR", | ||
"Upstream resource returns error", | ||
{ subaru: "shuba shuba shuba" } | ||
); | ||
|
||
expect(apiResponse).toEqual({ | ||
status: ApiStatus.Error, | ||
code: "UPSTREAM_ERROR", | ||
message: "Upstream resource returns error", | ||
data: { subaru: "shuba shuba shuba" }, | ||
}); | ||
}); | ||
}); | ||
|
||
describe("ApiError", () => { | ||
it("should return ApiError with default values", () => { | ||
const err = new ApiError("Bad API request"); | ||
|
||
expect(err.message).toBe("Bad API request"); | ||
expect(err.status).toBe(ApiStatus.Fail); | ||
expect(err.code).toBe(ApiCode.Failed); | ||
expect(err.statusCode).toBe(400); | ||
expect(err.data).toEqual(null); | ||
}); | ||
|
||
it("should return ApiError with custom values", () => { | ||
const err = new ApiError("Database unavailable", { | ||
status: ApiStatus.Error, | ||
statusCode: 500, | ||
code: "DB_ERROR", | ||
data: { subaru: "shuba shuba shuba" }, | ||
}); | ||
|
||
expect(err.message).toBe("Database unavailable"); | ||
expect(err.status).toBe(ApiStatus.Error); | ||
expect(err.code).toBe("DB_ERROR"); | ||
expect(err.statusCode).toBe(500); | ||
expect(err.data).toEqual({ subaru: "shuba shuba shuba" }); | ||
}); | ||
|
||
it("should return fail object if ApiError with status = fail is serialized into JSON", () => { | ||
const err = new ApiError("Bad API request"); | ||
|
||
expect(JSON.parse(JSON.stringify(err))).toEqual({ | ||
status: ApiStatus.Fail, | ||
code: ApiCode.Failed, | ||
message: "Bad API request", | ||
data: null, | ||
}); | ||
}); | ||
|
||
it("should return error object if ApiError with status = error is serialized into JSON", () => { | ||
const err = new ApiError("Database unavailable", { | ||
status: ApiStatus.Error, | ||
statusCode: 500, | ||
code: "DB_ERROR", | ||
data: { subaru: "shuba shuba shuba" }, | ||
}); | ||
|
||
expect(JSON.parse(JSON.stringify(err))).toEqual({ | ||
status: ApiStatus.Error, | ||
code: "DB_ERROR", | ||
message: "Database unavailable", | ||
data: { subaru: "shuba shuba shuba" }, | ||
}); | ||
}); | ||
}); |
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,79 @@ | ||
export enum ApiStatus { | ||
Success = "success", | ||
Fail = "fail", | ||
Error = "error", | ||
} | ||
|
||
export enum ApiCode { | ||
Failed = "FAILED", | ||
AuthInvalid = "AUTH_INVALID", | ||
AuthForbidden = "AUTH_FORBIDDEN", | ||
} | ||
|
||
export interface ApiResponse<T = unknown> { | ||
status: ApiStatus; | ||
data: T; | ||
code?: string; | ||
message?: string; | ||
} | ||
|
||
export function createApiSuccess<T = unknown>(data: T = null): ApiResponse<T> { | ||
return { status: ApiStatus.Success, data }; | ||
} | ||
|
||
export function createApiFail<T = unknown>( | ||
code: string, | ||
message: string, | ||
data: T = null | ||
): ApiResponse<T> { | ||
return { status: ApiStatus.Fail, code, message, data }; | ||
} | ||
|
||
export function createApiError<T = unknown>( | ||
code: string, | ||
message: string, | ||
data: T = null | ||
): ApiResponse<T> { | ||
return { status: ApiStatus.Error, code, message, data }; | ||
} | ||
|
||
interface ApiErrorConstructorOpts<T> { | ||
status?: ApiStatus.Fail | ApiStatus.Error; | ||
code?: string; | ||
data?: T; | ||
statusCode?: number; | ||
} | ||
|
||
export class ApiError<T = unknown> extends Error { | ||
readonly status: ApiStatus.Fail | ApiStatus.Error; | ||
readonly code: string; | ||
readonly data: T; | ||
readonly statusCode: number; | ||
|
||
constructor(message: string, opts: ApiErrorConstructorOpts<T> = {}) { | ||
const { | ||
status = ApiStatus.Fail, | ||
code = ApiCode.Failed, | ||
data = null, | ||
statusCode = 400, | ||
} = opts; | ||
|
||
super(message); | ||
|
||
this.code = code; | ||
this.status = status; | ||
this.statusCode = statusCode; | ||
this.data = data; | ||
} | ||
|
||
toJSON(): ApiResponse<T> { | ||
switch (this.status) { | ||
case ApiStatus.Fail: | ||
return createApiFail(this.code, this.message, this.data); | ||
case ApiStatus.Error: | ||
return createApiError(this.code, this.message, this.data); | ||
default: | ||
throw new Error(`Unknown status: ${this.status}`); | ||
} | ||
} | ||
} |
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 |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from "./handle"; | ||
export * from "./api"; |