diff --git a/apps/client/src/common/api/apiUtils.ts b/apps/client/src/common/api/apiUtils.ts index 091e05fef..420257057 100644 --- a/apps/client/src/common/api/apiUtils.ts +++ b/apps/client/src/common/api/apiUtils.ts @@ -8,7 +8,15 @@ import { nowInMillis } from '../utils/time'; export function maybeAxiosError(error: unknown) { if (axios.isAxiosError(error)) { const statusText = (error as AxiosError).response?.statusText ?? ''; - const data = (error as AxiosError).response?.data ?? ''; + let data = (error as AxiosError).response?.data ?? ''; + if (typeof data === 'object') { + // TODO: use error instead, when migrated + if ('message' in data) { + data = JSON.stringify(data.message); + } else { + data = JSON.stringify(data); + } + } return `${statusText}: ${data}`; } else { return error as string; diff --git a/packages/utils/src/externals/deepmerge.ts b/packages/utils/src/externals/deepmerge.ts new file mode 100644 index 000000000..23bc35e07 --- /dev/null +++ b/packages/utils/src/externals/deepmerge.ts @@ -0,0 +1,2 @@ +// evaluating the library, we re-export to make it easy to detach +export { deepmerge } from 'deepmerge-ts'; diff --git a/packages/utils/src/validate-events/validateEvent.test.ts b/packages/utils/src/validate-events/validateEvent.test.ts new file mode 100644 index 000000000..8958150a6 --- /dev/null +++ b/packages/utils/src/validate-events/validateEvent.test.ts @@ -0,0 +1,30 @@ +import { EndAction, TimerType } from 'ontime-types'; +import { expect } from 'vitest'; + +import { validateEndAction, validateTimerType } from './validateEvent'; + +describe('validateEndAction()', () => { + it('recognises a string representation of an action', () => { + const endAction = validateEndAction('load-next'); + expect(endAction).toBe(EndAction.LoadNext); + }); + it('returns fallback otherwise', () => { + const emptyAction = validateEndAction('', EndAction.Stop); + const invalidAction = validateEndAction('this-does-not-exist', EndAction.PlayNext); + expect(emptyAction).toBe(EndAction.Stop); + expect(invalidAction).toBe(EndAction.PlayNext); + }); +}); + +describe('validateTimerType()', () => { + it('recognises a string representation of an action', () => { + const timerType = validateTimerType('time-to-end'); + expect(timerType).toBe(TimerType.TimeToEnd); + }); + it('returns fallback otherwise', () => { + const emptyType = validateTimerType('', TimerType.Clock); + const invalidType = validateTimerType('this-does-not-exist', TimerType.CountDown); + expect(emptyType).toBe(TimerType.Clock); + expect(invalidType).toBe(TimerType.CountDown); + }); +}); diff --git a/packages/utils/src/validate-events/validateEvent.ts b/packages/utils/src/validate-events/validateEvent.ts new file mode 100644 index 000000000..536acae63 --- /dev/null +++ b/packages/utils/src/validate-events/validateEvent.ts @@ -0,0 +1,25 @@ +import { EndAction, TimerType } from 'ontime-types'; + +export function validateEndAction(maybeAction: unknown, fallback = EndAction.None) { + if (typeof maybeAction !== 'string') { + return fallback; + } + + const isAction = Object.values(EndAction).includes(maybeAction as EndAction); + if (isAction) { + return maybeAction as EndAction; + } + return fallback; +} + +export function validateTimerType(maybeTimerType: unknown, fallback = TimerType.CountDown) { + if (typeof maybeTimerType !== 'string') { + return fallback; + } + + const isTimerType = Object.values(TimerType).includes(maybeTimerType as TimerType); + if (isTimerType) { + return maybeTimerType as TimerType; + } + return fallback; +}