From 543a72d3647bb2f82c8c20ecce628b5c18400780 Mon Sep 17 00:00:00 2001 From: Seyi Adebajo Date: Thu, 22 Feb 2018 14:26:19 -0800 Subject: [PATCH] create api-error error subclass. checks status for error handling when reading compliance info --- wherehows-web/app/constants/errors/errors.ts | 10 +++---- .../app/utils/api/datasets/compliance.ts | 24 +++++++++++------ wherehows-web/app/utils/api/errors/errors.ts | 27 +++++++++++++++++-- wherehows-web/app/utils/api/shared.ts | 2 +- 4 files changed, 47 insertions(+), 16 deletions(-) diff --git a/wherehows-web/app/constants/errors/errors.ts b/wherehows-web/app/constants/errors/errors.ts index f12c793053..d1f15fa6f8 100644 --- a/wherehows-web/app/constants/errors/errors.ts +++ b/wherehows-web/app/constants/errors/errors.ts @@ -1,14 +1,14 @@ -import { ApiStatusNumber } from 'wherehows-web/utils/api/shared'; +import { ApiResponseStatus } from 'wherehows-web/utils/api/shared'; /** * Returns a default msg for a given status - * @param {ApiStatusNumber} status + * @param {ApiResponseStatus} status * @returns {string} */ -const apiErrorStatusMessage = (status: ApiStatusNumber): string => +const apiErrorStatusMessage = (status: ApiResponseStatus): string => (<{ [prop: number]: string }>{ - [ApiStatusNumber.NotFound]: 'Could not find the requested resource', - [ApiStatusNumber.InternalServerError]: 'An error occurred with the server' + [ApiResponseStatus.NotFound]: 'Could not find the requested resource', + [ApiResponseStatus.InternalServerError]: 'An error occurred with the server' })[status]; export { apiErrorStatusMessage }; diff --git a/wherehows-web/app/utils/api/datasets/compliance.ts b/wherehows-web/app/utils/api/datasets/compliance.ts index 6aae8376f1..6f9f812cde 100644 --- a/wherehows-web/app/utils/api/datasets/compliance.ts +++ b/wherehows-web/app/utils/api/datasets/compliance.ts @@ -1,4 +1,6 @@ import { assert } from '@ember/debug'; +import { ApiResponseStatus } from 'wherehows-web/utils/api'; +import { ApiError } from 'wherehows-web/utils/api/errors/errors'; import { createInitialComplianceInfo } from 'wherehows-web/utils/datasets/compliance-policy'; import { datasetUrlById, datasetUrlByUrn } from 'wherehows-web/utils/api/datasets/shared'; import { ApiStatus } from 'wherehows-web/utils/api/shared'; @@ -82,18 +84,24 @@ const readDatasetCompliance = async (id: number): Promise }; /** - * Reads the dataset compliance policy by urn - * @param {string} urn + * Reads the dataset compliance policy by urn. + * Resolves with a new compliance policy instance if remote response is ApiResponseStatus.NotFound + * @param {string} urn the urn for the related dataset * @return {Promise} */ const readDatasetComplianceByUrn = async (urn: string): Promise => { - let { complianceInfo } = await getJSON>({ - url: datasetComplianceUrlByUrn(urn) - }); - const isNewComplianceInfo = !complianceInfo; + let complianceInfo: IComplianceGetResponse['complianceInfo']; + let isNewComplianceInfo = false; - if (isNewComplianceInfo) { - complianceInfo = createInitialComplianceInfo(urn); + try { + ({ complianceInfo } = await getJSON>({ + url: datasetComplianceUrlByUrn(urn) + })); + } catch (e) { + if (e instanceof ApiError && e.status === ApiResponseStatus.NotFound) { + complianceInfo = createInitialComplianceInfo(urn); + isNewComplianceInfo = true; + } } return { isNewComplianceInfo, complianceInfo: complianceInfo! }; diff --git a/wherehows-web/app/utils/api/errors/errors.ts b/wherehows-web/app/utils/api/errors/errors.ts index 5a455c0f3c..6b28b2bbc8 100644 --- a/wherehows-web/app/utils/api/errors/errors.ts +++ b/wherehows-web/app/utils/api/errors/errors.ts @@ -1,4 +1,27 @@ import { apiErrorStatusMessage } from 'wherehows-web/constants/errors/errors'; +import { ApiResponseStatus } from 'wherehows-web/utils/api'; + +/** + * Extends the built-in Error class with attributes related to treating non 200 OK responses + * at the api layer as exceptions + * @class ApiError + * @extends {Error} + */ +class ApiError extends Error { + /** + * Timestamp of when the exception occurred + * @readonly + * @memberof ApiError + */ + readonly timestamp = new Date(); + + constructor(readonly status: ApiResponseStatus, message: string, ...args: Array) { + super(...[message, ...args]); + // Fixes downlevel compiler limitation with correct prototype chain adjustment + // i.e. ensuring this is also `instanceof` subclass + Object.setPrototypeOf(this, ApiError.prototype); + } +} /** * Wraps a Response object, pass through json response if no api error, @@ -12,10 +35,10 @@ const throwIfApiError = async (response: Response): Promise => { if (!ok) { const { msg = apiErrorStatusMessage(status) } = await response.json(); - throw new Error(msg); + throw new ApiError(status, msg); } return response.json(); }; -export { throwIfApiError }; +export { throwIfApiError, ApiError }; diff --git a/wherehows-web/app/utils/api/shared.ts b/wherehows-web/app/utils/api/shared.ts index fd84c2b543..e529ce9394 100644 --- a/wherehows-web/app/utils/api/shared.ts +++ b/wherehows-web/app/utils/api/shared.ts @@ -26,7 +26,7 @@ export enum ApiStatus { * Enumerates the currently available Api statuses * @type {number} */ -export enum ApiStatusNumber { +export enum ApiResponseStatus { NotFound = 404, UnAuthorized = 401, InternalServerError = 500