diff --git a/cmd/query/app/apiv3/http_gateway.go b/cmd/query/app/apiv3/http_gateway.go index e4e8874d748..0c577183e9a 100644 --- a/cmd/query/app/apiv3/http_gateway.go +++ b/cmd/query/app/apiv3/http_gateway.go @@ -135,10 +135,26 @@ func (h *HTTPGateway) getTrace(w http.ResponseWriter, r *http.Request) { if h.tryParamError(w, err, paramTraceID) { return } - // TODO: add start time & end time request := spanstore.GetTraceParameters{ TraceID: traceID, } + http_query := r.URL.Query() + startTime := http_query.Get(paramStartTime) + if startTime != "" { + timeParsed, err := time.Parse(time.RFC3339Nano, startTime) + if h.tryParamError(w, err, paramStartTime) { + return + } + request.StartTime = timeParsed.UTC() + } + endTime := http_query.Get(paramEndTime) + if endTime != "" { + timeParsed, err := time.Parse(time.RFC3339Nano, endTime) + if h.tryParamError(w, err, paramEndTime) { + return + } + request.EndTime = timeParsed.UTC() + } trc, err := h.QueryService.GetTrace(r.Context(), request) if h.tryHandleError(w, err, http.StatusInternalServerError) { return diff --git a/cmd/query/app/apiv3/http_gateway_test.go b/cmd/query/app/apiv3/http_gateway_test.go index 66fda73ef17..90d00b3396a 100644 --- a/cmd/query/app/apiv3/http_gateway_test.go +++ b/cmd/query/app/apiv3/http_gateway_test.go @@ -18,6 +18,7 @@ import ( "go.uber.org/zap" "github.com/jaegertracing/jaeger/cmd/query/app/querysvc" + "github.com/jaegertracing/jaeger/model" "github.com/jaegertracing/jaeger/pkg/jtracer" "github.com/jaegertracing/jaeger/pkg/testutils" "github.com/jaegertracing/jaeger/storage/spanstore" @@ -90,27 +91,111 @@ func TestHTTPGatewayTryHandleError(t *testing.T) { assert.Contains(t, string(w.Body.String()), e, "writes error message to body") } -func TestHTTPGatewayGetTraceErrors(t *testing.T) { - gw := setupHTTPGatewayNoServer(t, "") +func TestHTTPGatewayGetTrace(t *testing.T) { + traceId, _ := model.TraceIDFromString("123") + testCases := []struct { + name string + params map[string]string + expectedQuery spanstore.GetTraceParameters + }{ + { + name: "TestGetTrace", + params: map[string]string{}, + expectedQuery: spanstore.GetTraceParameters{ + TraceID: traceId, + }, + }, + { + name: "TestGetTraceWithTimeWindow", + params: map[string]string{ + "start_time": "2000-01-02T12:30:08.999999998Z", + "end_time": "2000-04-05T21:55:16.999999992+08:00", + }, + expectedQuery: spanstore.GetTraceParameters{ + TraceID: traceId, + StartTime: time.Date(2000, time.January, 0o2, 12, 30, 8, 999999998, time.UTC), + EndTime: time.Date(2000, time.April, 0o5, 13, 55, 16, 999999992, time.UTC), + }, + }, + } - // malformed trace id - r, err := http.NewRequest(http.MethodGet, "/api/v3/traces/xyz", nil) - require.NoError(t, err) - w := httptest.NewRecorder() - gw.router.ServeHTTP(w, r) - assert.Contains(t, w.Body.String(), "malformed parameter trace_id") + testUri := "/api/v3/traces/123" - // error from span reader - const simErr = "simulated error" + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + gw := setupHTTPGatewayNoServer(t, "") + gw.reader. + On("GetTrace", matchContext, tc.expectedQuery). + Return(&model.Trace{}, nil).Once() + + q := url.Values{} + for k, v := range tc.params { + q.Set(k, v) + } + testUrl := testUri + if len(tc.params) > 0 { + testUrl += "?" + q.Encode() + } + + r, err := http.NewRequest(http.MethodGet, testUrl, nil) + require.NoError(t, err) + w := httptest.NewRecorder() + gw.router.ServeHTTP(w, r) + gw.reader.AssertCalled(t, "GetTrace", matchContext, tc.expectedQuery) + }) + } +} + +func TestHTTPGatewayGetTraceMalformedInputErrors(t *testing.T) { + testCases := []struct { + name string + requestUrl string + expectedError string + }{ + { + name: "TestGetTrace", + requestUrl: "/api/v3/traces/xyz", + expectedError: "malformed parameter trace_id", + }, + { + name: "TestGetTraceWithInvalidStartTime", + requestUrl: "/api/v3/traces/123?start_time=abc", + expectedError: "malformed parameter start_time", + }, + { + name: "TestGetTraceWithInvalidEndTime", + requestUrl: "/api/v3/traces/123?end_time=xyz", + expectedError: "malformed parameter end_time", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + gw := setupHTTPGatewayNoServer(t, "") + gw.reader. + On("GetTrace", matchContext, matchGetTraceParameters). + Return(&model.Trace{}, nil).Once() + + r, err := http.NewRequest(http.MethodGet, tc.requestUrl, nil) + require.NoError(t, err) + w := httptest.NewRecorder() + gw.router.ServeHTTP(w, r) + assert.Contains(t, w.Body.String(), tc.expectedError) + }) + } +} + +func TestHTTPGatewayGetTraceInternalErrors(t *testing.T) { + gw := setupHTTPGatewayNoServer(t, "") gw.reader. On("GetTrace", matchContext, matchGetTraceParameters). - Return(nil, errors.New(simErr)).Once() + Return(nil, assert.AnError).Once() - r, err = http.NewRequest(http.MethodGet, "/api/v3/traces/123", nil) + r, err := http.NewRequest(http.MethodGet, "/api/v3/traces/123", nil) require.NoError(t, err) - w = httptest.NewRecorder() + w := httptest.NewRecorder() gw.router.ServeHTTP(w, r) - assert.Contains(t, w.Body.String(), simErr) + assert.Contains(t, w.Body.String(), assert.AnError.Error()) } func mockFindQueries() (url.Values, *spanstore.TraceQueryParameters) {