Skip to content

Commit 200c3d1

Browse files
committed
Add http logic for ListBaselineStatusCounts
This change adds the logic to call the spanner adapter layer for ListBaselineStatusCounts This is similar to ListMissingOneImplCounts (also used on the stats page)
1 parent 397ba9c commit 200c3d1

File tree

4 files changed

+246
-5
lines changed

4 files changed

+246
-5
lines changed

backend/pkg/httpserver/list_aggregated_baseline_status_counts.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ package httpserver
1616

1717
import (
1818
"context"
19-
"net/http"
19+
"errors"
20+
"log/slog"
2021

22+
"github.com/GoogleChrome/webstatus.dev/lib/gcpspanner/spanneradapters/backendtypes"
2123
"github.com/GoogleChrome/webstatus.dev/lib/gen/openapi/backend"
2224
)
2325

@@ -26,8 +28,30 @@ import (
2628
func (s *Server) ListAggregatedBaselineStatusCounts(
2729
ctx context.Context, request backend.ListAggregatedBaselineStatusCountsRequestObject) (
2830
backend.ListAggregatedBaselineStatusCountsResponseObject, error) {
29-
return backend.ListAggregatedBaselineStatusCounts400JSONResponse{
30-
Code: http.StatusBadRequest,
31-
Message: "TODO",
32-
}, nil
31+
page, err := s.wptMetricsStorer.ListBaselineStatusCounts(
32+
ctx,
33+
request.Params.StartAt.Time,
34+
request.Params.EndAt.Time,
35+
getPageSizeOrDefault(request.Params.PageSize),
36+
request.Params.PageToken,
37+
)
38+
if err != nil {
39+
if errors.Is(err, backendtypes.ErrInvalidPageToken) {
40+
slog.WarnContext(ctx, "invalid page token", "token", request.Params.PageToken, "error", err)
41+
42+
return backend.ListAggregatedBaselineStatusCounts400JSONResponse{
43+
Code: 400,
44+
Message: "invalid page token",
45+
}, nil
46+
}
47+
48+
slog.ErrorContext(ctx, "unable to get missing one implementation count", "error", err)
49+
50+
return backend.ListAggregatedBaselineStatusCounts500JSONResponse{
51+
Code: 500,
52+
Message: "unable to get missing one implementation metrics",
53+
}, nil
54+
}
55+
56+
return backend.ListAggregatedBaselineStatusCounts200JSONResponse(*page), nil
3357
}
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package httpserver
16+
17+
import (
18+
"net/http"
19+
"net/http/httptest"
20+
"testing"
21+
"time"
22+
23+
"github.com/GoogleChrome/webstatus.dev/lib/gcpspanner/spanneradapters/backendtypes"
24+
"github.com/GoogleChrome/webstatus.dev/lib/gen/openapi/backend"
25+
)
26+
27+
func TestListAggregatedBaselineStatusCounts(t *testing.T) {
28+
testCases := []struct {
29+
name string
30+
mockConfig MockListBaselineStatusCountsConfig
31+
expectedCallCount int
32+
request *http.Request
33+
expectedResponse *http.Response
34+
}{
35+
{
36+
name: "Success Case - no optional params - use defaults",
37+
mockConfig: MockListBaselineStatusCountsConfig{
38+
expectedStartAt: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC),
39+
expectedEndAt: time.Date(2000, time.January, 10, 0, 0, 0, 0, time.UTC),
40+
expectedPageSize: 100,
41+
expectedPageToken: nil,
42+
pageToken: nil,
43+
err: nil,
44+
page: &backend.BaselineStatusMetricsPage{
45+
Metadata: &backend.PageMetadata{
46+
NextPageToken: nil,
47+
},
48+
Data: []backend.BaselineStatusMetric{
49+
{
50+
Count: valuePtr[int64](10),
51+
Timestamp: time.Date(2000, time.January, 10, 0, 0, 0, 0, time.UTC),
52+
},
53+
{
54+
Count: valuePtr[int64](9),
55+
Timestamp: time.Date(2000, time.January, 9, 0, 0, 0, 0, time.UTC),
56+
},
57+
},
58+
},
59+
},
60+
expectedCallCount: 1,
61+
expectedResponse: testJSONResponse(200, `
62+
{
63+
"data":[
64+
{
65+
"count":10,
66+
"timestamp":"2000-01-10T00:00:00Z"
67+
},
68+
{
69+
"count":9,
70+
"timestamp":"2000-01-09T00:00:00Z"
71+
}
72+
],
73+
"metadata":{
74+
75+
}
76+
}`),
77+
request: httptest.NewRequest(http.MethodGet,
78+
"/v1/stats/baseline_status/low_date_feature_counts?"+
79+
"startAt=2000-01-01&endAt=2000-01-10", nil),
80+
},
81+
{
82+
name: "Success Case - include optional params",
83+
mockConfig: MockListBaselineStatusCountsConfig{
84+
expectedStartAt: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC),
85+
expectedEndAt: time.Date(2000, time.January, 10, 0, 0, 0, 0, time.UTC),
86+
expectedPageSize: 50,
87+
expectedPageToken: inputPageToken,
88+
err: nil,
89+
page: &backend.BaselineStatusMetricsPage{
90+
Metadata: &backend.PageMetadata{
91+
NextPageToken: nextPageToken,
92+
},
93+
Data: []backend.BaselineStatusMetric{
94+
{
95+
Count: valuePtr[int64](10),
96+
Timestamp: time.Date(2000, time.January, 10, 0, 0, 0, 0, time.UTC),
97+
},
98+
{
99+
Count: valuePtr[int64](9),
100+
Timestamp: time.Date(2000, time.January, 9, 0, 0, 0, 0, time.UTC),
101+
},
102+
},
103+
},
104+
pageToken: nextPageToken,
105+
},
106+
expectedCallCount: 1,
107+
expectedResponse: testJSONResponse(200, `
108+
{
109+
"data":[
110+
{
111+
"count":10,
112+
"timestamp":"2000-01-10T00:00:00Z"
113+
},
114+
{
115+
"count":9,
116+
"timestamp":"2000-01-09T00:00:00Z"
117+
}
118+
],
119+
"metadata":{
120+
"next_page_token":"next-page-token"
121+
}
122+
}`),
123+
request: httptest.NewRequest(http.MethodGet,
124+
"/v1/stats/baseline_status/low_date_feature_counts?"+
125+
"startAt=2000-01-01&endAt=2000-01-10&page_size=50&page_token="+*inputPageToken, nil),
126+
},
127+
{
128+
name: "500 case",
129+
mockConfig: MockListBaselineStatusCountsConfig{
130+
expectedStartAt: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC),
131+
expectedEndAt: time.Date(2000, time.January, 10, 0, 0, 0, 0, time.UTC),
132+
expectedPageSize: 100,
133+
expectedPageToken: nil,
134+
page: nil,
135+
pageToken: nil,
136+
err: errTest,
137+
},
138+
expectedCallCount: 1,
139+
expectedResponse: testJSONResponse(
140+
500, `{"code":500,"message":"unable to get missing one implementation metrics"}`),
141+
request: httptest.NewRequest(http.MethodGet,
142+
"/v1/stats/baseline_status/low_date_feature_counts?"+
143+
"startAt=2000-01-01&endAt=2000-01-10", nil),
144+
},
145+
{
146+
name: "400 case - invalid page token",
147+
mockConfig: MockListBaselineStatusCountsConfig{
148+
expectedStartAt: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC),
149+
expectedEndAt: time.Date(2000, time.January, 10, 0, 0, 0, 0, time.UTC),
150+
expectedPageSize: 100,
151+
expectedPageToken: badPageToken,
152+
page: nil,
153+
pageToken: nil,
154+
err: backendtypes.ErrInvalidPageToken,
155+
},
156+
expectedCallCount: 1,
157+
expectedResponse: testJSONResponse(400, `{"code":400,"message":"invalid page token"}`),
158+
request: httptest.NewRequest(http.MethodGet,
159+
"/v1/stats/baseline_status/low_date_feature_counts?"+
160+
"startAt=2000-01-01&endAt=2000-01-10&page_token"+*badPageToken, nil),
161+
},
162+
}
163+
164+
for _, tc := range testCases {
165+
t.Run(tc.name, func(t *testing.T) {
166+
// nolint: exhaustruct
167+
mockStorer := &MockWPTMetricsStorer{
168+
listBaselineStatusCountsCfg: tc.mockConfig,
169+
t: t,
170+
}
171+
myServer := Server{wptMetricsStorer: mockStorer, metadataStorer: nil}
172+
assertTestServerRequest(t, &myServer, tc.request, tc.expectedResponse)
173+
assertMockCallCount(t, tc.expectedCallCount, mockStorer.callCountListBaselineStatusCounts,
174+
"ListBaselineStatusCounts")
175+
})
176+
}
177+
}

backend/pkg/httpserver/server.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ type WPTMetricsStorer interface {
9797
pageSize int,
9898
pageToken *string,
9999
) (*backend.BrowserReleaseFeatureMetricsPage, error)
100+
ListBaselineStatusCounts(
101+
ctx context.Context,
102+
startAt time.Time,
103+
endAt time.Time,
104+
pageSize int,
105+
pageToken *string,
106+
) (*backend.BaselineStatusMetricsPage, error)
100107
}
101108

102109
type Server struct {

backend/pkg/httpserver/server_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,17 +144,29 @@ type MockListMissingOneImplCountsConfig struct {
144144
err error
145145
}
146146

147+
type MockListBaselineStatusCountsConfig struct {
148+
expectedStartAt time.Time
149+
expectedEndAt time.Time
150+
expectedPageSize int
151+
expectedPageToken *string
152+
pageToken *string
153+
page *backend.BaselineStatusMetricsPage
154+
err error
155+
}
156+
147157
type MockWPTMetricsStorer struct {
148158
featureCfg MockListMetricsForFeatureIDBrowserAndChannelConfig
149159
aggregateCfg MockListMetricsOverTimeWithAggregatedTotalsConfig
150160
featuresSearchCfg MockFeaturesSearchConfig
151161
listBrowserFeatureCountMetricCfg MockListBrowserFeatureCountMetricConfig
152162
listMissingOneImplCountCfg MockListMissingOneImplCountsConfig
163+
listBaselineStatusCountsCfg MockListBaselineStatusCountsConfig
153164
listChromiumDailyUsageStatsCfg MockListChromiumDailyUsageStatsConfig
154165
getFeatureByIDConfig MockGetFeatureByIDConfig
155166
getIDFromFeatureKeyConfig MockGetIDFromFeatureKeyConfig
156167
t *testing.T
157168
callCountListMissingOneImplCounts int
169+
callCountListBaselineStatusCounts int
158170
callCountListBrowserFeatureCountMetric int
159171
callCountFeaturesSearch int
160172
callCountListChromiumDailyUsageStats int
@@ -340,6 +352,27 @@ func (m *MockWPTMetricsStorer) ListMissingOneImplCounts(
340352
return m.listMissingOneImplCountCfg.page, m.listMissingOneImplCountCfg.err
341353
}
342354

355+
func (m *MockWPTMetricsStorer) ListBaselineStatusCounts(
356+
_ context.Context,
357+
startAt time.Time,
358+
endAt time.Time,
359+
pageSize int,
360+
pageToken *string,
361+
) (*backend.BaselineStatusMetricsPage, error) {
362+
m.callCountListBaselineStatusCounts++
363+
364+
if !startAt.Equal(m.listBaselineStatusCountsCfg.expectedStartAt) ||
365+
!endAt.Equal(m.listBaselineStatusCountsCfg.expectedEndAt) ||
366+
pageSize != m.listBaselineStatusCountsCfg.expectedPageSize ||
367+
!reflect.DeepEqual(pageToken, m.listBaselineStatusCountsCfg.expectedPageToken) {
368+
369+
m.t.Errorf("Incorrect arguments. Expected: %v, Got: { %s, %s, %d %v }",
370+
m.listBaselineStatusCountsCfg, startAt, endAt, pageSize, pageToken)
371+
}
372+
373+
return m.listBaselineStatusCountsCfg.page, m.listBaselineStatusCountsCfg.err
374+
}
375+
343376
func TestGetPageSizeOrDefault(t *testing.T) {
344377
testCases := []struct {
345378
name string

0 commit comments

Comments
 (0)