Skip to content

Commit aaabae1

Browse files
committed
wip: boilerplate
1 parent 3a526b7 commit aaabae1

File tree

9 files changed

+194
-74
lines changed

9 files changed

+194
-74
lines changed

meteor/__mocks__/helpers/database.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ export async function setupMockCore(doc?: Partial<ICoreSystem>): Promise<ICoreSy
171171
version: '0.0.0',
172172
previousVersion: '0.0.0',
173173
serviceMessages: {},
174+
lastBlueprintConfig: undefined,
174175
}
175176
const coreSystem = _.extend(defaultCore, doc)
176177
await CoreSystem.removeAsync(SYSTEM_ID)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const fakeCoreSystem: ICoreSystem = {
4242
version: '3',
4343
previousVersion: null,
4444
serviceMessages: {},
45+
lastBlueprintConfig: undefined,
4546
}
4647

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

meteor/server/coreSystem/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ async function initializeCoreSystem() {
6464
enabled: true,
6565
},
6666
},
67+
lastBlueprintConfig: undefined,
6768
})
6869

6970
if (!isRunningInJest()) {

meteor/server/migration/databaseMigration.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ export async function prepareMigration(returnAllChunks?: boolean): Promise<Prepa
254254
})
255255
} else if (blueprint.blueprintType === BlueprintManifestType.SYSTEM) {
256256
const bp = blueprintManifest as SystemBlueprintManifest
257+
258+
// If blueprint uses the new flow, don't attempt migrations
259+
if (typeof bp.applyConfig === 'function') continue
260+
257261
// Check if the coreSystem uses this blueprint
258262
const coreSystems = await CoreSystem.findFetchAsync({
259263
blueprintId: blueprint._id,
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import type { ShowStyleBaseId, TriggeredActionId } from '@sofie-automation/corelib/dist/dataModel/Ids'
2+
import { TriggeredActions } from '../../collections'
3+
import { Complete, getRandomId, literal, normalizeArrayToMap } from '@sofie-automation/corelib/dist/lib'
4+
import type { DBTriggeredActions } from '@sofie-automation/meteor-lib/dist/collections/TriggeredActions'
5+
import type { AnyBulkWriteOperation } from 'mongodb'
6+
import { wrapDefaultObject } from '@sofie-automation/corelib/dist/settings/objectWithOverrides'
7+
import type { IBlueprintTriggeredActions } from '@sofie-automation/blueprints-integration'
8+
9+
export async function updateTriggeredActionsForShowStyleBaseId(
10+
showStyleBaseId: ShowStyleBaseId | null,
11+
triggeredActions: IBlueprintTriggeredActions[]
12+
): Promise<void> {
13+
const oldTriggeredActionsArray = await TriggeredActions.findFetchAsync({
14+
showStyleBaseId: showStyleBaseId,
15+
blueprintUniqueId: { $ne: null },
16+
})
17+
const oldTriggeredActions = normalizeArrayToMap(oldTriggeredActionsArray, 'blueprintUniqueId')
18+
19+
const newDocIds: TriggeredActionId[] = []
20+
const bulkOps: AnyBulkWriteOperation<DBTriggeredActions>[] = []
21+
22+
for (const newTriggeredAction of triggeredActions) {
23+
const oldValue = oldTriggeredActions.get(newTriggeredAction._id)
24+
if (oldValue) {
25+
// Update an existing TriggeredAction
26+
newDocIds.push(oldValue._id)
27+
bulkOps.push({
28+
updateOne: {
29+
filter: {
30+
_id: oldValue._id,
31+
},
32+
update: {
33+
$set: {
34+
_rank: newTriggeredAction._rank,
35+
name: newTriggeredAction.name,
36+
'triggersWithOverrides.defaults': newTriggeredAction.triggers,
37+
'actionsWithOverrides.defaults': newTriggeredAction.actions,
38+
},
39+
},
40+
},
41+
})
42+
} else {
43+
// Insert a new TriggeredAction
44+
const newDocId = getRandomId<TriggeredActionId>()
45+
newDocIds.push(newDocId)
46+
bulkOps.push({
47+
insertOne: {
48+
document: literal<Complete<DBTriggeredActions>>({
49+
_id: newDocId,
50+
_rank: newTriggeredAction._rank,
51+
name: newTriggeredAction.name,
52+
showStyleBaseId: showStyleBaseId,
53+
blueprintUniqueId: newTriggeredAction._id,
54+
triggersWithOverrides: wrapDefaultObject(newTriggeredAction.triggers),
55+
actionsWithOverrides: wrapDefaultObject(newTriggeredAction.actions),
56+
styleClassNames: newTriggeredAction.styleClassNames,
57+
}),
58+
},
59+
})
60+
}
61+
}
62+
63+
// Remove any removed TriggeredAction
64+
// Future: should this orphan them or something? Will that cause issues if they get re-added?
65+
bulkOps.push({
66+
deleteMany: {
67+
filter: {
68+
showStyleBaseId: showStyleBaseId,
69+
blueprintUniqueId: { $ne: null },
70+
_id: { $nin: newDocIds },
71+
},
72+
},
73+
})
74+
75+
await TriggeredActions.bulkWriteAsync(bulkOps)
76+
}

meteor/server/migration/upgrades/showStyleBase.ts

Lines changed: 7 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,21 @@ import {
33
JSONBlobParse,
44
ShowStyleBlueprintManifest,
55
} from '@sofie-automation/blueprints-integration'
6-
import { ShowStyleBaseId, TriggeredActionId } from '@sofie-automation/corelib/dist/dataModel/Ids'
7-
import { normalizeArray, normalizeArrayToMap, getRandomId, literal, Complete } from '@sofie-automation/corelib/dist/lib'
8-
import {
9-
applyAndValidateOverrides,
10-
wrapDefaultObject,
11-
} from '@sofie-automation/corelib/dist/settings/objectWithOverrides'
6+
import { ShowStyleBaseId } from '@sofie-automation/corelib/dist/dataModel/Ids'
7+
import { normalizeArray } from '@sofie-automation/corelib/dist/lib'
8+
import { applyAndValidateOverrides } from '@sofie-automation/corelib/dist/settings/objectWithOverrides'
129
import { wrapTranslatableMessageFromBlueprints } from '@sofie-automation/corelib/dist/TranslatableMessage'
1310
import { BlueprintValidateConfigForStudioResult } from '@sofie-automation/corelib/dist/worker/studio'
1411
import { Meteor } from 'meteor/meteor'
15-
import { Blueprints, ShowStyleBases, TriggeredActions } from '../../collections'
12+
import { Blueprints, ShowStyleBases } from '../../collections'
1613
import { DBShowStyleBase } from '@sofie-automation/corelib/dist/dataModel/ShowStyleBase'
17-
import { DBTriggeredActions } from '@sofie-automation/meteor-lib/dist/collections/TriggeredActions'
1814
import { evalBlueprint } from '../../api/blueprints/cache'
1915
import { logger } from '../../logging'
2016
import { CommonContext } from './context'
21-
import type { AnyBulkWriteOperation } from 'mongodb'
2217
import { FixUpBlueprintConfigContext } from '@sofie-automation/corelib/dist/fixUpBlueprintConfig/context'
2318
import { Blueprint } from '@sofie-automation/corelib/dist/dataModel/Blueprint'
2419
import { BlueprintFixUpConfigMessage } from '@sofie-automation/meteor-lib/dist/api/migration'
20+
import { updateTriggeredActionsForShowStyleBaseId } from './lib'
2521

2622
export async function fixupConfigForShowStyleBase(
2723
showStyleBaseId: ShowStyleBaseId
@@ -100,7 +96,7 @@ export async function validateConfigForShowStyleBase(
10096
throwIfNeedsFixupConfigRunning(showStyleBase, blueprint, blueprintManifest)
10197

10298
const blueprintContext = new CommonContext(
103-
'applyConfig',
99+
'validateConfig',
104100
`showStyleBase:${showStyleBaseId},blueprint:${blueprint._id}`
105101
)
106102
const rawBlueprintConfig = applyAndValidateOverrides(showStyleBase.blueprintConfigWithOverrides).obj
@@ -146,69 +142,7 @@ export async function runUpgradeForShowStyleBase(showStyleBaseId: ShowStyleBaseI
146142
},
147143
})
148144

149-
const oldTriggeredActionsArray = await TriggeredActions.findFetchAsync({
150-
showStyleBaseId: showStyleBaseId,
151-
blueprintUniqueId: { $ne: null },
152-
})
153-
const oldTriggeredActions = normalizeArrayToMap(oldTriggeredActionsArray, 'blueprintUniqueId')
154-
155-
const newDocIds: TriggeredActionId[] = []
156-
const bulkOps: AnyBulkWriteOperation<DBTriggeredActions>[] = []
157-
158-
for (const newTriggeredAction of result.triggeredActions) {
159-
const oldValue = oldTriggeredActions.get(newTriggeredAction._id)
160-
if (oldValue) {
161-
// Update an existing TriggeredAction
162-
newDocIds.push(oldValue._id)
163-
bulkOps.push({
164-
updateOne: {
165-
filter: {
166-
_id: oldValue._id,
167-
},
168-
update: {
169-
$set: {
170-
_rank: newTriggeredAction._rank,
171-
name: newTriggeredAction.name,
172-
'triggersWithOverrides.defaults': newTriggeredAction.triggers,
173-
'actionsWithOverrides.defaults': newTriggeredAction.actions,
174-
},
175-
},
176-
},
177-
})
178-
} else {
179-
// Insert a new TriggeredAction
180-
const newDocId = getRandomId<TriggeredActionId>()
181-
newDocIds.push(newDocId)
182-
bulkOps.push({
183-
insertOne: {
184-
document: literal<Complete<DBTriggeredActions>>({
185-
_id: newDocId,
186-
_rank: newTriggeredAction._rank,
187-
name: newTriggeredAction.name,
188-
showStyleBaseId: showStyleBaseId,
189-
blueprintUniqueId: newTriggeredAction._id,
190-
triggersWithOverrides: wrapDefaultObject(newTriggeredAction.triggers),
191-
actionsWithOverrides: wrapDefaultObject(newTriggeredAction.actions),
192-
styleClassNames: newTriggeredAction.styleClassNames,
193-
}),
194-
},
195-
})
196-
}
197-
}
198-
199-
// Remove any removed TriggeredAction
200-
// Future: should this orphan them or something? Will that cause issues if they get re-added?
201-
bulkOps.push({
202-
deleteMany: {
203-
filter: {
204-
showStyleBaseId: showStyleBaseId,
205-
blueprintUniqueId: { $ne: null },
206-
_id: { $nin: newDocIds },
207-
},
208-
},
209-
})
210-
211-
await TriggeredActions.bulkWriteAsync(bulkOps)
145+
await updateTriggeredActionsForShowStyleBaseId(showStyleBaseId, result.triggeredActions)
212146
}
213147

214148
async function loadShowStyleAndBlueprint(showStyleBaseId: ShowStyleBaseId) {
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { Meteor } from 'meteor/meteor'
2+
import { getCoreSystemAsync } from '../../coreSystem/collection'
3+
import { logger } from '../../logging'
4+
import { Blueprints, CoreSystem } from '../../collections'
5+
import { BlueprintManifestType, SystemBlueprintManifest } from '@sofie-automation/blueprints-integration'
6+
import { evalBlueprint } from '../../api/blueprints/cache'
7+
import { CommonContext } from './context'
8+
import { updateTriggeredActionsForShowStyleBaseId } from './lib'
9+
import { SYSTEM_ID } from '@sofie-automation/meteor-lib/dist/collections/CoreSystem'
10+
11+
export async function runUpgradeForCoreSystem(): Promise<void> {
12+
logger.info(`Running upgrade for CoreSystem`)
13+
14+
const { coreSystem, blueprint, blueprintManifest } = await loadCoreSystemAndBlueprint()
15+
16+
if (typeof blueprintManifest.applyConfig !== 'function')
17+
throw new Meteor.Error(500, 'Blueprint does not support this config flow')
18+
19+
const blueprintContext = new CommonContext(
20+
'applyConfig',
21+
`coreSystem:${coreSystem._id},blueprint:${blueprint.blueprintId}`
22+
)
23+
24+
const result = blueprintManifest.applyConfig(blueprintContext)
25+
26+
await CoreSystem.updateAsync(SYSTEM_ID, {
27+
$set: {
28+
// 'sourceLayersWithOverrides.defaults': normalizeArray(result.sourceLayers, '_id'),
29+
// 'outputLayersWithOverrides.defaults': normalizeArray(result.outputLayers, '_id'),
30+
lastBlueprintConfig: {
31+
blueprintHash: blueprint.blueprintHash,
32+
blueprintId: blueprint._id,
33+
},
34+
},
35+
})
36+
37+
await updateTriggeredActionsForShowStyleBaseId(null, result.triggeredActions)
38+
}
39+
40+
async function loadCoreSystemAndBlueprint() {
41+
const coreSystem = await getCoreSystemAsync()
42+
if (!coreSystem) throw new Meteor.Error(404, `CoreSystem not found!`)
43+
44+
// if (!showStyleBase.blueprintConfigPresetId) throw new Meteor.Error(500, 'ShowStyleBase is missing config preset')
45+
46+
const blueprint = coreSystem.blueprintId
47+
? await Blueprints.findOneAsync({
48+
_id: coreSystem.blueprintId,
49+
blueprintType: BlueprintManifestType.SYSTEM,
50+
})
51+
: undefined
52+
if (!blueprint) throw new Meteor.Error(404, `Blueprint "${coreSystem.blueprintId}" not found!`)
53+
54+
if (!blueprint.blueprintHash) throw new Meteor.Error(500, 'Blueprint is not valid')
55+
56+
const blueprintManifest = evalBlueprint(blueprint) as SystemBlueprintManifest
57+
58+
return {
59+
coreSystem,
60+
blueprint,
61+
blueprintManifest,
62+
}
63+
}
Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,45 @@
1+
import type { IBlueprintTriggeredActions } from '../triggers'
2+
import type { ICommonContext } from '../context'
13
import type { MigrationStepSystem } from '../migrations'
24
import type { BlueprintManifestBase, BlueprintManifestType } from './base'
35

46
export interface SystemBlueprintManifest extends BlueprintManifestBase {
57
blueprintType: BlueprintManifestType.SYSTEM
68

7-
/** A list of Migration steps related to the Core system */
9+
/** A list of Migration steps related to the Core system
10+
* @deprecated This has been replaced with `validateConfig` and `applyConfig`
11+
*/
812
coreMigrations: MigrationStepSystem[]
913

1014
/** Translations connected to the studio (as stringified JSON) */
1115
translations?: string
16+
17+
// /**
18+
// * Apply automatic upgrades to the structure of user specified config overrides
19+
// * This lets you apply various changes to the user's values in an abstract way
20+
// */
21+
// fixUpConfig?: (context: IFixUpConfigContext<TRawConfig>) => void
22+
23+
// /**
24+
// * Validate the config passed to this blueprint
25+
// * In this you should do various sanity checks of the config and return a list of messages to display to the user.
26+
// * These messages do not stop `applyConfig` from being called.
27+
// */
28+
// validateConfig?: (context: ICommonContext, config: TRawConfig) => Array<IConfigMessage>
29+
30+
/**
31+
* Apply the config by generating the data to be saved into the db.
32+
* This should be written to give a predictable and stable result, it can be called with the same config multiple times
33+
*/
34+
applyConfig?: (
35+
context: ICommonContext
36+
// config: TRawConfig,
37+
) => BlueprintResultApplySystemConfig
38+
}
39+
40+
export interface BlueprintResultApplySystemConfig {
41+
// sourceLayers: ISourceLayer[]
42+
// outputLayers: IOutputLayer[]
43+
44+
triggeredActions: IBlueprintTriggeredActions[]
1245
}

packages/meteor-lib/src/collections/CoreSystem.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { LastBlueprintConfig } from '@sofie-automation/corelib/dist/dataModel/Blueprint'
12
import { LogLevel } from '../lib'
23
import { CoreSystemId, BlueprintId } from '@sofie-automation/corelib/dist/dataModel/Ids'
34
import { protectString } from '@sofie-automation/corelib/dist/protectedString'
@@ -107,6 +108,12 @@ export interface ICoreSystem {
107108
}
108109

109110
logo?: SofieLogo
111+
112+
/** Details on the last blueprint used to generate the defaults values for this
113+
* Note: This doesn't currently have any 'config' which it relates to.
114+
* The name is to be consistent with studio/showstyle, and in preparation for their being config/configpresets used here
115+
*/
116+
lastBlueprintConfig: Omit<LastBlueprintConfig, 'blueprintConfigPresetId' | 'config'> | undefined
110117
}
111118

112119
/** In the beginning, there was the database, and the database was with Sofie, and the database was Sofie.

0 commit comments

Comments
 (0)