From 54b1ab12cdbcd0c22a869ef67782fbc9fcf35238 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Mon, 21 Dec 2020 20:53:31 +0100 Subject: [PATCH] Passed API error while creating URLs to display proper error messages --- .../helpers/CreateShortUrlResult.tsx | 6 ++-- src/short-urls/reducers/shortUrlCreation.ts | 12 ++++++-- src/utils/services/types.ts | 30 ++++++++++++------- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/short-urls/helpers/CreateShortUrlResult.tsx b/src/short-urls/helpers/CreateShortUrlResult.tsx index 916f622e..ad4fa3bc 100644 --- a/src/short-urls/helpers/CreateShortUrlResult.tsx +++ b/src/short-urls/helpers/CreateShortUrlResult.tsx @@ -9,6 +9,7 @@ import { ShortUrlCreation } from '../reducers/shortUrlCreation'; import { StateFlagTimeout } from '../../utils/helpers/hooks'; import { Result } from '../../utils/Result'; import './CreateShortUrlResult.scss'; +import { isInvalidArgumentError } from '../../utils/services/types'; export interface CreateShortUrlResultProps extends ShortUrlCreation { resetCreateShortUrl: () => void; @@ -16,7 +17,7 @@ export interface CreateShortUrlResultProps extends ShortUrlCreation { } const CreateShortUrlResult = (useStateFlagTimeout: StateFlagTimeout) => ( - { error, result, resetCreateShortUrl, canBeClosed = false }: CreateShortUrlResultProps, + { error, errorData, result, resetCreateShortUrl, canBeClosed = false }: CreateShortUrlResultProps, ) => { const [ showCopyTooltip, setShowCopyTooltip ] = useStateFlagTimeout(); @@ -28,7 +29,8 @@ const CreateShortUrlResult = (useStateFlagTimeout: StateFlagTimeout) => ( return ( {canBeClosed && } - An error occurred while creating the URL :( + {errorData?.detail ?? 'An error occurred while creating the URL :('} + {isInvalidArgumentError(errorData) &&

Invalid elements: [{errorData.invalidElements.join(', ')}]

}
); } diff --git a/src/short-urls/reducers/shortUrlCreation.ts b/src/short-urls/reducers/shortUrlCreation.ts index 3f066655..0b617f45 100644 --- a/src/short-urls/reducers/shortUrlCreation.ts +++ b/src/short-urls/reducers/shortUrlCreation.ts @@ -3,6 +3,7 @@ import { GetState } from '../../container/types'; import { ShortUrl, ShortUrlData } from '../data'; import { buildReducer, buildActionCreator } from '../../utils/helpers/redux'; import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder'; +import { ProblemDetailsError } from '../../utils/services/types'; /* eslint-disable padding-line-between-statements */ export const CREATE_SHORT_URL_START = 'shlink/createShortUrl/CREATE_SHORT_URL_START'; @@ -15,21 +16,26 @@ export interface ShortUrlCreation { result: ShortUrl | null; saving: boolean; error: boolean; + errorData?: ProblemDetailsError; } export interface CreateShortUrlAction extends Action { result: ShortUrl; } +export interface CreateShortUrlFailedAction extends Action { + errorData?: ProblemDetailsError; +} + const initialState: ShortUrlCreation = { result: null, saving: false, error: false, }; -export default buildReducer({ +export default buildReducer({ [CREATE_SHORT_URL_START]: (state) => ({ ...state, saving: true, error: false }), - [CREATE_SHORT_URL_ERROR]: (state) => ({ ...state, saving: false, error: true }), + [CREATE_SHORT_URL_ERROR]: (state, { errorData }) => ({ ...state, saving: false, error: true, errorData }), [CREATE_SHORT_URL]: (_, { result }) => ({ result, saving: false, error: false }), [RESET_CREATE_SHORT_URL]: () => initialState, }, initialState); @@ -46,7 +52,7 @@ export const createShortUrl = (buildShlinkApiClient: ShlinkApiClientBuilder) => dispatch({ type: CREATE_SHORT_URL, result }); } catch (e) { - dispatch({ type: CREATE_SHORT_URL_ERROR }); + dispatch({ type: CREATE_SHORT_URL_ERROR, errorData: e.response?.data }); throw e; } diff --git a/src/utils/services/types.ts b/src/utils/services/types.ts index a098750b..ae50c352 100644 --- a/src/utils/services/types.ts +++ b/src/utils/services/types.ts @@ -25,12 +25,12 @@ interface ShlinkTagsStats { export interface ShlinkTags { tags: string[]; - stats?: ShlinkTagsStats[]; // Is only optional in old Shlink versions + stats?: ShlinkTagsStats[]; // Is only optional in Shlink older than v2.2 } export interface ShlinkTagsResponse { data: string[]; - stats?: ShlinkTagsStats[]; // Is only optional in old Shlink versions + stats?: ShlinkTagsStats[]; // Is only optional in Shlink older than v2.2 } export interface ShlinkPaginator { @@ -41,7 +41,7 @@ export interface ShlinkPaginator { export interface ShlinkVisits { data: Visit[]; - pagination?: ShlinkPaginator; // Is only optional in old Shlink versions + pagination: ShlinkPaginator; } export interface ShlinkVisitsOverview { @@ -60,14 +60,6 @@ export interface ShlinkShortUrlMeta extends ShortUrlMeta { longUrl?: string; } -export interface ProblemDetailsError { - type: string; - detail: string; - title: string; - status: number; - [extraProps: string]: any; -} - export interface ShlinkDomain { domain: string; isDefault: boolean; @@ -76,3 +68,19 @@ export interface ShlinkDomain { export interface ShlinkDomainsResponse { data: ShlinkDomain[]; } + +export interface ProblemDetailsError { + type: string; + detail: string; + title: string; + status: number; + [extraProps: string]: any; +} + +export interface InvalidArgumentError extends ProblemDetailsError { + type: 'INVALID_ARGUMENT'; + invalidElements: string[]; +} + +export const isInvalidArgumentError = (error?: ProblemDetailsError): error is InvalidArgumentError => + error?.type === 'INVALID_ARGUMENT';