Skip to content

Hook up filters to API where availiable#1712

Merged
Mbeaulne merged 1 commit intomasterfrom
01-27-hook_up_filters_to_api_where_availiable
Feb 26, 2026
Merged

Hook up filters to API where availiable#1712
Mbeaulne merged 1 commit intomasterfrom
01-27-hook_up_filters_to_api_where_availiable

Conversation

@Mbeaulne
Copy link
Collaborator

@Mbeaulne Mbeaulne commented Jan 27, 2026

Description

Implemented support for advanced pipeline run filters with a new JSON-based filter format while maintaining backward compatibility with the legacy key:value format. This change enables the pipeline run filters bar feature flag to be more than just a UI preview.

Type of Change

  • New feature
  • Improvement
  • Refactor

Checklist

  • I have tested this does not break current pipelines / runs functionality
  • I have tested the changes on staging

Test Instructions

  1. Enable the "pipeline-run-filters-bar" feature flag
  2. Verify that the "Created by" filter works correctly in both the new UI and via URL parameters
  3. Test that both JSON format filters and legacy key:value format filters work in the URL
  4. Confirm that only supported filters (currently only "created_by") are sent to the API

Additional Comments

This PR adds a new utility file pipelineRunFilterUtils.ts that handles parsing and serializing filter formats. The flag description has been updated to clarify that only the "Created by" filter is currently functional, while other filters are UI previews that will be connected to the API in future PRs.

@Mbeaulne Mbeaulne force-pushed the 01-27-add_filter_to_home_page branch from 1391b36 to 3970762 Compare January 27, 2026 18:51
@Mbeaulne Mbeaulne force-pushed the 01-27-hook_up_filters_to_api_where_availiable branch from db6c525 to 0eb32ca Compare January 27, 2026 18:51
@Mbeaulne Mbeaulne mentioned this pull request Jan 27, 2026
4 tasks
@Mbeaulne Mbeaulne force-pushed the 01-27-add_filter_to_home_page branch from 3970762 to c850afc Compare January 27, 2026 18:52
@Mbeaulne Mbeaulne force-pushed the 01-27-hook_up_filters_to_api_where_availiable branch from 0eb32ca to 6471b44 Compare January 27, 2026 18:52
@Mbeaulne Mbeaulne force-pushed the 01-27-add_filter_to_home_page branch from c850afc to 2db2535 Compare January 27, 2026 18:54
@Mbeaulne Mbeaulne force-pushed the 01-27-hook_up_filters_to_api_where_availiable branch 2 times, most recently from 063000c to 8553a9b Compare January 27, 2026 18:57
@Mbeaulne Mbeaulne force-pushed the 01-27-add_filter_to_home_page branch from 054242a to fb917b4 Compare January 27, 2026 19:02
@Mbeaulne Mbeaulne force-pushed the 01-27-hook_up_filters_to_api_where_availiable branch 2 times, most recently from 3c2b140 to dec6813 Compare January 28, 2026 16:24
@Mbeaulne Mbeaulne force-pushed the 01-27-add_filter_to_home_page branch from fb917b4 to 7e4a73c Compare January 28, 2026 20:46
@Mbeaulne Mbeaulne force-pushed the 01-27-hook_up_filters_to_api_where_availiable branch from dec6813 to 8500916 Compare January 28, 2026 20:46
@Mbeaulne Mbeaulne force-pushed the 01-27-add_filter_to_home_page branch from 7e4a73c to 71dda68 Compare February 2, 2026 19:32
@Mbeaulne Mbeaulne force-pushed the 01-27-hook_up_filters_to_api_where_availiable branch 2 times, most recently from 8305985 to 90cf7c6 Compare February 2, 2026 19:33
@Mbeaulne Mbeaulne force-pushed the 01-27-add_filter_to_home_page branch 2 times, most recently from 4f8ef54 to 847f05d Compare February 2, 2026 19:35
@Mbeaulne Mbeaulne force-pushed the 01-27-hook_up_filters_to_api_where_availiable branch 2 times, most recently from 5df4805 to 3e96b86 Compare February 2, 2026 20:31
@Mbeaulne Mbeaulne force-pushed the 01-27-add_filter_to_home_page branch 2 times, most recently from 0c5de25 to 33c547b Compare February 2, 2026 20:46
@Mbeaulne Mbeaulne force-pushed the 01-27-hook_up_filters_to_api_where_availiable branch from 3e96b86 to 2de5a77 Compare February 2, 2026 20:46
@Mbeaulne Mbeaulne force-pushed the 01-27-add_filter_to_home_page branch from 33c547b to 5f6f134 Compare February 2, 2026 20:51
@Mbeaulne Mbeaulne force-pushed the 01-27-hook_up_filters_to_api_where_availiable branch 2 times, most recently from 304c53e to 9a9788b Compare February 2, 2026 20:55
@Mbeaulne Mbeaulne force-pushed the 01-27-add_filter_to_home_page branch from 5f6f134 to 53cbf3d Compare February 2, 2026 20:55
@Mbeaulne Mbeaulne force-pushed the 01-27-add_filter_to_home_page branch from 5b0e0d3 to d26774f Compare February 9, 2026 17:11
@Mbeaulne Mbeaulne force-pushed the 01-27-hook_up_filters_to_api_where_availiable branch from f5b012b to fb18f10 Compare February 9, 2026 17:37
@Mbeaulne Mbeaulne force-pushed the 01-27-add_filter_to_home_page branch 2 times, most recently from d9ea006 to d7d3de4 Compare February 9, 2026 17:44
@Mbeaulne Mbeaulne force-pushed the 01-27-hook_up_filters_to_api_where_availiable branch from fb18f10 to af674bd Compare February 9, 2026 17:44
@Mbeaulne Mbeaulne force-pushed the 01-27-add_filter_to_home_page branch from d7d3de4 to 6628fae Compare February 9, 2026 17:55
@Mbeaulne Mbeaulne force-pushed the 01-27-hook_up_filters_to_api_where_availiable branch 3 times, most recently from e96885a to 9ac222f Compare February 9, 2026 19:37
@Mbeaulne Mbeaulne force-pushed the 01-27-add_filter_to_home_page branch from 27fe0eb to 06c105a Compare February 9, 2026 19:37
@Mbeaulne Mbeaulne force-pushed the 01-27-hook_up_filters_to_api_where_availiable branch from 9ac222f to eb47d23 Compare February 9, 2026 19:51
@Mbeaulne Mbeaulne force-pushed the 01-27-add_filter_to_home_page branch from 172b1eb to d111d46 Compare February 9, 2026 20:50
Copy link

Do you have links to the documentation for the current state of our API and the future state?

It would help with the review here


return filterDict;
};
// Supports both JSON (new) and key:value (legacy) URL formats
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a longer-term plan to deprecate the legacy format?

Copy link
Collaborator

I notice the filter url is slightly different between old vs new

image.png

image.png

just want to confirm this is intended and expected

.map(([key, value]) => `${key}:${value}`)
.join(",");
const hasRemainingFilters = Object.values(updatedFilters).some((val) => {
if (val === undefined || val === null || val === "") return false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (val === undefined || val === null || val === "") return false;
if (!val) return false;

Copy link
Collaborator

Choose a reason for hiding this comment

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

^ surely this works? the empty string case should be captured by the next if statement

Comment on lines +26 to +33
useEffect(() => {
// Only sync if value is different and not "me" (don't populate input with "me")
if (value !== undefined && value !== DEFAULT_CREATED_BY_ME_FILTER_VALUE) {
setSearchUser(value);
} else if (value === undefined) {
setSearchUser("");
}
}, [value]);
Copy link
Collaborator

@camielvs camielvs Feb 25, 2026

Choose a reason for hiding this comment

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

Suggested change
useEffect(() => {
// Only sync if value is different and not "me" (don't populate input with "me")
if (value !== undefined && value !== DEFAULT_CREATED_BY_ME_FILTER_VALUE) {
setSearchUser(value);
} else if (value === undefined) {
setSearchUser("");
}
}, [value]);
useEffect(() => {
if (!value) {
setSearchUser("");
}
// Only sync if value is different and not "me" (don't populate input with "me")
if (value !== DEFAULT_CREATED_BY_ME_FILTER_VALUE) {
setSearchUser(value);
}
}, [value]);

Copy link
Collaborator

Choose a reason for hiding this comment

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

^ I feel like this is more readable

Copy link
Collaborator

Choose a reason for hiding this comment

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

Agreed


for (const key of SUPPORTED_API_FILTERS) {
const value = filters[key];
if (value !== undefined && value !== null && value !== "") {
Copy link
Collaborator

@camielvs camielvs Feb 25, 2026

Choose a reason for hiding this comment

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

!!value && value !== ""?

Though given we seem to have this parretn coming up a fair bit maybe we make a typeguard for IsDefinedAndNotEmpty

Copy link
Collaborator

Tested according to instructions - looks good to me. Outgoing request is the same, despite different UI and URL on the frontend.

Screen Recording 2026-02-26 at 9.34.23 AM.mov (uploaded via Graphite)

Copy link
Collaborator

I see some issues with responsiveness:

Screen Recording 2026-02-26 at 9.36.28 AM.mov (uploaded via Graphite)

* // Input: { created_by: "me", status: "FAILED", annotations: [...] }
* // Output: "created_by:me" (status and annotations are stripped as unsupported)
*/
export function filtersToApiString(
Copy link
Collaborator

Choose a reason for hiding this comment

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

NIT: have unit tests for this function

Comment on lines +98 to +107
const parts: string[] = [];

for (const key of SUPPORTED_API_FILTERS) {
const value = filters[key];
if (value) {
parts.push(`${key}:${value}`);
}
}

return parts.length > 0 ? parts.join(",") : undefined;
Copy link
Collaborator

Choose a reason for hiding this comment

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

NIT: can be concise with filters() and map().

  const result = SUPPORTED_API_FILTERS.filter((key) => filters[key])
    .map((key) => `${key}:${filters[key]}`)
    .join(",");

  return result || undefined;

Comment on lines +119 to +137
const filters: PipelineRunFilters = {};
const parts = filterString.split(",");

for (const part of parts) {
const colonIndex = part.indexOf(":");
if (colonIndex === -1) continue;

const key = part.slice(0, colonIndex).trim();
const value = part.slice(colonIndex + 1).trim();

if (key && value) {
// Must be kept in sync with SUPPORTED_API_FILTERS as new filters are added
if (key === "created_by") {
filters.created_by = value;
}
}
}

return filters;
Copy link
Collaborator

Choose a reason for hiding this comment

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

NIT: using regexp might be a good solution in this case.. smth like

const filters: PipelineRunFilters = {};
  const pattern = /(\w+):([^,]+)/g;

  for (const [, key, value] of filterString.matchAll(pattern)) {
    const trimmedValue = value.trim();
    if (!trimmedValue) continue;

    // Must be kept in sync with SUPPORTED_API_FILTERS as new filters are added
    if (key === "created_by") {
      filters.created_by = trimmedValue;
    }
  }

image.png

Copy link
Collaborator

Choose a reason for hiding this comment

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

We might want also to go further in being fancy with types:

const SUPPORTED_API_STRING_FILTERS = ["created_by"] as const;

type SupportedApiStringFilter = (typeof SUPPORTED_API_STRING_FILTERS)[number];

const SUPPORTED_API_STRING_FILTER_SET = new Set<string>(
  SUPPORTED_API_STRING_FILTERS,
);

function isSupportedApiStringFilter(
  key: string,
): key is SupportedApiStringFilter {
  return SUPPORTED_API_STRING_FILTER_SET.has(key);
}

So we can keep logic generalized as SOLID

    if (isSupportedApiStringFilter(key)) {
      filters[key] = trimmedValue;
    }

Copy link
Collaborator

@maxy-shpfy maxy-shpfy left a comment

Choose a reason for hiding this comment

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

LGTM, I left a couple of NIT suggestions to improve readability.

Also responsiveness in UX should be addressed, but likely in a follow-up PR

Copy link
Collaborator Author

I will address the responsiveness in another PR. I think there will be enough changes later that we can lump it in there. Thanks!

Copy link
Collaborator Author

Mbeaulne commented Feb 26, 2026

Merge activity

  • Feb 26, 6:37 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Feb 26, 6:38 PM UTC: @Mbeaulne merged this pull request with Graphite.

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.

4 participants