Skip to content

Commit

Permalink
feat: domain inspector
Browse files Browse the repository at this point in the history
  • Loading branch information
darin-nee committed Nov 7, 2023
1 parent 3558e39 commit 95f3db0
Show file tree
Hide file tree
Showing 3 changed files with 263 additions and 0 deletions.
181 changes: 181 additions & 0 deletions fastly/domain_inspector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package fastly

import (
"encoding/json"
"strconv"
"strings"
"time"
)

// DomainInspector represents the response format returned for a request to
// the historical Domain Inspector metrics endpoint.
type DomainInspector struct {
Data []DomainData `mapstructure:"data"`
Meta DomainMeta `mapstructure:"meta"`
Status string `mapstructure:"status"`
}

// DomainData represents the series of values over time for a single
// dimension combination.
type DomainData struct {
Dimensions map[string]string `mapstructure:"dimensions"`
Values []DomainMetrics `mapstructure:"values"`
}

// DomainMetrics represents the possible metrics that can be returned by a call
// to the Domain Inspector endpoints.
type DomainMetrics struct {
Bandwidth uint64 `mapstructure:"bandwidth"`
BereqBodyBytes uint64 `mapstructure:"bereq_body_bytes"`
BereqHeaderBytes uint64 `mapstructure:"bereq_header_bytes"`
EdgeHitRatio float64 `mapstructure:"edge_hit_ratio"`
EdgeHitRequests uint64 `mapstructure:"edge_hit_requests"`
EdgeMissRequests uint64 `mapstructure:"edge_miss_requests"`
EdgeRequests uint64 `mapstructure:"edge_requests"`
EdgeRespBodyBytes uint64 `mapstructure:"edge_resp_body_bytes"`
EdgeRespHeaderBytes uint64 `mapstructure:"edge_resp_header_bytes"`
OriginFetchRespBodyBytes uint64 `mapstructure:"origin_fetch_resp_body_bytes"`
OriginFetchRespHeaderBytes uint64 `mapstructure:"origin_fetch_resp_header_bytes"`
OriginFetches uint64 `mapstructure:"origin_fetches"`
OriginOffload float64 `mapstructure:"origin_offload"`
OriginStatus1xx uint64 `mapstructure:"origin_status_1xx"`
OriginStatus200 uint64 `mapstructure:"origin_status_200"`
OriginStatus204 uint64 `mapstructure:"origin_status_204"`
OriginStatus206 uint64 `mapstructure:"origin_status_206"`
OriginStatus2xx uint64 `mapstructure:"origin_status_2xx"`
OriginStatus301 uint64 `mapstructure:"origin_status_301"`
OriginStatus302 uint64 `mapstructure:"origin_status_302"`
OriginStatus304 uint64 `mapstructure:"origin_status_304"`
OriginStatus3xx uint64 `mapstructure:"origin_status_3xx"`
OriginStatus400 uint64 `mapstructure:"origin_status_400"`
OriginStatus401 uint64 `mapstructure:"origin_status_401"`
OriginStatus403 uint64 `mapstructure:"origin_status_403"`
OriginStatus404 uint64 `mapstructure:"origin_status_404"`
OriginStatus416 uint64 `mapstructure:"origin_status_416"`
OriginStatus429 uint64 `mapstructure:"origin_status_429"`
OriginStatus4xx uint64 `mapstructure:"origin_status_4xx"`
OriginStatus500 uint64 `mapstructure:"origin_status_500"`
OriginStatus501 uint64 `mapstructure:"origin_status_501"`
OriginStatus502 uint64 `mapstructure:"origin_status_502"`
OriginStatus503 uint64 `mapstructure:"origin_status_503"`
OriginStatus504 uint64 `mapstructure:"origin_status_504"`
OriginStatus505 uint64 `mapstructure:"origin_status_505"`
OriginStatus5xx uint64 `mapstructure:"origin_status_5xx"`
Requests uint64 `mapstructure:"requests"`
RespBodyBytes uint64 `mapstructure:"resp_body_bytes"`
RespHeaderBytes uint64 `mapstructure:"resp_header_bytes"`
Status1xx uint64 `mapstructure:"status_1xx"`
Status200 uint64 `mapstructure:"status_200"`
Status204 uint64 `mapstructure:"status_204"`
Status206 uint64 `mapstructure:"status_206"`
Status2xx uint64 `mapstructure:"status_2xx"`
Status301 uint64 `mapstructure:"status_301"`
Status302 uint64 `mapstructure:"status_302"`
Status304 uint64 `mapstructure:"status_304"`
Status3xx uint64 `mapstructure:"status_3xx"`
Status400 uint64 `mapstructure:"status_400"`
Status401 uint64 `mapstructure:"status_401"`
Status403 uint64 `mapstructure:"status_403"`
Status404 uint64 `mapstructure:"status_404"`
Status416 uint64 `mapstructure:"status_416"`
Status429 uint64 `mapstructure:"status_429"`
Status4xx uint64 `mapstructure:"status_4xx"`
Status500 uint64 `mapstructure:"status_500"`
Status501 uint64 `mapstructure:"status_501"`
Status502 uint64 `mapstructure:"status_502"`
Status503 uint64 `mapstructure:"status_503"`
Status504 uint64 `mapstructure:"status_504"`
Status505 uint64 `mapstructure:"status_505"`
Status5xx uint64 `mapstructure:"status_5xx"`
Timestamp uint64 `mapstructure:"timestamp"`
}

// DomainMeta is the meta section returned for /metrics/domain responses
type DomainMeta struct {
Downsample string `mapstructure:"downsample"`
End string `mapstructure:"end"`
Filters map[string]string `mapstructure:"filters"`
GroupBy string `mapstructure:"group_by"`
Limit int `mapstructure:"limit"`
Metric string `mapstructure:"metric"`
NextCursor string `mapstructure:"next_cursor"`
Sort string `mapstructure:"sort"`
Start string `mapstructure:"start"`
}

// GetDomainMetricsInput is the input to a DomainMetrics request.
type GetDomainMetricsInput struct {
// Cursor is the value from a previous response to retrieve the next page. To request the first page, this should be empty.
Cursor string
// Datacenters limits query to one or more specific POPs.
Datacenters []string
// Domains limit query to one or more specific domains.
Domains []string
// Downsample is the duration of sample windows.
Downsample string
// End is a valid ISO-8601-formatted date and time, or UNIX timestamp, indicating the exclusive end of the query time range. If not provided, a default is chosen based on the provided downsample value.
End time.Time
// GroupBy is the dimensions to return in the query.
GroupBy []string
// Limit is the limit of returned data
Limit int
// Metrics is the metric to retrieve. Up to ten metrics are accepted.
Metrics []string
// Regions limits query to one or more specific geographic regions.
Regions []string
// ServiceID is an alphanumeric string identifying the service.
ServiceID string
// Start is a valid ISO-8601-formatted date and time, or UNIX timestamp, indicating the inclusive start of the query time range. If not provided, a default is chosen based on the provided downsample value.
Start time.Time
}

// GetDomainMetricsForService retrieves the specified resource.
func (c *Client) GetDomainMetricsForService(i *GetDomainMetricsInput) (*DomainInspector, error) {
var resp interface{}
if err := c.GetDomainMetricsForServiceJSON(i, &resp); err != nil {
return nil, err
}

var di *DomainInspector
if err := decodeMap(resp, &di); err != nil {
return nil, err
}
return di, nil
}

// GetDomainMetricsForServiceJSON retrieves the specified resource.
func (c *Client) GetDomainMetricsForServiceJSON(i *GetDomainMetricsInput, dst interface{}) error {
if i.ServiceID == "" {
return ErrMissingServiceID
}

p := "/metrics/domains/services/" + i.ServiceID

start := ""
if !i.Start.IsZero() {
start = strconv.FormatInt(i.Start.Unix(), 10)
}
end := ""
if !i.End.IsZero() {
end = strconv.FormatInt(i.End.Unix(), 10)
}

r, err := c.Get(p, &RequestOptions{
Params: map[string]string{
"start": start,
"end": end,
"downsample": i.Downsample,
"metric": strings.Join(i.Metrics, ","),
"domain": strings.Join(i.Domains, ","),
"datacenter": strings.Join(i.Datacenters, ","),
"region": strings.Join(i.Regions, ","),
"cursor": i.Cursor,
},
})
if err != nil {
return err
}
defer r.Body.Close()

return json.NewDecoder(r.Body).Decode(dst)
}
34 changes: 34 additions & 0 deletions fastly/domain_inspector_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package fastly

import (
"testing"
"time"
)

func TestClient_GetDomainMetricsForService(t *testing.T) {
t.Parallel()

// NOTE: Update this to a recent time when regenerating the test fixtures,
// otherwise the data may be outside of retention and an error will be
// returned.
end := time.Date(2023, 11, 7, 0, 0, 0, 0, time.UTC)
start := end.Add(-8 * time.Hour)
var err error
record(t, "domain_inspector/metrics_for_service", func(c *Client) {
_, err = c.GetDomainMetricsForService(&GetDomainMetricsInput{
ServiceID: testServiceID,
Start: start,
End: end,
Domains: []string{"domain_1.com", "domain_2.com"},
Datacenters: []string{"SJC", "STP"},
Metrics: []string{"resp_body_bytes", "status_2xx"},
GroupBy: []string{"domain"},
Downsample: "hour",
Regions: []string{"usa"},
Cursor: "",
})
})
if err != nil {
t.Fatal(err)
}
}
48 changes: 48 additions & 0 deletions fastly/fixtures/domain_inspector/metrics_for_service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
version: 1
interactions:
- request:
body: ""
form: {}
headers:
User-Agent:
- FastlyGo/8.6.4 (+github.com/fastly/go-fastly; go1.20.4)
url: https://api.fastly.com/metrics/domains/services/7i6HN3TK9wS159v2gPAZ8A?cursor=&datacenter=SJC%2CSTP&domain=domain_1.com%2Cdomain_2.com&downsample=hour&end=1699315200&metric=resp_body_bytes%2Cstatus_2xx&region=usa&start=1699286400
method: GET
response:
body: |
{"data":[{"dimensions":{"domain":"domain_1.com"},"values":[{"resp_body_bytes":3441795,"status_2xx":20,"timestamp":1699286400},{"resp_body_bytes":3756657,"status_2xx":16,"timestamp":1699290000},{"resp_body_bytes":3531110,"status_2xx":13,"timestamp":1699293600},{"resp_body_bytes":3476768,"status_2xx":9,"timestamp":1699297200},{"resp_body_bytes":4810115,"status_2xx":16,"timestamp":1699300800},{"resp_body_bytes":5384860,"status_2xx":21,"timestamp":1699304400},{"resp_body_bytes":4037251,"status_2xx":11,"timestamp":1699308000},{"resp_body_bytes":7816494,"status_2xx":20,"timestamp":1699311600}]},{"dimensions":{"domain":"domain_2.com"},"values":[{"resp_body_bytes":13653987068,"status_2xx":516069,"timestamp":1699286400},{"resp_body_bytes":13565871035,"status_2xx":625752,"timestamp":1699290000},{"resp_body_bytes":13932434066,"status_2xx":522300,"timestamp":1699293600},{"resp_body_bytes":15202809344,"status_2xx":532950,"timestamp":1699297200},{"resp_body_bytes":16112253051,"status_2xx":517396,"timestamp":1699300800},{"resp_body_bytes":14427752268,"status_2xx":503554,"timestamp":1699304400},{"resp_body_bytes":13491682169,"status_2xx":488470,"timestamp":1699308000},{"resp_body_bytes":13376860809,"status_2xx":485002,"timestamp":1699311600}]}],"meta":{"start":"2023-11-06T16:00:00Z","end":"2023-11-07T00:00:00Z","downsample":"hour","metric":"resp_body_bytes,status_2xx","limit":100,"next_cursor":"","sort":"domain","group_by":"domain","filters":{"datacenters":"SJC,STP","domains":"domain_1.com,domain_2.com","regions":"usa"}},"status":"success"}
headers:
Accept-Ranges:
- bytes
Age:
- "0"
Cache-Control:
- no-store
Content-Length:
- "1561"
Content-Type:
- application/json
Date:
- Tue, 07 Nov 2023 23:20:14 GMT
Pragma:
- no-cache
Status:
- 200 OK
Strict-Transport-Security:
- max-age=31536000
Vary:
- Accept-Encoding
Via:
- 1.1 varnish, 1.1 varnish
X-Cache:
- MISS, MISS
X-Cache-Hits:
- 0, 0
X-Served-By:
- cache-control-cp-aws-us-east-2-prod-4-CONTROL-AWS-UE2, cache-pao-kpao1770049-PAO
X-Timer:
- S1699399214.860266,VS0,VE398
status: 200 OK
code: 200
duration: ""

0 comments on commit 95f3db0

Please sign in to comment.