mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-19 14:14:17 +00:00
feat: http integration (#575)
* addtime endpoint * create universal Subscription * add http integration * catch error from HTTP integration emit * unify osc and http validateSubscriptionEntry * add necessary endpoint for http subscription * make subscription part of modal generic * add http subscription to integration modal * remove log * Revert "addtime endpoint" This reverts commit4c039220dc. * reuse agent and test url compatibility * simplify retun path * add todo in UI * test for http protocol * import not needed yet * lint * fix merge * lint * fix httpPlaceholder * wip: prepare endpoints * wip: temporary fix to get form to work * disable HTTP integration if enabledOut==false * register/unregister http * refactor: subscription types and form register * refactor: validation * cleanup * cleanup * try GOT * allow https * split url and searchParams allow for post option * add options to post * add retry count * fix test * Revert "fix test" This reverts commit927e88370f. * Revert "add options to post" This reverts commit0523a68ef4. * Revert "split url and searchParams allow for post option" This reverts commit54ab8d4ffe. * missing retryCount in httpPlaceholder * remove global this * remove retry count * remove https --------- Co-authored-by: Carlos Valente <carlosvalente@pm.me>
This commit is contained in:
committed by
GitHub
parent
abe27d54a2
commit
2586b0b10c
@@ -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,162 @@
|
||||
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 noFtp = [{ message: 'ftp://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(noFtp)).toBe(false);
|
||||
expect(validateHttpSubscriptionCycle(noEmpty)).toBe(false);
|
||||
});
|
||||
it('should return true when given an HttpSubscription matches definition', () => {
|
||||
const validHttp = [{ message: 'http://', enabled: true }];
|
||||
const invalidHttps = [{ message: 'https://', enabled: true }];
|
||||
|
||||
expect(validateHttpSubscriptionCycle(validHttp)).toBe(true);
|
||||
expect(validateHttpSubscriptionCycle(invalidHttps)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
parseAliases,
|
||||
parseProject,
|
||||
parseOsc,
|
||||
parseHttp,
|
||||
parseRundown,
|
||||
parseSettings,
|
||||
parseUserFields,
|
||||
@@ -275,7 +276,7 @@ export const parseJson = async (jsonData): Promise<DatabaseModel | null> => {
|
||||
// Import OSC settings if any
|
||||
returnData.osc = parseOsc(jsonData) ?? dbModel.osc;
|
||||
// Import HTTP settings if any
|
||||
// returnData.http = parseHttp(jsonData, enforce);
|
||||
returnData.http = parseHttp(jsonData) ?? dbModel.http;
|
||||
|
||||
return returnData as DatabaseModel;
|
||||
};
|
||||
|
||||
@@ -2,14 +2,17 @@ import { generateId } from 'ontime-utils';
|
||||
import {
|
||||
Alias,
|
||||
OntimeRundown,
|
||||
HttpSettings,
|
||||
OSCSettings,
|
||||
OscSubscription,
|
||||
OscSubscriptionOptions,
|
||||
ProjectData,
|
||||
Settings,
|
||||
TimerLifeCycle,
|
||||
UserFields,
|
||||
ViewSettings,
|
||||
OscSubscription,
|
||||
HttpSubscription,
|
||||
OscSubscriptionOptions,
|
||||
HttpSubscriptionOptions,
|
||||
} from 'ontime-types';
|
||||
|
||||
import { block as blockDef, delay as delayDef } from '../models/eventsDefinition.js';
|
||||
@@ -159,12 +162,12 @@ export const parseViewSettings = (data): ViewSettings => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses and validates subscription entry
|
||||
* Parses and validates OSC subscription cycle options
|
||||
* @param data
|
||||
*/
|
||||
export const validateOscSubscriptionEntry = (data: OscSubscriptionOptions): 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;
|
||||
}
|
||||
}
|
||||
@@ -172,22 +175,23 @@ export const validateOscSubscriptionEntry = (data: OscSubscriptionOptions): bool
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses and validates subscription object
|
||||
* Parses and validates OSC subscription object
|
||||
* @param data
|
||||
*/
|
||||
export const validateOscObject = (data: OscSubscription): 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;
|
||||
@@ -200,8 +204,9 @@ export const parseOsc = (data: { osc?: Partial<OSCSettings> }): OSCSettings => {
|
||||
if ('osc' in data) {
|
||||
console.log('Found OSC definition, importing...');
|
||||
|
||||
// TODO: this can be improved by only merging known keys
|
||||
const loadedConfig = data.osc || {};
|
||||
const validatedSubscriptions = validateOscObject(loadedConfig.subscriptions)
|
||||
const validatedSubscriptions = validateOscSubscriptionObject(loadedConfig.subscriptions)
|
||||
? loadedConfig.subscriptions
|
||||
: dbModel.osc.subscriptions;
|
||||
|
||||
@@ -216,20 +221,63 @@ export const parseOsc = (data: { osc?: Partial<OSCSettings> }): OSCSettings => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses and validates HTTP subscription cycle options
|
||||
* @param data
|
||||
*/
|
||||
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;
|
||||
}
|
||||
const isValid = validateHttpSubscriptionCycle(data[key]);
|
||||
if (!isValid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse Http portion of an entry
|
||||
* @param {object} data - data object
|
||||
* @param {boolean} enforce - whether to create a definition if one is missing
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseHttp = (data, enforce) => {
|
||||
const newHttp = {};
|
||||
export const parseHttp = (data: { http?: Partial<HttpSettings> }): HttpSettings => {
|
||||
if ('http' in data) {
|
||||
console.log('Found HTTP definition, importing...');
|
||||
} else if (enforce) {
|
||||
/* Not yet */
|
||||
|
||||
// TODO: this can be improved by only merging known keys
|
||||
const loadedConfig = data?.http || {};
|
||||
const validatedSubscriptions = validateHttpSubscriptionObject(loadedConfig.subscriptions)
|
||||
? loadedConfig.subscriptions
|
||||
: dbModel.http.subscriptions;
|
||||
|
||||
return {
|
||||
enabledOut: loadedConfig.enabledOut ?? dbModel.http.enabledOut,
|
||||
subscriptions: validatedSubscriptions,
|
||||
};
|
||||
}
|
||||
return newHttp;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user