diff --git a/frontend/src/constants/statusCodes.ts b/frontend/src/constants/statusCodes.ts index ebe1b608a4..7ab9b76376 100644 --- a/frontend/src/constants/statusCodes.ts +++ b/frontend/src/constants/statusCodes.ts @@ -4,3 +4,4 @@ export const CREATED = 201; export const NOT_FOUND = 404; export const FORBIDDEN = 403; export const UNAUTHORIZED = 401; +export const UNAVAILABLE = 503; diff --git a/frontend/src/hooks/api/actions/useApi/useApi.ts b/frontend/src/hooks/api/actions/useApi/useApi.ts index f3a11549e7..ef7d5f60b1 100644 --- a/frontend/src/hooks/api/actions/useApi/useApi.ts +++ b/frontend/src/hooks/api/actions/useApi/useApi.ts @@ -5,6 +5,7 @@ import { NOT_FOUND, OK, UNAUTHORIZED, + UNAVAILABLE, } from 'constants/statusCodes'; import { AuthenticationError, @@ -12,6 +13,7 @@ import { ForbiddenError, headers, NotFoundError, + UnavailableError, } from 'utils/apiUtils'; import { formatApiPath } from 'utils/formatPath'; import { ACCESS_DENIED_TEXT } from 'utils/formatAccessText'; @@ -27,6 +29,7 @@ interface IUseAPI { handleNotFound?: ApiErrorHandler; handleUnauthorized?: ApiErrorHandler; handleForbidden?: ApiErrorHandler; + handleUnavailable?: ApiErrorHandler; propagateErrors?: boolean; } @@ -35,6 +38,7 @@ const useAPI = ({ handleNotFound, handleForbidden, handleUnauthorized, + handleUnavailable, propagateErrors = false, }: IUseAPI) => { const [errors, setErrors] = useState>({}); @@ -104,6 +108,22 @@ const useAPI = ({ } } + if (res.status === UNAVAILABLE) { + if (handleUnavailable) { + return handleUnavailable(setErrors, res, requestId); + } else { + setErrors((prev) => ({ + ...prev, + unavailable: 'This operation is unavailable', + })); + } + + if (propagateErrors) { + const response = await res.json(); + throw new UnavailableError(res.status, response); + } + } + if (res.status > 399) { const response = await res.json(); if (response?.details?.length > 0 && propagateErrors) { diff --git a/frontend/src/utils/apiUtils.ts b/frontend/src/utils/apiUtils.ts index 41dbcfa240..0df8132323 100644 --- a/frontend/src/utils/apiUtils.ts +++ b/frontend/src/utils/apiUtils.ts @@ -3,12 +3,17 @@ import { FORBIDDEN, NOT_FOUND, UNAUTHORIZED, + UNAVAILABLE, } from 'constants/statusCodes'; export interface IErrorBody { + message?: string; details?: { message: string }[]; } +const getErrorMessage = (body: IErrorBody) => + body.details?.[0]?.message || body.message; + export class AuthenticationError extends Error { statusCode: number; @@ -24,23 +29,31 @@ export class ForbiddenError extends Error { body: IErrorBody; constructor(statusCode: number = FORBIDDEN, body: IErrorBody = {}) { - super( - body.details?.length - ? body.details[0].message - : 'You cannot perform this action', - ); + super(getErrorMessage(body) || 'You cannot perform this action'); this.name = 'ForbiddenError'; this.statusCode = statusCode; this.body = body; } } +export class UnavailableError extends Error { + statusCode: number; + body: IErrorBody; + + constructor(statusCode: number = UNAVAILABLE, body: IErrorBody = {}) { + super(getErrorMessage(body) || 'This operation is unavailable'); + this.name = 'UnavailableError'; + this.statusCode = statusCode; + this.body = body; + } +} + export class BadRequestError extends Error { statusCode: number; body: IErrorBody; constructor(statusCode: number = BAD_REQUEST, body: IErrorBody = {}) { - super(body.details?.length ? body.details[0].message : 'Bad request'); + super(getErrorMessage(body) || 'Bad request'); this.name = 'BadRequestError'; this.statusCode = statusCode; this.body = body; diff --git a/src/lib/middleware/maintenance-middleware.ts b/src/lib/middleware/maintenance-middleware.ts index ad03618d21..d800f599cb 100644 --- a/src/lib/middleware/maintenance-middleware.ts +++ b/src/lib/middleware/maintenance-middleware.ts @@ -2,6 +2,9 @@ import { IUnleashConfig } from '../types'; import MaintenanceService from '../services/maintenance-service'; import { IAuthRequest } from '../routes/unleash-types'; +export const MAINTENANCE_MODE_ENABLED = + 'Unleash is currently in maintenance mode.'; + const maintenanceMiddleware = ( { getLogger }: Pick, maintenanceService: MaintenanceService, @@ -17,7 +20,9 @@ const maintenanceMiddleware = ( writeMethod && (await maintenanceService.isMaintenanceMode()) ) { - res.status(503).send({}); + res.status(503).send({ + message: MAINTENANCE_MODE_ENABLED, + }); } else { next(); }