fix(httpapi): GetGraph 500s when rel_type is omitted
req.Params.RelType is *[]string; passing the nil pointer straight through as a pgx query arg (both in the blast_radius() call and in ListGraphEdges) panics because pgx can't infer the array element type from a nil *[]string, only from a concrete (possibly nil) []string. Dereference once up front instead. Also affected the sqlc-based ListGraphEdges path added by the R3 refactor, which had the same bug. Add a regression test for GET /api/v1/graph?root=X&depth=N with no rel_type. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -263,6 +263,20 @@ func TestAPIEndToEnd(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Regression: rel_type is an optional array param (*[]string); when
|
||||||
|
// omitted entirely (not an empty list), passing the nil pointer straight
|
||||||
|
// through to pgx as a query arg panics because pgx can't infer the array
|
||||||
|
// element type from a nil *[]string. root+depth alone must still work.
|
||||||
|
t.Run("graph without rel_type", func(t *testing.T) {
|
||||||
|
rec, body := get(t, h, "/api/v1/graph?root=host:hubris&depth=1", nil)
|
||||||
|
if rec.Code != 200 {
|
||||||
|
t.Fatalf("status %d", rec.Code)
|
||||||
|
}
|
||||||
|
if len(body["nodes"].([]any)) < 2 {
|
||||||
|
t.Errorf("graph too small: %d nodes", len(body["nodes"].([]any)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("ontology", func(t *testing.T) {
|
t.Run("ontology", func(t *testing.T) {
|
||||||
rec, body := get(t, h, "/api/v1/ontology", nil)
|
rec, body := get(t, h, "/api/v1/ontology", nil)
|
||||||
if rec.Code != 200 {
|
if rec.Code != 200 {
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ func (s *Server) GetEntityRelations(ctx context.Context, req gen.GetEntityRelati
|
|||||||
Type: r.Type,
|
Type: r.Type,
|
||||||
Attributes: attrs,
|
Attributes: attrs,
|
||||||
ValidFrom: r.ValidFrom,
|
ValidFrom: r.ValidFrom,
|
||||||
ValidTo: validTo,
|
ValidTo: validTo,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return gen.GetEntityRelations200JSONResponse{Items: items}, nil
|
return gen.GetEntityRelations200JSONResponse{Items: items}, nil
|
||||||
@@ -282,6 +282,15 @@ func (s *Server) GetGraph(ctx context.Context, req gen.GetGraphRequestObject) (g
|
|||||||
var err error
|
var err error
|
||||||
truncated := false
|
truncated := false
|
||||||
|
|
||||||
|
// pgx can't infer the array element type from a nil *[]string (the
|
||||||
|
// param is absent from the request, not an empty list), so dereference
|
||||||
|
// to a plain []string first — nil there still encodes as SQL NULL, but
|
||||||
|
// pgx has a concrete type to work with.
|
||||||
|
var relTypes []string
|
||||||
|
if req.Params.RelType != nil {
|
||||||
|
relTypes = *req.Params.RelType
|
||||||
|
}
|
||||||
|
|
||||||
if req.Params.Root != nil && *req.Params.Root != "" {
|
if req.Params.Root != nil && *req.Params.Root != "" {
|
||||||
rootID, rerr := s.resolveEntityID(ctx, *req.Params.Root)
|
rootID, rerr := s.resolveEntityID(ctx, *req.Params.Root)
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
@@ -291,7 +300,7 @@ func (s *Server) GetGraph(ctx context.Context, req gen.GetGraphRequestObject) (g
|
|||||||
SELECT `+entityCols+`
|
SELECT `+entityCols+`
|
||||||
FROM blast_radius($1, $2, $3) b JOIN entities e ON e.id = b.entity_id
|
FROM blast_radius($1, $2, $3) b JOIN entities e ON e.id = b.entity_id
|
||||||
LEFT JOIN entity_status st ON st.entity_id = e.id
|
LEFT JOIN entity_status st ON st.entity_id = e.id
|
||||||
ORDER BY e.slug`, rootID, depth, req.Params.RelType)
|
ORDER BY e.slug`, rootID, depth, relTypes)
|
||||||
} else {
|
} else {
|
||||||
// Whole-graph view: pick the most-connected entities first so the
|
// Whole-graph view: pick the most-connected entities first so the
|
||||||
// graph shows actual topology, not just whatever sorts first
|
// graph shows actual topology, not just whatever sorts first
|
||||||
@@ -327,7 +336,7 @@ func (s *Server) GetGraph(ctx context.Context, req gen.GetGraphRequestObject) (g
|
|||||||
}
|
}
|
||||||
edgeRows, err := sqlcgen.New(s.pool).ListGraphEdges(ctx, sqlcgen.ListGraphEdgesParams{
|
edgeRows, err := sqlcgen.New(s.pool).ListGraphEdges(ctx, sqlcgen.ListGraphEdgesParams{
|
||||||
Ids: ids,
|
Ids: ids,
|
||||||
RelTypes: *req.Params.RelType,
|
RelTypes: relTypes,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -348,7 +357,7 @@ func (s *Server) GetGraph(ctx context.Context, req gen.GetGraphRequestObject) (g
|
|||||||
Type: r.Type,
|
Type: r.Type,
|
||||||
Attributes: attrs,
|
Attributes: attrs,
|
||||||
ValidFrom: r.ValidFrom,
|
ValidFrom: r.ValidFrom,
|
||||||
ValidTo: validTo,
|
ValidTo: validTo,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user