Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/source-vars-2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hyperdx/api': minor
---

feat: support data source and connection variables in the external API and MCP
124 changes: 109 additions & 15 deletions packages/api/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1809,7 +1809,7 @@
},
"sourceId": {
"type": "string",
"description": "ID of the data source to query.",
"description": "ID of the data source to query, or `$name` of a DATA_SOURCE filter on the dashboard.",
"example": "65f5e4a3b9e77c001a111111"
},
"select": {
Expand Down Expand Up @@ -1906,7 +1906,7 @@
},
"sourceId": {
"type": "string",
"description": "ID of the data source to query.",
"description": "ID of the data source to query, or `$name` of a DATA_SOURCE filter on the dashboard.",
"example": "65f5e4a3b9e77c001a111111"
},
"select": {
Expand Down Expand Up @@ -1983,7 +1983,7 @@
},
"sourceId": {
"type": "string",
"description": "ID of the data source to query.",
"description": "ID of the data source to query, or `$name` of a DATA_SOURCE filter on the dashboard.",
"example": "65f5e4a3b9e77c001a111111"
},
"select": {
Expand Down Expand Up @@ -2070,7 +2070,7 @@
},
"sourceId": {
"type": "string",
"description": "ID of the data source to query.",
"description": "ID of the data source to query, or `$name` of a DATA_SOURCE filter on the dashboard.",
"example": "65f5e4a3b9e77c001a111111"
},
"select": {
Expand Down Expand Up @@ -2153,7 +2153,7 @@
},
"sourceId": {
"type": "string",
"description": "ID of the data source to query.",
"description": "ID of the data source to query, or `$name` of a DATA_SOURCE filter on the dashboard.",
"example": "65f5e4a3b9e77c001a111111"
},
"select": {
Expand Down Expand Up @@ -2214,7 +2214,7 @@
},
"sourceId": {
"type": "string",
"description": "ID of the data source to query.",
"description": "ID of the data source to query, or `$name` of a DATA_SOURCE filter on the dashboard.",
"example": "65f5e4a3b9e77c001a111111"
},
"select": {
Expand Down Expand Up @@ -2269,7 +2269,7 @@
},
"sourceId": {
"type": "string",
"description": "ID of the data source to query.",
"description": "ID of the data source to query, or `$name` of a DATA_SOURCE filter on the dashboard.",
"example": "65f5e4a3b9e77c001a111111"
},
"select": {
Expand Down Expand Up @@ -2363,7 +2363,7 @@
},
"sourceId": {
"type": "string",
"description": "ID of the data source to query.",
"description": "ID of the data source to query, or `$name` of a DATA_SOURCE filter on the dashboard.",
"example": "65f5e4a3b9e77c001a111111"
},
"select": {
Expand Down Expand Up @@ -2421,7 +2421,7 @@
},
"sourceId": {
"type": "string",
"description": "ID of the data source to query.",
"description": "ID of the data source to query, or `$name` of a DATA_SOURCE filter on the dashboard.",
"example": "65f5e4a3b9e77c001a111111"
},
"select": {
Expand Down Expand Up @@ -2485,7 +2485,7 @@
},
"sourceId": {
"type": "string",
"description": "ID of the data source to query.",
"description": "ID of the data source to query, or `$name` of a DATA_SOURCE filter on the dashboard.",
"example": "65f5e4a3b9e77c001a111111"
},
"select": {
Expand Down Expand Up @@ -2525,7 +2525,7 @@
},
"sourceId": {
"type": "string",
"description": "ID of the data source to mine patterns from.",
"description": "ID of the data source to mine patterns from, or `$name` of a DATA_SOURCE filter on the dashboard.",
"example": "65f5e4a3b9e77c001a111111"
},
"select": {
Expand Down Expand Up @@ -2590,7 +2590,7 @@
},
"connectionId": {
"type": "string",
"description": "ID of the ClickHouse connection to execute the query against.",
"description": "ID of the ClickHouse connection to execute the query against, or\n`$name` of a CONNECTION filter on the dashboard.\n",
"example": "65f5e4a3b9e77c001a567890"
},
"sqlTemplate": {
Expand All @@ -2601,7 +2601,7 @@
},
"sourceId": {
"type": "string",
"description": "Optional ID of the data source associated with this Raw SQL chart. Used for applying dashboard filters.",
"description": "Optional ID of the data source associated with this Raw SQL chart, or `$name` of a DATA_SOURCE filter on the dashboard. Used for applying dashboard filters.",
"example": "65f5e4a3b9e77c001a567890"
},
"numberFormat": {
Expand Down Expand Up @@ -3951,14 +3951,108 @@
},
{
"$ref": "#/components/schemas/PrometheusLabelFilterInput"
},
{
"$ref": "#/components/schemas/DataSourceFilterInput"
},
{
"$ref": "#/components/schemas/ConnectionFilterInput"
}
],
"discriminator": {
"propertyName": "type",
"mapping": {
"QUERY_EXPRESSION": "#/components/schemas/QueryExpressionFilterInput",
"STATIC_LIST": "#/components/schemas/StaticListFilterInput",
"PROMETHEUS_LABEL": "#/components/schemas/PrometheusLabelFilterInput"
"PROMETHEUS_LABEL": "#/components/schemas/PrometheusLabelFilterInput",
"DATA_SOURCE": "#/components/schemas/DataSourceFilterInput",
"CONNECTION": "#/components/schemas/ConnectionFilterInput"
}
}
},
"DataSourceFilterInput": {
"type": "object",
"description": "A variable whose single, required value is a source. Tiles reference it\nas `$variableName` in their sourceId, and other filters in sourceId or\nappliesToSourceIds, so viewers can switch the source those read. Tiles\nthat use it do not load until a source is selected, and cannot have\nalerts.\n",
"required": [
"type",
"name",
"allowedSourceKinds"
],
"properties": {
"type": {
"type": "string",
"enum": [
"DATA_SOURCE"
],
"description": "Filter type discriminator. Must be \"DATA_SOURCE\".",
"example": "DATA_SOURCE"
},
"name": {
"type": "string",
"minLength": 1,
"description": "Display name of the variable's dropdown",
"example": "Logs source"
},
"variableName": {
"type": "string",
"maxLength": 64,
"pattern": "^[a-zA-Z][a-zA-Z0-9_]*$",
"description": "Token tiles and filters reference the selected source by, as `$variableName`.",
"example": "logs"
},
"allowedSourceKinds": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"enum": [
"log",
"trace",
"session",
"metric",
"promql"
]
},
"description": "Kinds of sources offered. A field can only reference the variable\nif it accepts every listed kind.\n",
"example": [
"log"
]
},
"connectionId": {
"type": "string",
"description": "Optional connection the offered sources are limited to: a\nconnection ID, or `$name` of a CONNECTION filter on the dashboard.\n",
"example": "$conn"
}
}
},
"ConnectionFilterInput": {
"type": "object",
"description": "A variable whose single, required value is a connection. Raw SQL tiles\nreference it as `$variableName` in their connectionId, and data source\nvariables in theirs. Tiles that use it do not load until a connection is\nselected, and cannot have alerts.\n",
"required": [
"type",
"name"
],
"properties": {
"type": {
"type": "string",
"enum": [
"CONNECTION"
],
"description": "Filter type discriminator. Must be \"CONNECTION\".",
"example": "CONNECTION"
},
"name": {
"type": "string",
"minLength": 1,
"description": "Display name of the variable's dropdown",
"example": "Connection"
},
"variableName": {
"type": "string",
"maxLength": 64,
"pattern": "^[a-zA-Z][a-zA-Z0-9_]*$",
"description": "Token raw SQL tiles reference the selected connection by, as `$variableName`.",
"example": "conn"
}
}
},
Expand Down Expand Up @@ -3994,7 +4088,7 @@
},
"sourceId": {
"type": "string",
"description": "Source ID this dashboard filter key applies to",
"description": "Source ID this dashboard filter key applies to, or `$name` of a DATA_SOURCE filter on the dashboard",
"example": "65f5e4a3b9e77c001a111111"
},
"sourceMetricType": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1400,4 +1400,76 @@ describe('MCP Dashboard Tools - clickstack_patch_dashboard', () => {
expect(tile.config.showOperandSeries).toBe(false);
});
});

describe('reference variables', () => {
const createDashboard = () =>
new Dashboard({
name: 'Variable dashboard',
tiles: [
{
id: 'tile',
x: 0,
y: 0,
w: 6,
h: 3,
config: {
name: 'Tile',
source: ctx.traceSource._id.toString(),
displayType: 'line',
select: [{ aggFn: 'count', valueExpression: '' }],
where: '',
},
},
],
filters: [
{
id: 'ds-filter',
type: 'DATA_SOURCE',
name: 'Traces source',
variableName: 'traces',
allowedSourceKinds: [SourceKind.Trace],
minSelections: 1,
maxSelections: 1,
isBroadcastEnabled: false,
isVariableEnabled: true,
},
],
team: ctx.team._id,
}).save();

const patchSource = (dashboardId: string, sourceId: string) =>
callTool(ctx.client!, 'clickstack_patch_dashboard', {
dashboardId,
tileId: 'tile',
tile: {
name: 'Tile',
config: {
displayType: 'line',
sourceId,
select: [{ aggFn: 'count' }],
},
},
});

it('patches a tile to read from a declared data source variable', async () => {
const dashboard = await createDashboard();

const result = await patchSource(dashboard._id.toString(), '$traces');

expect(result.isError).toBeFalsy();
const stored = await Dashboard.findById(dashboard._id).lean();
expect(stored?.tiles[0].config).toMatchObject({ source: '$traces' });
});

it('rejects a patch referencing an undeclared variable', async () => {
const dashboard = await createDashboard();

const result = await patchSource(dashboard._id.toString(), '$nope');

expect(result.isError).toBe(true);
expect(getFirstText(result)).toContain(
'does not name a data source variable',
);
});
});
});
Loading
Loading