Skip to content

fix(logging): register the operation REST filter param - #758

Merged
dkotter merged 3 commits into
WordPress:developfrom
i-anubhav-anand:fix/log-operation-rest-param
Jul 15, 2026
Merged

fix(logging): register the operation REST filter param#758
dkotter merged 3 commits into
WordPress:developfrom
i-anubhav-anand:fix/log-operation-rest-param

Conversation

@i-anubhav-anand

@i-anubhav-anand i-anubhav-anand commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

What?

Registers the operation query parameter on the AI Request Log REST endpoint (/ai/v1/logs).

operation is read and applied as a filter server-side and is sent by the Request Log admin UI, but it was the only filter parameter missing from AI_Request_Log_Controller::get_collection_params().

Why?

Because the parameter was unregistered:

  1. It bypassed the REST framework's argument validation. The repository parses it as a comma-separated string:

    // includes/Logging/AI_Request_Log_Repository.php
    $operations = array_filter( array_map( 'trim', explode( ',', $args['operation'] ) ) );

    The admin UI always sends a string (operations.join(',')), but the public endpoint accepts whatever a client sends. A non-string value reaches explode() and raises a fatal:

    GET /ai/v1/logs?operation[]=openai:completions&operation[]=google:generateContent
    → TypeError: explode(): Argument #2 ($string) must be of type string, array given
      at includes/Logging/AI_Request_Log_Repository.php:475   (HTTP 500)
    
  2. It was missing from the REST schema, so API consumers couldn't discover it via schema introspection — unlike every sibling filter (type, status, provider, search, …).

How?

Add operation to get_collection_params() as a string parameter, matching the documented comma-separated contract. WordPress then validates the type before the value reaches the repository (a non-string value is rejected with a 400 instead of fataling), and the parameter is now part of the endpoint schema.

'operation' => array(
    'description' => __( 'Filter by operation. Accepts a single value or a comma-separated list.', 'ai' ),
    'type'        => 'string',
    'default'     => '',
),

This is a one-parameter, surgical change; no behavior changes for the existing (string) filter path.

Use of AI Tools

AI assistance: Yes
Tool(s): AI coding assistant (agentic CLI)
Used for: auditing the REST controller/repository, reproducing the bug, and drafting the fix + tests. All code was written test-first, executed, and reviewed by me; I take responsibility for it.

Testing Instructions

npm run wp-env:test start
npm run test:php -- --filter AI_Request_Log_ControllerTest

Added/extended tests in tests/Integration/Includes/Logging/AI_Request_Log_ControllerTest.php:

  • test_get_collection_params_returns_expected_keys — now also asserts operation is a registered param.
  • test_get_logs_filters_by_operation — the filter returns only matching rows (baseline; passes before and after).
  • test_get_logs_rejects_non_string_operation — an array operation value is handled with a 400 rather than a fatal.

Fail-before / pass-after (source registration reverted, tests kept):

# before:
1) ...::test_get_collection_params_returns_expected_keys
Failed asserting that an array has the key 'operation'.

2) ...::test_get_logs_rejects_non_string_operation
TypeError: explode(): Argument #2 ($string) must be of type string, array given
   at includes/Logging/AI_Request_Log_Repository.php:475

# after:
OK (3 tests, 22 assertions)

Full Logging suite is green (122 tests); full integration suite is green (840 tests, 34 pre-existing skips).

Changelog Entry

Fixed - The AI Request Log REST endpoint now registers its operation filter parameter, so it appears in the REST schema and a non-string value returns a 400 instead of causing a fatal error.

Open WordPress Playground Preview
The /ai/v1/logs endpoint reads and applies an `operation` filter (sent by the Request Log admin UI as a comma-separated string and parsed with explode()), but it was the only filter parameter missing from get_collection_params(). As a result it was absent from the REST schema and bypassed the framework's argument validation, so a non-string value (e.g. operation[]=a&operation[]=b) reached explode() and raised a TypeError, returning a 500.

Register `operation` as a string collection param, matching the documented comma-separated contract. WordPress now validates the type before the value reaches the repository, and the parameter is discoverable in the REST schema like every sibling filter.
@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: i-anubhav-anand <anubhav24@git.wordpress.org>
Co-authored-by: dkotter <dkotter@git.wordpress.org>
Co-authored-by: jeffpaul <jeffpaul@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@codecov

codecov Bot commented Jun 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.44%. Comparing base (656fbde) to head (4acfb53).

Additional details and impacted files
@@              Coverage Diff              @@
##             develop     #758      +/-   ##
=============================================
+ Coverage      78.43%   78.44%   +0.01%     
  Complexity      2454     2454              
=============================================
  Files            104      104              
  Lines           9920     9925       +5     
=============================================
+ Hits            7781     7786       +5     
  Misses          2139     2139              
Flag Coverage Δ
unit 78.44% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
@i-anubhav-anand
i-anubhav-anand marked this pull request as draft June 22, 2026 17:51
@jeffpaul jeffpaul added this to the 1.2.0 milestone Jun 26, 2026
@jeffpaul jeffpaul moved this from Triage to In progress in WordPress AI Roadmap Jun 26, 2026
@jeffpaul

Copy link
Copy Markdown
Member

@i-anubhav-anand is this still in progress or can it come out of draft for review?

@jeffpaul jeffpaul modified the milestones: 1.2.0, 1.3.0 Jul 13, 2026
@jeffpaul

Copy link
Copy Markdown
Member

@i-anubhav-anand checking back to see if this is still in progress or ready for review?

@i-anubhav-anand
i-anubhav-anand marked this pull request as ready for review July 15, 2026 12:32
@i-anubhav-anand
i-anubhav-anand requested a review from a team July 15, 2026 12:32
@i-anubhav-anand

Copy link
Copy Markdown
Contributor Author

Thanks for checking in @jeffpaul — this one is code-complete, it was just left in draft. Marking it ready for review now.

For reference: the operation filter param is registered in AI_Request_Log_Controller::get_collection_params() (type string, matching the documented comma-separated contract), which both adds it to the REST schema and lets WordPress reject a non-string value with a 400 before it reaches the explode() in AI_Request_Log_Repository (previously a fatal TypeError). Coverage is green and the three tests described above cover the schema registration, the filter behaviour, and the non-string rejection path.

@dkotter
dkotter merged commit adb9afa into WordPress:develop Jul 15, 2026
24 checks passed
@github-project-automation github-project-automation Bot moved this from In progress to Done in WordPress AI Roadmap Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants