mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-22 15:39:11 +00:00
refactor: generate ids for new rundowns
This commit is contained in:
committed by
Carlos Valente
parent
b70ef80320
commit
f2b78e0f32
@@ -1,19 +1,21 @@
|
|||||||
import { DatabaseModel, AutomationSettings, NormalisedAutomation, Trigger } from 'ontime-types';
|
import { DatabaseModel, AutomationSettings, NormalisedAutomation, Trigger } from 'ontime-types';
|
||||||
|
|
||||||
import { dbModel } from '../../models/dataModel.js';
|
|
||||||
import type { ErrorEmitter } from '../../utils/parserUtils.js';
|
import type { ErrorEmitter } from '../../utils/parserUtils.js';
|
||||||
|
import { getPartialProject } from '../../models/dataModel.js';
|
||||||
|
|
||||||
export function parseAutomationSettings(data: Partial<DatabaseModel>, emitError?: ErrorEmitter): AutomationSettings {
|
export function parseAutomationSettings(data: Partial<DatabaseModel>, emitError?: ErrorEmitter): AutomationSettings {
|
||||||
|
const defaultAutomation: AutomationSettings = getPartialProject('automation');
|
||||||
|
|
||||||
if (!data.automation) {
|
if (!data.automation) {
|
||||||
emitError?.('No data found to import');
|
emitError?.('No data found to import');
|
||||||
return { ...dbModel.automation };
|
return defaultAutomation;
|
||||||
}
|
}
|
||||||
console.log('Found Automation settings, importing...');
|
console.log('Found Automation settings, importing...');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
enabledAutomations: data.automation.enabledAutomations ?? dbModel.automation.enabledAutomations,
|
enabledAutomations: data.automation.enabledAutomations ?? defaultAutomation.enabledAutomations,
|
||||||
enabledOscIn: data.automation.enabledOscIn ?? dbModel.automation.enabledOscIn,
|
enabledOscIn: data.automation.enabledOscIn ?? defaultAutomation.enabledOscIn,
|
||||||
oscPortIn: data.automation.oscPortIn ?? dbModel.automation.oscPortIn,
|
oscPortIn: data.automation.oscPortIn ?? defaultAutomation.oscPortIn,
|
||||||
triggers: parseTriggers(data.automation.triggers),
|
triggers: parseTriggers(data.automation.triggers),
|
||||||
automations: parseAutomations(data.automation.automations),
|
automations: parseAutomations(data.automation.automations),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -49,15 +49,9 @@ export async function patchPartialProjectFile(req: Request, res: Response<Databa
|
|||||||
*/
|
*/
|
||||||
export async function createProjectFile(req: Request, res: Response<{ filename: string } | ErrorResponse>) {
|
export async function createProjectFile(req: Request, res: Response<{ filename: string } | ErrorResponse>) {
|
||||||
try {
|
try {
|
||||||
const newFileName = await projectService.createProject(req.body.filename || 'untitled', {
|
const { filename, ...project } = req.body;
|
||||||
project: {
|
const newFileName = await projectService.createProjectWithPatch(filename, {
|
||||||
title: req.body?.title ?? '',
|
project,
|
||||||
description: req.body?.description ?? '',
|
|
||||||
url: req.body?.url ?? '',
|
|
||||||
info: req.body?.info ?? '',
|
|
||||||
logo: req.body?.logo ?? null,
|
|
||||||
custom: req.body?.custom ?? [],
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(200).send({
|
res.status(200).send({
|
||||||
|
|||||||
@@ -17,11 +17,12 @@ import {
|
|||||||
URLPreset,
|
URLPreset,
|
||||||
ViewSettings,
|
ViewSettings,
|
||||||
} from 'ontime-types';
|
} from 'ontime-types';
|
||||||
import { is } from '../../../utils/is.js';
|
|
||||||
import { dbModel } from '../../../models/dataModel.js';
|
|
||||||
import { customFieldLabelToKey, checkRegex, isKnownTimerType, validateEndAction } from 'ontime-utils';
|
import { customFieldLabelToKey, checkRegex, isKnownTimerType, validateEndAction } from 'ontime-utils';
|
||||||
|
|
||||||
|
import { is } from '../../../utils/is.js';
|
||||||
import { event as eventModel } from '../../../models/eventsDefinition.js';
|
import { event as eventModel } from '../../../models/eventsDefinition.js';
|
||||||
import { ONTIME_VERSION } from '../../../ONTIME_VERSION.js';
|
import { ONTIME_VERSION } from '../../../ONTIME_VERSION.js';
|
||||||
|
import { getPartialProject } from '../../../models/dataModel.js';
|
||||||
|
|
||||||
// the methodology of the migrations is to just change the necessary keys to match with v4
|
// the methodology of the migrations is to just change the necessary keys to match with v4
|
||||||
// and then let the normal project parser handle ensuring the the file is correct
|
// and then let the normal project parser handle ensuring the the file is correct
|
||||||
@@ -148,8 +149,8 @@ export function migrateProjectData(jsonData: object): ProjectData | undefined {
|
|||||||
description,
|
description,
|
||||||
url: backstageUrl,
|
url: backstageUrl,
|
||||||
info: backstageInfo,
|
info: backstageInfo,
|
||||||
logo: logo ?? dbModel.project.logo,
|
logo: logo ?? null,
|
||||||
custom: custom ?? dbModel.project.custom,
|
custom: custom ?? [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,7 +245,7 @@ export function migrateAutomations(jsonData: object): AutomationSettings | undef
|
|||||||
}
|
}
|
||||||
|
|
||||||
let foundOldSetting = false;
|
let foundOldSetting = false;
|
||||||
const migratedOldStuff = structuredClone(dbModel.automation);
|
const migratedOldStuff: AutomationSettings = getPartialProject('automation');
|
||||||
const migratedAutomations: NormalisedAutomation = {};
|
const migratedAutomations: NormalisedAutomation = {};
|
||||||
const migratedTriggers: Trigger[] = [];
|
const migratedTriggers: Trigger[] = [];
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
ViewSettings,
|
ViewSettings,
|
||||||
} from 'ontime-types';
|
} from 'ontime-types';
|
||||||
import * as v3 from './db.migration.v3.js';
|
import * as v3 from './db.migration.v3.js';
|
||||||
import { dbModel } from '../../../models/dataModel.js';
|
|
||||||
import { ONTIME_VERSION } from '../../../ONTIME_VERSION.js';
|
import { ONTIME_VERSION } from '../../../ONTIME_VERSION.js';
|
||||||
|
|
||||||
describe('v3 to v4', () => {
|
describe('v3 to v4', () => {
|
||||||
@@ -469,7 +469,7 @@ describe('v3 to v4', () => {
|
|||||||
const expectedAutomation: AutomationSettings = {
|
const expectedAutomation: AutomationSettings = {
|
||||||
enabledAutomations: true,
|
enabledAutomations: true,
|
||||||
enabledOscIn: true,
|
enabledOscIn: true,
|
||||||
oscPortIn: dbModel.automation.oscPortIn,
|
oscPortIn: 8888,
|
||||||
triggers: [
|
triggers: [
|
||||||
{
|
{
|
||||||
id: '1ge4r8-T',
|
id: '1ge4r8-T',
|
||||||
|
|||||||
@@ -1,25 +1,27 @@
|
|||||||
import { DatabaseModel, ProjectData } from 'ontime-types';
|
import { DatabaseModel, ProjectData } from 'ontime-types';
|
||||||
|
|
||||||
import { dbModel } from '../../models/dataModel.js';
|
|
||||||
import { ErrorEmitter } from '../../utils/parserUtils.js';
|
import { ErrorEmitter } from '../../utils/parserUtils.js';
|
||||||
|
import { getPartialProject } from '../../models/dataModel.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse event portion of an entry
|
* Parse event portion of an entry
|
||||||
*/
|
*/
|
||||||
export function parseProjectData(data: Partial<DatabaseModel>, emitError?: ErrorEmitter): ProjectData {
|
export function parseProjectData(data: Partial<DatabaseModel>, emitError?: ErrorEmitter): ProjectData {
|
||||||
|
const defaultProject: ProjectData = getPartialProject('project');
|
||||||
|
|
||||||
if (!data.project) {
|
if (!data.project) {
|
||||||
emitError?.('No data found to import');
|
emitError?.('No data found to import');
|
||||||
return { ...dbModel.project };
|
return defaultProject;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Found project data, importing...');
|
console.log('Found project data, importing...');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: data.project.title ?? dbModel.project.title,
|
title: data.project.title ?? defaultProject.title,
|
||||||
description: data.project.description ?? dbModel.project.description,
|
description: data.project.description ?? defaultProject.description,
|
||||||
url: data.project.url ?? dbModel.project.url,
|
url: data.project.url ?? defaultProject.url,
|
||||||
info: data.project.info ?? dbModel.project.info,
|
info: data.project.info ?? defaultProject.info,
|
||||||
logo: data.project.logo ?? dbModel.project.logo,
|
logo: data.project.logo ?? defaultProject.logo,
|
||||||
custom: data.project.custom ?? dbModel.project.custom,
|
custom: data.project.custom ?? defaultProject.custom,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
OntimeMilestone,
|
OntimeMilestone,
|
||||||
} from 'ontime-types';
|
} from 'ontime-types';
|
||||||
|
|
||||||
import { defaultRundown } from '../../../models/dataModel.js';
|
import { makeNewRundown } from '../../../models/dataModel.js';
|
||||||
|
|
||||||
const baseEvent = {
|
const baseEvent = {
|
||||||
type: SupportedEntry.Event,
|
type: SupportedEntry.Event,
|
||||||
@@ -63,7 +63,7 @@ export function makeOntimeMilestone(patch: Partial<OntimeMilestone>): OntimeMile
|
|||||||
*/
|
*/
|
||||||
export function makeRundown(patch: Partial<Rundown>): Rundown {
|
export function makeRundown(patch: Partial<Rundown>): Rundown {
|
||||||
return {
|
return {
|
||||||
...defaultRundown,
|
...makeNewRundown(),
|
||||||
...patch,
|
...patch,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,25 @@
|
|||||||
import { SupportedEntry, OntimeEvent, OntimeGroup, Rundown, CustomFields } from 'ontime-types';
|
import { SupportedEntry, OntimeEvent, OntimeGroup, Rundown, CustomFields } from 'ontime-types';
|
||||||
|
|
||||||
import { defaultRundown } from '../../../models/dataModel.js';
|
import { makeNewRundown } from '../../../models/dataModel.js';
|
||||||
import { makeOntimeGroup, makeOntimeEvent, makeOntimeMilestone } from '../__mocks__/rundown.mocks.js';
|
|
||||||
|
|
||||||
|
import { makeOntimeGroup, makeOntimeEvent, makeOntimeMilestone } from '../__mocks__/rundown.mocks.js';
|
||||||
import { parseRundowns, parseRundown, sanitiseCustomFields } from '../rundown.parser.js';
|
import { parseRundowns, parseRundown, sanitiseCustomFields } from '../rundown.parser.js';
|
||||||
|
|
||||||
describe('parseRundowns()', () => {
|
describe('parseRundowns()', () => {
|
||||||
it('returns a default project rundown if nothing is given', () => {
|
it('returns a default project rundown if nothing is given', () => {
|
||||||
const errorEmitter = vi.fn();
|
const errorEmitter = vi.fn();
|
||||||
|
const defaultRundown = makeNewRundown();
|
||||||
const result = parseRundowns({}, {}, errorEmitter);
|
const result = parseRundowns({}, {}, errorEmitter);
|
||||||
expect(result).toStrictEqual({ default: defaultRundown });
|
const rundownIds = Object.keys(result);
|
||||||
|
|
||||||
|
expect(rundownIds).toHaveLength(1);
|
||||||
|
expect(result[rundownIds[0]]).toMatchObject({
|
||||||
|
id: rundownIds[0],
|
||||||
|
title: defaultRundown.title,
|
||||||
|
order: expect.any(Array),
|
||||||
|
entries: expect.any(Object),
|
||||||
|
});
|
||||||
|
|
||||||
// one for not having custom fields
|
// one for not having custom fields
|
||||||
// one for not having a rundown
|
// one for not having a rundown
|
||||||
expect(errorEmitter).toHaveBeenCalledTimes(1);
|
expect(errorEmitter).toHaveBeenCalledTimes(1);
|
||||||
@@ -17,6 +27,7 @@ describe('parseRundowns()', () => {
|
|||||||
|
|
||||||
it('ensures the rundown IDs are consistent', () => {
|
it('ensures the rundown IDs are consistent', () => {
|
||||||
const errorEmitter = vi.fn();
|
const errorEmitter = vi.fn();
|
||||||
|
const defaultRundown = makeNewRundown();
|
||||||
const r1 = { ...defaultRundown, id: '1' };
|
const r1 = { ...defaultRundown, id: '1' };
|
||||||
const r2 = { ...defaultRundown, id: '2' };
|
const r2 = { ...defaultRundown, id: '2' };
|
||||||
const result = parseRundowns(
|
const result = parseRundowns(
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import {
|
|||||||
} from 'ontime-types';
|
} from 'ontime-types';
|
||||||
import { isObjectEmpty, generateId, getLinkedTimes, getTimeFrom, isNewLatest } from 'ontime-utils';
|
import { isObjectEmpty, generateId, getLinkedTimes, getTimeFrom, isNewLatest } from 'ontime-utils';
|
||||||
|
|
||||||
import { defaultRundown } from '../../models/dataModel.js';
|
|
||||||
import { delay as delayDef } from '../../models/eventsDefinition.js';
|
import { delay as delayDef } from '../../models/eventsDefinition.js';
|
||||||
|
import { makeNewRundown } from '../../models/dataModel.js';
|
||||||
import type { ErrorEmitter } from '../../utils/parserUtils.js';
|
import type { ErrorEmitter } from '../../utils/parserUtils.js';
|
||||||
|
|
||||||
import { calculateDayOffset, cleanupCustomFields, createGroup, createEvent, createMilestone } from './rundown.utils.js';
|
import { calculateDayOffset, cleanupCustomFields, createGroup, createEvent, createMilestone } from './rundown.utils.js';
|
||||||
@@ -39,10 +39,9 @@ export function parseRundowns(
|
|||||||
// this is important since the rest of the app assumes this exist
|
// this is important since the rest of the app assumes this exist
|
||||||
if (!data.rundowns || isObjectEmpty(data.rundowns)) {
|
if (!data.rundowns || isObjectEmpty(data.rundowns)) {
|
||||||
emitError?.('No data found to import');
|
emitError?.('No data found to import');
|
||||||
|
const defaultRundown = makeNewRundown();
|
||||||
return {
|
return {
|
||||||
[defaultRundown.id]: {
|
[defaultRundown.id]: defaultRundown,
|
||||||
...defaultRundown,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
import { ErrorResponse, OntimeEntry, ProjectRundownsList, Rundown } from 'ontime-types';
|
import { ErrorResponse, OntimeEntry, ProjectRundownsList, Rundown } from 'ontime-types';
|
||||||
import { generateId, getErrorMessage } from 'ontime-utils';
|
import { getErrorMessage } from 'ontime-utils';
|
||||||
|
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
|
||||||
|
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
|
||||||
|
import { makeNewRundown } from '../../models/dataModel.js';
|
||||||
|
|
||||||
|
import { paramsWithId } from '../validation-utils/validationFunction.js';
|
||||||
|
|
||||||
import { getCurrentRundown } from './rundown.dao.js';
|
import { getCurrentRundown } from './rundown.dao.js';
|
||||||
import {
|
import {
|
||||||
addEntry,
|
addEntry,
|
||||||
@@ -30,9 +35,6 @@ import {
|
|||||||
validateRundownMutation,
|
validateRundownMutation,
|
||||||
clonePostValidator,
|
clonePostValidator,
|
||||||
} from './rundown.validation.js';
|
} from './rundown.validation.js';
|
||||||
import { paramsWithId } from '../validation-utils/validationFunction.js';
|
|
||||||
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
|
|
||||||
import { defaultRundown } from '../../models/dataModel.js';
|
|
||||||
import { duplicateRundown, normalisedToRundownArray } from './rundown.utils.js';
|
import { duplicateRundown, normalisedToRundownArray } from './rundown.utils.js';
|
||||||
|
|
||||||
export const router = express.Router();
|
export const router = express.Router();
|
||||||
@@ -85,8 +87,9 @@ router.post('/:id/load', paramsWithId, async (req: Request, res: Response<Projec
|
|||||||
*/
|
*/
|
||||||
router.post('/', rundownPostValidator, async (req: Request, res: Response<ProjectRundownsList | ErrorResponse>) => {
|
router.post('/', rundownPostValidator, async (req: Request, res: Response<ProjectRundownsList | ErrorResponse>) => {
|
||||||
try {
|
try {
|
||||||
const id = generateId();
|
const emptyRundown = makeNewRundown();
|
||||||
await getDataProvider().setRundown(id, { ...defaultRundown, id, title: req.body.title });
|
emptyRundown.title = req.body.title;
|
||||||
|
await getDataProvider().setRundown(emptyRundown.id, emptyRundown);
|
||||||
|
|
||||||
const projectRundowns = getDataProvider().getProjectRundowns();
|
const projectRundowns = getDataProvider().getProjectRundowns();
|
||||||
res.status(201).json({ loaded: getCurrentRundown().id, rundowns: normalisedToRundownArray(projectRundowns) });
|
res.status(201).json({ loaded: getCurrentRundown().id, rundowns: normalisedToRundownArray(projectRundowns) });
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { DatabaseModel, Settings } from 'ontime-types';
|
import { DatabaseModel, Settings } from 'ontime-types';
|
||||||
|
|
||||||
import { dbModel } from '../../models/dataModel.js';
|
import { getPartialProject } from '../../models/dataModel.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse settings portion of a project file
|
* Parse settings portion of a project file
|
||||||
*/
|
*/
|
||||||
export function parseSettings(data: Partial<DatabaseModel>): Settings {
|
export function parseSettings(data: Partial<DatabaseModel>): Settings {
|
||||||
|
const defaultSettings: Settings = getPartialProject('settings');
|
||||||
|
|
||||||
// skip if file definition is missing
|
// skip if file definition is missing
|
||||||
// TODO: skip parsing if the version is not correct
|
// TODO: skip parsing if the version is not correct
|
||||||
if (!data.settings || data.settings?.version == null) {
|
if (!data.settings || data.settings?.version == null) {
|
||||||
@@ -15,11 +17,11 @@ export function parseSettings(data: Partial<DatabaseModel>): Settings {
|
|||||||
console.log('Found settings, importing...');
|
console.log('Found settings, importing...');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
version: dbModel.settings.version,
|
version: defaultSettings.version,
|
||||||
serverPort: data.settings.serverPort ?? dbModel.settings.serverPort,
|
serverPort: data.settings.serverPort ?? defaultSettings.serverPort,
|
||||||
editorKey: data.settings.editorKey ?? null,
|
editorKey: data.settings.editorKey ?? defaultSettings.editorKey,
|
||||||
operatorKey: data.settings.operatorKey ?? null,
|
operatorKey: data.settings.operatorKey ?? defaultSettings.operatorKey,
|
||||||
timeFormat: data.settings.timeFormat ?? '24',
|
timeFormat: data.settings.timeFormat ?? defaultSettings.timeFormat,
|
||||||
language: data.settings.language ?? 'en',
|
language: data.settings.language ?? defaultSettings.language,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
import { DatabaseModel, ViewSettings } from 'ontime-types';
|
import { DatabaseModel, ViewSettings } from 'ontime-types';
|
||||||
|
|
||||||
import { dbModel } from '../../models/dataModel.js';
|
import { getPartialProject } from '../../models/dataModel.js';
|
||||||
import { ErrorEmitter } from '../../utils/parserUtils.js';
|
import { ErrorEmitter } from '../../utils/parserUtils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse viewSettings portion of a project file
|
* Parse viewSettings portion of a project file
|
||||||
*/
|
*/
|
||||||
export function parseViewSettings(data: Partial<DatabaseModel>, emitError?: ErrorEmitter): ViewSettings {
|
export function parseViewSettings(data: Partial<DatabaseModel>, emitError?: ErrorEmitter): ViewSettings {
|
||||||
|
const defaultViewSettings: ViewSettings = getPartialProject('viewSettings');
|
||||||
|
|
||||||
if (!data.viewSettings) {
|
if (!data.viewSettings) {
|
||||||
emitError?.('No data found to import');
|
emitError?.('No data found to import');
|
||||||
return { ...dbModel.viewSettings };
|
return defaultViewSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Found view settings, importing...');
|
console.log('Found view settings, importing...');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dangerColor: data.viewSettings.dangerColor ?? dbModel.viewSettings.dangerColor,
|
dangerColor: data.viewSettings.dangerColor ?? defaultViewSettings.dangerColor,
|
||||||
normalColor: data.viewSettings.normalColor ?? dbModel.viewSettings.normalColor,
|
normalColor: data.viewSettings.normalColor ?? defaultViewSettings.normalColor,
|
||||||
overrideStyles: data.viewSettings.overrideStyles ?? dbModel.viewSettings.overrideStyles,
|
overrideStyles: data.viewSettings.overrideStyles ?? defaultViewSettings.overrideStyles,
|
||||||
warningColor: data.viewSettings.warningColor ?? dbModel.viewSettings.warningColor,
|
warningColor: data.viewSettings.warningColor ?? defaultViewSettings.warningColor,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { DatabaseModel, Rundown } from 'ontime-types';
|
import { DatabaseModel, Rundown } from 'ontime-types';
|
||||||
import { ONTIME_VERSION } from '../ONTIME_VERSION.js';
|
import { ONTIME_VERSION } from '../ONTIME_VERSION.js';
|
||||||
|
import { generateId } from 'ontime-utils';
|
||||||
|
|
||||||
export const defaultRundown: Rundown = {
|
const defaultRundown: Rundown = {
|
||||||
id: 'default',
|
id: 'default',
|
||||||
title: 'Default',
|
title: 'Default',
|
||||||
order: [],
|
order: [],
|
||||||
@@ -10,7 +11,7 @@ export const defaultRundown: Rundown = {
|
|||||||
revision: 0,
|
revision: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const dbModel: DatabaseModel = {
|
const dbModel: DatabaseModel = {
|
||||||
rundowns: {
|
rundowns: {
|
||||||
default: { ...defaultRundown },
|
default: { ...defaultRundown },
|
||||||
},
|
},
|
||||||
@@ -46,3 +47,36 @@ export const dbModel: DatabaseModel = {
|
|||||||
automations: {},
|
automations: {},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new project with a single rundown of given Id
|
||||||
|
*/
|
||||||
|
export function makeNewProject(defaultRundownId?: string): DatabaseModel {
|
||||||
|
const rundown = makeNewRundown(defaultRundownId);
|
||||||
|
const newProject = structuredClone(dbModel);
|
||||||
|
newProject.rundowns = {
|
||||||
|
[rundown.id]: rundown,
|
||||||
|
};
|
||||||
|
|
||||||
|
return newProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new rundown with given Id
|
||||||
|
*/
|
||||||
|
export function makeNewRundown(id?: string): Rundown {
|
||||||
|
const rundownId = id || generateId();
|
||||||
|
const newRundown = structuredClone(defaultRundown);
|
||||||
|
newRundown.id = rundownId;
|
||||||
|
return newRundown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a top level property of the DatabaseModel
|
||||||
|
*/
|
||||||
|
export function getPartialProject<T extends keyof DatabaseModel>(key: T): DatabaseModel[T] {
|
||||||
|
if (Object.hasOwn(dbModel, key)) {
|
||||||
|
return structuredClone(dbModel[key]);
|
||||||
|
}
|
||||||
|
throw new Error(`Key ${key} does not exist on DatabaseModel`);
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { DatabaseModel, LogOrigin, ProjectFileListResponse } from 'ontime-types'
|
|||||||
import { getErrorMessage, getFirstRundown } from 'ontime-utils';
|
import { getErrorMessage, getFirstRundown } from 'ontime-utils';
|
||||||
|
|
||||||
import { copyFile } from 'fs/promises';
|
import { copyFile } from 'fs/promises';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
import { logger } from '../../classes/Logger.js';
|
import { logger } from '../../classes/Logger.js';
|
||||||
import { publicDir } from '../../setup/index.js';
|
import { publicDir } from '../../setup/index.js';
|
||||||
@@ -15,15 +16,16 @@ import {
|
|||||||
getFileNameFromPath,
|
getFileNameFromPath,
|
||||||
removeFileExtension,
|
removeFileExtension,
|
||||||
} from '../../utils/fileManagement.js';
|
} from '../../utils/fileManagement.js';
|
||||||
import { dbModel } from '../../models/dataModel.js';
|
|
||||||
import { parseRundowns } from '../../api-data/rundown/rundown.parser.js';
|
import { parseRundowns } from '../../api-data/rundown/rundown.parser.js';
|
||||||
import { demoDb } from '../../models/demoProject.js';
|
import { demoDb } from '../../models/demoProject.js';
|
||||||
import { config } from '../../setup/config.js';
|
import { config } from '../../setup/config.js';
|
||||||
import { getDataProvider, initPersistence } from '../../classes/data-provider/DataProvider.js';
|
import { getDataProvider, initPersistence } from '../../classes/data-provider/DataProvider.js';
|
||||||
import { safeMerge } from '../../classes/data-provider/DataProvider.utils.js';
|
|
||||||
import { initRundown } from '../../api-data/rundown/rundown.service.js';
|
import { initRundown } from '../../api-data/rundown/rundown.service.js';
|
||||||
import { parseDatabaseModel } from '../../api-data/db/db.parser.js';
|
import { parseDatabaseModel } from '../../api-data/db/db.parser.js';
|
||||||
import { parseCustomFields } from '../../api-data/custom-fields/customFields.parser.js';
|
import { parseCustomFields } from '../../api-data/custom-fields/customFields.parser.js';
|
||||||
|
import { makeNewProject } from '../../models/dataModel.js';
|
||||||
|
import { safeMerge } from '../../classes/data-provider/DataProvider.utils.js';
|
||||||
|
import { getCurrentRundown } from '../../api-data/rundown/rundown.dao.js';
|
||||||
|
|
||||||
import { getLastLoaded, isLastLoadedProject, setLastLoaded } from '../app-state-service/AppStateService.js';
|
import { getLastLoaded, isLastLoadedProject, setLastLoaded } from '../app-state-service/AppStateService.js';
|
||||||
import { runtimeService } from '../runtime-service/runtime.service.js';
|
import { runtimeService } from '../runtime-service/runtime.service.js';
|
||||||
@@ -35,7 +37,6 @@ import {
|
|||||||
moveCorruptFile,
|
moveCorruptFile,
|
||||||
parseJsonFile,
|
parseJsonFile,
|
||||||
} from './projectServiceUtils.js';
|
} from './projectServiceUtils.js';
|
||||||
import { join } from 'path';
|
|
||||||
|
|
||||||
type ProjectState =
|
type ProjectState =
|
||||||
| {
|
| {
|
||||||
@@ -94,10 +95,6 @@ async function loadProject(projectData: DatabaseModel, fileName: string, rundown
|
|||||||
? projectData.rundowns[rundownId]
|
? projectData.rundowns[rundownId]
|
||||||
: getFirstRundown(projectData.rundowns);
|
: getFirstRundown(projectData.rundowns);
|
||||||
|
|
||||||
if (!rundown) {
|
|
||||||
throw new Error('No rundown found in project');
|
|
||||||
}
|
|
||||||
|
|
||||||
await initRundown(rundown, projectData.customFields, true);
|
await initRundown(rundown, projectData.customFields, true);
|
||||||
|
|
||||||
// persist the project selection
|
// persist the project selection
|
||||||
@@ -124,7 +121,8 @@ export async function loadDemoProject(): Promise<string> {
|
|||||||
* to be composed in the loading functions
|
* to be composed in the loading functions
|
||||||
*/
|
*/
|
||||||
async function loadNewProject(): Promise<string> {
|
async function loadNewProject(): Promise<string> {
|
||||||
return createProject(config.newProject, dbModel);
|
const emptyProject = makeNewProject();
|
||||||
|
return createProject(config.newProject, emptyProject);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -297,13 +295,23 @@ export async function renameProjectFile(originalFile: string, newFilename: strin
|
|||||||
* @param fileName file name of the project including the extension
|
* @param fileName file name of the project including the extension
|
||||||
* @param initialData db to initialize the project with
|
* @param initialData db to initialize the project with
|
||||||
*/
|
*/
|
||||||
export async function createProject(fileName: string, initialData: Partial<DatabaseModel>): Promise<string> {
|
export async function createProject(fileName: string, initialData: DatabaseModel): Promise<string> {
|
||||||
const data = safeMerge(dbModel, initialData);
|
|
||||||
const fileNameWithExtension = generateUniqueFileName(publicDir.projectsDir, ensureJsonExtension(fileName));
|
const fileNameWithExtension = generateUniqueFileName(publicDir.projectsDir, ensureJsonExtension(fileName));
|
||||||
await loadProject(data, fileNameWithExtension);
|
await loadProject(initialData, fileNameWithExtension);
|
||||||
return fileNameWithExtension;
|
return fileNameWithExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new project file from a patch and applies its result
|
||||||
|
* @param fileName file name of the project including the extension
|
||||||
|
* @param initialData patch of DB to initialize the project with
|
||||||
|
*/
|
||||||
|
export async function createProjectWithPatch(fileName: string, initialData: Partial<DatabaseModel>): Promise<string> {
|
||||||
|
const newProject = makeNewProject();
|
||||||
|
const sanitisedData = safeMerge(newProject, initialData);
|
||||||
|
return createProject(fileName, sanitisedData);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a project file
|
* Deletes a project file
|
||||||
*/
|
*/
|
||||||
@@ -328,6 +336,7 @@ export async function patchCurrentProject(data: Partial<DatabaseModel>) {
|
|||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- we need to remove the fields before merging
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- we need to remove the fields before merging
|
||||||
const { rundowns, customFields, ...rest } = data;
|
const { rundowns, customFields, ...rest } = data;
|
||||||
|
|
||||||
// we can pass some stuff straight to the data provider
|
// we can pass some stuff straight to the data provider
|
||||||
await getDataProvider().mergeIntoData(rest);
|
await getDataProvider().mergeIntoData(rest);
|
||||||
|
|
||||||
@@ -340,9 +349,23 @@ export async function patchCurrentProject(data: Partial<DatabaseModel>) {
|
|||||||
|
|
||||||
// then we can check the rundown
|
// then we can check the rundown
|
||||||
if (rundowns) {
|
if (rundowns) {
|
||||||
const parsedCustomFields = await getDataProvider().getCustomFields();
|
// use the already parsed custom fields if available, otherwise fetch from provider
|
||||||
const parsedRundowns = parseRundowns(data, parsedCustomFields);
|
const projectCustomFields = getDataProvider().getCustomFields();
|
||||||
await getDataProvider().mergeIntoData({ rundowns: parsedRundowns });
|
const parsedRundowns = parseRundowns(data, projectCustomFields);
|
||||||
|
const currentRundown = getCurrentRundown();
|
||||||
|
|
||||||
|
const mergedData = await getDataProvider().mergeIntoData({ rundowns: parsedRundowns });
|
||||||
|
|
||||||
|
// check if the currently loaded rundown was modified
|
||||||
|
const didOverrideCurrentRundown = currentRundown.id in parsedRundowns;
|
||||||
|
|
||||||
|
if (didOverrideCurrentRundown) {
|
||||||
|
// verify the rundown exists in the merged data before reinitializing
|
||||||
|
const updatedCurrentRundown = mergedData.rundowns[currentRundown.id];
|
||||||
|
if (updatedCurrentRundown) {
|
||||||
|
await initRundown(updatedCurrentRundown, projectCustomFields, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedData = await getDataProvider().getData();
|
const updatedData = await getDataProvider().getData();
|
||||||
|
|||||||
Reference in New Issue
Block a user