Skip to content

Commit 296b981

Browse files
Merge branch 'main' into warren/HDX-3732-appnav-user-menu-crash
2 parents 49e060a + a03cecc commit 296b981

81 files changed

Lines changed: 2367 additions & 613 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@hyperdx/app': patch
3+
---
4+
5+
fix: escape service filter values on Services page to handle quoted names safely

.changeset/link-button-variant.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hyperdx/app": patch
3+
---
4+
5+
feat: Add `link` variant for Button and ActionIcon components

.changeset/olive-hounds-double.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hyperdx/app": patch
3+
---
4+
5+
feat: show inline span durations in trace timeline
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@hyperdx/common-utils": patch
3+
"@hyperdx/app": patch
4+
---
5+
6+
feat: Add support for dashboard filters on Raw SQL Charts
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Vulnerability Alerts
2+
3+
on:
4+
schedule:
5+
- cron: '0 9 * * *' # Daily at 9am UTC
6+
workflow_dispatch:
7+
8+
jobs:
9+
alert:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: kunalnagarco/action-cve@v1.14.23
13+
with:
14+
org: hyperdxio
15+
token: ${{ secrets.DEPENDABOT_NOTIF_PAT }}
16+
slack_webhook: ${{ secrets.SLACK_WEBHOOK_VULNERABILITIES }}
17+
severity: medium,high,critical

AGENTS.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,27 @@ skip hooks. If the pre-commit hook fails (e.g. due to husky not being set up in
152152
a worktree), run `npx lint-staged` manually before committing to ensure lint and
153153
formatting checks pass. Fix any issues before creating the commit.
154154

155+
## Merge Conflict Resolution
156+
157+
1. **Never blindly pick a side.** Read both sides of every conflict to
158+
understand the intent of each change before choosing a resolution.
159+
160+
2. **Refactor/move conflicts require extra verification.** When one side
161+
refactored, moved, or extracted code (e.g., inline components to separate
162+
files), always diff the discarded side against the destination files before
163+
declaring the conflict resolved. Code can diverge after extraction — the
164+
other branch may have made fixes or additions that the extracting branch
165+
never picked up. A naive "keep ours" resolution silently drops those changes.
166+
167+
3. **Verify the result compiles.** After resolving, check for missing imports,
168+
broken references, or type errors introduced by the resolution — especially
169+
when discarding a side that added new dependencies or exports.
170+
171+
4. **Ask for help when uncertain.** If you are not 100% confident about which
172+
side to keep, or whether a change can be safely discarded, stop and ask for
173+
manual intervention rather than guessing. A wrong guess silently breaks
174+
things; asking is always cheaper than debugging later.
175+
155176
---
156177

157178
_Need more details? Check the `agent_docs/` directory or ask which documentation

agent_docs/code_style.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ The project uses Mantine UI with **custom variants** defined in `packages/app/sr
3636
| `variant="primary"` | Primary actions (Submit, Save, Create, Run) | `<Button variant="primary">Save</Button>` |
3737
| `variant="secondary"` | Secondary actions (Cancel, Clear, auxiliary actions) | `<Button variant="secondary">Cancel</Button>` |
3838
| `variant="danger"` | Destructive actions (Delete, Remove, Rotate API Key) | `<Button variant="danger">Delete</Button>` |
39+
| `variant="link"` | Link-style actions with no background or border (View Details, navigation-style CTAs) | `<Button variant="link">View Details</Button>` |
3940

4041
### DO NOT USE (Forbidden Patterns)
4142

@@ -58,11 +59,15 @@ The following patterns are **NOT ALLOWED** for Button and ActionIcon:
5859
<Button variant="primary">Save</Button>
5960
<Button variant="secondary">Cancel</Button>
6061
<Button variant="danger">Delete</Button>
62+
<Button variant="link">View Details</Button>
6163
<ActionIcon variant="primary">...</ActionIcon>
6264
<ActionIcon variant="secondary">...</ActionIcon>
6365
<ActionIcon variant="danger">...</ActionIcon>
66+
<ActionIcon variant="link">...</ActionIcon>
6467
```
6568

69+
**Link variant details**: Renders with no background, no border, and muted text color. On hover, text brightens to full contrast. Use for link-style CTAs that should blend into surrounding content (e.g., "View Details", "View Full Trace").
70+
6671
**Note**: `variant="filled"` is still valid for **form inputs** (Select, TextInput, etc.), just not for Button/ActionIcon.
6772

6873
### Icon-Only Buttons → ActionIcon

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
"brace-expansion": "^2.0.2",
7777
"diff": "^5.2.2",
7878
"on-headers": "^1.1.0",
79-
"fast-xml-parser": "^4.4.0",
79+
"fast-xml-parser": "^4.5.4",
8080
"systeminformation": "^5.24.0"
8181
}
8282
}

packages/api/openapi.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,6 +1389,11 @@
13891389
"description": "SQL query template to execute. Supports HyperDX template variables.",
13901390
"example": "SELECT count() FROM otel_logs WHERE timestamp > now() - INTERVAL 1 HOUR"
13911391
},
1392+
"sourceId": {
1393+
"type": "string",
1394+
"description": "Optional ID of the data source associated with this Raw SQL chart. Used for applying dashboard filters.",
1395+
"example": "65f5e4a3b9e77c001a567890"
1396+
},
13921397
"numberFormat": {
13931398
"$ref": "#/components/schemas/NumberFormat",
13941399
"description": "Number formatting options for displayed values."

packages/api/src/routers/external-api/__tests__/dashboards.test.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,6 +2311,7 @@ describe('External API v2 Dashboards - new format', () => {
23112311

23122312
it('can round-trip all raw SQL chart config types', async () => {
23132313
const connectionId = connection._id.toString();
2314+
const sourceId = traceSource._id.toString();
23142315
const sqlTemplate = 'SELECT count() FROM otel_logs WHERE {timeFilter}';
23152316

23162317
const lineRawSql: ExternalDashboardTile = {
@@ -2324,6 +2325,7 @@ describe('External API v2 Dashboards - new format', () => {
23242325
displayType: 'line',
23252326
connectionId,
23262327
sqlTemplate,
2328+
sourceId,
23272329
compareToPreviousPeriod: true,
23282330
fillNulls: true,
23292331
alignDateRangeToGranularity: true,
@@ -2342,6 +2344,7 @@ describe('External API v2 Dashboards - new format', () => {
23422344
displayType: 'stacked_bar',
23432345
connectionId,
23442346
sqlTemplate,
2347+
sourceId,
23452348
fillNulls: false,
23462349
alignDateRangeToGranularity: false,
23472350
numberFormat: { output: 'byte', decimalBytes: true },
@@ -2359,6 +2362,7 @@ describe('External API v2 Dashboards - new format', () => {
23592362
displayType: 'table',
23602363
connectionId,
23612364
sqlTemplate,
2365+
sourceId,
23622366
numberFormat: { output: 'percent', mantissa: 1 },
23632367
},
23642368
};
@@ -2374,6 +2378,7 @@ describe('External API v2 Dashboards - new format', () => {
23742378
displayType: 'number',
23752379
connectionId,
23762380
sqlTemplate,
2381+
sourceId,
23772382
numberFormat: { output: 'currency', currencySymbol: '$' },
23782383
},
23792384
};
@@ -2389,6 +2394,7 @@ describe('External API v2 Dashboards - new format', () => {
23892394
displayType: 'pie',
23902395
connectionId,
23912396
sqlTemplate,
2397+
sourceId,
23922398
},
23932399
};
23942400

@@ -2457,6 +2463,43 @@ describe('External API v2 Dashboards - new format', () => {
24572463
});
24582464
});
24592465

2466+
it('should return 400 when source connection does not match tile connection', async () => {
2467+
const otherConnection = await Connection.create({
2468+
team: team._id,
2469+
name: 'Other Connection',
2470+
host: config.CLICKHOUSE_HOST,
2471+
username: config.CLICKHOUSE_USER,
2472+
password: config.CLICKHOUSE_PASSWORD,
2473+
});
2474+
2475+
const response = await authRequest('post', BASE_URL)
2476+
.send({
2477+
name: 'Dashboard with Mismatched Source Connection',
2478+
tiles: [
2479+
{
2480+
name: 'Raw SQL Tile',
2481+
x: 0,
2482+
y: 0,
2483+
w: 6,
2484+
h: 3,
2485+
config: {
2486+
configType: 'sql',
2487+
displayType: 'table',
2488+
connectionId: otherConnection._id.toString(),
2489+
sourceId: traceSource._id.toString(),
2490+
sqlTemplate: 'SELECT count() FROM otel_logs',
2491+
},
2492+
},
2493+
],
2494+
tags: [],
2495+
})
2496+
.expect(400);
2497+
2498+
expect(response.body).toEqual({
2499+
message: `The following source IDs do not match the specified connections: ${traceSource._id.toString()}`,
2500+
});
2501+
});
2502+
24602503
it('should create a dashboard with filters', async () => {
24612504
const dashboardPayload = {
24622505
name: 'Dashboard with Filters',
@@ -3100,6 +3143,7 @@ describe('External API v2 Dashboards - new format', () => {
31003143

31013144
it('can round-trip all raw SQL chart config types', async () => {
31023145
const connectionId = connection._id.toString();
3146+
const sourceId = traceSource._id.toString();
31033147
const sqlTemplate = 'SELECT count() FROM otel_logs WHERE {timeFilter}';
31043148

31053149
const lineRawSql: ExternalDashboardTileWithId = {
@@ -3114,6 +3158,7 @@ describe('External API v2 Dashboards - new format', () => {
31143158
displayType: 'line',
31153159
connectionId,
31163160
sqlTemplate,
3161+
sourceId,
31173162
compareToPreviousPeriod: true,
31183163
fillNulls: true,
31193164
alignDateRangeToGranularity: true,
@@ -3133,6 +3178,7 @@ describe('External API v2 Dashboards - new format', () => {
31333178
displayType: 'stacked_bar',
31343179
connectionId,
31353180
sqlTemplate,
3181+
sourceId,
31363182
fillNulls: false,
31373183
alignDateRangeToGranularity: false,
31383184
numberFormat: { output: 'byte', decimalBytes: true },
@@ -3151,6 +3197,7 @@ describe('External API v2 Dashboards - new format', () => {
31513197
displayType: 'table',
31523198
connectionId,
31533199
sqlTemplate,
3200+
sourceId,
31543201
numberFormat: { output: 'percent', mantissa: 1 },
31553202
},
31563203
};
@@ -3167,6 +3214,7 @@ describe('External API v2 Dashboards - new format', () => {
31673214
displayType: 'number',
31683215
connectionId,
31693216
sqlTemplate,
3217+
sourceId,
31703218
numberFormat: { output: 'currency', currencySymbol: '$' },
31713219
},
31723220
};
@@ -3183,6 +3231,7 @@ describe('External API v2 Dashboards - new format', () => {
31833231
displayType: 'pie',
31843232
connectionId,
31853233
sqlTemplate,
3234+
sourceId,
31863235
},
31873236
};
31883237

@@ -3271,6 +3320,45 @@ describe('External API v2 Dashboards - new format', () => {
32713320
});
32723321
});
32733322

3323+
it('should return 400 when source connection does not match tile connection', async () => {
3324+
const dashboard = await createTestDashboard();
3325+
const otherConnection = await Connection.create({
3326+
team: team._id,
3327+
name: 'Other Connection',
3328+
host: config.CLICKHOUSE_HOST,
3329+
username: config.CLICKHOUSE_USER,
3330+
password: config.CLICKHOUSE_PASSWORD,
3331+
});
3332+
3333+
const response = await authRequest('put', `${BASE_URL}/${dashboard._id}`)
3334+
.send({
3335+
name: 'Updated Dashboard with Mismatched Source Connection',
3336+
tiles: [
3337+
{
3338+
id: new ObjectId().toString(),
3339+
name: 'Raw SQL Tile',
3340+
x: 0,
3341+
y: 0,
3342+
w: 6,
3343+
h: 3,
3344+
config: {
3345+
configType: 'sql',
3346+
displayType: 'table',
3347+
connectionId: otherConnection._id.toString(),
3348+
sourceId: traceSource._id.toString(),
3349+
sqlTemplate: 'SELECT count() FROM otel_logs',
3350+
},
3351+
},
3352+
],
3353+
tags: [],
3354+
})
3355+
.expect(400);
3356+
3357+
expect(response.body).toEqual({
3358+
message: `The following source IDs do not match the specified connections: ${traceSource._id.toString()}`,
3359+
});
3360+
});
3361+
32743362
it('should delete alert when tile is updated from builder to raw SQL config', async () => {
32753363
const tileId = new ObjectId().toString();
32763364
const dashboard = await createTestDashboard({

0 commit comments

Comments
 (0)