Skip to content

Commit cff6388

Browse files
authored
feat: Add static value list filters to schemas and APIs (#3017)
1 parent b917308 commit cff6388

41 files changed

Lines changed: 2338 additions & 246 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@hyperdx/common-utils': patch
3+
'@hyperdx/api': patch
4+
'@hyperdx/app': patch
5+
---
6+
7+
feat: Add static filters to schemas and APIs

‎packages/api/openapi.json‎

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3040,8 +3040,26 @@
30403040
]
30413041
},
30423042
"FilterInput": {
3043+
"description": "A drop-down filter on a dashboard. Depending on type, filters can either broadcast\nselected value(s) to every tile (isBroadcastEnabled), expose selected\nvalues as a variable (isVariableEnabled) or both. Typically a filter should\ndo just one of these things.\n",
3044+
"oneOf": [
3045+
{
3046+
"$ref": "#/components/schemas/QueryExpressionFilterInput"
3047+
},
3048+
{
3049+
"$ref": "#/components/schemas/StaticListFilterInput"
3050+
}
3051+
],
3052+
"discriminator": {
3053+
"propertyName": "type",
3054+
"mapping": {
3055+
"QUERY_EXPRESSION": "#/components/schemas/QueryExpressionFilterInput",
3056+
"STATIC_LIST": "#/components/schemas/StaticListFilterInput"
3057+
}
3058+
}
3059+
},
3060+
"QueryExpressionFilterInput": {
30433061
"type": "object",
3044-
"description": "A drop-down filter on a dashboard. Every filter can either broadcast its\nselected value(s) to every tile (isBroadcastEnabled), expose its selected\nvalue as a variable (isVariableEnabled) or both. Typically a filter should\ndo just one of these things.\n",
3062+
"description": "A filter whose dropdown values are queried from ClickHouse: \"expression\"\nnames the column and \"sourceId\" the source to read them from. Its\nselection can be broadcast into matching tiles' WHERE clauses.\n",
30453063
"required": [
30463064
"type",
30473065
"name",
@@ -3054,7 +3072,7 @@
30543072
"enum": [
30553073
"QUERY_EXPRESSION"
30563074
],
3057-
"description": "Filter type. Must be \"QUERY_EXPRESSION\".",
3075+
"description": "Filter type discriminator. Must be \"QUERY_EXPRESSION\".",
30583076
"example": "QUERY_EXPRESSION"
30593077
},
30603078
"name": {
@@ -3132,6 +3150,72 @@
31323150
}
31333151
}
31343152
},
3153+
"StaticListFilterInput": {
3154+
"type": "object",
3155+
"description": "A filter whose dropdown offers a static custom \"options\" list. It carries no\nexpression, so it cannot be broadcast, and supports only variable mode.\n",
3156+
"required": [
3157+
"type",
3158+
"name",
3159+
"options"
3160+
],
3161+
"properties": {
3162+
"type": {
3163+
"type": "string",
3164+
"enum": [
3165+
"STATIC_LIST"
3166+
],
3167+
"description": "Discriminator. Must be \"STATIC_LIST\".",
3168+
"example": "STATIC_LIST"
3169+
},
3170+
"name": {
3171+
"type": "string",
3172+
"minLength": 1,
3173+
"description": "Display name for the dashboard filter key",
3174+
"example": "Environment"
3175+
},
3176+
"options": {
3177+
"type": "array",
3178+
"minItems": 1,
3179+
"maxItems": 1000,
3180+
"items": {
3181+
"type": "string",
3182+
"minLength": 1,
3183+
"maxLength": 10000
3184+
},
3185+
"description": "The values this filter's dropdown offers. Must be non-empty and\nfree of duplicates.\n",
3186+
"example": [
3187+
"prod",
3188+
"staging",
3189+
"dev"
3190+
]
3191+
},
3192+
"isBroadcastEnabled": {
3193+
"type": "boolean",
3194+
"enum": [
3195+
false
3196+
],
3197+
"default": false,
3198+
"description": "Must be false — there is no expression to broadcast. Omit it and it\nis false.\n",
3199+
"example": false
3200+
},
3201+
"isVariableEnabled": {
3202+
"type": "boolean",
3203+
"enum": [
3204+
true
3205+
],
3206+
"default": true,
3207+
"description": "Must be true if provided, and is true when omitted. Tiles reference\nthe selection as `$variableName`, or with the `$__filter(<expression>, $<variableName>)`\nand `$__conditionalAll(<condition>, $<variableName>)` macros. The\none-argument `$__filter($<variableName>)` form renders the filter's\nown expression, which this kind of filter does not have, so it\nreports that the expression must be passed explicitly.\n",
3208+
"example": true
3209+
},
3210+
"variableName": {
3211+
"type": "string",
3212+
"maxLength": 64,
3213+
"pattern": "^[a-zA-Z][a-zA-Z0-9_]*$",
3214+
"description": "Token tiles reference this filter's selected value by, as\n`$variableName`. Must start with a letter and may contain only\nletters, numbers, and underscores. Defaults to the display name with\nwhitespace replaced by underscores and remaining illegal characters\nremoved, so a variable-enabled filter whose name derives nothing\nusable must send this field explicitly. Variable names must be unique\nacross a dashboard's variable-enabled filters. Names the variable\nonly, so the field is rejected when isVariableEnabled is not true, and\nis omitted from responses for such a filter.\n",
3215+
"example": "environment"
3216+
}
3217+
}
3218+
},
31353219
"Filter": {
31363220
"allOf": [
31373221
{
@@ -6147,6 +6231,18 @@
61476231
"name": "Service",
61486232
"expression": "service_name",
61496233
"sourceId": "65f5e4a3b9e77c001a111111"
6234+
},
6235+
{
6236+
"type": "STATIC_LIST",
6237+
"name": "Environment",
6238+
"options": [
6239+
"prod",
6240+
"staging",
6241+
"dev"
6242+
],
6243+
"isBroadcastEnabled": false,
6244+
"isVariableEnabled": true,
6245+
"variableName": "env"
61506246
}
61516247
]
61526248
}

‎packages/api/src/mcp/__tests__/dashboards/validation.test.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import {
1414
getTileVariableWarnings,
1515
} from '@/mcp/tools/dashboards/validation';
1616
import type {
17-
ExternalDashboardFilterWithId,
1817
ExternalDashboardTileWithId,
18+
ExternalQueryExpressionFilterWithId,
1919
} from '@/utils/zod';
2020

2121
const connectionId = new mongoose.Types.ObjectId().toString();
@@ -448,8 +448,8 @@ describe('getRawSqlTileMacroHints', () => {
448448
// ─── Variable-enabled filters ────────────────────────────────────────────────
449449

450450
function makeFilter(
451-
overrides: Partial<ExternalDashboardFilterWithId> = {},
452-
): ExternalDashboardFilterWithId {
451+
overrides: Partial<ExternalQueryExpressionFilterWithId> = {},
452+
): ExternalQueryExpressionFilterWithId {
453453
return {
454454
id: new mongoose.Types.ObjectId().toString(),
455455
type: 'QUERY_EXPRESSION',
@@ -803,7 +803,7 @@ describe('getFilterVariableWarnings', () => {
803803
});
804804

805805
const endpointFilter = (
806-
overrides: Partial<ExternalDashboardFilterWithId> = {},
806+
overrides: Partial<ExternalQueryExpressionFilterWithId> = {},
807807
) =>
808808
makeFilter({
809809
name: 'Endpoint',

‎packages/api/src/mcp/tools/dashboards/schemas.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1152,7 +1152,8 @@ const mcpDashboardFilterSchema = z
11521152
'one. On CREATE (no top-level `id` on the dashboard call), filter `id` may ' +
11531153
'be omitted and one will be generated server-side.',
11541154
),
1155-
type: DashboardFilterType.describe(
1155+
// TODO: Add static value filter type when variables are supported in MCP
1156+
type: DashboardFilterType.extract(['QUERY_EXPRESSION']).describe(
11561157
'Filter type. Currently only "QUERY_EXPRESSION" is supported.',
11571158
),
11581159
name: z

‎packages/api/src/mcp/tools/dashboards/validation.ts‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import {
1515
TIME_RANGE_MACROS,
1616
} from '@hyperdx/common-utils/dist/macros';
1717
import {
18-
DashboardFilter,
1918
DisplayType,
19+
QueryExpressionDashboardFilter,
2020
SavedChartConfig,
2121
SearchConditionLanguage,
2222
} from '@hyperdx/common-utils/dist/types';
@@ -151,9 +151,12 @@ export function getRawSqlTileMacroWarnings(
151151
return hints;
152152
}
153153

154-
/** Minimal projection needed to check a filter's dropdown-values query. */
154+
/**
155+
* Minimal projection needed to check a filter's dropdown-values query. Static
156+
* list filters have no dropdown query, so `where` / `whereLanguage` are optional.
157+
*/
155158
export type FilterForVariableValidation = FilterForVariableDeclaration &
156-
Partial<Pick<DashboardFilter, 'where' | 'whereLanguage'>>;
159+
Partial<Pick<QueryExpressionDashboardFilter, 'where' | 'whereLanguage'>>;
157160

158161
/**
159162
* Non-blocking checks on the dashboard variables references in a filter's `where`.

0 commit comments

Comments
 (0)