Hook up filters to API where availiable#1712
Conversation
1391b36 to
3970762
Compare
db6c525 to
0eb32ca
Compare
3970762 to
c850afc
Compare
0eb32ca to
6471b44
Compare
c850afc to
2db2535
Compare
063000c to
8553a9b
Compare
054242a to
fb917b4
Compare
3c2b140 to
dec6813
Compare
fb917b4 to
7e4a73c
Compare
dec6813 to
8500916
Compare
7e4a73c to
71dda68
Compare
8305985 to
90cf7c6
Compare
4f8ef54 to
847f05d
Compare
5df4805 to
3e96b86
Compare
0c5de25 to
33c547b
Compare
3e96b86 to
2de5a77
Compare
33c547b to
5f6f134
Compare
304c53e to
9a9788b
Compare
5f6f134 to
53cbf3d
Compare
5b0e0d3 to
d26774f
Compare
f5b012b to
fb18f10
Compare
d9ea006 to
d7d3de4
Compare
fb18f10 to
af674bd
Compare
d7d3de4 to
6628fae
Compare
e96885a to
9ac222f
Compare
27fe0eb to
06c105a
Compare
9ac222f to
eb47d23
Compare
172b1eb to
d111d46
Compare
|
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 |
There was a problem hiding this comment.
Is there a longer-term plan to deprecate the legacy format?
| .map(([key, value]) => `${key}:${value}`) | ||
| .join(","); | ||
| const hasRemainingFilters = Object.values(updatedFilters).some((val) => { | ||
| if (val === undefined || val === null || val === "") return false; |
There was a problem hiding this comment.
| if (val === undefined || val === null || val === "") return false; | |
| if (!val) return false; |
There was a problem hiding this comment.
^ surely this works? the empty string case should be captured by the next if statement
| 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]); |
There was a problem hiding this comment.
| 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]); |
There was a problem hiding this comment.
^ I feel like this is more readable
src/utils/pipelineRunFilterUtils.ts
Outdated
|
|
||
| for (const key of SUPPORTED_API_FILTERS) { | ||
| const value = filters[key]; | ||
| if (value !== undefined && value !== null && value !== "") { |
There was a problem hiding this comment.
!!value && value !== ""?
Though given we seem to have this parretn coming up a fair bit maybe we make a typeguard for IsDefinedAndNotEmpty
|
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) |
|
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( |
There was a problem hiding this comment.
NIT: have unit tests for this function
| 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; |
There was a problem hiding this comment.
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;| 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; |
There was a problem hiding this comment.
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;
}
}There was a problem hiding this comment.
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;
}
maxy-shpfy
left a comment
There was a problem hiding this comment.
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
|
I will address the responsiveness in another PR. I think there will be enough changes later that we can lump it in there. Thanks! |




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
Checklist
Test Instructions
Additional Comments
This PR adds a new utility file
pipelineRunFilterUtils.tsthat 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.