Feat/navigate (#81)

* feat/navigate upgrade react router
* feat/navigate migrate to react router 6
* feat/navigate style modal
* feat/navigate create endpoints for app settings
* feat/navigate protect editor with pin
* feat/navigate apply dynamic routing draft
* feat/navigate upgrade relevant packages
* feat/navigate restructure directory and add tests
* feat/navigate test aliases validation
* feat/navigate validate aliases before sending
* feat/navigate create data endpoint
* feat/navigate config: prettier
* feat/navigate invalidate empty strings
* feat/navigate create endpoints
* feat/navigate parse on import
* feat/navigate navigate to alias
* feat/navigate user help and sample data
* feat/navigate refact aliases modal
* feat/navigate refact settings style
* feat/navigate update sample db
* feat/navigate link is relative to hostname
* feat/navigate navigate to first match
* feat/navigate update readme and version bump
* feat/navigate config: create shared module
* feat/navigate config: cheat module install
* feat/navigate fix tests
* feat/navigate run tests in pull request
* Update ontime_cy.yml
This commit is contained in:
Carlos Valente
2022-01-04 22:12:59 +01:00
committed by GitHub
parent dd43d5e20a
commit 2fa397548a
74 changed files with 2592 additions and 1428 deletions
+210 -128
View File
@@ -6,8 +6,8 @@ import {
block as blockDef,
} from '../models/eventsDefinition.js';
import { dbModelv1 } from '../models/dataModel.js';
import { generateId } from './generate_id.js';
import { excelDateStringToMillis } from './time.js';
import { generateId } from 'ontime-utils/generate_id.js';
import { excelDateStringToMillis } from 'ontime-utils/time.js';
export const EXCEL_MIME =
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
@@ -36,8 +36,8 @@ export const fileHandler = async (file) => {
// we only look at worksheets called ontime or event schedule
if (excelData?.data) {
const dataFromExcel = await parseExcelv1(excelData.data);
res.data = await parseJsonv1(dataFromExcel);
const dataFromExcel = await parseExcel_v1(excelData.data);
res.data = await parseJson_v1(dataFromExcel);
res.message = 'success';
} else {
console.log('Error: No sheets found named ontime or event schedule');
@@ -64,7 +64,7 @@ export const fileHandler = async (file) => {
if (uploadedJson.settings.version === 1) {
try {
res.data = await parseJsonv1(uploadedJson);
res.data = await parseJson_v1(uploadedJson);
res.message = 'success';
} catch (error) {
res = { error: true, message: `Error parsing file: ${error}` };
@@ -75,7 +75,7 @@ export const fileHandler = async (file) => {
}
// delete file
deleteFile(file);
await deleteFile(file);
return res;
};
@@ -85,7 +85,7 @@ export const fileHandler = async (file) => {
* @param {array} excelData - array with excel sheet
* @returns {object} - parsed object
*/
export const parseExcelv1 = async (excelData) => {
export const parseExcel_v1 = async (excelData) => {
let eventData = {
title: '',
url: '',
@@ -200,136 +200,27 @@ export const parseExcelv1 = async (excelData) => {
* @param {boolean} [enforce=false] - flag, tells to create an object anyway
* @returns {object} - parsed object
*/
export const parseJsonv1 = async (jsonData, enforce=false) => {
export const parseJson_v1 = async (jsonData, enforce = false) => {
if (!jsonData || typeof jsonData !== 'object') {
console.log('ERROR: Invalid JSON format');
return -1;
}
let numEntries = 0;
// object containing the parsed data
let returnData = {};
if ('events' in jsonData) {
console.log('Found events definition, importing...');
let events = [];
let ids = [];
for (const e of jsonData.events) {
// double check unique ids
if (ids.indexOf(e?.id) !== -1) {
console.log('ERROR: ID collision on import, skipping');
continue;
}
if (e.type === 'event') {
let event = validateEventv1(e);
if (event != null) {
events.push(event);
ids.push(event.id);
numEntries++;
}
} else if (e.type === 'delay') {
events.push({
...delayDef,
duration: e.duration,
id: e.id || generateId(),
});
numEntries++;
} else if (e.type === 'block') {
events.push({ ...blockDef, id: e.id || generateId() });
numEntries++;
} else {
console.log('ERROR: undefined event type, skipping');
}
}
// write to db
returnData.events = events;
console.log(`Uploaded file with ${numEntries} entries`);
} else if (enforce) {
returnData.events = [];
console.log(`Created events object in db`);
}
if ('event' in jsonData) {
console.log('Found event data, importing...');
const e = jsonData.event;
// filter known properties and write to db
returnData.event = {
...dbModelv1.event,
title: e.title || dbModelv1.event.title,
url: e.url || dbModelv1.event.url,
publicInfo: e.publicInfo || dbModelv1.event.publicInfo,
backstageInfo: e.backstageInfo || dbModelv1.event.backstageInfo,
endMessage: e.endMessage || dbModelv1.event.endMessage,
};
} else if (enforce) {
returnData.event = dbModelv1.event;
console.log(`Created event object in db`);
}
// parse Events
returnData.events = parseEvents_v1(jsonData);
// parse Event
returnData.event = parseEvent_v1(jsonData, enforce);
// Settings handled partially
if ('settings' in jsonData) {
console.log('Found settings definition, importing...');
const s = jsonData.settings;
// skip if file definition is missing
if (s.app == null || s.version == null) {
console.log('ERROR: unknown app version, skipping');
} else {
let settings = {
lock: s.lock || null,
};
// write to db
returnData.settings = {
...dbModelv1.settings,
...settings,
};
}
} else if (enforce) {
returnData.settings = dbModelv1.settings;
console.log(`Created settings object in db`);
}
returnData.settings = parseSettings_v1(jsonData, enforce);
// Import OSC settings if any
if ('osc' in jsonData) {
console.log('Found OSC definition, importing...');
const s = jsonData.osc;
let osc = {};
if (s.port) osc.port = s.port;
if (s.portOut) osc.portOut = s.portOut;
if (s.targetIP) osc.targetIP = s.targetIP;
if (s.enabled) osc.enabled = s.enabled;
// write to db
returnData.osc = {
...dbModelv1.osc,
...osc,
};
} else if (enforce) {
returnData.osc = dbModelv1.osc;
console.log(`Created osc object in db`);
}
returnData.osc = parseOsc_v1(jsonData, enforce);
// Import HTTP settings if any
if ('http' in jsonData) {
console.log('Found HTTP definition, importing...');
const h = jsonData.osc;
let http = {};
if (h.user) http.user = h.user;
if (h.pwd) http.pwd = h.pwd;
// write to db
returnData.http = {
...dbModelv1.http,
...http,
};
} else if (enforce) {
returnData.http = dbModelv1.http;
console.log(`Created http object in db`);
}
returnData.http = parseHttp_v1(jsonData, enforce);
// Import Aliases if any
returnData.aliases = parseAliases_v1(jsonData);
return returnData;
};
@@ -352,7 +243,7 @@ export const makeString = (val, fallback = '') => {
* @returns {object|null} - formatted object or null in case is invalid
*/
export const validateEventv1 = (eventArgs) => {
export const validateEvent_v1 = (eventArgs) => {
// ensure id is defined and unique
const id = eventArgs.id || generateId();
let event = null;
@@ -418,3 +309,194 @@ export const validateFile = (file) => {
return false;
}
};
/**
* Parse events array of an entry
* @param {object} data - data object
* @returns {object} - event object data
*/
export const parseEvents_v1 = (data) => {
let newEvents = [];
if ('events' in data) {
console.log('Found events definition, importing...');
let events = [];
let ids = [];
for (const e of data.events) {
// double check unique ids
if (ids.indexOf(e?.id) !== -1) {
console.log('ERROR: ID collision on import, skipping');
continue;
}
if (e.type === 'event') {
let event = validateEvent_v1(e);
if (event != null) {
events.push(event);
ids.push(event.id);
}
} else if (e.type === 'delay') {
events.push({
...delayDef,
duration: e.duration,
id: e.id || generateId(),
});
} else if (e.type === 'block') {
events.push({ ...blockDef, id: e.id || generateId() });
} else {
console.log('ERROR: undefined event type, skipping');
}
}
// write to db
newEvents = events;
console.log(`Uploaded file with ${events.length} entries`);
}
return newEvents;
};
/**
* Parse event 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 parseEvent_v1 = (data, enforce) => {
let newEvent = {};
if ('event' in data) {
console.log('Found event data, importing...');
const e = data.event;
// filter known properties and write to db
newEvent = {
...dbModelv1.event,
title: e.title || dbModelv1.event.title,
url: e.url || dbModelv1.event.url,
publicInfo: e.publicInfo || dbModelv1.event.publicInfo,
backstageInfo: e.backstageInfo || dbModelv1.event.backstageInfo,
endMessage: e.endMessage || dbModelv1.event.endMessage,
};
} else if (enforce) {
newEvent = dbModelv1.event;
console.log(`Created event object in db`);
}
return newEvent;
};
/**
* Parse settings 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 parseSettings_v1 = (data, enforce) => {
let newSettings = {};
if ('settings' in data) {
console.log('Found settings definition, importing...');
const s = data.settings;
// skip if file definition is missing
if (s.app == null || s.version == null) {
console.log('ERROR: unknown app version, skipping');
} else {
let settings = {
lock: s.lock || null,
pinCode: s.pinCode || null,
};
// write to db
newSettings = {
...dbModelv1.settings,
...settings,
};
}
} else if (enforce) {
newSettings = dbModelv1.settings;
console.log(`Created settings object in db`);
}
return newSettings;
};
/**
* Parse osc 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 parseOsc_v1 = (data, enforce) => {
let newOsc = {};
if ('osc' in data) {
console.log('Found OSC definition, importing...');
const s = data.osc;
let osc = {};
if (s.port) osc.port = s.port;
if (s.portOut) osc.portOut = s.portOut;
if (s.targetIP) osc.targetIP = s.targetIP;
if (s.enabled) osc.enabled = s.enabled;
// write to db
newOsc = {
...dbModelv1.osc,
...osc,
};
} else if (enforce) {
newOsc = dbModelv1.osc;
console.log(`Created OSC object in db`);
}
return newOsc;
};
/**
* 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_v1 = (data, enforce) => {
let newHttp = {};
if ('http' in data) {
console.log('Found HTTP definition, importing...');
const h = data.osc;
let http = {};
if (h.user) http.user = h.user;
if (h.pwd) http.pwd = h.pwd;
// write to db
newHttp.http = {
...dbModelv1.http,
...http,
};
} else if (enforce) {
newHttp.http = dbModelv1.http;
console.log(`Created http object in db`);
}
return newHttp;
};
/**
* Parse aliases portion of an entry
* @param {object} data - data object
* @returns {object} - event object data
*/
export const parseAliases_v1 = (data) => {
let newAliases = [];
if ('aliases' in data) {
console.log('Found Aliases definition, importing...');
const ids = [];
for (const a of data.aliases) {
// double check unique ids
if (ids.indexOf(a?.id) !== -1) {
console.log('ERROR: ID collision on import, skipping');
continue;
}
const newAlias = {
id: a.id || generateId(),
enabled: a.enabled || false,
alias: a.alias || '',
pathAndParams: a.pathAndParams || '',
};
ids.push(newAlias.id);
newAliases.push(newAlias);
}
console.log(`Uploaded ${newAliases?.length || 0} alias(es)`);
}
return newAliases;
};