Passed API error while creating URLs to display proper error messages

This commit is contained in:
Alejandro Celaya 2020-12-21 20:53:31 +01:00
parent 18d417e78c
commit 54b1ab12cd
3 changed files with 32 additions and 16 deletions

View file

@ -9,6 +9,7 @@ import { ShortUrlCreation } from '../reducers/shortUrlCreation';
import { StateFlagTimeout } from '../../utils/helpers/hooks'; import { StateFlagTimeout } from '../../utils/helpers/hooks';
import { Result } from '../../utils/Result'; import { Result } from '../../utils/Result';
import './CreateShortUrlResult.scss'; import './CreateShortUrlResult.scss';
import { isInvalidArgumentError } from '../../utils/services/types';
export interface CreateShortUrlResultProps extends ShortUrlCreation { export interface CreateShortUrlResultProps extends ShortUrlCreation {
resetCreateShortUrl: () => void; resetCreateShortUrl: () => void;
@ -16,7 +17,7 @@ export interface CreateShortUrlResultProps extends ShortUrlCreation {
} }
const CreateShortUrlResult = (useStateFlagTimeout: StateFlagTimeout) => ( const CreateShortUrlResult = (useStateFlagTimeout: StateFlagTimeout) => (
{ error, result, resetCreateShortUrl, canBeClosed = false }: CreateShortUrlResultProps, { error, errorData, result, resetCreateShortUrl, canBeClosed = false }: CreateShortUrlResultProps,
) => { ) => {
const [ showCopyTooltip, setShowCopyTooltip ] = useStateFlagTimeout(); const [ showCopyTooltip, setShowCopyTooltip ] = useStateFlagTimeout();
@ -28,7 +29,8 @@ const CreateShortUrlResult = (useStateFlagTimeout: StateFlagTimeout) => (
return ( return (
<Result type="error" className="mt-3"> <Result type="error" className="mt-3">
{canBeClosed && <FontAwesomeIcon icon={closeIcon} className="float-right pointer" onClick={resetCreateShortUrl} />} {canBeClosed && <FontAwesomeIcon icon={closeIcon} className="float-right pointer" onClick={resetCreateShortUrl} />}
An error occurred while creating the URL :( {errorData?.detail ?? 'An error occurred while creating the URL :('}
{isInvalidArgumentError(errorData) && <p>Invalid elements: [{errorData.invalidElements.join(', ')}]</p>}
</Result> </Result>
); );
} }

View file

@ -3,6 +3,7 @@ import { GetState } from '../../container/types';
import { ShortUrl, ShortUrlData } from '../data'; import { ShortUrl, ShortUrlData } from '../data';
import { buildReducer, buildActionCreator } from '../../utils/helpers/redux'; import { buildReducer, buildActionCreator } from '../../utils/helpers/redux';
import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder'; import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder';
import { ProblemDetailsError } from '../../utils/services/types';
/* eslint-disable padding-line-between-statements */ /* eslint-disable padding-line-between-statements */
export const CREATE_SHORT_URL_START = 'shlink/createShortUrl/CREATE_SHORT_URL_START'; export const CREATE_SHORT_URL_START = 'shlink/createShortUrl/CREATE_SHORT_URL_START';
@ -15,21 +16,26 @@ export interface ShortUrlCreation {
result: ShortUrl | null; result: ShortUrl | null;
saving: boolean; saving: boolean;
error: boolean; error: boolean;
errorData?: ProblemDetailsError;
} }
export interface CreateShortUrlAction extends Action<string> { export interface CreateShortUrlAction extends Action<string> {
result: ShortUrl; result: ShortUrl;
} }
export interface CreateShortUrlFailedAction extends Action<string> {
errorData?: ProblemDetailsError;
}
const initialState: ShortUrlCreation = { const initialState: ShortUrlCreation = {
result: null, result: null,
saving: false, saving: false,
error: false, error: false,
}; };
export default buildReducer<ShortUrlCreation, CreateShortUrlAction>({ export default buildReducer<ShortUrlCreation, CreateShortUrlAction & CreateShortUrlFailedAction>({
[CREATE_SHORT_URL_START]: (state) => ({ ...state, saving: true, error: false }), [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 }), [CREATE_SHORT_URL]: (_, { result }) => ({ result, saving: false, error: false }),
[RESET_CREATE_SHORT_URL]: () => initialState, [RESET_CREATE_SHORT_URL]: () => initialState,
}, initialState); }, initialState);
@ -46,7 +52,7 @@ export const createShortUrl = (buildShlinkApiClient: ShlinkApiClientBuilder) =>
dispatch<CreateShortUrlAction>({ type: CREATE_SHORT_URL, result }); dispatch<CreateShortUrlAction>({ type: CREATE_SHORT_URL, result });
} catch (e) { } catch (e) {
dispatch({ type: CREATE_SHORT_URL_ERROR }); dispatch<CreateShortUrlFailedAction>({ type: CREATE_SHORT_URL_ERROR, errorData: e.response?.data });
throw e; throw e;
} }

View file

@ -25,12 +25,12 @@ interface ShlinkTagsStats {
export interface ShlinkTags { export interface ShlinkTags {
tags: string[]; 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 { export interface ShlinkTagsResponse {
data: string[]; 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 { export interface ShlinkPaginator {
@ -41,7 +41,7 @@ export interface ShlinkPaginator {
export interface ShlinkVisits { export interface ShlinkVisits {
data: Visit[]; data: Visit[];
pagination?: ShlinkPaginator; // Is only optional in old Shlink versions pagination: ShlinkPaginator;
} }
export interface ShlinkVisitsOverview { export interface ShlinkVisitsOverview {
@ -60,14 +60,6 @@ export interface ShlinkShortUrlMeta extends ShortUrlMeta {
longUrl?: string; longUrl?: string;
} }
export interface ProblemDetailsError {
type: string;
detail: string;
title: string;
status: number;
[extraProps: string]: any;
}
export interface ShlinkDomain { export interface ShlinkDomain {
domain: string; domain: string;
isDefault: boolean; isDefault: boolean;
@ -76,3 +68,19 @@ export interface ShlinkDomain {
export interface ShlinkDomainsResponse { export interface ShlinkDomainsResponse {
data: ShlinkDomain[]; 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';