Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SOFIE-192 - Ability to configure Core system settings via blueprints #33

Draft
wants to merge 11 commits into
base: bbc-release52
Choose a base branch
from
4 changes: 2 additions & 2 deletions meteor/__mocks__/defaultCollectionObjects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ export function defaultStudio(_id: StudioId): DBStudio {
mappingsWithOverrides: wrapDefaultObject({}),
supportedShowStyleBase: [],
blueprintConfigWithOverrides: wrapDefaultObject({}),
settings: {
settingsWithOverrides: wrapDefaultObject({
frameRate: 25,
mediaPreviewsUrl: '',
minimumTakeSpan: DEFAULT_MINIMUM_TAKE_SPAN,
fallbackPartDuration: DEFAULT_FALLBACK_PART_DURATION,
},
}),
_rundownVersionHash: '',
routeSetsWithOverrides: wrapDefaultObject({}),
routeSetExclusivityGroupsWithOverrides: wrapDefaultObject({}),
Expand Down
19 changes: 19 additions & 0 deletions meteor/__mocks__/helpers/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,25 @@ export async function setupMockCore(doc?: Partial<ICoreSystem>): Promise<ICoreSy
version: '0.0.0',
previousVersion: '0.0.0',
serviceMessages: {},
settingsWithOverrides: wrapDefaultObject({
cron: {
casparCGRestart: {
enabled: true,
},
storeRundownSnapshots: {
enabled: false,
},
},
support: {
message: '',
},
evaluationsMessage: {
enabled: false,
heading: '',
message: '',
},
}),
lastBlueprintConfig: undefined,
}
const coreSystem = _.extend(defaultCore, doc)
await CoreSystem.removeAsync(SYSTEM_ID)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from '@sofie-automation/meteor-lib/dist/collections/CoreSystem'
import { CoreSystem } from '../../../collections'
import { SupressLogMessages } from '../../../../__mocks__/suppressLogging'
import { wrapDefaultObject } from '@sofie-automation/corelib/dist/settings/objectWithOverrides'

function convertExternalToServiceMessage(message: ExternalServiceMessage): ServiceMessage {
return {
Expand Down Expand Up @@ -42,6 +43,8 @@ const fakeCoreSystem: ICoreSystem = {
version: '3',
previousVersion: null,
serviceMessages: {},
settingsWithOverrides: wrapDefaultObject({} as any),
lastBlueprintConfig: undefined,
}

describe('Service messages internal API', () => {
Expand Down
4 changes: 3 additions & 1 deletion meteor/server/api/evaluations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { sendSlackMessageToWebhook } from './integration/slack'
import { OrganizationId, UserId } from '@sofie-automation/corelib/dist/dataModel/Ids'
import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist'
import { Evaluations, RundownPlaylists } from '../collections'
import { applyAndValidateOverrides } from '@sofie-automation/corelib/dist/settings/objectWithOverrides'

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

const webhookUrls = _.compact((studio.settings.slackEvaluationUrls || '').split(','))
const webhookUrls = _.compact((studioSettings.slackEvaluationUrls || '').split(','))

if (webhookUrls.length) {
// Only send notes if not everything is OK
Expand Down
8 changes: 6 additions & 2 deletions meteor/server/api/rest/v1/typeConversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,13 +266,17 @@ export async function studioFrom(apiStudio: APIStudio, existingId?: StudioId): P
? updateOverrides(studio.blueprintConfigWithOverrides, apiStudio.config as IBlueprintConfig)
: wrapDefaultObject({})

const studioSettings = studioSettingsFrom(apiStudio.settings)

return {
_id: existingId ?? getRandomId(),
name: apiStudio.name,
blueprintId: blueprint?._id,
blueprintConfigPresetId: apiStudio.blueprintConfigPresetId,
blueprintConfigWithOverrides: blueprintConfig,
settings: studioSettingsFrom(apiStudio.settings),
settingsWithOverrides: studio
? updateOverrides(studio.settingsWithOverrides, studioSettings)
: wrapDefaultObject(studioSettings),
supportedShowStyleBase: apiStudio.supportedShowStyleBase?.map((id) => protectString<ShowStyleBaseId>(id)) ?? [],
organizationId: null,
mappingsWithOverrides: wrapDefaultObject({}),
Expand All @@ -293,7 +297,7 @@ export async function studioFrom(apiStudio: APIStudio, existingId?: StudioId): P
}

export function APIStudioFrom(studio: DBStudio): APIStudio {
const studioSettings = APIStudioSettingsFrom(studio.settings)
const studioSettings = APIStudioSettingsFrom(applyAndValidateOverrides(studio.settingsWithOverrides).obj)

return {
name: studio.name,
Expand Down
4 changes: 2 additions & 2 deletions meteor/server/api/studio/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ export async function insertStudioInner(organizationId: OrganizationId | null, n
supportedShowStyleBase: [],
blueprintConfigWithOverrides: wrapDefaultObject({}),
// testToolsConfig?: ITestToolsConfig
settings: {
settingsWithOverrides: wrapDefaultObject({
frameRate: 25,
mediaPreviewsUrl: '',
minimumTakeSpan: DEFAULT_MINIMUM_TAKE_SPAN,
},
}),
_rundownVersionHash: '',
routeSetsWithOverrides: wrapDefaultObject({}),
routeSetExclusivityGroupsWithOverrides: wrapDefaultObject({}),
Expand Down
5 changes: 2 additions & 3 deletions meteor/server/collections/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,13 @@ export const CoreSystem = createAsyncOnlyMongoCollection<ICoreSystem>(Collection
if (!access.update) return logNotAllowed('CoreSystem', access.reason)

return allowOnlyFields(doc, fields, [
'support',
'systemInfo',
'name',
'logLevel',
'apm',
'cron',
'logo',
'evaluations',
'blueprintId',
'settingsWithOverrides',
])
},
})
Expand Down
25 changes: 20 additions & 5 deletions meteor/server/coreSystem/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import { getEnvLogLevel, logger, LogLevel, setLogLevel } from '../logging'
const PackageInfo = require('../../package.json')
import { startAgent } from '../api/profiler/apm'
import { profiler } from '../api/profiler'
import { TMP_TSR_VERSION } from '@sofie-automation/blueprints-integration'
import { ICoreSystemSettings, TMP_TSR_VERSION } from '@sofie-automation/blueprints-integration'
import { getAbsolutePath } from '../lib'
import * as fs from 'fs/promises'
import path from 'path'
import { checkDatabaseVersions } from './checkDatabaseVersions'
import PLazy from 'p-lazy'
import { getCoreSystemAsync } from './collection'
import { wrapDefaultObject } from '@sofie-automation/corelib/dist/settings/objectWithOverrides'

export { PackageInfo }

Expand Down Expand Up @@ -59,11 +60,25 @@ async function initializeCoreSystem() {
enabled: false,
transactionSampleRate: -1,
},
cron: {
casparCGRestart: {
enabled: true,
settingsWithOverrides: wrapDefaultObject<ICoreSystemSettings>({
cron: {
casparCGRestart: {
enabled: true,
},
storeRundownSnapshots: {
enabled: false,
},
},
},
support: {
message: '',
},
evaluationsMessage: {
enabled: false,
heading: '',
message: '',
},
}),
lastBlueprintConfig: undefined,
})

if (!isRunningInJest()) {
Expand Down
21 changes: 12 additions & 9 deletions meteor/server/cronjobs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ import { deferAsync, normalizeArrayToMap } from '@sofie-automation/corelib/dist/
import { getCoreSystemAsync } from './coreSystem/collection'
import { cleanupOldDataInner } from './api/cleanup'
import { CollectionCleanupResult } from '@sofie-automation/meteor-lib/dist/api/system'
import { ICoreSystem } from '@sofie-automation/meteor-lib/dist/collections/CoreSystem'
import { ICoreSystemSettings } from '@sofie-automation/shared-lib/dist/core/model/CoreSystemSettings'
import { executePeripheralDeviceFunctionWithCustomTimeout } from './api/peripheralDevice/executeFunction'
import {
interpollateTranslation,
isTranslatableMessage,
translateMessage,
} from '@sofie-automation/corelib/dist/TranslatableMessage'
import { applyAndValidateOverrides } from '@sofie-automation/corelib/dist/settings/objectWithOverrides'

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

const systemSettings = system && applyAndValidateOverrides(system.settingsWithOverrides).obj

await Promise.allSettled([
cleanupOldDataCronjob().catch((error) => {
logger.error(`Cronjob: Error when cleaning up old data: ${stringifyError(error)}`)
logger.error(error)
}),
restartCasparCG(system, previousLastNightlyCronjob).catch((e) => {
restartCasparCG(systemSettings, previousLastNightlyCronjob).catch((e) => {
logger.error(`Cron: Restart CasparCG error: ${stringifyError(e)}`)
}),
storeSnapshots(system).catch((e) => {
storeSnapshots(systemSettings).catch((e) => {
logger.error(`Cron: Rundown Snapshots error: ${stringifyError(e)}`)
}),
])
Expand All @@ -81,8 +84,8 @@ async function cleanupOldDataCronjob() {

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

async function restartCasparCG(system: ICoreSystem | undefined, previousLastNightlyCronjob: number) {
if (!system?.cron?.casparCGRestart?.enabled) return
async function restartCasparCG(systemSettings: ICoreSystemSettings | undefined, previousLastNightlyCronjob: number) {
if (!systemSettings?.cron?.casparCGRestart?.enabled) return

let shouldRetryAttempt = false
const ps: Array<Promise<any>> = []
Expand Down Expand Up @@ -176,10 +179,10 @@ async function restartCasparCG(system: ICoreSystem | undefined, previousLastNigh
}
}

async function storeSnapshots(system: ICoreSystem | undefined) {
if (system?.cron?.storeRundownSnapshots?.enabled) {
const filter = system.cron.storeRundownSnapshots.rundownNames?.length
? { name: { $in: system.cron.storeRundownSnapshots.rundownNames } }
async function storeSnapshots(systemSettings: ICoreSystemSettings | undefined) {
if (systemSettings?.cron?.storeRundownSnapshots?.enabled) {
const filter = systemSettings.cron.storeRundownSnapshots.rundownNames?.length
? { name: { $in: systemSettings.cron.storeRundownSnapshots.rundownNames } }
: {}

const playlists = await RundownPlaylists.findFetchAsync(filter)
Expand Down
2 changes: 1 addition & 1 deletion meteor/server/logo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ logoRouter.get('/', async (ctx) => {
const logo = core?.logo ?? SofieLogo.Default

const paths: Record<SofieLogo, string> = {
[SofieLogo.Default]: '/images/sofie-logo.svg',
[SofieLogo.Default]: '/images/sofie-logo-default.svg',
[SofieLogo.Pride]: '/images/sofie-logo-pride.svg',
[SofieLogo.Norway]: '/images/sofie-logo-norway.svg',
[SofieLogo.Christmas]: '/images/sofie-logo-christmas.svg',
Expand Down
Loading
Loading