diff --git a/apps/server/src/api-integration/integration.controller.ts b/apps/server/src/api-integration/integration.controller.ts index fcc170703..250cd15b0 100644 --- a/apps/server/src/api-integration/integration.controller.ts +++ b/apps/server/src/api-integration/integration.controller.ts @@ -24,6 +24,7 @@ import { throttle } from '../utils/throttle.js'; import { coerceEnum } from '../utils/coerceType.js'; import { editEntry } from '../api-data/rundown/rundown.service.js'; import { willCauseRegeneration } from '../api-data/rundown/rundown.utils.js'; +import { getCurrentRundown } from '../api-data/rundown/rundown.dao.js'; const throttledEditEvent = throttle(editEntry, 20); let lastRequest: Date | null = null; @@ -63,32 +64,39 @@ const actionHandlers: Record = { throw new Error('Missing Event ID'); } + const { entries } = getCurrentRundown(); + + const targetEntry = entries[id]; + if (!targetEntry) { + throw new Error('ID do not exits in rundown'); + } + const data = payload[id as keyof typeof payload]; - const patchEvent: PatchWithId = { id }; + const patchEntry: PatchWithId = { id }; // It is not necessarily an event could also be milestone or group but we don't need to track that in the typing here let shouldThrottle = false; Object.entries(data).forEach(([property, value]) => { - if (typeof property !== 'string' || value === undefined) { + if (typeof property !== 'string' || value === undefined || !(property in targetEntry)) { throw new Error('Invalid property or value'); } // parseProperty is async because of the data lock const newObjectProperty = parseProperty(property, value); - const key = Object.keys(newObjectProperty)[0] as keyof OntimeEvent; + const key = Object.keys(newObjectProperty)[0]; shouldThrottle = shouldThrottle || willCauseRegeneration(key); - if (patchEvent.custom && newObjectProperty.custom) { - Object.assign(patchEvent.custom, newObjectProperty.custom); + if (patchEntry.custom && newObjectProperty.custom) { + Object.assign(patchEntry.custom, newObjectProperty.custom); } else { - Object.assign(patchEvent, newObjectProperty); + Object.assign(patchEntry, newObjectProperty); } }); if (shouldThrottle) { - if (throttledEditEvent(patchEvent)) { + if (throttledEditEvent(patchEntry)) { return { payload: 'throttled' }; } } else { - editEntry(patchEvent).catch((_error) => { + editEntry(patchEntry).catch((_error) => { /** No error handling */ }); } diff --git a/apps/server/src/api-integration/integration.utils.ts b/apps/server/src/api-integration/integration.utils.ts index b409ac269..53a377441 100644 --- a/apps/server/src/api-integration/integration.utils.ts +++ b/apps/server/src/api-integration/integration.utils.ts @@ -16,12 +16,14 @@ function clampDuration(value: number): number { return value; } -const propertyConversion = { +const propertyConversion: Record unknown> = { title: coerceString, note: coerceString, cue: coerceString, skip: coerceBoolean, + flag: coerceBoolean, + countToEnd: coerceBoolean, colour: coerceColour, @@ -36,6 +38,7 @@ const propertyConversion = { linkStart: coerceBoolean, timeStrategy: (value: unknown) => coerceEnum(value, TimeStrategy), + targetDuration: (value: unknown) => clampDuration(coerceNumber(value)), // only exist in the group duration: (value: unknown) => clampDuration(coerceNumber(value)), timeStart: (value: unknown) => clampDuration(coerceNumber(value)), timeEnd: (value: unknown) => clampDuration(coerceNumber(value)), @@ -43,7 +46,7 @@ const propertyConversion = { export function parseProperty(property: string, value: unknown) { if (property.startsWith('custom:')) { - const customKey = property.split(':')[1].toLocaleLowerCase(); // all custom fields keys are lowercase + const customKey = property.split(':')[1]; const customFields = getDataProvider().getCustomFields(); if (!(customKey in customFields)) { throw new Error(`Custom field ${customKey} not found`);