Skip to content

Commit b32c492

Browse files
committed
feat: implement GetDependencies method for api_v3 QueryService
- Add GetDependencies gRPC handler in cmd/query/app/querysvc/v3api/ - Implement request validation for end_time and lookback parameters - Add comprehensive test coverage with 8 test cases - Support for proto-defined GetDependenciesRequest/Response types - Proper error handling and logging - Add GetDependencyReader getter to v2 QueryService - Update server.go to register v3api handlers Signed-off-by: harsh6921 <[email protected]>
1 parent 528476f commit b32c492

File tree

14 files changed

+1120
-243
lines changed

14 files changed

+1120
-243
lines changed

cmd/query/app/querysvc/v2/querysvc/service.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,12 @@ func (qs QueryService) GetCapabilities() StorageCapabilities {
167167
}
168168
}
169169

170+
// GetDependencyReader returns the dependency store reader
171+
func (qs QueryService) GetDependencyReader() depstore.Reader {
172+
return qs.dependencyReader
173+
}
174+
175+
170176
func (opts *QueryServiceOptions) hasArchiveStorage() bool {
171177
return opts.ArchiveTraceReader != nil && opts.ArchiveTraceWriter != nil
172178
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package v3api
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
7+
8+
api_v3 "github.com/jaegertracing/jaeger-idl/proto-gen/api_v3"
9+
"github.com/jaegertracing/jaeger/internal/storage/v1/api/dependencystore"
10+
"go.uber.org/zap"
11+
"google.golang.org/grpc/codes"
12+
"google.golang.org/grpc/status"
13+
)
14+
15+
// Handler implements api_v3.QueryServiceServer
16+
type Handler struct {
17+
api_v3.UnimplementedQueryServiceServer
18+
19+
depReader dependencystore.Reader
20+
logger *zap.Logger
21+
}
22+
23+
// HandlerOptions contains options for creating a new Handler
24+
type HandlerOptions struct {
25+
DependencyReader dependencystore.Reader
26+
Logger *zap.Logger
27+
}
28+
29+
// NewHandler creates a new v3 API handler
30+
func NewHandler(options HandlerOptions) *Handler {
31+
return &Handler{
32+
depReader: options.DependencyReader,
33+
logger: options.Logger,
34+
}
35+
}
36+
37+
// GetDependencies retrieves service dependency links for a given time interval
38+
func (h *Handler) GetDependencies(
39+
ctx context.Context,
40+
req *api_v3.GetDependenciesRequest,
41+
) (*api_v3.GetDependenciesResponse, error) {
42+
// Validate request
43+
if err := h.validateGetDependenciesRequest(req); err != nil {
44+
h.logger.Error("Invalid GetDependencies request", zap.Error(err))
45+
return nil, err
46+
}
47+
48+
endTime := req.EndTime.AsTime()
49+
lookback := req.Lookback.AsDuration()
50+
51+
h.logger.Debug("Fetching dependencies",
52+
zap.Time("end_time", endTime),
53+
zap.Duration("lookback", lookback),
54+
)
55+
56+
// Fetch dependencies from storage
57+
dependencies, err := h.depReader.GetDependencies(ctx, endTime, lookback)
58+
if err != nil {
59+
h.logger.Error("Failed to fetch dependencies from storage", zap.Error(err))
60+
return nil, status.Error(
61+
codes.Internal,
62+
fmt.Sprintf("failed to fetch dependencies: %v", err),
63+
)
64+
}
65+
66+
// Convert model.DependencyLink to api_v3.DependencyLink
67+
links := make([]*api_v3.DependencyLink, 0, len(dependencies))
68+
for _, dep := range dependencies {
69+
links = append(links, &api_v3.DependencyLink{
70+
Parent: dep.Parent,
71+
Child: dep.Child,
72+
CallCount: dep.CallCount,
73+
Source: dep.Source,
74+
})
75+
}
76+
77+
h.logger.Debug("Successfully fetched dependencies",
78+
zap.Int("count", len(links)),
79+
)
80+
81+
return &api_v3.GetDependenciesResponse{
82+
Dependencies: links,
83+
}, nil
84+
}
85+
86+
// validateGetDependenciesRequest validates the GetDependencies request parameters
87+
func (h *Handler) validateGetDependenciesRequest(req *api_v3.GetDependenciesRequest) error {
88+
if req == nil {
89+
return status.Error(codes.InvalidArgument, "request cannot be nil")
90+
}
91+
92+
if req.EndTime == nil {
93+
return status.Error(codes.InvalidArgument, "end_time is required")
94+
}
95+
96+
if req.Lookback == nil {
97+
return status.Error(codes.InvalidArgument, "lookback is required")
98+
}
99+
100+
endTime := req.EndTime.AsTime()
101+
lookback := req.Lookback.AsDuration()
102+
103+
// Validate lookback is positive
104+
if lookback <= 0 {
105+
return status.Error(
106+
codes.InvalidArgument,
107+
"lookback must be a positive duration",
108+
)
109+
}
110+
111+
// Validate end_time is not too far in the future (allow small clock skew)
112+
maxFutureTime := time.Now().Add(1 * time.Hour)
113+
if endTime.After(maxFutureTime) {
114+
return status.Error(
115+
codes.InvalidArgument,
116+
"end_time cannot be more than 1 hour in the future",
117+
)
118+
}
119+
120+
// Validate the time range is reasonable (not too far back)
121+
startTime := endTime.Add(-lookback)
122+
minTime := time.Unix(0, 0)
123+
if startTime.Before(minTime) {
124+
return status.Error(
125+
codes.InvalidArgument,
126+
"time range extends before Unix epoch",
127+
)
128+
}
129+
130+
return nil
131+
}

0 commit comments

Comments
 (0)