Skip to content

Commit c807053

Browse files
committed
SOFIE-261 | add http rest api for t timers
1 parent 3c30f2f commit c807053

22 files changed

Lines changed: 1267 additions & 65 deletions

File tree

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { registerRoutes } from '../playlists'
2+
import { ClientAPI } from '@sofie-automation/meteor-lib/dist/api/client'
3+
import { protectString } from '@sofie-automation/corelib/dist/protectedString'
4+
import { PlaylistsRestAPI } from '../../../../lib/rest/v1'
5+
6+
describe('Playlists REST API Routes', () => {
7+
let mockRegisterRoute: jest.Mock
8+
let mockServerAPI: jest.Mocked<PlaylistsRestAPI>
9+
10+
beforeEach(() => {
11+
mockRegisterRoute = jest.fn()
12+
mockServerAPI = {
13+
tTimerStartCountdown: jest.fn().mockResolvedValue(ClientAPI.responseSuccess(undefined)),
14+
tTimerStartFreeRun: jest.fn().mockResolvedValue(ClientAPI.responseSuccess(undefined)),
15+
tTimerPause: jest.fn().mockResolvedValue(ClientAPI.responseSuccess(undefined)),
16+
tTimerResume: jest.fn().mockResolvedValue(ClientAPI.responseSuccess(undefined)),
17+
tTimerRestart: jest.fn().mockResolvedValue(ClientAPI.responseSuccess(undefined)),
18+
} as any
19+
20+
registerRoutes(mockRegisterRoute)
21+
})
22+
23+
test('should register T-timer countdown route', () => {
24+
const countdownRoute = mockRegisterRoute.mock.calls.find(
25+
(call) => call[1] === '/playlists/:playlistId/t-timers/:timerIndex/countdown'
26+
)
27+
expect(countdownRoute).toBeDefined()
28+
expect(countdownRoute[0]).toBe('post')
29+
})
30+
31+
test('T-timer countdown handler should call serverAPI.tTimerStartCountdown', async () => {
32+
const countdownRoute = mockRegisterRoute.mock.calls.find(
33+
(call) => call[1] === '/playlists/:playlistId/t-timers/:timerIndex/countdown'
34+
)
35+
const handler = countdownRoute[4]
36+
37+
const params = { playlistId: 'playlist0', timerIndex: '1' }
38+
const body = { duration: 60, stopAtZero: true, startPaused: false }
39+
const connection = {} as any
40+
const event = 'test-event'
41+
42+
await handler(mockServerAPI, connection, event, params, body)
43+
44+
expect(mockServerAPI.tTimerStartCountdown).toHaveBeenCalledWith(
45+
connection,
46+
event,
47+
protectString('playlist0'),
48+
1,
49+
60,
50+
true,
51+
false
52+
)
53+
})
54+
55+
test('should register T-timer pause route', () => {
56+
const pauseRoute = mockRegisterRoute.mock.calls.find(
57+
(call) => call[1] === '/playlists/:playlistId/t-timers/:timerIndex/pause'
58+
)
59+
expect(pauseRoute).toBeDefined()
60+
expect(pauseRoute[0]).toBe('post')
61+
})
62+
63+
test('T-timer pause handler should call serverAPI.tTimerPause', async () => {
64+
const pauseRoute = mockRegisterRoute.mock.calls.find(
65+
(call) => call[1] === '/playlists/:playlistId/t-timers/:timerIndex/pause'
66+
)
67+
const handler = pauseRoute[4]
68+
69+
const params = { playlistId: 'playlist0', timerIndex: '2' }
70+
const connection = {} as any
71+
const event = 'test-event'
72+
73+
await handler(mockServerAPI, connection, event, params, {})
74+
75+
expect(mockServerAPI.tTimerPause).toHaveBeenCalledWith(connection, event, protectString('playlist0'), 2)
76+
})
77+
})

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

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
RundownPlaylistId,
1414
SegmentId,
1515
} from '@sofie-automation/corelib/dist/dataModel/Ids'
16+
import { RundownTTimerIndex } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist'
1617
import { Match, check } from '../../../lib/check'
1718
import { PlaylistsRestAPI } from '../../../lib/rest/v1'
1819
import { Meteor } from 'meteor/meteor'
@@ -544,6 +545,133 @@ class PlaylistsServerAPI implements PlaylistsRestAPI {
544545
}
545546
)
546547
}
548+
549+
async tTimerStartCountdown(
550+
connection: Meteor.Connection,
551+
event: string,
552+
rundownPlaylistId: RundownPlaylistId,
553+
timerIndex: RundownTTimerIndex,
554+
duration: number,
555+
stopAtZero?: boolean,
556+
startPaused?: boolean
557+
): Promise<ClientAPI.ClientResponse<void>> {
558+
return ServerClientAPI.runUserActionInLogForPlaylistOnWorker(
559+
this.context.getMethodContext(connection),
560+
event,
561+
getCurrentTime(),
562+
rundownPlaylistId,
563+
() => {
564+
check(rundownPlaylistId, String)
565+
check(timerIndex, Number)
566+
check(duration, Number)
567+
check(stopAtZero, Match.Optional(Boolean))
568+
check(startPaused, Match.Optional(Boolean))
569+
},
570+
StudioJobs.TTimerStartCountdown,
571+
{
572+
playlistId: rundownPlaylistId,
573+
timerIndex,
574+
duration,
575+
stopAtZero: !!stopAtZero,
576+
startPaused: !!startPaused,
577+
}
578+
)
579+
}
580+
581+
async tTimerStartFreeRun(
582+
connection: Meteor.Connection,
583+
event: string,
584+
rundownPlaylistId: RundownPlaylistId,
585+
timerIndex: RundownTTimerIndex,
586+
startPaused?: boolean
587+
): Promise<ClientAPI.ClientResponse<void>> {
588+
return ServerClientAPI.runUserActionInLogForPlaylistOnWorker(
589+
this.context.getMethodContext(connection),
590+
event,
591+
getCurrentTime(),
592+
rundownPlaylistId,
593+
() => {
594+
check(rundownPlaylistId, String)
595+
check(timerIndex, Number)
596+
check(startPaused, Match.Optional(Boolean))
597+
},
598+
StudioJobs.TTimerStartFreeRun,
599+
{
600+
playlistId: rundownPlaylistId,
601+
timerIndex,
602+
startPaused: !!startPaused,
603+
}
604+
)
605+
}
606+
607+
async tTimerPause(
608+
connection: Meteor.Connection,
609+
event: string,
610+
rundownPlaylistId: RundownPlaylistId,
611+
timerIndex: RundownTTimerIndex
612+
): Promise<ClientAPI.ClientResponse<void>> {
613+
return ServerClientAPI.runUserActionInLogForPlaylistOnWorker(
614+
this.context.getMethodContext(connection),
615+
event,
616+
getCurrentTime(),
617+
rundownPlaylistId,
618+
() => {
619+
check(rundownPlaylistId, String)
620+
check(timerIndex, Number)
621+
},
622+
StudioJobs.TTimerPause,
623+
{
624+
playlistId: rundownPlaylistId,
625+
timerIndex,
626+
}
627+
)
628+
}
629+
630+
async tTimerResume(
631+
connection: Meteor.Connection,
632+
event: string,
633+
rundownPlaylistId: RundownPlaylistId,
634+
timerIndex: RundownTTimerIndex
635+
): Promise<ClientAPI.ClientResponse<void>> {
636+
return ServerClientAPI.runUserActionInLogForPlaylistOnWorker(
637+
this.context.getMethodContext(connection),
638+
event,
639+
getCurrentTime(),
640+
rundownPlaylistId,
641+
() => {
642+
check(rundownPlaylistId, String)
643+
check(timerIndex, Number)
644+
},
645+
StudioJobs.TTimerResume,
646+
{
647+
playlistId: rundownPlaylistId,
648+
timerIndex,
649+
}
650+
)
651+
}
652+
653+
async tTimerRestart(
654+
connection: Meteor.Connection,
655+
event: string,
656+
rundownPlaylistId: RundownPlaylistId,
657+
timerIndex: RundownTTimerIndex
658+
): Promise<ClientAPI.ClientResponse<void>> {
659+
return ServerClientAPI.runUserActionInLogForPlaylistOnWorker(
660+
this.context.getMethodContext(connection),
661+
event,
662+
getCurrentTime(),
663+
rundownPlaylistId,
664+
() => {
665+
check(rundownPlaylistId, String)
666+
check(timerIndex, Number)
667+
},
668+
StudioJobs.TTimerRestart,
669+
{
670+
playlistId: rundownPlaylistId,
671+
timerIndex,
672+
}
673+
)
674+
}
547675
}
548676

549677
class PlaylistsAPIFactory implements APIFactory<PlaylistsRestAPI> {
@@ -877,4 +1005,102 @@ export function registerRoutes(registerRoute: APIRegisterHook<PlaylistsRestAPI>)
8771005
return await serverAPI.recallStickyPiece(connection, event, playlistId, sourceLayerId)
8781006
}
8791007
)
1008+
1009+
registerRoute<
1010+
{ playlistId: string; timerIndex: string },
1011+
{ duration: number; stopAtZero?: boolean; startPaused?: boolean },
1012+
void
1013+
>(
1014+
'post',
1015+
'/playlists/:playlistId/t-timers/:timerIndex/countdown',
1016+
new Map([[404, [UserErrorMessage.RundownPlaylistNotFound]]]),
1017+
playlistsAPIFactory,
1018+
async (serverAPI, connection, event, params, body) => {
1019+
const rundownPlaylistId = protectString<RundownPlaylistId>(params.playlistId)
1020+
const timerIndex = Number.parseInt(params.timerIndex) as RundownTTimerIndex
1021+
logger.info(`API POST: t-timer countdown ${rundownPlaylistId} ${timerIndex}`)
1022+
1023+
check(rundownPlaylistId, String)
1024+
check(timerIndex, Number)
1025+
return await serverAPI.tTimerStartCountdown(
1026+
connection,
1027+
event,
1028+
rundownPlaylistId,
1029+
timerIndex,
1030+
body.duration,
1031+
body.stopAtZero,
1032+
body.startPaused
1033+
)
1034+
}
1035+
)
1036+
1037+
registerRoute<{ playlistId: string; timerIndex: string }, { startPaused?: boolean }, void>(
1038+
'post',
1039+
'/playlists/:playlistId/t-timers/:timerIndex/free-run',
1040+
new Map([[404, [UserErrorMessage.RundownPlaylistNotFound]]]),
1041+
playlistsAPIFactory,
1042+
async (serverAPI, connection, event, params, body) => {
1043+
const rundownPlaylistId = protectString<RundownPlaylistId>(params.playlistId)
1044+
const timerIndex = Number.parseInt(params.timerIndex) as RundownTTimerIndex
1045+
logger.info(`API POST: t-timer free-run ${rundownPlaylistId} ${timerIndex}`)
1046+
1047+
check(rundownPlaylistId, String)
1048+
check(timerIndex, Number)
1049+
return await serverAPI.tTimerStartFreeRun(
1050+
connection,
1051+
event,
1052+
rundownPlaylistId,
1053+
timerIndex,
1054+
body.startPaused
1055+
)
1056+
}
1057+
)
1058+
1059+
registerRoute<{ playlistId: string; timerIndex: string }, never, void>(
1060+
'post',
1061+
'/playlists/:playlistId/t-timers/:timerIndex/pause',
1062+
new Map([[404, [UserErrorMessage.RundownPlaylistNotFound]]]),
1063+
playlistsAPIFactory,
1064+
async (serverAPI, connection, event, params, _) => {
1065+
const rundownPlaylistId = protectString<RundownPlaylistId>(params.playlistId)
1066+
const timerIndex = Number.parseInt(params.timerIndex) as RundownTTimerIndex
1067+
logger.info(`API POST: t-timer pause ${rundownPlaylistId} ${timerIndex}`)
1068+
1069+
check(rundownPlaylistId, String)
1070+
check(timerIndex, Number)
1071+
return await serverAPI.tTimerPause(connection, event, rundownPlaylistId, timerIndex)
1072+
}
1073+
)
1074+
1075+
registerRoute<{ playlistId: string; timerIndex: string }, never, void>(
1076+
'post',
1077+
'/playlists/:playlistId/t-timers/:timerIndex/resume',
1078+
new Map([[404, [UserErrorMessage.RundownPlaylistNotFound]]]),
1079+
playlistsAPIFactory,
1080+
async (serverAPI, connection, event, params, _) => {
1081+
const rundownPlaylistId = protectString<RundownPlaylistId>(params.playlistId)
1082+
const timerIndex = Number.parseInt(params.timerIndex) as RundownTTimerIndex
1083+
logger.info(`API POST: t-timer resume ${rundownPlaylistId} ${timerIndex}`)
1084+
1085+
check(rundownPlaylistId, String)
1086+
check(timerIndex, Number)
1087+
return await serverAPI.tTimerResume(connection, event, rundownPlaylistId, timerIndex)
1088+
}
1089+
)
1090+
1091+
registerRoute<{ playlistId: string; timerIndex: string }, never, void>(
1092+
'post',
1093+
'/playlists/:playlistId/t-timers/:timerIndex/restart',
1094+
new Map([[404, [UserErrorMessage.RundownPlaylistNotFound]]]),
1095+
playlistsAPIFactory,
1096+
async (serverAPI, connection, event, params, _) => {
1097+
const rundownPlaylistId = protectString<RundownPlaylistId>(params.playlistId)
1098+
const timerIndex = Number.parseInt(params.timerIndex) as RundownTTimerIndex
1099+
logger.info(`API POST: t-timer restart ${rundownPlaylistId} ${timerIndex}`)
1100+
1101+
check(rundownPlaylistId, String)
1102+
check(timerIndex, Number)
1103+
return await serverAPI.tTimerRestart(connection, event, rundownPlaylistId, timerIndex)
1104+
}
1105+
)
8801106
}

0 commit comments

Comments
 (0)