Skip to content
This repository has been archived by the owner on Dec 10, 2024. It is now read-only.

Commit

Permalink
feat: add support for getting DORA metrics from projects and groups
Browse files Browse the repository at this point in the history
  • Loading branch information
heidiberry committed May 4, 2024
1 parent f3f2bd3 commit bd2b78d
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 0 deletions.
96 changes: 96 additions & 0 deletions dora_metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package gitlab

import (
"fmt"
"net/http"
)

// DORAMetric represents a single DORA metric data point.
//
// Gitlab API docs: https://docs.gitlab.com/ee/api/dora/metrics.html
type DORAMetric struct {
Date string `json:"date"`
Value float64 `json:"value"`
}

// Gets a string representation of a DORAMetric data point
//
// GitLab API docs: https://docs.gitlab.com/ee/api/dora/metrics.html
func (m DORAMetric) String() string {
return Stringify(m)
}

// GetDORAMetricsOptions represent the request body options for getting
// DORA metrics
//
// GitLab API docs: https://docs.gitlab.com/ee/api/dora/metrics.html
type GetDORAMetricsOptions struct {
Metric DORAMetricType `url:"metric,omitempty" json:"metric,omitempty"`
EndDate *ISOTime `url:"end_date,omitempty" json:"end_date,omitempty"`
EnvironmentTiers *[]string `url:"environment_tiers,omitempty" del:"," json:"environment_tiers,omitempty"`
Interval *DORAMetricInterval `url:"interval,omitempty" json:"interval,omitempty"`
StartDate *ISOTime `url:"start_date,omitempty" json:"start_date,omitempty"`

// Deprecated, use environment tiers instead
EnvironmentTier *string `url:"environment_tier,omitempty" json:"environment_tier,omitempty"`
}

// DORAMetricsService handles communication with the DORA metrics related methods
// of the GitLab API
//
// Gitlab API docs: https://docs.gitlab.com/ee/api/dora/metrics.html
type DORAMetricsService struct {
client *Client
}

// GetProjectDORAMetrics gets the DORA metrics for a project.
//
// GitLab API Docs:
// https://docs.gitlab.com/ee/api/dora/metrics.html#get-project-level-dora-metrics
func (s *DORAMetricsService) GetProjectDORAMetrics(pid interface{}, opt GetDORAMetricsOptions, options ...RequestOptionFunc) ([]DORAMetric, *Response, error) {
project, err := parseID(pid)
if err != nil {
return nil, nil, err
}

u := fmt.Sprintf("projects/%s/dora/metrics", PathEscape(project))

req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
if err != nil {
return nil, nil, err
}

var metrics []DORAMetric
resp, err := s.client.Do(req, &metrics)
if err != nil {
return nil, resp, err
}

return metrics, resp, err
}

// GetGroupDORAMetrics gets the DORA metrics for a group.
//
// GitLab API Docs:
// https://docs.gitlab.com/ee/api/dora/metrics.html#get-group-level-dora-metrics
func (s *DORAMetricsService) GetGroupDORAMetrics(gid interface{}, opt GetDORAMetricsOptions, options ...RequestOptionFunc) ([]DORAMetric, *Response, error) {
group, err := parseID(gid)
if err != nil {
return nil, nil, err
}

u := fmt.Sprintf("groups/%s/dora/metrics", PathEscape(group))

req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
if err != nil {
return nil, nil, err
}

var metrics []DORAMetric
resp, err := s.client.Do(req, &metrics)
if err != nil {
return nil, resp, err
}

return metrics, resp, err
}
118 changes: 118 additions & 0 deletions dora_metrics_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package gitlab

import (
"fmt"
"net/http"
"testing"
"time"

"github.com/stretchr/testify/require"
)

func TestDORAMetrics_GetProjectDORAMetrics(t *testing.T) {
mux, client := setup(t)

mux.HandleFunc("/api/v4/projects/1/dora/metrics", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodGet)
query := r.URL.Query()
for k, v := range map[string]string{
"metric": "deployment_frequency",
"start_date": "2021-03-01",
"end_date": "2021-03-08",
} {
if query.Get(k) != v {
t.Errorf("Query parameter %s: %s, want %s", k, query.Get(k), v)
}
}

fmt.Fprint(w, `
[
{ "date": "2021-03-01", "value": 3 },
{ "date": "2021-03-02", "value": 6 },
{ "date": "2021-03-03", "value": 0 },
{ "date": "2021-03-04", "value": 0 },
{ "date": "2021-03-05", "value": 0 },
{ "date": "2021-03-06", "value": 0 },
{ "date": "2021-03-07", "value": 0 },
{ "date": "2021-03-08", "value": 4 }
]
`)
})

want := []DORAMetric{
{Date: "2021-03-01", Value: 3},
{Date: "2021-03-02", Value: 6},
{Date: "2021-03-03", Value: 0},
{Date: "2021-03-04", Value: 0},
{Date: "2021-03-05", Value: 0},
{Date: "2021-03-06", Value: 0},
{Date: "2021-03-07", Value: 0},
{Date: "2021-03-08", Value: 4},
}

startDate := ISOTime(time.Date(2021, time.March, 1, 0, 0, 0, 0, time.UTC))
endDate := ISOTime(time.Date(2021, time.March, 8, 0, 0, 0, 0, time.UTC))

d, resp, err := client.DORAMetrics.GetProjectDORAMetrics(1, GetDORAMetricsOptions{
Metric: DORAMetricDeploymentFrequency,
StartDate: &startDate,
EndDate: &endDate,
})
require.NoError(t, err)
require.NotNil(t, resp)
require.Equal(t, want, d)
}

func TestDORAMetrics_GetGroupDORAMetrics(t *testing.T) {
mux, client := setup(t)

mux.HandleFunc("/api/v4/groups/1/dora/metrics", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodGet)
query := r.URL.Query()
for k, v := range map[string]string{
"metric": "deployment_frequency",
"start_date": "2021-03-01",
"end_date": "2021-03-08",
} {
if query.Get(k) != v {
t.Errorf("Query parameter %s: %s, want %s", k, query.Get(k), v)
}
}

fmt.Fprint(w, `
[
{ "date": "2021-03-01", "value": 3 },
{ "date": "2021-03-02", "value": 6 },
{ "date": "2021-03-03", "value": 0 },
{ "date": "2021-03-04", "value": 0 },
{ "date": "2021-03-05", "value": 0 },
{ "date": "2021-03-06", "value": 0 },
{ "date": "2021-03-07", "value": 0 },
{ "date": "2021-03-08", "value": 4 }
]
`)
})

want := []DORAMetric{
{Date: "2021-03-01", Value: 3},
{Date: "2021-03-02", Value: 6},
{Date: "2021-03-03", Value: 0},
{Date: "2021-03-04", Value: 0},
{Date: "2021-03-05", Value: 0},
{Date: "2021-03-06", Value: 0},
{Date: "2021-03-07", Value: 0},
{Date: "2021-03-08", Value: 4},
}

startDate := ISOTime(time.Date(2021, time.March, 1, 0, 0, 0, 0, time.UTC))
endDate := ISOTime(time.Date(2021, time.March, 8, 0, 0, 0, 0, time.UTC))

d, resp, err := client.DORAMetrics.GetGroupDORAMetrics(1, GetDORAMetricsOptions{
Metric: DORAMetricDeploymentFrequency,
StartDate: &startDate,
EndDate: &endDate,
})
require.NoError(t, err)
require.NotNil(t, resp)
require.Equal(t, want, d)
}
2 changes: 2 additions & 0 deletions gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ type Client struct {
Deployments *DeploymentsService
Discussions *DiscussionsService
DockerfileTemplate *DockerfileTemplatesService
DORAMetrics *DORAMetricsService
DraftNotes *DraftNotesService
Environments *EnvironmentsService
EpicIssues *EpicIssuesService
Expand Down Expand Up @@ -358,6 +359,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) {
c.Deployments = &DeploymentsService{client: c}
c.Discussions = &DiscussionsService{client: c}
c.DockerfileTemplate = &DockerfileTemplatesService{client: c}
c.DORAMetrics = &DORAMetricsService{client: c}
c.DraftNotes = &DraftNotesService{client: c}
c.Environments = &EnvironmentsService{client: c}
c.EpicIssues = &EpicIssuesService{client: c}
Expand Down
30 changes: 30 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,36 @@ func DeploymentStatus(v DeploymentStatusValue) *DeploymentStatusValue {
return Ptr(v)
}

// DORAMetricType represents the name of the four types of metric in DORA
//
// GitLab API docs: https://docs.gitlab.com/ee/api/dora/metrics.html
type DORAMetricType string

// List of available DORA metric type names
//
// GitLab API docs: https://docs.gitlab.com/ee/api/dora/metrics.html
const (
DORAMetricDeploymentFrequency DORAMetricType = "deployment_frequency"
DORAMetricLeadTimeForChanges DORAMetricType = "lead_time_for_changes"
DORAMetricTimeToRestoreService DORAMetricType = "time_to_restore_service"
DORAMetricChangeFailureRate DORAMetricType = "change_failure_rate"
)

// DORAMetricInterval represents the time period over which the
// metrics are aggregated
//
// GitLab API docs: https://docs.gitlab.com/ee/api/dora/metrics.html
type DORAMetricInterval string

// List of available DORA metric interval types
//
// GitLab API docs: https://docs.gitlab.com/ee/api/dora/metrics.html
const (
DORAMetricIntervalDaily DORAMetricInterval = "daily"
DORAMetricIntervalMonthly DORAMetricInterval = "monthly"
DORAMetricIntervalAll DORAMetricInterval = "all"
)

// EventTypeValue represents actions type for contribution events
type EventTypeValue string

Expand Down

0 comments on commit bd2b78d

Please sign in to comment.