Skip to content

Forms: Add source filter to feedback REST API#48027

Open
enejb wants to merge 10 commits intotrunkfrom
fix/forms-source-filter-responses
Open

Forms: Add source filter to feedback REST API#48027
enejb wants to merge 10 commits intotrunkfrom
fix/forms-source-filter-responses

Conversation

@enejb
Copy link
Copy Markdown
Member

@enejb enejb commented Apr 9, 2026

Fixes FORMS-671

The issue that this PR is trying to fix is the following.

When a new feedback comes in from that is added via a synced form. We end up in the situation where we are not able to filter this feedback by source any more.

The main issue is that source is ID is not stored as the post parent any more. Since the post parent is now the jetpack_form.

This PR fixes this by setting the source ID on a post meta. This makes it possible to filter posts by query the post meta which gets stored when the feedback gets created. For backwards compatibility we also add the post meta when feedback that don't have it set yet. In the future versions we could remove the filtering by post_parent completely since most feedback will contain the source id in the post meta.

Proposed changes

  • Add a new source REST API query parameter to the feedback endpoints (/wp/v2/feedback and /wp/v2/feedback/counts) for filtering responses by the page/post they were submitted from.
  • Store source post ID as _feedback_source_post_id meta on new feedback submissions using add_post_meta.
  • Implement get_all_source_post_ids() on the Feedback class to populate the source filter dropdown, using meta with fallback to post_parent for legacy data (excluding jetpack_form parents).
  • Add object caching for source post ID lookups with invalidation on feedback save, delete, and backfill.
  • Lazily backfill _feedback_source_post_id meta for old feedback via maybe_backfill_source_meta(), called from prepare_item_for_response() — gradually migrates legacy data as it's served through the REST API.
  • Extract shared get_source_filter_sql() helper to avoid duplicating JOIN/WHERE SQL between get_counts() and WP_Query filter hooks.
  • Guard join_source_meta / filter_by_source_id with post_type check so they only modify feedback queries, not unrelated queries in the same request.
  • Invalidate source IDs cache when a feedback post is permanently deleted (deleted_post hook).
  • Update the JS dashboard (store, resolvers, hooks, stage views) to use the new source parameter instead of overloading parent.

Related product discussion/links

Does this pull request change what data or activity we track or use?

No. This adds a new post meta (_feedback_source_post_id) that stores the same source post ID that was previously only available via post_parent. No new tracking or external data collection.

Testing instructions

  1. Set up a site with the Jetpack Forms package.
  2. Create two pages, each with a contact form. Submit feedback from both pages.
  3. Go to the Forms dashboard (Responses view).
  4. Use the "Source" filter dropdown — verify both source pages appear.
  5. Select one source — verify only feedback from that page is shown.
  6. Check the counts (inbox/spam/trash tabs) update correctly when a source filter is active.
  7. Submit a new feedback entry — verify the source filter dropdown updates to include it.
  8. Permanently delete all feedback from one source — verify that source disappears from the dropdown after cache refresh.
  9. If you have old feedback (pre-existing before this PR), verify it still appears when filtering by source — the fallback to post_parent should handle legacy data, and it will be lazily backfilled with meta as it's served.

… by source post ID

Introduces a `source` query parameter to the feedback REST API and counts endpoint.
Uses `_feedback_source_post_id` meta with fallback to `post_parent` for legacy data.
Includes caching for source post ID lookups and cache invalidation on save/delete.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@enejb enejb added the [Status] Needs Review This PR is ready for review. label Apr 9, 2026
@enejb enejb self-assigned this Apr 9, 2026
@enejb enejb added the [Status] Needs Review This PR is ready for review. label Apr 9, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

  • To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate the Jetpack Beta plugin. Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (Jetpack), and enable the fix/forms-source-filter-responses branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack fix/forms-source-filter-responses

Interested in more tips and information?

  • In your local development environment, use the jetpack rsync command to sync your changes to a WoA dev blog.
  • Read more about our development workflow here: PCYsg-eg0-p2
  • Figure out when your changes will be shipped to customers here: PCYsg-eg5-p2

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

Thank you for your PR!

When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:

  • ✅ Include a description of your PR changes.
  • ✅ Add a "[Status]" label (In Progress, Needs Review, ...).
  • ✅ Add testing instructions.
  • ✅ Specify whether this PR includes any changes to data or privacy.
  • ✅ Add changelog entries to affected projects

This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖


Follow this PR Review Process:

  1. Ensure all required checks appearing at the bottom of this PR are passing.
  2. Make sure to test your changes on all platforms that it applies to. You're responsible for the quality of the code you ship.
  3. You can use GitHub's Reviewers functionality to request a review.
  4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

If you have questions about anything, reach out in #jetpack-developers for guidance!

@jp-launch-control
Copy link
Copy Markdown

jp-launch-control bot commented Apr 9, 2026

Code Coverage Summary

Coverage changed in 7 files. Only the first 5 are listed here.

File Coverage Δ% Δ Uncovered
projects/packages/forms/src/contact-form/class-contact-form-endpoint.php 887/1050 (84.48%) -0.57% 13 💔
projects/packages/forms/src/contact-form/class-contact-form-plugin.php 591/1518 (38.93%) -0.66% 10 💔
projects/packages/forms/src/contact-form/class-feedback.php 763/807 (94.55%) -0.08% 3 ❤️‍🩹
projects/packages/forms/src/dashboard/inbox/stage/actions.tsx 0/332 (0.00%) 0.00% 2 ❤️‍🩹
projects/packages/forms/src/dashboard/store/resolvers.js 6/29 (20.69%) -1.53% 2 ❤️‍🩹

Full summary · PHP report · JS report

If appropriate, add one of these labels to override the failing coverage check: Covered by non-unit tests Use to ignore the Code coverage requirement check when E2Es or other non-unit tests cover the code Coverage tests to be added later Use to ignore the Code coverage requirement check when tests will be added in a follow-up PR I don't care about code coverage for this PR Use this label to ignore the check for insufficient code coveage.

…meta

- Switch from update_post_meta to add_post_meta (with unique flag) for new
  feedback since the meta doesn't exist yet on a new post.
- Add maybe_backfill_source_meta() that writes the source meta for old feedback
  using the source ID resolved from the Feedback object's parsed content.
- Call the backfill in prepare_item_for_response so old feedback gradually gets
  the meta as it's served through the REST API.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@enejb
Copy link
Copy Markdown
Member Author

enejb commented Apr 9, 2026

@copilot review this PR.

@enejb enejb requested a review from a team April 9, 2026 18:15
enejb and others added 2 commits April 9, 2026 11:51
- Fix Phan error: use `= null` instead of `unset()` for declared class property
- Rewrite get_all_source_post_ids tests to mock SQL via wordbless_wpdb_query_results
  filter since WorDBless has no database
- Rewrite endpoint source filter tests to verify SQL clause generation and hook
  cleanup rather than asserting on query results
- Add query structure test to verify UNION, meta key, and post_parent fallback

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a dedicated source filter for Jetpack Forms feedback REST endpoints and updates the Forms dashboard to use it, while persisting the source post ID in post meta for reliable querying and backwards compatibility.

Changes:

  • Add source query parameter support to /wp/v2/feedback and /wp/v2/feedback/counts, including SQL filtering with meta + legacy post_parent fallback.
  • Persist _feedback_source_post_id meta on new feedback saves and add a cached Feedback::get_all_source_post_ids() for populating the “Source” dropdown.
  • Update dashboard query plumbing (store, hooks, stage routing) to use source instead of overloading parent, and add PHPUnit coverage.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
projects/packages/forms/tests/php/contact-form/Feedback_Creation_Test.php Adds tests for source meta persistence, source ID lookup query/caching, and cache invalidation on save.
projects/packages/forms/tests/php/contact-form/Contact_Form_Endpoint_Test.php Adds REST schema and SQL-behavior tests for the new source parameter.
projects/packages/forms/src/dashboard/store/resolvers.js Includes source in counts request parameters.
projects/packages/forms/src/dashboard/store/reducer.js Includes source in query cache key generation.
projects/packages/forms/src/dashboard/store/actions.js Triggers query updates when source changes.
projects/packages/forms/src/dashboard/inbox/stage/types.tsx Extends query param typing to include source.
projects/packages/forms/src/dashboard/inbox/stage/index.js Routes “Source” filter UI to source query param (not parent).
projects/packages/forms/src/dashboard/inbox/stage/actions.tsx Includes source when building count query params.
projects/packages/forms/src/dashboard/hooks/use-inbox-data.ts Adds source to feedback fetch params.
projects/packages/forms/src/contact-form/class-feedback.php Introduces source meta key, cached source ID discovery, cache invalidation hook, and meta backfill helper.
projects/packages/forms/src/contact-form/class-contact-form-endpoint.php Registers source param, applies source filtering to collection and counts queries, and triggers lazy meta backfill.
projects/packages/forms/src/class-jetpack-forms.php Hooks cache invalidation on permanent feedback deletion.
projects/packages/forms/routes/responses/stage.tsx Updates router-stage query args to use source for the “Source” filter.
projects/packages/forms/changelog/fix-forms-source-filter-responses Adds changelog entry for the new source filter parameter.

- Delete test_source_meta_key_constant_exists (coverage chaser)
- Delete test_source_filter_returns_200 (dodger, covered by other tests)
- Rewrite test_source_filter_modifies_query to test get_source_filter_sql
  directly via reflection, avoiding WorDBless WP_Query caching issues
- Rewrite test_counts_with_source to verify source filter SQL is applied
- Soften UNION assertion to only check semantic SQL content
- Add test_source_filter_with_zero_value_is_ignored edge case
- Use wordbless_wpdb_query_results consistently for SQL capture

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 9, 2026

@copilot review this PR.

Review completed above. Summary of findings (all suggestions, no blockers):

  1. get_source_filter_sql() is called twice per get_items() request — join_source_meta() and filter_by_source_id() each call it independently. Minor inefficiency.
  2. source in get_collection_params() is missing sanitize_callback/validate_callback, unlike is_unread in the same method and source in the counts route definition.
  3. Filter callbacks don't guard against non-feedback WP_Query instances — adding a post_type check would make them more defensive.
  4. get_all_source_post_ids() uses the literal 'feedback' string instead of self::POST_TYPE in its SQL query, inconsistent with the rest of the class.

Overall: well-structured, secure, and well-tested. Minor issues — can merge after addressing.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/graphql
    • Triggering command: /usr/bin/gh gh pr view --json number,title,body,headRefName,baseRefName,files,additions,deletions,author,state,isDraft (http block)
    • Triggering command: /usr/bin/gh gh pr list --head fix/forms-source-filter-responses (http block)

If you need me to access, download, or install something from one of these locations, you can either:

- Add post_type guard to join_source_meta and filter_by_source_id so they
  only modify feedback queries, preventing unintended SQL changes to
  unrelated WP_Query calls during the same request
- Invalidate source IDs cache after successful backfill in
  maybe_backfill_source_meta to keep the filter dropdown fresh

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot finished work on behalf of enejb April 9, 2026 19:13
enejb and others added 4 commits April 9, 2026 12:44
- Cache get_source_filter_sql() result so join_source_meta and
  filter_by_source_id reuse it instead of recomputing per hook call
- Add sanitize_callback and validate_callback to source param in
  get_collection_params for consistency with other params
- Replace hardcoded 'feedback' string with self::POST_TYPE in
  get_all_source_post_ids SQL query

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ReflectionMethod::setAccessible() is deprecated in PHP 8.5 and has
no effect since PHP 8.1. Remove the call to fix the CI deprecation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Only call ReflectionMethod::setAccessible() on PHP < 8.1 where it is
required. On 8.1+ it has no effect, and on 8.5+ it triggers a
deprecation notice.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants