Skip to content

Commit 460a2a9

Browse files
authored
feat: configure Core system/studio settings via blueprints SOFIE-192 (#33)
1 parent 3a526b7 commit 460a2a9

File tree

115 files changed

+2397
-1369
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

115 files changed

+2397
-1369
lines changed

meteor/__mocks__/defaultCollectionObjects.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,12 @@ export function defaultStudio(_id: StudioId): DBStudio {
105105
mappingsWithOverrides: wrapDefaultObject({}),
106106
supportedShowStyleBase: [],
107107
blueprintConfigWithOverrides: wrapDefaultObject({}),
108-
settings: {
108+
settingsWithOverrides: wrapDefaultObject({
109109
frameRate: 25,
110110
mediaPreviewsUrl: '',
111111
minimumTakeSpan: DEFAULT_MINIMUM_TAKE_SPAN,
112112
fallbackPartDuration: DEFAULT_FALLBACK_PART_DURATION,
113-
},
113+
}),
114114
_rundownVersionHash: '',
115115
routeSetsWithOverrides: wrapDefaultObject({}),
116116
routeSetExclusivityGroupsWithOverrides: wrapDefaultObject({}),

meteor/__mocks__/helpers/database.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,25 @@ export async function setupMockCore(doc?: Partial<ICoreSystem>): Promise<ICoreSy
171171
version: '0.0.0',
172172
previousVersion: '0.0.0',
173173
serviceMessages: {},
174+
settingsWithOverrides: wrapDefaultObject({
175+
cron: {
176+
casparCGRestart: {
177+
enabled: true,
178+
},
179+
storeRundownSnapshots: {
180+
enabled: false,
181+
},
182+
},
183+
support: {
184+
message: '',
185+
},
186+
evaluationsMessage: {
187+
enabled: false,
188+
heading: '',
189+
message: '',
190+
},
191+
}),
192+
lastBlueprintConfig: undefined,
174193
}
175194
const coreSystem = _.extend(defaultCore, doc)
176195
await CoreSystem.removeAsync(SYSTEM_ID)

meteor/server/__tests__/api/serviceMessages/serviceMessagesApi.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
} from '@sofie-automation/meteor-lib/dist/collections/CoreSystem'
1010
import { CoreSystem } from '../../../collections'
1111
import { SupressLogMessages } from '../../../../__mocks__/suppressLogging'
12+
import { wrapDefaultObject } from '@sofie-automation/corelib/dist/settings/objectWithOverrides'
1213

1314
function convertExternalToServiceMessage(message: ExternalServiceMessage): ServiceMessage {
1415
return {
@@ -42,6 +43,8 @@ const fakeCoreSystem: ICoreSystem = {
4243
version: '3',
4344
previousVersion: null,
4445
serviceMessages: {},
46+
settingsWithOverrides: wrapDefaultObject({} as any),
47+
lastBlueprintConfig: undefined,
4548
}
4649

4750
describe('Service messages internal API', () => {

meteor/server/api/evaluations.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { sendSlackMessageToWebhook } from './integration/slack'
1010
import { OrganizationId, UserId } from '@sofie-automation/corelib/dist/dataModel/Ids'
1111
import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist'
1212
import { Evaluations, RundownPlaylists } from '../collections'
13+
import { applyAndValidateOverrides } from '@sofie-automation/corelib/dist/settings/objectWithOverrides'
1314

1415
export async function saveEvaluation(
1516
credentials: {
@@ -33,8 +34,9 @@ export async function saveEvaluation(
3334
deferAsync(async () => {
3435
const studio = await fetchStudioLight(evaluation.studioId)
3536
if (!studio) throw new Meteor.Error(500, `Studio ${evaluation.studioId} not found!`)
37+
const studioSettings = applyAndValidateOverrides(studio.settingsWithOverrides).obj
3638

37-
const webhookUrls = _.compact((studio.settings.slackEvaluationUrls || '').split(','))
39+
const webhookUrls = _.compact((studioSettings.slackEvaluationUrls || '').split(','))
3840

3941
if (webhookUrls.length) {
4042
// Only send notes if not everything is OK

meteor/server/api/rest/v1/typeConversion.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import {
3838
DEFAULT_FALLBACK_PART_DURATION,
3939
} from '@sofie-automation/shared-lib/dist/core/constants'
4040
import { Bucket } from '@sofie-automation/meteor-lib/dist/collections/Buckets'
41-
import { ForceQuickLoopAutoNext } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist'
41+
import { ForceQuickLoopAutoNext } from '@sofie-automation/shared-lib/dist/core/model/StudioSettings'
4242

4343
/*
4444
This file contains functions that convert between the internal Sofie-Core types and types exposed to the external API.
@@ -266,13 +266,17 @@ export async function studioFrom(apiStudio: APIStudio, existingId?: StudioId): P
266266
? updateOverrides(studio.blueprintConfigWithOverrides, apiStudio.config as IBlueprintConfig)
267267
: wrapDefaultObject({})
268268

269+
const studioSettings = studioSettingsFrom(apiStudio.settings)
270+
269271
return {
270272
_id: existingId ?? getRandomId(),
271273
name: apiStudio.name,
272274
blueprintId: blueprint?._id,
273275
blueprintConfigPresetId: apiStudio.blueprintConfigPresetId,
274276
blueprintConfigWithOverrides: blueprintConfig,
275-
settings: studioSettingsFrom(apiStudio.settings),
277+
settingsWithOverrides: studio
278+
? updateOverrides(studio.settingsWithOverrides, studioSettings)
279+
: wrapDefaultObject(studioSettings),
276280
supportedShowStyleBase: apiStudio.supportedShowStyleBase?.map((id) => protectString<ShowStyleBaseId>(id)) ?? [],
277281
organizationId: null,
278282
mappingsWithOverrides: wrapDefaultObject({}),
@@ -293,7 +297,7 @@ export async function studioFrom(apiStudio: APIStudio, existingId?: StudioId): P
293297
}
294298

295299
export function APIStudioFrom(studio: DBStudio): APIStudio {
296-
const studioSettings = APIStudioSettingsFrom(studio.settings)
300+
const studioSettings = APIStudioSettingsFrom(applyAndValidateOverrides(studio.settingsWithOverrides).obj)
297301

298302
return {
299303
name: studio.name,

meteor/server/api/studio/api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ export async function insertStudioInner(organizationId: OrganizationId | null, n
4545
supportedShowStyleBase: [],
4646
blueprintConfigWithOverrides: wrapDefaultObject({}),
4747
// testToolsConfig?: ITestToolsConfig
48-
settings: {
48+
settingsWithOverrides: wrapDefaultObject({
4949
frameRate: 25,
5050
mediaPreviewsUrl: '',
5151
minimumTakeSpan: DEFAULT_MINIMUM_TAKE_SPAN,
52-
},
52+
}),
5353
_rundownVersionHash: '',
5454
routeSetsWithOverrides: wrapDefaultObject({}),
5555
routeSetExclusivityGroupsWithOverrides: wrapDefaultObject({}),

meteor/server/collections/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,13 @@ export const CoreSystem = createAsyncOnlyMongoCollection<ICoreSystem>(Collection
6767
if (!access.update) return logNotAllowed('CoreSystem', access.reason)
6868

6969
return allowOnlyFields(doc, fields, [
70-
'support',
7170
'systemInfo',
7271
'name',
7372
'logLevel',
7473
'apm',
75-
'cron',
7674
'logo',
77-
'evaluations',
75+
'blueprintId',
76+
'settingsWithOverrides',
7877
])
7978
},
8079
})

meteor/server/coreSystem/index.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ import { getEnvLogLevel, logger, LogLevel, setLogLevel } from '../logging'
1010
const PackageInfo = require('../../package.json')
1111
import { startAgent } from '../api/profiler/apm'
1212
import { profiler } from '../api/profiler'
13-
import { TMP_TSR_VERSION } from '@sofie-automation/blueprints-integration'
13+
import { ICoreSystemSettings, TMP_TSR_VERSION } from '@sofie-automation/blueprints-integration'
1414
import { getAbsolutePath } from '../lib'
1515
import * as fs from 'fs/promises'
1616
import path from 'path'
1717
import { checkDatabaseVersions } from './checkDatabaseVersions'
1818
import PLazy from 'p-lazy'
1919
import { getCoreSystemAsync } from './collection'
20+
import { wrapDefaultObject } from '@sofie-automation/corelib/dist/settings/objectWithOverrides'
2021

2122
export { PackageInfo }
2223

@@ -59,11 +60,25 @@ async function initializeCoreSystem() {
5960
enabled: false,
6061
transactionSampleRate: -1,
6162
},
62-
cron: {
63-
casparCGRestart: {
64-
enabled: true,
63+
settingsWithOverrides: wrapDefaultObject<ICoreSystemSettings>({
64+
cron: {
65+
casparCGRestart: {
66+
enabled: true,
67+
},
68+
storeRundownSnapshots: {
69+
enabled: false,
70+
},
6571
},
66-
},
72+
support: {
73+
message: '',
74+
},
75+
evaluationsMessage: {
76+
enabled: false,
77+
heading: '',
78+
message: '',
79+
},
80+
}),
81+
lastBlueprintConfig: undefined,
6782
})
6883

6984
if (!isRunningInJest()) {

meteor/server/cronjobs.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@ import { deferAsync, normalizeArrayToMap } from '@sofie-automation/corelib/dist/
1818
import { getCoreSystemAsync } from './coreSystem/collection'
1919
import { cleanupOldDataInner } from './api/cleanup'
2020
import { CollectionCleanupResult } from '@sofie-automation/meteor-lib/dist/api/system'
21-
import { ICoreSystem } from '@sofie-automation/meteor-lib/dist/collections/CoreSystem'
21+
import { ICoreSystemSettings } from '@sofie-automation/shared-lib/dist/core/model/CoreSystemSettings'
2222
import { executePeripheralDeviceFunctionWithCustomTimeout } from './api/peripheralDevice/executeFunction'
2323
import {
2424
interpollateTranslation,
2525
isTranslatableMessage,
2626
translateMessage,
2727
} from '@sofie-automation/corelib/dist/TranslatableMessage'
28+
import { applyAndValidateOverrides } from '@sofie-automation/corelib/dist/settings/objectWithOverrides'
2829

2930
const lowPrioFcn = (fcn: () => any) => {
3031
// Do it at a random time in the future:
@@ -49,15 +50,17 @@ export async function nightlyCronjobInner(): Promise<void> {
4950
logger.info('Nightly cronjob: starting...')
5051
const system = await getCoreSystemAsync()
5152

53+
const systemSettings = system && applyAndValidateOverrides(system.settingsWithOverrides).obj
54+
5255
await Promise.allSettled([
5356
cleanupOldDataCronjob().catch((error) => {
5457
logger.error(`Cronjob: Error when cleaning up old data: ${stringifyError(error)}`)
5558
logger.error(error)
5659
}),
57-
restartCasparCG(system, previousLastNightlyCronjob).catch((e) => {
60+
restartCasparCG(systemSettings, previousLastNightlyCronjob).catch((e) => {
5861
logger.error(`Cron: Restart CasparCG error: ${stringifyError(e)}`)
5962
}),
60-
storeSnapshots(system).catch((e) => {
63+
storeSnapshots(systemSettings).catch((e) => {
6164
logger.error(`Cron: Rundown Snapshots error: ${stringifyError(e)}`)
6265
}),
6366
])
@@ -81,8 +84,8 @@ async function cleanupOldDataCronjob() {
8184

8285
const CASPARCG_LAST_SEEN_PERIOD_MS = 3 * 60 * 1000 // Note: this must be higher than the ping interval used by playout-gateway
8386

84-
async function restartCasparCG(system: ICoreSystem | undefined, previousLastNightlyCronjob: number) {
85-
if (!system?.cron?.casparCGRestart?.enabled) return
87+
async function restartCasparCG(systemSettings: ICoreSystemSettings | undefined, previousLastNightlyCronjob: number) {
88+
if (!systemSettings?.cron?.casparCGRestart?.enabled) return
8689

8790
let shouldRetryAttempt = false
8891
const ps: Array<Promise<any>> = []
@@ -176,10 +179,10 @@ async function restartCasparCG(system: ICoreSystem | undefined, previousLastNigh
176179
}
177180
}
178181

179-
async function storeSnapshots(system: ICoreSystem | undefined) {
180-
if (system?.cron?.storeRundownSnapshots?.enabled) {
181-
const filter = system.cron.storeRundownSnapshots.rundownNames?.length
182-
? { name: { $in: system.cron.storeRundownSnapshots.rundownNames } }
182+
async function storeSnapshots(systemSettings: ICoreSystemSettings | undefined) {
183+
if (systemSettings?.cron?.storeRundownSnapshots?.enabled) {
184+
const filter = systemSettings.cron.storeRundownSnapshots.rundownNames?.length
185+
? { name: { $in: systemSettings.cron.storeRundownSnapshots.rundownNames } }
183186
: {}
184187

185188
const playlists = await RundownPlaylists.findFetchAsync(filter)

meteor/server/logo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ logoRouter.get('/', async (ctx) => {
1313
const logo = core?.logo ?? SofieLogo.Default
1414

1515
const paths: Record<SofieLogo, string> = {
16-
[SofieLogo.Default]: '/images/sofie-logo.svg',
16+
[SofieLogo.Default]: '/images/sofie-logo-default.svg',
1717
[SofieLogo.Pride]: '/images/sofie-logo-pride.svg',
1818
[SofieLogo.Norway]: '/images/sofie-logo-norway.svg',
1919
[SofieLogo.Christmas]: '/images/sofie-logo-christmas.svg',

0 commit comments

Comments
 (0)