mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-13 03:13:47 +00:00
Feat/table (#105)
* feat/table add react-table * feat/table add protected table route * feat/table upgrade dependencies * feat/table support parsing of new properties Co-authored-by: DeepSource Bot <bot@deepsource.io>
This commit is contained in:
@@ -1,12 +1,8 @@
|
||||
import jest from 'jest-mock';
|
||||
import {
|
||||
makeString,
|
||||
parseAliases_v1,
|
||||
parseExcel_v1,
|
||||
parseJson_v1,
|
||||
validateEvent_v1,
|
||||
} from '../parser.js';
|
||||
import { dbModelv1 as dbModel } from '../../models/dataModel.js';
|
||||
import { dbModelv1, dbModelv1 as dbModel } from '../../models/dataModel.js';
|
||||
import { parseExcel_v1, parseJson_v1, validateEvent_v1 } from '../parser.js';
|
||||
import { makeString, validateDuration } from '../parserUtils.js';
|
||||
import { parseAliases_v1, parseUserFields_v1 } from '../parserUtils_v1.js';
|
||||
|
||||
describe('test json parser with valid def', () => {
|
||||
const testData = {
|
||||
@@ -18,10 +14,23 @@ describe('test json parser with valid def', () => {
|
||||
note: '',
|
||||
timeStart: 31500000,
|
||||
timeEnd: 32400000,
|
||||
timeType: 'start-end',
|
||||
duration: 32400000 - 31500000,
|
||||
isPublic: false,
|
||||
colour: '',
|
||||
type: 'event',
|
||||
revision: 0,
|
||||
id: '4b31',
|
||||
user0: '',
|
||||
user1: '',
|
||||
user2: '',
|
||||
user3: '',
|
||||
user4: '',
|
||||
user5: '',
|
||||
user6: '',
|
||||
user7: '',
|
||||
user8: '',
|
||||
user9: '',
|
||||
},
|
||||
{
|
||||
title: 'Good Morning',
|
||||
@@ -30,10 +39,23 @@ describe('test json parser with valid def', () => {
|
||||
note: '',
|
||||
timeStart: 32400000,
|
||||
timeEnd: 36000000,
|
||||
timeType: 'start-end',
|
||||
duration: 36000000 - 32400000,
|
||||
isPublic: true,
|
||||
colour: '',
|
||||
type: 'event',
|
||||
revision: 0,
|
||||
id: 'f24d',
|
||||
user0: '',
|
||||
user1: '',
|
||||
user2: '',
|
||||
user3: '',
|
||||
user4: '',
|
||||
user5: '',
|
||||
user6: '',
|
||||
user7: '',
|
||||
user8: '',
|
||||
user9: '',
|
||||
},
|
||||
{
|
||||
title: 'Stage 2 setup',
|
||||
@@ -42,10 +64,23 @@ describe('test json parser with valid def', () => {
|
||||
note: '',
|
||||
timeStart: 32400000,
|
||||
timeEnd: 37200000,
|
||||
timeType: 'start-end',
|
||||
duration: 37200000 - 32400000,
|
||||
isPublic: false,
|
||||
colour: '',
|
||||
type: 'event',
|
||||
revision: 0,
|
||||
id: 'bbc5',
|
||||
user0: '',
|
||||
user1: '',
|
||||
user2: '',
|
||||
user3: '',
|
||||
user4: '',
|
||||
user5: '',
|
||||
user6: '',
|
||||
user7: '',
|
||||
user8: '',
|
||||
user9: '',
|
||||
},
|
||||
{
|
||||
title: 'Working Procedures',
|
||||
@@ -54,7 +89,10 @@ describe('test json parser with valid def', () => {
|
||||
note: '',
|
||||
timeStart: 37200000,
|
||||
timeEnd: 39000000,
|
||||
timeType: 'start-end',
|
||||
duration: 39000000 - 37200000,
|
||||
isPublic: true,
|
||||
colour: '',
|
||||
type: 'event',
|
||||
revision: 0,
|
||||
id: '5b3e',
|
||||
@@ -66,10 +104,23 @@ describe('test json parser with valid def', () => {
|
||||
note: '',
|
||||
timeStart: 39600000,
|
||||
timeEnd: 45000000,
|
||||
timeType: 'start-end',
|
||||
duration: 37200000 - 32400000,
|
||||
isPublic: false,
|
||||
colour: '',
|
||||
type: 'event',
|
||||
revision: 0,
|
||||
id: '8e2c',
|
||||
user0: '',
|
||||
user1: '',
|
||||
user2: '',
|
||||
user3: '',
|
||||
user4: '',
|
||||
user5: '',
|
||||
user6: '',
|
||||
user7: '',
|
||||
user8: '',
|
||||
user9: '',
|
||||
},
|
||||
{
|
||||
title: 'A day being carlos',
|
||||
@@ -78,10 +129,23 @@ describe('test json parser with valid def', () => {
|
||||
note: '',
|
||||
timeStart: 46800000,
|
||||
timeEnd: 50400000,
|
||||
timeType: 'start-end',
|
||||
duration: 37200000 - 32400000,
|
||||
isPublic: true,
|
||||
colour: '',
|
||||
type: 'event',
|
||||
revision: 0,
|
||||
id: '08e9',
|
||||
user0: '',
|
||||
user1: '',
|
||||
user2: '',
|
||||
user3: '',
|
||||
user4: '',
|
||||
user5: '',
|
||||
user6: '',
|
||||
user7: '',
|
||||
user8: '',
|
||||
user9: '',
|
||||
},
|
||||
{
|
||||
title: 'Hamburgers and Cheese',
|
||||
@@ -90,10 +154,23 @@ describe('test json parser with valid def', () => {
|
||||
note: '',
|
||||
timeStart: 54000000,
|
||||
timeEnd: 57600000,
|
||||
timeType: 'start-end',
|
||||
duration: 37200000 - 32400000,
|
||||
isPublic: true,
|
||||
colour: '',
|
||||
type: 'event',
|
||||
revision: 0,
|
||||
id: 'e25a',
|
||||
user0: '',
|
||||
user1: '',
|
||||
user2: '',
|
||||
user3: '',
|
||||
user4: '',
|
||||
user5: '',
|
||||
user6: '',
|
||||
user7: '',
|
||||
user8: '',
|
||||
user9: '',
|
||||
},
|
||||
],
|
||||
event: {
|
||||
@@ -128,10 +205,23 @@ describe('test json parser with valid def', () => {
|
||||
note: '',
|
||||
timeStart: 31500000,
|
||||
timeEnd: 32400000,
|
||||
timeType: 'start-end',
|
||||
duration: 32400000 - 31500000,
|
||||
isPublic: false,
|
||||
colour: '',
|
||||
type: 'event',
|
||||
revision: 0,
|
||||
id: '4b31',
|
||||
user0: '',
|
||||
user1: '',
|
||||
user2: '',
|
||||
user3: '',
|
||||
user4: '',
|
||||
user5: '',
|
||||
user6: '',
|
||||
user7: '',
|
||||
user8: '',
|
||||
user9: '',
|
||||
};
|
||||
expect(first).toStrictEqual(expected);
|
||||
});
|
||||
@@ -192,9 +282,7 @@ describe('test parser edge cases', () => {
|
||||
};
|
||||
|
||||
const parseResponse = await parseJson_v1(testData);
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'ERROR: ID collision on import, skipping'
|
||||
);
|
||||
expect(console.log).toHaveBeenCalledWith('ERROR: ID collision on import, skipping');
|
||||
expect(parseResponse?.events.length).toBe(1);
|
||||
});
|
||||
|
||||
@@ -214,10 +302,7 @@ describe('test parser edge cases', () => {
|
||||
};
|
||||
|
||||
const parseResponse = await parseJson_v1(testData);
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'ERROR: undefined event type, skipping'
|
||||
);
|
||||
|
||||
expect(console.log).toHaveBeenCalledWith('ERROR: undefined event type, skipping');
|
||||
expect(parseResponse?.events.length).toBe(0);
|
||||
});
|
||||
|
||||
@@ -230,9 +315,7 @@ describe('test parser edge cases', () => {
|
||||
};
|
||||
|
||||
await parseJson_v1(testData);
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'ERROR: unknown app version, skipping'
|
||||
);
|
||||
expect(console.log).toHaveBeenCalledWith('ERROR: unknown app version, skipping');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -356,6 +439,17 @@ describe('test event validator', () => {
|
||||
revision: expect.any(Number),
|
||||
type: expect.any(String),
|
||||
id: expect.any(String),
|
||||
colour: expect.any(String),
|
||||
user0: expect.any(String),
|
||||
user1: expect.any(String),
|
||||
user2: expect.any(String),
|
||||
user3: expect.any(String),
|
||||
user4: expect.any(String),
|
||||
user5: expect.any(String),
|
||||
user6: expect.any(String),
|
||||
user7: expect.any(String),
|
||||
user8: expect.any(String),
|
||||
user9: expect.any(String),
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -441,6 +535,17 @@ describe('test parseExcel function', () => {
|
||||
'Event Subtitle',
|
||||
'Is Public? (x)',
|
||||
'Notes',
|
||||
'User0:test0',
|
||||
'User1:test1',
|
||||
'User2:test2',
|
||||
'User3:test3',
|
||||
'User4:test4',
|
||||
'User5:test5',
|
||||
'User6:test6',
|
||||
'user7:test7',
|
||||
'user8:test8',
|
||||
'user9:test9',
|
||||
'Colour',
|
||||
],
|
||||
[
|
||||
'1899-12-30T07:00:00.000Z',
|
||||
@@ -450,6 +555,21 @@ describe('test parseExcel function', () => {
|
||||
'Getting things started',
|
||||
'x',
|
||||
'Ballyhoo',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'a0',
|
||||
'a1',
|
||||
'a2',
|
||||
'a3',
|
||||
'a4',
|
||||
'a5',
|
||||
'a6',
|
||||
'a7',
|
||||
'a8',
|
||||
'a9',
|
||||
'red',
|
||||
],
|
||||
[
|
||||
'1899-12-30T08:00:00.000Z',
|
||||
@@ -459,6 +579,21 @@ describe('test parseExcel function', () => {
|
||||
'Derailing early',
|
||||
'',
|
||||
'Rainbow chase',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'b0',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'b5',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'#F00',
|
||||
],
|
||||
[],
|
||||
];
|
||||
@@ -472,6 +607,17 @@ describe('test parseExcel function', () => {
|
||||
subtitle: 'Getting things started',
|
||||
isPublic: true,
|
||||
note: 'Ballyhoo',
|
||||
user0: 'a0',
|
||||
user1: 'a1',
|
||||
user2: 'a2',
|
||||
user3: 'a3',
|
||||
user4: 'a4',
|
||||
user5: 'a5',
|
||||
user6: 'a6',
|
||||
user7: 'a7',
|
||||
user8: 'a8',
|
||||
user9: 'a9',
|
||||
colour: 'red',
|
||||
type: 'event',
|
||||
},
|
||||
{
|
||||
@@ -482,6 +628,9 @@ describe('test parseExcel function', () => {
|
||||
subtitle: 'Derailing early',
|
||||
isPublic: false,
|
||||
note: 'Rainbow chase',
|
||||
user0: 'b0',
|
||||
user5: 'b5',
|
||||
colour: '#F00',
|
||||
type: 'event',
|
||||
},
|
||||
];
|
||||
@@ -499,25 +648,145 @@ describe('test parseExcel function', () => {
|
||||
});
|
||||
|
||||
describe('test aliases import', () => {
|
||||
it('imports a well defined alias', () => {});
|
||||
const testData = {
|
||||
events: [],
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
},
|
||||
aliases: [
|
||||
{
|
||||
enabled: false,
|
||||
alias: 'testalias',
|
||||
pathAndParams: 'testpathAndParams',
|
||||
it('imports a well defined alias', () => {
|
||||
const testData = {
|
||||
events: [],
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
aliases: [
|
||||
{
|
||||
enabled: false,
|
||||
alias: 'testalias',
|
||||
pathAndParams: 'testpathAndParams',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const parsed = parseAliases_v1(testData, true);
|
||||
expect(parsed.length).toBe(1);
|
||||
const parsed = parseAliases_v1(testData);
|
||||
expect(parsed.length).toBe(1);
|
||||
|
||||
// generates missing id
|
||||
expect(parsed[0].id).toBeDefined();
|
||||
// generates missing id
|
||||
expect(parsed[0].id).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('test userFields import', () => {
|
||||
const model = dbModelv1.userFields;
|
||||
it('imports a fully defined user fields', () => {
|
||||
const testUserFields = {
|
||||
user0: 'test0',
|
||||
user1: 'test1',
|
||||
user2: 'test2',
|
||||
user3: 'test3',
|
||||
user4: 'test4',
|
||||
user5: 'test5',
|
||||
user6: 'test6',
|
||||
user7: 'test7',
|
||||
user8: 'test8',
|
||||
user9: 'test9',
|
||||
};
|
||||
|
||||
const testData = {
|
||||
events: [],
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
},
|
||||
userFields: testUserFields,
|
||||
};
|
||||
|
||||
const parsed = parseUserFields_v1(testData);
|
||||
expect(parsed).toStrictEqual(testUserFields);
|
||||
});
|
||||
|
||||
it('imports a partially defined user fields', () => {
|
||||
const testUserFields = {
|
||||
user0: 'test0',
|
||||
user1: 'test1',
|
||||
user7: 'test7',
|
||||
user8: 'test8',
|
||||
user9: 'test9',
|
||||
};
|
||||
|
||||
const expected = {
|
||||
...model,
|
||||
...testUserFields,
|
||||
};
|
||||
|
||||
const testData = {
|
||||
events: [],
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
},
|
||||
userFields: testUserFields,
|
||||
};
|
||||
|
||||
const parsed = parseUserFields_v1(testData);
|
||||
expect(parsed).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
it('handles missing user fields', () => {
|
||||
const testData = {
|
||||
events: [],
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
},
|
||||
};
|
||||
|
||||
const parsed = parseUserFields_v1(testData);
|
||||
expect(parsed).toStrictEqual(model);
|
||||
expect(parsed).toStrictEqual(model);
|
||||
});
|
||||
|
||||
it('ignores badly defined fields', () => {
|
||||
const testData = {
|
||||
events: [],
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
},
|
||||
userFields: {
|
||||
notThis: 'this shouldng be accepted',
|
||||
orThis: 'this neither',
|
||||
},
|
||||
};
|
||||
|
||||
const parsed = parseUserFields_v1(testData);
|
||||
expect(parsed).toStrictEqual(model);
|
||||
});
|
||||
});
|
||||
|
||||
describe('test validateDuration()', () => {
|
||||
describe('handles valid inputs', () => {
|
||||
const valid = [
|
||||
{ test: 'zero values', timeStart: 0, timeEnd: 0 },
|
||||
{ test: 'end after start', timeStart: 0, timeEnd: 1 },
|
||||
];
|
||||
|
||||
valid.forEach((t) => {
|
||||
it(t.test, () => {
|
||||
const d = validateDuration(t.timeStart, t.timeEnd);
|
||||
expect(d).toBe(t.timeEnd - t.timeStart);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('handles edge cases', () => {
|
||||
// edge cases
|
||||
const testData = [
|
||||
{ test: 'negative 0', timeStart: -0, timeEnd: -0, expected: 0 },
|
||||
{ test: 'end before start', timeStart: 2, timeEnd: 1, expected: 0 },
|
||||
];
|
||||
|
||||
testData.forEach((t) => {
|
||||
it(t.test, () => {
|
||||
const d = validateDuration(t.timeStart, t.timeEnd);
|
||||
expect(d).toBe(t.expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+125
-258
@@ -1,18 +1,23 @@
|
||||
import fs from 'fs';
|
||||
import xlsx from 'node-xlsx';
|
||||
import {
|
||||
block as blockDef,
|
||||
delay as delayDef,
|
||||
event as eventDef,
|
||||
} from '../models/eventsDefinition.js';
|
||||
import { event as eventDef } from '../models/eventsDefinition.js';
|
||||
import { dbModelv1 } from '../models/dataModel.js';
|
||||
import { generateId } from 'ontime-utils/generate_id.js';
|
||||
import { excelDateStringToMillis } from 'ontime-utils/time.js';
|
||||
import { deleteFile, makeString, validateDuration } from './parserUtils.js';
|
||||
import {
|
||||
parseAliases_v1,
|
||||
parseEvent_v1,
|
||||
parseEvents_v1,
|
||||
parseHttp_v1,
|
||||
parseOsc_v1,
|
||||
parseSettings_v1,
|
||||
parseUserFields_v1,
|
||||
} from './parserUtils_v1.js';
|
||||
|
||||
export const EXCEL_MIME =
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||
export const EXCEL_MIME = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||
export const JSON_MIME = 'application/json';
|
||||
export const ALLOWED_TYPES = ['JSON', 'EXCEL'];
|
||||
export const MAX_EVENTS = 99;
|
||||
|
||||
/**
|
||||
* @description Middleware function that checks file type and calls relevant parser
|
||||
@@ -29,9 +34,7 @@ export const fileHandler = async (file) => {
|
||||
const excelData = xlsx
|
||||
.parse(file, { cellDates: true })
|
||||
.find(
|
||||
({ name }) =>
|
||||
name.toLowerCase() === 'ontime' ||
|
||||
name.toLowerCase() === 'event schedule'
|
||||
({ name }) => name.toLowerCase() === 'ontime' || name.toLowerCase() === 'event schedule'
|
||||
);
|
||||
|
||||
// we only look at worksheets called ontime or event schedule
|
||||
@@ -76,7 +79,6 @@ export const fileHandler = async (file) => {
|
||||
|
||||
// delete file
|
||||
await deleteFile(file);
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
@@ -90,7 +92,7 @@ export const parseExcel_v1 = async (excelData) => {
|
||||
title: '',
|
||||
url: '',
|
||||
};
|
||||
|
||||
let customUserFields = {};
|
||||
let events = [];
|
||||
let timeStartIndex = null;
|
||||
let timeEndIndex = null;
|
||||
@@ -99,6 +101,17 @@ export const parseExcel_v1 = async (excelData) => {
|
||||
let subtitleIndex = null;
|
||||
let isPublicIndex = null;
|
||||
let notesIndex = null;
|
||||
let colourIndex = null;
|
||||
let user0Index = null;
|
||||
let user1Index = null;
|
||||
let user2Index = null;
|
||||
let user3Index = null;
|
||||
let user4Index = null;
|
||||
let user5Index = null;
|
||||
let user6Index = null;
|
||||
let user7Index = null;
|
||||
let user8Index = null;
|
||||
let user9Index = null;
|
||||
|
||||
excelData
|
||||
.filter((e) => e.length > 0)
|
||||
@@ -127,14 +140,41 @@ export const parseExcel_v1 = async (excelData) => {
|
||||
event.subtitle = column;
|
||||
} else if (j === isPublicIndex) {
|
||||
// whether column is not empty
|
||||
event.isPublic = column !== '';
|
||||
let c = column;
|
||||
if (typeof column === 'string') {
|
||||
c = column.replace(/\s+/g, '');
|
||||
}
|
||||
event.isPublic = c !== '';
|
||||
} else if (j === notesIndex) {
|
||||
event.note = column;
|
||||
} else if (j === colourIndex) {
|
||||
event.colour = column;
|
||||
} else if (j === user0Index) {
|
||||
event.user0 = column;
|
||||
} else if (j === user1Index) {
|
||||
event.user1 = column;
|
||||
} else if (j === user2Index) {
|
||||
event.user2 = column;
|
||||
} else if (j === user3Index) {
|
||||
event.user3 = column;
|
||||
} else if (j === user4Index) {
|
||||
event.user4 = column;
|
||||
} else if (j === user5Index) {
|
||||
event.user5 = column;
|
||||
} else if (j === user6Index) {
|
||||
event.user6 = column;
|
||||
} else if (j === user7Index) {
|
||||
event.user7 = column;
|
||||
} else if (j === user8Index) {
|
||||
event.user8 = column;
|
||||
} else if (j === user9Index) {
|
||||
event.user9 = column;
|
||||
} else {
|
||||
if (typeof column === 'string') {
|
||||
const col = column.toLowerCase();
|
||||
// look for keywords
|
||||
// need to make sure it is a string first
|
||||
switch (column.toLowerCase()) {
|
||||
switch (col) {
|
||||
case 'event name':
|
||||
eventTitleNext = true;
|
||||
break;
|
||||
@@ -171,12 +211,56 @@ export const parseExcel_v1 = async (excelData) => {
|
||||
case 'notes':
|
||||
notesIndex = j;
|
||||
break;
|
||||
case 'colour':
|
||||
case 'color':
|
||||
colourIndex = j;
|
||||
break;
|
||||
default:
|
||||
// look for user defined
|
||||
if (col.startsWith('user')) {
|
||||
const index = column.charAt(4);
|
||||
// name is the bit after the :
|
||||
const [, name] = column.split(':');
|
||||
if (typeof name !== 'undefined') {
|
||||
if (index === '0') {
|
||||
customUserFields.user0 = name;
|
||||
user0Index = j;
|
||||
} else if (index === '1') {
|
||||
customUserFields.user1 = name;
|
||||
user1Index = j;
|
||||
} else if (index === '2') {
|
||||
customUserFields.user2 = name;
|
||||
user2Index = j;
|
||||
} else if (index === '3') {
|
||||
customUserFields.user3 = name;
|
||||
user3Index = j;
|
||||
} else if (index === '4') {
|
||||
customUserFields.user4 = name;
|
||||
user4Index = j;
|
||||
} else if (index === '5') {
|
||||
customUserFields.user5 = name;
|
||||
user5Index = j;
|
||||
} else if (index === '6') {
|
||||
customUserFields.user6 = name;
|
||||
user6Index = j;
|
||||
} else if (index === '7') {
|
||||
customUserFields.user7 = name;
|
||||
user7Index = j;
|
||||
} else if (index === '8') {
|
||||
customUserFields.user8 = name;
|
||||
user8Index = j;
|
||||
} else if (index === '9') {
|
||||
customUserFields.user9 = name;
|
||||
user9Index = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.keys(event).length > 0) {
|
||||
// if any data was found, push to array
|
||||
// take care of it in the next step
|
||||
@@ -190,6 +274,7 @@ export const parseExcel_v1 = async (excelData) => {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
},
|
||||
userFields: { ...dbModelv1.userFields, ...customUserFields },
|
||||
};
|
||||
};
|
||||
|
||||
@@ -220,22 +305,12 @@ export const parseJson_v1 = async (jsonData, enforce = false) => {
|
||||
returnData.http = parseHttp_v1(jsonData, enforce);
|
||||
// Import Aliases if any
|
||||
returnData.aliases = parseAliases_v1(jsonData);
|
||||
// Import user fields if any
|
||||
returnData.userFields = parseUserFields_v1(jsonData);
|
||||
|
||||
return returnData;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Ensures variable is string, it skips object types
|
||||
* @param {any} val - variable to convert
|
||||
* @param {string} [fallback=''] - fallback value
|
||||
* @returns {string} - value as string or fallback if not possible
|
||||
*/
|
||||
export const makeString = (val, fallback = '') => {
|
||||
if (typeof val === 'string') return val;
|
||||
else if (val == null || val.constructor === Object) return fallback;
|
||||
else return val.toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Enforces formatting for events
|
||||
* @param {object} eventArgs - attributes of event
|
||||
@@ -254,26 +329,36 @@ export const validateEvent_v1 = (eventArgs) => {
|
||||
|
||||
const e = eventArgs;
|
||||
const d = eventDef;
|
||||
const start =
|
||||
e.timeStart != null && typeof e.timeStart === 'number' ? e.timeStart : d.timeStart;
|
||||
const end = e.timeEnd != null && typeof e.timeEnd === 'number' ? e.timeEnd : d.timeEnd;
|
||||
|
||||
event = {
|
||||
...d,
|
||||
|
||||
title: makeString(e.title, d.title),
|
||||
subtitle: makeString(e.subtitle, d.subtitle),
|
||||
presenter: makeString(e.presenter, d.presenter),
|
||||
timeStart: start,
|
||||
timeEnd: end,
|
||||
timeType: 'start-end',
|
||||
duration: validateDuration(start, end),
|
||||
isPublic: e.isPublic != null && typeof e.isPublic === 'boolean' ? e.isPublic : d.isPublic,
|
||||
note: makeString(e.note, d.note),
|
||||
timeStart:
|
||||
e.timeStart != null && typeof e.timeStart === 'number'
|
||||
? e.timeStart
|
||||
: d.timeStart,
|
||||
timeEnd:
|
||||
e.timeEnd != null && typeof e.timeEnd === 'number'
|
||||
? e.timeEnd
|
||||
: d.timeEnd,
|
||||
isPublic:
|
||||
e.isPublic != null && typeof e.isPublic === 'boolean'
|
||||
? e.isPublic
|
||||
: d.isPublic,
|
||||
user0: makeString(e.user0, d.user0),
|
||||
user1: makeString(e.user1, d.user1),
|
||||
user2: makeString(e.user2, d.user2),
|
||||
user3: makeString(e.user3, d.user3),
|
||||
user4: makeString(e.user4, d.user4),
|
||||
user5: makeString(e.user5, d.user5),
|
||||
user6: makeString(e.user6, d.user6),
|
||||
user7: makeString(e.user7, d.user7),
|
||||
user8: makeString(e.user8, d.user8),
|
||||
user9: makeString(e.user9, d.user9),
|
||||
// deciding not to validate colour
|
||||
// this adds flexibility to the user to write hex codes, rgb,
|
||||
// but also colour names like blue and red
|
||||
// CSS.supports is only available in frontend
|
||||
colour: makeString(e.colour, d.colour),
|
||||
id,
|
||||
type: 'event',
|
||||
};
|
||||
@@ -281,221 +366,3 @@ export const validateEvent_v1 = (eventArgs) => {
|
||||
|
||||
return event;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Delete file from system
|
||||
* @param {string} file - reference to file
|
||||
*/
|
||||
const deleteFile = async (file) => {
|
||||
// delete a file
|
||||
fs.unlink(file, (err) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Delete file from system
|
||||
* @param {string} file - reference to file
|
||||
* @returns {boolean} - whether file is valid JSON
|
||||
*/
|
||||
export const validateFile = (file) => {
|
||||
try {
|
||||
JSON.parse(fs.readFileSync(file));
|
||||
return true;
|
||||
} catch (err) {
|
||||
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 !== undefined) 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;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import fs from 'fs';
|
||||
|
||||
/**
|
||||
* @description Ensures variable is string, it skips object types
|
||||
* @param {any} val - variable to convert
|
||||
* @param {string} [fallback=''] - fallback value
|
||||
* @returns {string} - value as string or fallback if not possible
|
||||
*/
|
||||
export const makeString = (val, fallback = '') => {
|
||||
if (typeof val === 'string') return val;
|
||||
else if (val == null || val.constructor === Object) return fallback;
|
||||
else return val.toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* @description validates a duration value against options
|
||||
* @param {number} timeStart
|
||||
* @param {number} timeEnd
|
||||
* @returns {number}
|
||||
*/
|
||||
export const validateDuration = (timeStart, timeEnd) => {
|
||||
// Todo: this would go into a switch statement when expanded
|
||||
// Durations must be positive
|
||||
return Math.max(timeEnd - timeStart, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Delete file from system
|
||||
* @param {string} file - reference to file
|
||||
*/
|
||||
export const deleteFile = async (file) => {
|
||||
// delete a file
|
||||
fs.unlink(file, (err) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Delete file from system
|
||||
* @param {string} file - reference to file
|
||||
* @returns {boolean} - whether file is valid JSON
|
||||
*/
|
||||
export const validateFile = (file) => {
|
||||
try {
|
||||
JSON.parse(fs.readFileSync(file, 'utf-8'));
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,225 @@
|
||||
import { block as blockDef, delay as delayDef } from '../models/eventsDefinition.js';
|
||||
import { generateId } from 'ontime-utils/generate_id.js';
|
||||
import { dbModelv1 } from '../models/dataModel.js';
|
||||
import { MAX_EVENTS, validateEvent_v1 } from './parser.js';
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// cap number of events
|
||||
if (events.length >= MAX_EVENTS) {
|
||||
console.log(`ERROR: Reached limit number of ${MAX_EVENTS} events`);
|
||||
break;
|
||||
}
|
||||
|
||||
// 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 !== undefined) 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse userFields entry
|
||||
* @param {object} data - data object
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseUserFields_v1 = (data) => {
|
||||
let newUserFields = dbModelv1.userFields;
|
||||
|
||||
if ('userFields' in data) {
|
||||
console.log('Found User Fields definition, importing...');
|
||||
// we will only be importing the fields we know, so look for that
|
||||
let fieldsFound = 0;
|
||||
for (let n in newUserFields) {
|
||||
if (n in data.userFields) {
|
||||
fieldsFound++;
|
||||
newUserFields[n] = data.userFields[n];
|
||||
}
|
||||
}
|
||||
console.log(`Uploaded ${fieldsFound} user fields`);
|
||||
}
|
||||
return { ...dbModelv1.userFields, ...newUserFields };
|
||||
};
|
||||
Reference in New Issue
Block a user