Skip to content

Commit 7c90645

Browse files
committed
Merge branch 'feat/R51-live-status-gateway-on-R50' into release51
# Conflicts: # packages/live-status-gateway/api/schemas/adLibs.yaml # packages/live-status-gateway/src/collections/pieceInstancesHandler.ts # packages/live-status-gateway/src/coreHandler.ts
2 parents c5d04c6 + 8712e51 commit 7c90645

File tree

10 files changed

+90
-40
lines changed

10 files changed

+90
-40
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { stringifyError } from '@sofie-automation/shared-lib/dist/lib/stringifyError'
2+
import { UserError, UserErrorMessage } from '../error'
3+
4+
describe('UserError', () => {
5+
test('stringifyError', () => {
6+
const rawError = new Error('raw')
7+
rawError.stack = 'mock stack'
8+
const userError = UserError.from(rawError, UserErrorMessage.PartNotFound, { key: 'translatable message' })
9+
10+
expect(stringifyError(userError)).toEqual(
11+
'UserError: ' +
12+
JSON.stringify({
13+
rawError: 'Error: raw, mock stack',
14+
message: {
15+
key: 'The selected part does not exist',
16+
args: {
17+
key: 'translatable message',
18+
},
19+
},
20+
key: 25,
21+
errorCode: 500,
22+
})
23+
)
24+
25+
// serialized and restored
26+
const restored = JSON.parse(userError.toString())
27+
expect(stringifyError(restored)).toEqual('raw, mock stack')
28+
})
29+
})

packages/live-status-gateway/src/coreHandler.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,7 @@ import {
2020
PeripheralDeviceType,
2121
} from '@sofie-automation/shared-lib/dist/peripheralDevice/peripheralDeviceAPI'
2222
import { protectString } from '@sofie-automation/shared-lib/dist/lib/protectedString'
23-
import {
24-
PeripheralDeviceCommandId,
25-
PeripheralDeviceId,
26-
StudioId,
27-
} from '@sofie-automation/shared-lib/dist/core/model/Ids'
23+
import { PeripheralDeviceCommandId, StudioId } from '@sofie-automation/shared-lib/dist/core/model/Ids'
2824
import { StatusCode } from '@sofie-automation/shared-lib/dist/lib/status'
2925
import { PeripheralDeviceCommand } from '@sofie-automation/shared-lib/dist/core/model/PeripheralDeviceCommand'
3026
import { LiveStatusGatewayConfig } from './generated/options'
@@ -152,8 +148,9 @@ export class CoreHandler {
152148
}
153149
// setup observers
154150
const observer = this.core.observe(PeripheralDevicePubSubCollectionsNames.peripheralDeviceForDevice)
155-
observer.added = (id) => this.onDeviceChanged(id)
156-
observer.changed = (id) => this.onDeviceChanged(id)
151+
observer.added = () => this.onDeviceChanged()
152+
observer.changed = () => this.onDeviceChanged()
153+
this.onDeviceChanged() // set initial settings
157154
this.setupObserverForPeripheralDeviceCommands(this)
158155
}
159156
async destroy(): Promise<void> {
@@ -193,13 +190,16 @@ export class CoreHandler {
193190
this._onConnected = fcn
194191
}
195192

196-
onDeviceChanged(id: PeripheralDeviceId): void {
197-
if (id !== this.core.deviceId) return
193+
onDeviceChanged(): void {
198194
const col = this.core.getCollection(PeripheralDevicePubSubCollectionsNames.peripheralDeviceForDevice)
199195
if (!col) throw new Error('collection "peripheralDeviceForDevice" not found!')
200-
const device = col.findOne(id)
201196

202-
this.deviceSettings = device?.deviceSettings || {}
197+
const device = col.findOne(this.core.deviceId)
198+
if (!device) {
199+
throw new Error(`No "peripheralDeviceForDevice" with id "${this.core.deviceId}" found!`)
200+
}
201+
202+
this.deviceSettings = device.deviceSettings || {}
203203
const logLevel = this.deviceSettings['debugLogging'] ? 'debug' : 'info'
204204
if (logLevel !== this.logger.level) {
205205
this.logger.level = logLevel
@@ -211,7 +211,7 @@ export class CoreHandler {
211211
this.logger.info('Loglevel: ' + this.logger.level)
212212
}
213213

214-
const studioId = device?.studioId
214+
const studioId = device.studioId
215215
if (studioId === undefined) {
216216
throw new Error(`Live status gateway must be attached to a studio`)
217217
}

packages/live-status-gateway/src/topics/activePiecesTopic.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,7 @@ export class ActivePiecesTopic
6060
activePieces: [],
6161
})
6262

63-
for (const subscriber of subscribers) {
64-
this.sendMessage(subscriber, message)
65-
}
63+
this.sendMessage(subscribers, message)
6664
}
6765

6866
async update(

packages/live-status-gateway/src/topics/activePlaylistTopic.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,7 @@ export class ActivePlaylistTopic
153153
publicData: undefined,
154154
})
155155

156-
for (const subscriber of subscribers) {
157-
this.sendMessage(subscriber, message)
158-
}
156+
this.sendMessage(subscribers, message)
159157
}
160158

161159
private isDataInconsistent() {

packages/live-status-gateway/src/topics/adLibsTopic.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,7 @@ export class AdLibsTopic
236236
}
237237
: { event: 'adLibs', rundownPlaylistId: null, adLibs: [], globalAdLibs: [] }
238238

239-
for (const subscriber of subscribers) {
240-
this.sendMessage(subscriber, adLibsStatus)
241-
}
239+
this.sendMessage(subscribers, adLibsStatus)
242240
}
243241

244242
async update(

packages/live-status-gateway/src/topics/root.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export class RootChannel extends WebSocketTopicBase implements WebSocketTopic {
5353

5454
constructor(logger: Logger) {
5555
super('Root', logger)
56-
this._heartbeat = setInterval(() => this._subscribers.forEach((ws) => this.sendHeartbeat(ws)), 2000)
56+
this._heartbeat = setInterval(() => this.sendHeartbeat(this._subscribers), 2000)
5757
}
5858

5959
close(): void {

packages/live-status-gateway/src/topics/segmentsTopic.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,7 @@ export class SegmentsTopic
7575
}),
7676
}
7777

78-
for (const subscriber of subscribers) {
79-
this.sendMessage(subscriber, segmentsStatus)
80-
}
78+
this.sendMessage(subscribers, segmentsStatus)
8179
}
8280

8381
async update(source: string, data: DBRundownPlaylist | DBSegment[] | DBPart[] | undefined): Promise<void> {

packages/live-status-gateway/src/topics/studioTopic.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,8 @@ export class StudioTopic
5454
name: '',
5555
playlists: [],
5656
}
57-
for (const subscriber of subscribers) {
58-
this.sendMessage(subscriber, studioStatus)
59-
}
57+
58+
this.sendMessage(subscribers, studioStatus)
6059
}
6160

6261
async update(source: string, data: DBStudio | DBRundownPlaylist[] | undefined): Promise<void> {

packages/live-status-gateway/src/wsHandler.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,24 @@ export abstract class WebSocketTopicBase {
3434
this._logger.error(`Process ${this._name} message not expected '${JSON.stringify(msg)}'`)
3535
}
3636

37-
sendMessage(ws: WebSocket, msg: object): void {
38-
const msgStr = JSON.stringify(msg)
39-
this._logger.debug(`Send ${this._name} message '${msgStr}'`)
40-
ws.send(msgStr)
37+
sendMessage(recipients: WebSocket | Iterable<WebSocket>, msg: object): void {
38+
recipients = isIterable(recipients) ? recipients : [recipients]
39+
40+
let count = 0
41+
let msgStr = ''
42+
for (const ws of recipients) {
43+
if (!msgStr) msgStr = JSON.stringify(msg) // Optimization: only stringify if there are any recipients
44+
count++
45+
ws.send(msgStr)
46+
}
47+
this._logger.silly(`Send ${this._name} message '${msgStr}' to ${count} recipients`)
4148
}
4249

43-
sendHeartbeat(ws: WebSocket): void {
50+
sendHeartbeat(recipients: Set<WebSocket>): void {
4451
const msgStr = JSON.stringify({ event: 'heartbeat' })
45-
this._logger.silly(`Send ${this._name} message '${msgStr}'`)
46-
ws.send(msgStr)
52+
for (const ws of recipients.values()) {
53+
ws.send(msgStr)
54+
}
4755
}
4856

4957
protected logUpdateReceived(collectionName: string, source: string, extraInfo?: string): void {
@@ -165,3 +173,10 @@ export interface CollectionObserver<T> {
165173
observerName: string
166174
update(source: string, data: T | undefined): Promise<void>
167175
}
176+
function isIterable<T>(obj: T | Iterable<T>): obj is Iterable<T> {
177+
// checks for null and undefined
178+
if (obj == null) {
179+
return false
180+
}
181+
return typeof (obj as any)[Symbol.iterator] === 'function'
182+
}

packages/shared-lib/src/lib/stringifyError.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ export function stringifyError(error: unknown, noStack = false): string {
1111
// Has a custom toString() method
1212
str = `${(error as any).toString()}`
1313
} else {
14-
str = ''
15-
if ((error as Error).message) str += `${(error as Error).message} ` // Is an Error
16-
if ((error as any).reason) str += `${(error as any).reason} ` // Is a Meteor.Error
17-
if ((error as any).details) str += `${(error as any).details} `
14+
const strings: (string | undefined)[] = [
15+
stringify((error as any).rawError), // UserError
16+
stringify((error as Error).message), // Error
17+
stringify((error as any).reason), // Meteor.Error
18+
stringify((error as any).details),
19+
]
20+
str = strings.filter(Boolean).join(', ')
1821
}
1922

2023
if (!str) {
@@ -34,7 +37,7 @@ export function stringifyError(error: unknown, noStack = false): string {
3437
}
3538

3639
if (!noStack) {
37-
if (error && typeof error === 'object' && (error as any).stack) {
40+
if (error && typeof error === 'object' && typeof (error as any).stack === 'string') {
3841
str += ', ' + (error as any).stack
3942
}
4043
}
@@ -43,3 +46,15 @@ export function stringifyError(error: unknown, noStack = false): string {
4346

4447
return str
4548
}
49+
50+
function stringify(v: any): string | undefined {
51+
// Tries to stringify objects if they have a toString() that returns something sensible
52+
if (v === undefined) return undefined
53+
if (v === null) return 'null'
54+
55+
if (typeof v === 'object') {
56+
const str = `${v}`
57+
if (str !== '[object Object]') return str
58+
return undefined
59+
} else return `${v}`
60+
}

0 commit comments

Comments
 (0)