chore: create feature endpoints

This commit is contained in:
cv
2023-09-27 15:09:48 +02:00
parent 13d72dd1a4
commit 9fc04f2fd1
5 changed files with 202 additions and 43 deletions
+96 -36
View File
@@ -1,4 +1,4 @@
import { Alias, LogOrigin, ProjectData } from 'ontime-types';
import { Alias, DatabaseModel, LogOrigin, ProjectData } from 'ontime-types';
import { RequestHandler } from 'express';
import fs from 'fs';
@@ -7,13 +7,15 @@ import { networkInterfaces } from 'os';
import { fileHandler } from '../utils/parser.js';
import { DataProvider } from '../classes/data-provider/DataProvider.js';
import { failEmptyObjects, failIsNotArray } from '../utils/routerUtils.js';
import { mergeObject } from '../utils/parserUtils.js';
import { PlaybackService } from '../services/PlaybackService.js';
import { eventStore } from '../stores/EventStore.js';
import { isDocker, resolveDbPath } from '../setup.js';
import { oscIntegration } from '../services/integration-service/OscIntegration.js';
import { logger } from '../classes/Logger.js';
import { deleteAllEvents, forceReset } from '../services/rundown-service/RundownService.js';
import { deleteAllEvents, notifyChanges } from '../services/rundown-service/RundownService.js';
import { deepmerge } from 'ontime-utils';
import { runtimeCacheStore } from '../stores/cachingStore.js';
import { delayedRundownCacheKey } from '../services/rundown-service/delayedRundown.utils.js';
// Create controller for GET request to '/ontime/poll'
// Returns data for current state
@@ -43,44 +45,43 @@ export const dbDownload = async (req, res) => {
});
};
// TODO: docs
// TODO: cleanup usage
/**
* handles file upload
* Parses a file and returns the result objects
* @param file
* @param _req
* @param _res
* @param options
*/
async function parseFile(file, _req, _res, options) {
if (!fs.existsSync(file)) {
throw new Error('Upload failed');
}
const result = await fileHandler(file, options);
return result.data;
}
/**
* parse an uploaded file and apply its parsed objects
* @param file
* @param req
* @param res
* @param [options]
* @returns {Promise<void>}
*/
const uploadAndParse = async (file, req, res, options) => {
if (!fs.existsSync(file)) {
res.status(500).send({ message: 'Upload failed' });
return;
}
const parseAndApply = async (file, _req, res, options) => {
const result = await parseFile(file, _req, res, options);
try {
const result = await fileHandler(file);
PlaybackService.stop();
if ('error' in result && result.error) {
res.status(400).send({ message: result.message });
} else if ('data' in result && result.message === 'success') {
PlaybackService.stop();
// explicitly write objects
if (typeof result !== 'undefined') {
const newRundown = result.data.rundown || [];
if (options?.onlyRundown === 'true') {
await DataProvider.setRundown(newRundown);
} else {
await DataProvider.mergeIntoData(result.data);
}
}
forceReset();
res.sendStatus(200);
} else {
res.status(400).send({ message: 'Failed parsing, no data' });
}
} catch (error) {
res.status(400).send({ message: `Failed parsing ${error}` });
const newRundown = result.rundown || [];
if (options?.onlyRundown === 'true') {
await DataProvider.setRundown(newRundown);
} else {
await DataProvider.mergeIntoData(result);
}
notifyChanges({ timer: true, external: true, reset: true });
};
/**
@@ -169,7 +170,7 @@ export const postUserFields = async (req, res) => {
}
try {
const persistedData = DataProvider.getUserFields();
const newData = mergeObject(persistedData, req.body);
const newData = deepmerge(persistedData, req.body);
await DataProvider.setUserFields(newData);
res.status(200).send(newData);
} catch (error) {
@@ -319,8 +320,38 @@ export const postOSC = async (req, res) => {
}
};
// Create controller for POST request to '/ontime/db'
// Returns -
export async function patchPartialProjectFile(req, res) {
if (failEmptyObjects(req.body, res)) {
return;
}
try {
const patchDb: Partial<DatabaseModel> = {
project: req.body?.project,
settings: req.body?.settings,
viewSettings: req.body?.viewSettings,
osc: req.body?.osc,
aliases: req.body?.aliases,
userFields: req.body?.userFields,
rundown: req.body?.rundown,
};
await DataProvider.mergeIntoData(patchDb);
if (patchDb.rundown !== undefined) {
// it is likely cheaper to invalidate cache than to calculate diff
PlaybackService.stop();
runtimeCacheStore.invalidate(delayedRundownCacheKey);
notifyChanges({ external: true, reset: true });
}
res.status(200).send();
} catch (error) {
res.status(400).send(error);
}
}
/**
* uploads and parses a given file
*/
export const dbUpload = async (req, res) => {
if (!req.file) {
res.status(400).send({ message: 'File not found' });
@@ -328,10 +359,39 @@ export const dbUpload = async (req, res) => {
}
const options = req.query;
const file = req.file.path;
await uploadAndParse(file, req, res, options);
try {
await parseAndApply(file, req, res, options);
res.status(200).send();
} catch (error) {
res.status(400).send({ message: `Failed parsing ${error}` });
}
};
// Create controller for POST request to '/ontime/new'
/**
* uploads and parses an excel file
* @returns parsed result
*/
export async function previewExcel(req, res) {
if (!req.file) {
res.status(400).send({ message: 'File not found' });
return;
}
try {
const options = JSON.parse(req.body.options);
const file = req.file.path;
const data = await parseFile(file, req, res, options);
res.status(200).send(data);
} catch (error) {
res.status(500).send({ message: error.toString() });
}
}
/**
* Meant to create a new project file, it will clear only fields which are specific to a project
* @param req
* @param res
*/
export const postNew: RequestHandler = async (req, res) => {
try {
const newProjectData: ProjectData = {
@@ -118,3 +118,18 @@ export const validateOscSubscription = [
next();
},
];
export const validatePatchProjectFile = [
body('rundown').isArray().optional({ nullable: false }),
body('project').isObject().optional({ nullable: false }),
body('settings').isObject().optional({ nullable: false }),
body('viewSettings').isObject().optional({ nullable: false }),
body('aliases').isArray().optional({ nullable: false }),
body('userFields').isObject().optional({ nullable: false }),
body('osc').isObject().optional({ nullable: false }),
(req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
];