mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-24 16:39:13 +00:00
fix: api change custom fields (#1888)
* fix: validate nested custom path * chore: add test
This commit is contained in:
committed by
GitHub
parent
80ec2d1186
commit
f128f7acd2
@@ -0,0 +1,91 @@
|
|||||||
|
import { EntryCustomFields, OntimeEvent } from 'ontime-types';
|
||||||
|
import { isValidChangeProperty } from '../integration.utils.js';
|
||||||
|
|
||||||
|
describe('isValidChangeProperty()', () => {
|
||||||
|
test('correct value and property', () => {
|
||||||
|
const testEvent = {
|
||||||
|
id: 'test',
|
||||||
|
duration: 111,
|
||||||
|
} as OntimeEvent;
|
||||||
|
|
||||||
|
expect(isValidChangeProperty(testEvent, 'duration', 123)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('correct property and undefined value', () => {
|
||||||
|
const testEvent = {
|
||||||
|
id: 'test',
|
||||||
|
duration: 111,
|
||||||
|
} as OntimeEvent;
|
||||||
|
|
||||||
|
expect(isValidChangeProperty(testEvent, 'duration', undefined)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('missing property and undefined value', () => {
|
||||||
|
const testEvent = {
|
||||||
|
id: 'test',
|
||||||
|
duration: 111,
|
||||||
|
} as OntimeEvent;
|
||||||
|
|
||||||
|
expect(isValidChangeProperty(testEvent, 'missing', 123)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('non existing custom value', () => {
|
||||||
|
const testEvent = {
|
||||||
|
id: 'test',
|
||||||
|
duration: 111,
|
||||||
|
custom: {
|
||||||
|
field: 'test',
|
||||||
|
} as EntryCustomFields,
|
||||||
|
} as OntimeEvent;
|
||||||
|
|
||||||
|
expect(isValidChangeProperty(testEvent, 'custom:test', 123)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('existing custom value', () => {
|
||||||
|
const testEvent = {
|
||||||
|
id: 'test',
|
||||||
|
duration: 111,
|
||||||
|
custom: {
|
||||||
|
test: 'test',
|
||||||
|
} as EntryCustomFields,
|
||||||
|
} as OntimeEvent;
|
||||||
|
|
||||||
|
expect(isValidChangeProperty(testEvent, 'custom:test', 123)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('empty custom definition', () => {
|
||||||
|
const testEvent = {
|
||||||
|
id: 'test',
|
||||||
|
duration: 111,
|
||||||
|
custom: {
|
||||||
|
test: 'test',
|
||||||
|
} as EntryCustomFields,
|
||||||
|
} as OntimeEvent;
|
||||||
|
|
||||||
|
expect(isValidChangeProperty(testEvent, 'custom:', 123)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('build-in in custom', () => {
|
||||||
|
const testEvent = {
|
||||||
|
id: 'test',
|
||||||
|
duration: 111,
|
||||||
|
custom: {
|
||||||
|
test: 'test',
|
||||||
|
} as EntryCustomFields,
|
||||||
|
} as OntimeEvent;
|
||||||
|
|
||||||
|
expect(isValidChangeProperty(testEvent, 'custom:toString', 123)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('build-in in top object', () => {
|
||||||
|
const testEvent = {
|
||||||
|
id: 'test',
|
||||||
|
duration: 111,
|
||||||
|
custom: {
|
||||||
|
test: 'test',
|
||||||
|
} as EntryCustomFields,
|
||||||
|
} as OntimeEvent;
|
||||||
|
|
||||||
|
expect(isValidChangeProperty(testEvent, 'toString', 123)).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -18,7 +18,7 @@ import { validateMessage, validateTimerMessage } from '../services/message-servi
|
|||||||
import { runtimeService } from '../services/runtime-service/runtime.service.js';
|
import { runtimeService } from '../services/runtime-service/runtime.service.js';
|
||||||
import { eventStore } from '../stores/EventStore.js';
|
import { eventStore } from '../stores/EventStore.js';
|
||||||
import * as assert from '../utils/assert.js';
|
import * as assert from '../utils/assert.js';
|
||||||
import { parseProperty } from './integration.utils.js';
|
import { parseProperty, isValidChangeProperty } from './integration.utils.js';
|
||||||
import { socket } from '../adapters/WebsocketAdapter.js';
|
import { socket } from '../adapters/WebsocketAdapter.js';
|
||||||
import { throttle } from '../utils/throttle.js';
|
import { throttle } from '../utils/throttle.js';
|
||||||
import { coerceEnum } from '../utils/coerceType.js';
|
import { coerceEnum } from '../utils/coerceType.js';
|
||||||
@@ -77,10 +77,9 @@ const actionHandlers: Record<ApiActionTag, ActionHandler> = {
|
|||||||
let shouldThrottle = false;
|
let shouldThrottle = false;
|
||||||
|
|
||||||
Object.entries(data).forEach(([property, value]) => {
|
Object.entries(data).forEach(([property, value]) => {
|
||||||
if (typeof property !== 'string' || value === undefined || !(property in targetEntry)) {
|
if (!isValidChangeProperty(targetEntry, property, value)) {
|
||||||
throw new Error('Invalid property or value');
|
throw new Error('Invalid property or value');
|
||||||
}
|
}
|
||||||
// parseProperty is async because of the data lock
|
|
||||||
const newObjectProperty = parseProperty(property, value);
|
const newObjectProperty = parseProperty(property, value);
|
||||||
const key = Object.keys(newObjectProperty)[0];
|
const key = Object.keys(newObjectProperty)[0];
|
||||||
shouldThrottle = shouldThrottle || willCauseRegeneration(key);
|
shouldThrottle = shouldThrottle || willCauseRegeneration(key);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EndAction, TimeStrategy, TimerType, isKeyOfType } from 'ontime-types';
|
import { EndAction, OntimeEntry, TimeStrategy, TimerType, isKeyOfType } from 'ontime-types';
|
||||||
import { maxDuration } from 'ontime-utils';
|
import { maxDuration } from 'ontime-utils';
|
||||||
|
|
||||||
import { coerceBoolean, coerceColour, coerceEnum, coerceNumber, coerceString } from '../utils/coerceType.js';
|
import { coerceBoolean, coerceColour, coerceEnum, coerceNumber, coerceString } from '../utils/coerceType.js';
|
||||||
@@ -60,3 +60,14 @@ export function parseProperty(property: string, value: unknown) {
|
|||||||
const parserFn = propertyConversion[property];
|
const parserFn = propertyConversion[property];
|
||||||
return { [property]: parserFn(value) };
|
return { [property]: parserFn(value) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isValidChangeProperty(target: OntimeEntry, property: string, value: unknown): boolean {
|
||||||
|
if (typeof property !== 'string') return false;
|
||||||
|
if (value === undefined) return false;
|
||||||
|
if (property.startsWith('custom:') && 'custom' in target) {
|
||||||
|
const customProperty = property.slice('custom:'.length);
|
||||||
|
if (!customProperty) return false;
|
||||||
|
return Object.hasOwn(target.custom, customProperty);
|
||||||
|
}
|
||||||
|
return Object.hasOwn(target, property);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user