refactor: validation

This commit is contained in:
Carlos Valente
2023-12-01 13:08:05 +01:00
parent f720c56345
commit efcd6494be
20 changed files with 263 additions and 172 deletions
+5 -5
View File
@@ -3,12 +3,12 @@ import {
Alias,
DatabaseModel,
GetInfo,
HTTPSettings,
HttpSettings,
OntimeRundown,
OSCSettings,
OscSubscription,
ProjectData,
Settings,
Subscription,
UserFields,
ViewSettings,
} from 'ontime-types';
@@ -109,7 +109,7 @@ export async function getOSC(): Promise<OSCSettings> {
* @description HTTP request to retrieve http settings
* @return {Promise}
*/
export async function getHTTP(): Promise<HTTPSettings> {
export async function getHTTP(): Promise<HttpSettings> {
const res = await axios.get(`${ontimeURL}/http`);
return res.data;
}
@@ -118,7 +118,7 @@ export async function getHTTP(): Promise<HTTPSettings> {
* @description HTTP request to mutate http settings
* @return {Promise}
*/
export async function postHTTP(data: HTTPSettings) {
export async function postHTTP(data: HttpSettings) {
return axios.post(`${ontimeURL}/http`, data);
}
@@ -134,7 +134,7 @@ export async function postOSC(data: OSCSettings) {
* @description HTTP request to mutate osc subscriptions
* @return {Promise}
*/
export async function postOscSubscriptions(data: Subscription) {
export async function postOscSubscriptions(data: OscSubscription) {
return axios.post(`${ontimeURL}/osc-subscriptions`, data);
}
@@ -1,5 +1,5 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import { HTTPSettings } from 'ontime-types';
import { HttpSettings } from 'ontime-types';
import { queryRefetchIntervalSlow } from '../../ontimeConfig';
import { HTTP_SETTINGS } from '../api/apiConstants';
@@ -20,7 +20,7 @@ export function useHttpSettings() {
});
// we need to jump through some hoops because of the type op port
return { data: data! as unknown as HTTPSettings, status, isFetching, isError, refetch };
return { data: data! as unknown as HttpSettings, status, isFetching, isError, refetch };
}
export function usePostHttpSettings() {
+2 -2
View File
@@ -1,6 +1,6 @@
import { HTTPSettings } from 'ontime-types';
import { HttpSettings } from 'ontime-types';
export const httpPlaceholder: HTTPSettings = {
export const httpPlaceholder: HttpSettings = {
enabledOut: false,
subscriptions: {
onLoad: [],
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { Switch } from '@chakra-ui/react';
import type { HTTPSettings } from 'ontime-types';
import type { HttpSettings } from 'ontime-types';
import { TimerLifeCycle } from 'ontime-types';
import { useHttpSettings, usePostHttpSettings } from '../../../../common/hooks-query/useHttpSettings';
@@ -24,7 +24,7 @@ export default function HttpIntegration() {
register,
reset,
formState: { isSubmitting, isDirty, isValid },
} = useForm<HTTPSettings>({
} = useForm<HttpSettings>({
mode: 'onBlur',
defaultValues: data,
values: data,
@@ -45,9 +45,9 @@ export default function HttpIntegration() {
reset(data);
};
const onSubmit = async (values: HTTPSettings) => {
const onSubmit = async (values: HttpSettings) => {
try {
const newSettings: HTTPSettings = {
const newSettings: HttpSettings = {
enabledOut: Boolean(values.enabledOut),
subscriptions: {
onLoad: values.subscriptions.onLoad ?? [],
@@ -59,8 +59,6 @@ export default function HttpIntegration() {
},
};
console.log('debug will submit', newSettings);
await mutateAsync(newSettings);
} catch (error) {
emitError(`Error setting HTML: ${error}`);
@@ -2,7 +2,7 @@ import { Control, useFieldArray, UseFormRegister } from 'react-hook-form';
import { Button, IconButton, Input, Switch } from '@chakra-ui/react';
import { FiChevronUp } from '@react-icons/all-files/fi/FiChevronUp';
import { IoRemove } from '@react-icons/all-files/io5/IoRemove';
import { HTTPSettings, TimerLifeCycle } from 'ontime-types';
import { HttpSettings, TimerLifeCycle } from 'ontime-types';
import { useEmitLog } from '../../../../common/stores/logger';
import { startsWithHttp } from '../../../../common/utils/regex';
@@ -16,8 +16,8 @@ interface SubscriptionRowProps {
subtitle: string;
visible: boolean;
setShowSection: (cycle: TimerLifeCycle) => void;
register: UseFormRegister<HTTPSettings>;
control: Control<HTTPSettings>;
register: UseFormRegister<HttpSettings>;
control: Control<HttpSettings>;
placeholder: string;
}
+2 -2
View File
@@ -1,4 +1,4 @@
import { HTTPSettings, LogOrigin, OSCSettings } from 'ontime-types';
import { HttpSettings, LogOrigin, OSCSettings } from 'ontime-types';
import 'dotenv/config';
import express from 'express';
@@ -194,7 +194,7 @@ export const startOSCServer = async (overrideConfig = null) => {
/**
* starts integrations
*/
export const startIntegrations = async (config?: { osc: OSCSettings; http: HTTPSettings }) => {
export const startIntegrations = async (config?: { osc: OSCSettings; http: HttpSettings }) => {
checkStart(OntimeStartOrder.InitIO);
const { osc, http } = config ?? DataProvider.getData();
@@ -1,5 +1,5 @@
import { LogOrigin } from 'ontime-types';
import type { Alias, DatabaseModel, GetInfo, HTTPSettings, ProjectData } from 'ontime-types';
import type { Alias, DatabaseModel, GetInfo, HttpSettings, ProjectData } from 'ontime-types';
import { RequestHandler, Request, Response } from 'express';
import fs from 'fs';
@@ -335,7 +335,7 @@ export const postOscSubscriptions = async (req, res) => {
};
// Create controller for GET request to '/ontime/http'
export const getHTTP = async (_req, res: Response<HTTPSettings>) => {
export const getHTTP = async (_req, res: Response<HttpSettings>) => {
const http = DataProvider.getHttp();
res.status(200).send(http);
};
@@ -1,5 +1,9 @@
import { body, check, validationResult } from 'express-validator';
import { validateOscObject, validateSubscriptionEntry } from '../utils/parserFunctions.js';
import {
validateHttpSubscriptionObject,
validateOscSubscriptionObject,
validateOscSubscriptionCycle,
} from '../utils/parserFunctions.js';
/**
* @description Validates object for POST /ontime/views
@@ -82,7 +86,7 @@ export const validateOSC = [
body('enabledOut').exists().isBoolean(),
body('subscriptions')
.isObject()
.custom((value) => validateOscObject(value)),
.custom((value) => validateOscSubscriptionObject(value)),
(req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
@@ -97,7 +101,7 @@ export const validateHTTP = [
body('enabledOut').exists().isBoolean(),
body('subscriptions')
.isObject()
.custom((value) => validateOscObject(value)),
.custom((value) => validateHttpSubscriptionObject(value)),
(req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
@@ -108,25 +112,25 @@ export const validateHTTP = [
/**
* @description Validates object for POST /ontime/osc-subscriptions
*/
export const validateSubscription = [
export const validateOscSubscription = [
body('onLoad')
.isArray()
.custom((value) => validateSubscriptionEntry(value)),
.custom((value) => validateOscSubscriptionCycle(value)),
body('onStart')
.isArray()
.custom((value) => validateSubscriptionEntry(value)),
.custom((value) => validateOscSubscriptionCycle(value)),
body('onPause')
.isArray()
.custom((value) => validateSubscriptionEntry(value)),
.custom((value) => validateOscSubscriptionCycle(value)),
body('onStop')
.isArray()
.custom((value) => validateSubscriptionEntry(value)),
.custom((value) => validateOscSubscriptionCycle(value)),
body('onUpdate')
.isArray()
.custom((value) => validateSubscriptionEntry(value)),
.custom((value) => validateOscSubscriptionCycle(value)),
body('onFinish')
.isArray()
.custom((value) => validateSubscriptionEntry(value)),
.custom((value) => validateOscSubscriptionCycle(value)),
(req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
+2 -2
View File
@@ -26,7 +26,7 @@ import {
import {
validateAliases,
validateOSC,
validateSubscription,
validateOscSubscriptionObject,
validatePatchProjectFile,
validateSettings,
validateUserFields,
@@ -86,7 +86,7 @@ router.get('/osc', getOSC);
router.post('/osc', validateOSC, postOSC);
// create route between controller and '/ontime/osc-subscriptions' endpoint
router.post('/osc-subscriptions', validateSubscription, postOscSubscriptions);
router.post('/osc-subscriptions', validateOscSubscriptionObject, postOscSubscriptions);
// create route between controller and '/ontime/http' endpoint
router.get('/http', getHTTP);
@@ -1,11 +1,11 @@
//TODO: cleanup stuff left over from OSC copy
import http from 'node:http';
import { HTTPSettings, LogOrigin, Subscription } from 'ontime-types';
import { HttpSettings, HttpSubscription, HttpSubscriptionOptions, LogOrigin } from 'ontime-types';
import IIntegration, { TimerLifeCycleKey } from './IIntegration.js';
import { parseTemplateNested } from './integrationUtils.js';
import { dbModel } from '../../models/dataModel.js';
import { validateHttpObject } from '../../utils/parserFunctions.js';
import { validateHttpSubscriptionEntry } from '../../utils/parserFunctions.js';
import { logger } from '../../classes/Logger.js';
import { URL } from 'node:url';
@@ -15,9 +15,9 @@ type Action = TimerLifeCycleKey | string;
* @description Class contains logic towards outgoing HTTP communications
* @class
*/
export class HttpIntegration implements IIntegration {
export class HttpIntegration implements IIntegration<HttpSubscriptionOptions> {
protected httpAgent: null | http.Agent;
subscriptions: Subscription;
subscriptions: HttpSubscription;
constructor() {
this.httpAgent = null;
@@ -27,7 +27,7 @@ export class HttpIntegration implements IIntegration {
/**
* Initializes httpClient
*/
init(config: HTTPSettings) {
init(config: HttpSettings) {
const { subscriptions, enabledOut } = config;
if (!enabledOut) {
@@ -60,8 +60,9 @@ export class HttpIntegration implements IIntegration {
}
}
initSubscriptions(subscriptionOptions: Subscription) {
if (validateHttpObject(subscriptionOptions)) {
initSubscriptions(subscriptionOptions: HttpSubscription) {
// TODO: here we have to validate the entire subscription object
if (validateHttpSubscriptionEntry(subscriptionOptions)) {
this.subscriptions = { ...subscriptionOptions };
}
}
@@ -2,8 +2,8 @@ import { TimerLifeCycle, Subscription } from 'ontime-types';
export type TimerLifeCycleKey = keyof typeof TimerLifeCycle;
export default interface IIntegration {
subscriptions: Subscription;
export default interface IIntegration<T> {
subscriptions: Subscription<T>;
init: (config: unknown) => OperationReturn;
dispatch: (action: TimerLifeCycleKey, state?: object) => OperationReturn;
emit: (...args: unknown[]) => unknown;
@@ -2,17 +2,17 @@ import IIntegration, { TimerLifeCycleKey } from './IIntegration.js';
import { eventStore } from '../../stores/EventStore.js';
class IntegrationService {
private integrations: IIntegration[];
private integrations: IIntegration<unknown>[];
constructor() {
this.integrations = [];
}
register(integrationService: IIntegration) {
register(integrationService: IIntegration<unknown>) {
this.integrations.push(integrationService);
}
unregister(integrationService: IIntegration) {
unregister(integrationService: IIntegration<unknown>) {
this.integrations = this.integrations.filter((int) => int !== integrationService);
}
@@ -1,11 +1,11 @@
import { ArgumentType, Client, Message } from 'node-osc';
import { OSCSettings, Subscription } from 'ontime-types';
import { OSCSettings, OscSubscription, OscSubscriptionOptions } from 'ontime-types';
import IIntegration, { TimerLifeCycleKey } from './IIntegration.js';
import { parseTemplateNested } from './integrationUtils.js';
import { isObject } from '../../utils/varUtils.js';
import { dbModel } from '../../models/dataModel.js';
import { validateOscObject } from '../../utils/parserFunctions.js';
import { validateOscSubscriptionObject } from '../../utils/parserFunctions.js';
type Action = TimerLifeCycleKey | string;
@@ -13,9 +13,9 @@ type Action = TimerLifeCycleKey | string;
* @description Class contains logic towards outgoing OSC communications
* @class
*/
export class OscIntegration implements IIntegration {
export class OscIntegration implements IIntegration<OscSubscriptionOptions> {
protected oscClient: null | Client;
subscriptions: Subscription;
subscriptions: OscSubscription;
constructor() {
this.oscClient = null;
@@ -65,8 +65,8 @@ export class OscIntegration implements IIntegration {
}
}
initSubscriptions(subscriptionOptions: Subscription) {
if (validateOscObject(subscriptionOptions)) {
initSubscriptions(subscriptionOptions: OscSubscription) {
if (validateOscSubscriptionObject(subscriptionOptions)) {
this.subscriptions = { ...subscriptionOptions };
}
}
@@ -1,91 +0,0 @@
import { validateOscObject } from '../parserFunctions.ts';
test('validateOscSubscription()', () => {
it('should return true when given a valid OscSubscription', () => {
const validSubscription = {
onLoad: [{ id: '1', message: 'test', enabled: true }],
onStart: [{ id: '2', message: 'test', enabled: false }],
onPause: [{ id: '3', message: 'test', enabled: true }],
onStop: [{ id: '4', message: 'test', enabled: false }],
onUpdate: [{ id: '5', message: 'test', enabled: true }],
onFinish: [{ id: '6', message: 'test', enabled: false }],
};
const result = validateOscObject(validSubscription);
expect(result).toBe(true);
});
it('should return false when given undefined', () => {
const result = validateOscObject(undefined);
expect(result).toBe(false);
});
it('should return false when given null', () => {
const result = validateOscObject(null);
expect(result).toBe(false);
});
it('should return false when given an empty object', () => {
const result = validateOscObject({});
expect(result).toBe(false);
});
it('should return false when given an empty array', () => {
const result = validateOscObject([]);
expect(result).toBe(false);
});
it('should return false when given an object that is not an OscSubscription', () => {
const invalidObject = { foo: 'bar' };
const result = validateOscObject(invalidObject);
expect(result).toBe(false);
});
it('should return false when given an OscSubscription with a missing property', () => {
const invalidSubscription = {
onLoad: [{ id: '1', message: 'test', enabled: true }],
onStart: [{ id: '2', message: 'test', enabled: false }],
onPause: [{ id: '3', message: 'test', enabled: true }],
// Missing onStop
onUpdate: [{ id: '5', message: 'test', enabled: true }],
onFinish: [{ id: '6', message: 'test', enabled: false }],
};
const result = validateOscObject(invalidSubscription);
expect(result).toBe(false);
});
it('should return false when given an OscSubscription with an invalid property value', () => {
const invalidSubscription = {
onLoad: [{ id: '1', message: 'test', enabled: true }],
onStart: [{ id: '2', message: 'test', enabled: false }],
onPause: [{ id: '3', message: 'test', enabled: true }],
onStop: [{ id: '4', message: 'test', enabled: false }],
onUpdate: [{ id: '5', message: 'test', enabled: true }],
onFinish: [{ id: '6', message: 'test', enabled: 'not a boolean' }],
};
const result = validateOscObject(invalidSubscription);
expect(result).toBe(false);
});
it('should return true if the message field is empty', () => {
const invalidSubscription = {
onLoad: [{ id: '1', message: 'test', enabled: true }],
onStart: [{ id: '2', message: '', enabled: false }],
onPause: [{ id: '3', message: '', enabled: true }],
onStop: [{ id: '4', message: 'test', enabled: false }],
onUpdate: [{ id: '5', message: 'test', enabled: true }],
onFinish: [{ id: '6', message: 'test', enabled: 'not a boolean' }],
};
const result = validateOscObject(invalidSubscription);
expect(result).toBe(true);
});
});
@@ -0,0 +1,161 @@
import { HttpSubscription, OscSubscription } from 'ontime-types';
import {
validateOscSubscriptionObject,
validateOscSubscriptionCycle,
validateHttpSubscriptionCycle,
validateHttpSubscriptionObject,
} from '../parserFunctions.js';
describe('validateOscSubscriptionCycle()', () => {
it('should return false when given an OscSubscription with an invalid property value', () => {
const invalidEntry = [{ message: 'test', enabled: 'not a boolean' }];
// @ts-expect-error -- since this comes from the client, we check things that typescript would have caught
const result = validateOscSubscriptionCycle(invalidEntry);
expect(result).toBe(false);
});
});
describe('validateOscSubscriptionObject()', () => {
it('should return true when given a valid OscSubscription', () => {
const validSubscription: OscSubscription = {
onLoad: [{ message: 'test', enabled: true }],
onStart: [{ message: 'test', enabled: false }],
onPause: [{ message: 'test', enabled: true }],
onStop: [{ message: 'test', enabled: false }],
onUpdate: [{ message: 'test', enabled: true }],
onFinish: [{ message: 'test', enabled: false }],
};
const result = validateOscSubscriptionObject(validSubscription);
expect(result).toBe(true);
});
it('should return false when given undefined', () => {
const result = validateOscSubscriptionObject(undefined);
expect(result).toBe(false);
});
it('should return false when given null', () => {
const result = validateOscSubscriptionObject(null);
expect(result).toBe(false);
});
it('should return false when given an empty object', () => {
// @ts-expect-error -- since this comes from the client, we check things that typescript would have caught
const result = validateOscSubscriptionObject({});
expect(result).toBe(false);
});
it('should return false when given an empty array', () => {
// @ts-expect-error -- since this comes from the client, we check things that typescript would have caught
const result = validateOscSubscriptionObject([]);
expect(result).toBe(false);
});
it('should return false when given an object that is not an OscSubscription', () => {
const invalidObject = { foo: 'bar' };
// @ts-expect-error -- since this comes from the client, we check things that typescript would have caught
const result = validateOscSubscriptionObject(invalidObject);
expect(result).toBe(false);
});
it('should return false when given an OscSubscription with a missing property', () => {
const invalidSubscription = {
onLoad: [{ message: 'test', enabled: true }],
onStart: [{ message: 'test', enabled: false }],
onPause: [{ message: 'test', enabled: true }],
// Missing onStop
onUpdate: [{ message: 'test', enabled: true }],
onFinish: [{ message: 'test', enabled: false }],
};
// @ts-expect-error -- since this comes from the client, we check things that typescript would have caught
const result = validateOscSubscriptionObject(invalidSubscription);
expect(result).toBe(false);
});
});
describe('validateHttpSubscriptionCycle()', () => {
it('should return false when given an HttpSubscription with an invalid property value', () => {
const invalidBoolean = [{ message: 'http://', enabled: 'not a boolean' }];
const invalidHttp = [{ message: 'test', enabled: true }];
const noHttps = [{ message: 'https://test', enabled: true }];
const noEmpty = [{ message: '', enabled: true }];
// @ts-expect-error -- since this comes from the client, we check things that typescript would have caught
expect(validateHttpSubscriptionCycle(invalidBoolean)).toBe(false);
expect(validateHttpSubscriptionCycle(invalidHttp)).toBe(false);
expect(validateHttpSubscriptionCycle(noHttps)).toBe(false);
expect(validateHttpSubscriptionCycle(noEmpty)).toBe(false);
});
it('should return true when given an HttpSubscription matches definition', () => {
const validHttp = [{ message: 'http://', enabled: true }];
const result = validateHttpSubscriptionCycle(validHttp);
expect(result).toBe(true);
});
});
describe('validateHttpSubscriptionObject()', () => {
it('should return true when given a valid HttpSubscription', () => {
const validSubscription: HttpSubscription = {
onLoad: [{ message: 'http://', enabled: true }],
onStart: [{ message: 'http://', enabled: false }],
onPause: [{ message: 'http://', enabled: true }],
onStop: [{ message: 'http://', enabled: false }],
onUpdate: [{ message: 'http://', enabled: true }],
onFinish: [{ message: 'http://', enabled: false }],
};
const result = validateHttpSubscriptionObject(validSubscription);
expect(result).toBe(true);
});
it('should return false when given undefined', () => {
const result = validateHttpSubscriptionObject(undefined);
expect(result).toBe(false);
});
it('should return false when given null', () => {
const result = validateHttpSubscriptionObject(null);
expect(result).toBe(false);
});
it('should return false when given an empty object', () => {
// @ts-expect-error -- since this comes from the client, we check things that typescript would have caught
const result = validateOscSubscriptionObject({});
expect(result).toBe(false);
});
it('should return false when given an empty array', () => {
// @ts-expect-error -- since this comes from the client, we check things that typescript would have caught
const result = validateHttpSubscriptionObject([]);
expect(result).toBe(false);
});
it('should return false when given an object that is not an HttpSubscription', () => {
const invalidObject = { foo: 'bar' };
// @ts-expect-error -- since this comes from the client, we check things that typescript would have caught
const result = validateHttpSubscriptionObject(invalidObject);
expect(result).toBe(false);
});
it('should return false when given an HttpSubscription with a missing property', () => {
const invalidSubscription = {
onLoad: [{ message: 'http://', enabled: true }],
onStart: [{ message: 'http://', enabled: false }],
onPause: [{ message: 'http://', enabled: true }],
// Missing onStop
onUpdate: [{ message: 'http://', enabled: true }],
onFinish: [{ message: 'http://', enabled: false }],
};
// @ts-expect-error -- since this comes from the client, we check things that typescript would have caught
const result = validateHttpSubscriptionObject(invalidSubscription);
expect(result).toBe(false);
});
});
+39 -22
View File
@@ -2,15 +2,17 @@ import { generateId } from 'ontime-utils';
import {
Alias,
OntimeRundown,
HTTPSettings,
HttpSettings,
OSCSettings,
Subscription,
SubscriptionOptions,
ProjectData,
Settings,
TimerLifeCycle,
UserFields,
ViewSettings,
OscSubscription,
HttpSubscription,
OscSubscriptionOptions,
HttpSubscriptionOptions,
} from 'ontime-types';
import { block as blockDef, delay as delayDef } from '../models/eventsDefinition.js';
@@ -160,12 +162,12 @@ export const parseViewSettings = (data): ViewSettings => {
};
/**
* Parses and validates subscription entry
* Parses and validates OSC subscription cycle options
* @param data
*/
export const validateSubscriptionEntry = (data: SubscriptionOptions): boolean => {
for (const subscription in data) {
if (typeof data[subscription].message !== 'string' || typeof data[subscription].enabled !== 'boolean') {
export const validateOscSubscriptionCycle = (data: OscSubscriptionOptions[]): boolean => {
for (const subscriptionOption of data) {
if (typeof subscriptionOption.message !== 'string' || typeof subscriptionOption.enabled !== 'boolean') {
return false;
}
}
@@ -173,22 +175,23 @@ export const validateSubscriptionEntry = (data: SubscriptionOptions): boolean =>
};
/**
* Parses and validates subscription object
* Parses and validates OSC subscription object
* @param data
*/
export const validateOscObject = (data: Subscription): boolean => {
export const validateOscSubscriptionObject = (data: OscSubscription): boolean => {
if (!data) {
return false;
}
const timerKeys = Object.keys(TimerLifeCycle);
for (const key of timerKeys) {
// must contains all keys and be an array
if (!(key in data) || !Array.isArray(data[key])) {
return false;
}
for (const subscription of data[key]) {
if (typeof subscription.message !== 'string' || typeof subscription.enabled !== 'boolean') {
return false;
}
const isValid = validateOscSubscriptionCycle(data[key]);
if (!isValid) {
return false;
}
}
return true;
@@ -202,7 +205,7 @@ export const parseOsc = (data: { osc?: Partial<OSCSettings> }): OSCSettings => {
console.log('Found OSC definition, importing...');
const loadedConfig = data.osc || {};
const validatedSubscriptions = validateOscObject(loadedConfig.subscriptions)
const validatedSubscriptions = validateOscSubscriptionObject(loadedConfig.subscriptions)
? loadedConfig.subscriptions
: dbModel.osc.subscriptions;
@@ -218,22 +221,36 @@ export const parseOsc = (data: { osc?: Partial<OSCSettings> }): OSCSettings => {
};
/**
* Parses and validates subscription object
* Parses and validates HTTP subscription cycle options
* @param data
*/
export const validateHttpObject = (data: Subscription): boolean => {
export const validateHttpSubscriptionCycle = (data: HttpSubscriptionOptions[]): boolean => {
for (const subscriptionOption of data) {
const isHttp = subscriptionOption.message?.startsWith('http://');
if (typeof subscriptionOption.message !== 'string' || !isHttp || typeof subscriptionOption.enabled !== 'boolean') {
return false;
}
}
return true;
};
/**
* Parses and validates HTTP subscription object
* @param data
*/
export const validateHttpSubscriptionObject = (data: HttpSubscription): boolean => {
if (!data) {
return false;
}
const timerKeys = Object.keys(TimerLifeCycle);
// must contains all keys and be an array
for (const key of timerKeys) {
if (!(key in data) || !Array.isArray(data[key])) {
return false;
}
for (const subscription of data[key]) {
if (typeof subscription.message !== 'string' || typeof subscription.enabled !== 'boolean') {
return false;
}
const isValid = validateHttpSubscriptionCycle(data[key]);
if (!isValid) {
return false;
}
}
return true;
@@ -245,12 +262,12 @@ export const validateHttpObject = (data: Subscription): boolean => {
* @param {boolean} enforce - whether to create a definition if one is missing
* @returns {object} - event object data
*/
export const parseHttp = (data: { http?: Partial<HTTPSettings> }): HTTPSettings => {
export const parseHttp = (data: { http?: Partial<HttpSettings> }): HttpSettings => {
if ('http' in data) {
console.log('Found HTTP definition, importing...');
const loadedConfig = data?.http || {};
const validatedSubscriptions = validateHttpObject(loadedConfig.subscriptions)
const validatedSubscriptions = validateHttpSubscriptionObject(loadedConfig.subscriptions)
? loadedConfig.subscriptions
: dbModel.http.subscriptions;
@@ -5,7 +5,7 @@ import { OSCSettings } from './core/OscSettings.type.js';
import { Settings } from './core/Settings.type.js';
import { UserFields } from './core/UserFields.type.js';
import { ViewSettings } from './core/Views.type.js';
import { HTTPSettings } from '../index.js';
import { HttpSettings } from '../index.js';
export type DatabaseModel = {
rundown: OntimeRundown;
@@ -15,5 +15,5 @@ export type DatabaseModel = {
aliases: Alias[];
userFields: UserFields;
osc: OSCSettings;
http: HTTPSettings;
http: HttpSettings;
};
@@ -1,9 +1,9 @@
import { Subscription } from './Subscription.type.js';
type HttpSubscriptionOptions = { message: string; enabled: boolean };
export type HttpSubscriptionOptions = { message: string; enabled: boolean };
export type HttpSubscription = Subscription<HttpSubscriptionOptions>;
export interface HTTPSettings {
export interface HttpSettings {
enabledOut: boolean;
subscriptions: HttpSubscription;
}
@@ -1,6 +1,6 @@
import { Subscription } from './Subscription.type.js';
type OscSubscriptionOptions = { message: string; enabled: boolean };
export type OscSubscriptionOptions = { message: string; enabled: boolean };
export type OscSubscription = Subscription<OscSubscriptionOptions>;
export interface OSCSettings {
+3 -2
View File
@@ -32,9 +32,10 @@ export type { UserFields } from './definitions/core/UserFields.type.js';
export type { Subscription } from './definitions/core/Subscription.type.js';
// ---> OSC
export type { OSCSettings, OscSubscription } from './definitions/core/OscSettings.type.js';
export type { OSCSettings, OscSubscription, OscSubscriptionOptions } from './definitions/core/OscSettings.type.js';
// ---> HTTP
export type { HTTPSettings, HttpSubscription } from './definitions/core/HttpSettings.type.js';
export type { HttpSettings, HttpSubscription, HttpSubscriptionOptions } from './definitions/core/HttpSettings.type.js';
// SERVER RESPONSES
export type { NetworkInterface, GetInfo } from './api/ontime-controller/BackendResponse.type.js';