Skip to content

Commit 4161cc3

Browse files
Merge branch 'main' into warren/HDX-3277-service-page-quote-escape-bug
2 parents ce7e09c + 2b53b8e commit 4161cc3

File tree

79 files changed

+2378
-612
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+2378
-612
lines changed

.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

.opencode/commands/do-linear.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
description:
3+
Fetch a Linear ticket, implement the fix/feature, test, commit, push, and
4+
raise a PR
5+
---
6+
7+
Look up the Linear ticket $ARGUMENTS. Read the ticket description, comments, and
8+
any linked resources thoroughly.
9+
10+
## Phase 1: Understand the Ticket
11+
12+
- Summarize the ticket — what is being asked for (bug fix, feature, refactor,
13+
etc.)
14+
- Identify acceptance criteria or expected behavior from the description
15+
- Note any linked issues, related tickets, or dependencies
16+
17+
If the ticket description is too vague or lacks enough information to proceed
18+
confidently, **stop and ask me for clarification** before writing any code.
19+
Explain exactly what information is missing and what assumptions you would need
20+
to make.
21+
22+
## Phase 2: Plan and Implement
23+
24+
Before writing code, read the relevant documentation from the `agent_docs/`
25+
directory to understand architecture and code patterns.
26+
27+
1. Explore the codebase to understand the relevant code paths and existing
28+
patterns
29+
2. Create an implementation plan — which files to change, what approach to take
30+
3. Implement the fix or feature following existing codebase patterns
31+
4. Keep changes minimal and focused on the ticket scope
32+
33+
## Phase 3: Verify
34+
35+
Run lint and type checks, then run the appropriate tests based on which packages
36+
were modified:
37+
38+
1. Run `make ci-lint` to verify lint and TypeScript types pass
39+
2. Run `make ci-unit` to verify unit tests pass across all packages
40+
3. If any checks fail, fix the issues and re-run until everything passes
41+
42+
## Phase 4: Commit, Push, and Open PR
43+
44+
1. Create a new branch named `<current-user>/$ARGUMENTS-<short-description>`.
45+
Use the current git/OS username when available, and use `whoami` as a
46+
fallback to determine the prefix (e.g. `warren/HDX-1234-fix-search-filter`)
47+
2. Commit the changes using conventional commit format (e.g. `feat:`, `fix:`,
48+
`chore:`) and reference the ticket ID
49+
3. Push the branch to the remote
50+
4. Open a pull request with:
51+
- Title: `[$ARGUMENTS] <description>`
52+
- Body: Use `.github/pull_request_template.md` as the template and fill it in
53+
with the relevant summary, testing notes, and Linear ticket link
54+
- Label: Attach the `ai-generated` label

.opencode/commands/plan-linear.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
description: Research a Linear ticket and create an implementation plan
3+
agent: plan
4+
---
5+
6+
Look up the Linear ticket $ARGUMENTS. Read the ticket description and all
7+
comments on the ticket thoroughly.
8+
9+
Based on your research of the ticket, come up with a detailed implementation
10+
plan. The plan should include:
11+
12+
1. **Summary** — A concise summary of what the ticket is asking for
13+
2. **Context** — Relevant information gathered from the ticket description and
14+
comments
15+
3. **Approach** — Step-by-step implementation plan with specific files and
16+
components to modify
17+
4. **Testing** — How the changes should be tested
18+
5. **Open Questions** — Any ambiguities or decisions that need clarification
19+
20+
Before writing the plan, explore the codebase to understand the relevant code
21+
paths and existing patterns. Reference specific files and line numbers where
22+
changes will be needed.

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)