Skip to content

feat: add activate-adlib-testing API endpoint#1636

Open
imaretic wants to merge 1 commit intoSofie-Automation:mainfrom
evs-broadcast:contribute/user-actions-adlib-testing-mode
Open

feat: add activate-adlib-testing API endpoint#1636
imaretic wants to merge 1 commit intoSofie-Automation:mainfrom
evs-broadcast:contribute/user-actions-adlib-testing-mode

Conversation

@imaretic
Copy link
Contributor

@imaretic imaretic commented Feb 5, 2026

About the Contributor

This PR is posted on behalf of EVS Broadcast Equipment.

Type of Contribution

This is a: Feature

Current Behavior

There is no API endpoint to activate adlib testing mode.

New Behavior

A new endpoint for user actions has been added. Adlib testing mode can now be activated by sending a POST request to /playlists/{playlistId}/rundowns/{rundownId}/activate-adlib-testing.

Testing

  • I have added one or more unit tests for this PR
  • I have updated the relevant unit tests
  • No unit test changes are needed for this PR

Affected areas

User actions API.

Status

  • PR is ready to be reviewed.
  • The functionality has been tested by the author.
  • Relevant unit tests has been added / updated.
  • Relevant documentation (code comments, system documentation) has been added / updated.

Summary

This PR introduces a new API endpoint to activate ad-lib testing mode for a rundown within a playlist.

Changes Overview

API Implementation (meteor/server/api/rest/v1/playlists.ts):

  • Added activateAdLibTesting method to PlaylistsServerAPI that validates the rundownPlaylistId and rundownId parameters and dispatches StudioJobs.ActivateAdlibTesting with the provided payload
  • Registered new PUT route /playlists/:playlistId/rundowns/:rundownId/activate-adlib-testing to handle requests

API Interface (meteor/server/lib/rest/v1/playlists.ts):

  • Declared public method signature for activateAdLibTesting returning Promise<ClientAPI.ClientResponse<void>>

OpenAPI Specification:

  • Added path reference in packages/openapi/api/actions.yaml
  • Defined complete endpoint specification in packages/openapi/api/definitions/playlists.yaml with:
    • PUT method with operationId activateAdlibTesting
    • Required path parameters: playlistId and rundownId (both strings)
    • Responses for success (200), not found (404), and server error (500)

Testing (packages/openapi/src/__tests__/playlists.spec.ts):

  • Added unit test verifying the endpoint can activate ad-lib testing mode and returns a 200 status response

Impact

The new endpoint enables users to activate ad-lib testing mode for a specific rundown within a playlist via a PUT request, extending the User actions API surface.

@imaretic imaretic requested a review from a team as a code owner February 5, 2026 16:06
@coderabbitai
Copy link

coderabbitai bot commented Feb 5, 2026

Walkthrough

A new API endpoint is added to activate AdLib testing mode for a playlist/rundown combination, including server implementation, REST API interface, OpenAPI specification documentation, and test coverage. The implementation validates inputs and dispatches a StudioJobs action.

Changes

Cohort / File(s) Summary
Server API Implementation
meteor/server/api/rest/v1/playlists.ts, meteor/server/lib/rest/v1/playlists.ts
Added activateAdLibTesting method to handle PUT requests for activating AdLib testing mode, including input validation and StudioJobs dispatch in the implementation layer, with corresponding method declaration in the REST API interface.
OpenAPI Specification
packages/openapi/api/actions.yaml, packages/openapi/api/definitions/playlists.yaml
Added reference to new endpoint in actions registry and full endpoint definition (PUT method) with required path parameters (playlistId, rundownId) and standard HTTP response codes (200, 404, 500).
Test Coverage
packages/openapi/src/__tests__/playlists.spec.ts
Added test case validating the new activateAdlibTesting API method with successful 200 response assertion.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A testing mode for ad-libs so fine,
With playlists and rundowns in perfect line,
New endpoints spring forth with validation sound,
OpenAPI specs and tests abound!
Hopping through code, this feature's divine! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding a new API endpoint for activating adlib testing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Feb 5, 2026

Codecov Report

❌ Patch coverage is 0% with 53 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
meteor/server/api/rest/v1/playlists.ts 0.00% 43 Missing ⚠️
meteor/server/lib/rest/v1/playlists.ts 0.00% 10 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@meteor/server/api/rest/v1/playlists.ts`:
- Line 623: The log message emitted by logger.info when activating AdLib testing
mode contains a stray closing brace in the template literal; fix the string in
the logger.info call so it reads `API PUT: activate AdLib testing mode, playlist
${rundownPlaylistId}, rundown ${rundownId}` (remove the extra `}`) in the
function where logger.info is called, ensuring the template variables
rundownPlaylistId and rundownId remain unchanged.
- Around line 625-626: The handler is only validating rundownPlaylistId but not
rundownId; add an explicit validation check(rundownId, String) before calling
serverAPI.activateAdLibTesting to mirror other routes and the implementation
(which also checks rundownId at line 94). Locate the route handler that calls
serverAPI.activateAdLibTesting(connection, event, rundownPlaylistId, rundownId)
and insert the check for rundownId immediately after the existing
check(rundownPlaylistId, String) so invalid inputs are rejected early and
consistent with the pattern used elsewhere.

In `@packages/openapi/src/__tests__/playlists.spec.ts`:
- Around line 65-71: The test uses a hardcoded rundownId and should be gated by
the test harness; wrap the test that calls playlistsApi.activateAdlibTesting
(which uses playlistIds[0] and the rundownId path param) inside an if
(testServer) block so it only runs when test-specific data exists, and add an
else branch with test.todo() to document the dependency in non-test
environments; update the surrounding test scope accordingly to mirror the
pattern used by setNextSegment / executeAdLib.
🧹 Nitpick comments (3)
packages/openapi/api/definitions/playlists.yaml (1)

92-117: Consider documenting additional error responses.

The endpoint currently documents 404 for playlistNotFound, but the rundownId parameter validation could also fail. Consider:

  1. The error mapping in the route handler (Lines 615-618 in meteor/server/api/rest/v1/playlists.ts) includes UserErrorMessage.RundownAlreadyActive for 412, but this isn't documented here.
  2. If an invalid rundownId is provided (e.g., doesn't belong to the playlist), that error case isn't documented.

Aligning the OpenAPI spec with the actual error responses improves API discoverability for consumers.

meteor/server/lib/rest/v1/playlists.ts (1)

48-56: JSDoc could be more complete for consistency.

The JSDoc for activateAdLibTesting is minimal compared to other methods in the interface. Consider adding parameter descriptions and expected error conditions for consistency:

📝 Suggested JSDoc enhancement
-	/**
-	 * Activates AdLibs testing mode.
-	 */
+	/**
+	 * Activates AdLibs testing mode.
+	 *
+	 * Throws if there is already an active Playlist for the studio that the Playlist belongs to.
+	 * `@param` connection Connection data including client and header details
+	 * `@param` event User event string
+	 * `@param` rundownPlaylistId Playlist to activate testing mode for.
+	 * `@param` rundownId Rundown to activate testing mode for.
+	 */
 	activateAdLibTesting(
meteor/server/api/rest/v1/playlists.ts (1)

612-612: Body type declaration is unused.

The route declares { rehearsal: boolean } as the body type, but the body parameter is ignored (_body). Since this endpoint doesn't require a request body (as per the OpenAPI spec which has no requestBody), consider using never for the body type to match similar endpoints like deactivate.

♻️ Suggested fix
-	registerRoute<{ playlistId: string; rundownId: string }, { rehearsal: boolean }, void>(
+	registerRoute<{ playlistId: string; rundownId: string }, never, void>(

async (serverAPI, connection, event, params, _body) => {
const rundownPlaylistId = protectString<RundownPlaylistId>(params.playlistId)
const rundownId = protectString<RundownId>(params.rundownId)
logger.info(`API PUT: activate AdLib testing mode, playlist ${rundownPlaylistId}, rundown ${rundownId}}`)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Typo in log message: extra closing brace.

Line 623 has an extra } at the end of the log message.

🐛 Fix typo
-			logger.info(`API PUT: activate AdLib testing mode, playlist ${rundownPlaylistId}, rundown ${rundownId}}`)
+			logger.info(`API PUT: activate AdLib testing mode, playlist ${rundownPlaylistId}, rundown ${rundownId}`)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.info(`API PUT: activate AdLib testing mode, playlist ${rundownPlaylistId}, rundown ${rundownId}}`)
logger.info(`API PUT: activate AdLib testing mode, playlist ${rundownPlaylistId}, rundown ${rundownId}`)
🤖 Prompt for AI Agents
In `@meteor/server/api/rest/v1/playlists.ts` at line 623, The log message emitted
by logger.info when activating AdLib testing mode contains a stray closing brace
in the template literal; fix the string in the logger.info call so it reads `API
PUT: activate AdLib testing mode, playlist ${rundownPlaylistId}, rundown
${rundownId}` (remove the extra `}`) in the function where logger.info is
called, ensuring the template variables rundownPlaylistId and rundownId remain
unchanged.

Comment on lines +625 to +626
check(rundownPlaylistId, String)
return await serverAPI.activateAdLibTesting(connection, event, rundownPlaylistId, rundownId)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing validation for rundownId parameter.

The route handler only validates rundownPlaylistId but not rundownId. Other route handlers validate all path parameters. Additionally, the implementation method at line 94 performs check(rundownId, String), so adding it here would be consistent with the pattern and provide earlier validation.

🛡️ Add rundownId validation
 			check(rundownPlaylistId, String)
+			check(rundownId, String)
 			return await serverAPI.activateAdLibTesting(connection, event, rundownPlaylistId, rundownId)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
check(rundownPlaylistId, String)
return await serverAPI.activateAdLibTesting(connection, event, rundownPlaylistId, rundownId)
check(rundownPlaylistId, String)
check(rundownId, String)
return await serverAPI.activateAdLibTesting(connection, event, rundownPlaylistId, rundownId)
🤖 Prompt for AI Agents
In `@meteor/server/api/rest/v1/playlists.ts` around lines 625 - 626, The handler
is only validating rundownPlaylistId but not rundownId; add an explicit
validation check(rundownId, String) before calling
serverAPI.activateAdLibTesting to mirror other routes and the implementation
(which also checks rundownId at line 94). Locate the route handler that calls
serverAPI.activateAdLibTesting(connection, event, rundownPlaylistId, rundownId)
and insert the check for rundownId immediately after the existing
check(rundownPlaylistId, String) so invalid inputs are rejected early and
consistent with the pattern used elsewhere.

Comment on lines +65 to +71
test('can activate adlib testing mode', async () => {
const active = await playlistsApi.activateAdlibTesting({
playlistId: playlistIds[0],
rundownId: 'rundownId',
})
expect(active.status).toBe(200)
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n packages/openapi/src/__tests__/playlists.spec.ts | head -150

Repository: Sofie-Automation/sofie-core

Length of output: 5769


🏁 Script executed:

# Search for how rundownId is retrieved or used in similar tests
rg -n "rundownId" packages/openapi/src/__tests__/ -B 3 -A 3

Repository: Sofie-Automation/sofie-core

Length of output: 649


🏁 Script executed:

# Search for rundownId patterns in the broader codebase to understand how it's typically obtained
rg -n "rundownId" packages/openapi/src/ --type typescript --type typescriptreact -B 2 -A 2 | head -100

Repository: Sofie-Automation/sofie-core

Length of output: 105


🏁 Script executed:

# Search for rundownId patterns in the codebase more broadly
rg -n "rundownId" packages/openapi/src/ -B 2 -A 2 | head -150

Repository: Sofie-Automation/sofie-core

Length of output: 486


🏁 Script executed:

# Check the API definition to understand what activateAdlibTesting expects
rg -n "activateAdlibTesting" packages/openapi/src/ -B 5 -A 5

Repository: Sofie-Automation/sofie-core

Length of output: 901


🏁 Script executed:

# Find and examine the PlaylistsApi client implementation
find packages/openapi -name "*.ts" -type f | grep -v node_modules | grep -v __tests__ | head -20

Repository: Sofie-Automation/sofie-core

Length of output: 168


🏁 Script executed:

# The test file imports from '../../client/ts/index.js', let's find that directory structure
find packages/openapi -type d -name "client" -o -type d -name "ts"

Repository: Sofie-Automation/sofie-core

Length of output: 53


🏁 Script executed:

# Let's check if there's a generated or built client directory
find packages/openapi -type f -name "*api*" | grep -i playlist | head -20

Repository: Sofie-Automation/sofie-core

Length of output: 53


🏁 Script executed:

# List the full structure of packages/openapi
find packages/openapi -type f -name "*.ts" -o -type f -name "*.js" | head -50

Repository: Sofie-Automation/sofie-core

Length of output: 667


🏁 Script executed:

# Check if there's a client directory at the root or in packages
find . -type d -name "client" | grep -v node_modules | head -10

Repository: Sofie-Automation/sofie-core

Length of output: 98


🏁 Script executed:

# Check if client directory exists or is in .gitignore
ls -la packages/openapi/ | grep -E "client|^d"
cat packages/openapi/.gitignore 2>/dev/null || echo "No .gitignore found"

Repository: Sofie-Automation/sofie-core

Length of output: 460


🏁 Script executed:

# The client directory is gitignored (likely generated). Let's check jest.setup.js and test configuration
cat packages/openapi/jest.setup.js

Repository: Sofie-Automation/sofie-core

Length of output: 808


🏁 Script executed:

# Let's check the api/actions.yaml to understand the API structure
cat packages/openapi/api/actions.yaml | grep -A 20 "activateAdlibTesting" | head -40

Repository: Sofie-Automation/sofie-core

Length of output: 1231


🏁 Script executed:

# Check the playlists.yaml definition
cat packages/openapi/api/definitions/playlists.yaml | grep -A 30 "activateAdlibTesting"

Repository: Sofie-Automation/sofie-core

Length of output: 1084


🏁 Script executed:

# Check if the playlists response includes rundown data that could be extracted
rg -n "rundown" packages/openapi/src/__tests__/playlists.spec.ts -i

Repository: Sofie-Automation/sofie-core

Length of output: 100


🏁 Script executed:

# Let's look at the full playlists response to see if rundownId is included
cat packages/openapi/api/definitions/playlists.yaml | grep -A 100 "Playlist:" | head -150

Repository: Sofie-Automation/sofie-core

Length of output: 3793


Test uses hardcoded rundownId that may not exist.

The test passes a hardcoded string 'rundownId' which is a required path parameter but unlikely to correspond to an actual rundown. This test should follow the established pattern in this file and be wrapped with if (testServer) similar to other tests that depend on specific test data (e.g., lines 107-118 for setNextSegment, lines 125-135 for executeAdLib).

Wrap this test in an if (testServer) condition, or add a complementary test.todo() block for non-test environments to clarify the dependency on test-specific data.

🤖 Prompt for AI Agents
In `@packages/openapi/src/__tests__/playlists.spec.ts` around lines 65 - 71, The
test uses a hardcoded rundownId and should be gated by the test harness; wrap
the test that calls playlistsApi.activateAdlibTesting (which uses playlistIds[0]
and the rundownId path param) inside an if (testServer) block so it only runs
when test-specific data exists, and add an else branch with test.todo() to
document the dependency in non-test environments; update the surrounding test
scope accordingly to mirror the pattern used by setNextSegment / executeAdLib.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant