Skip to content

Commit d917b68

Browse files
itsgarethlehmanju
andauthored
External: Get workflow runs (#417)
this adds some suggested changes to the external pr that was opened #397. this pr hasn't been updated or commented on in the past week so we have made the suggested changes. --------- Co-authored-by: Julius Lehmann <[email protected]>
1 parent a9f2d33 commit d917b68

File tree

18 files changed

+465
-4
lines changed

18 files changed

+465
-4
lines changed

docs/sources/_index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ weight: 10
1919

2020
The GitHub data source plugin for Grafana lets you to query the GitHub API in Grafana so you can visualize your GitHub repositories and projects.
2121

22-
Watch this video to learn more about setting up the Grafana GitHub data source plugin: {{< youtube id="DW693S3cO48">}}
22+
Watch this video to learn more about setting up the Grafana GitHub data source plugin: {{< youtube id="DW693S3cO48">}}
2323

2424
{{< docs/play title="GitHub data source plugin demo" url="https://play.grafana.org/d/cdgx261sa1ypsa/3-single-repo-with-override-examples" >}}
2525

@@ -42,6 +42,7 @@ The plugin supports the following query types:
4242
- Stargazers
4343
- Workflows
4444
- Workflow usage
45+
- Workflow runs
4546

4647
## Supported features
4748

pkg/github/client/client.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ func (client *Client) GetWorkflowUsage(ctx context.Context, owner, repo, workflo
177177
}
178178
var workflowRuns []*googlegithub.WorkflowRun
179179
var err error
180-
workflowRuns, page, err = client.getWorkflowRuns(ctx, owner, repo, workflow, timeRange, page)
180+
workflowRuns, page, err = client.getWorkflowRuns(ctx, owner, repo, workflow, "", timeRange, page)
181181
if err != nil {
182182
return models.WorkflowUsage{}, fmt.Errorf("fetching workflow runs: %w", err)
183183
}
@@ -280,7 +280,29 @@ func (client *Client) getWorkflowUsage(ctx context.Context, owner, repo string,
280280
return client.restClient.Actions.GetWorkflowUsageByFileName(ctx, owner, repo, workflow)
281281
}
282282

283-
func (client *Client) getWorkflowRuns(ctx context.Context, owner, repo, workflow string, timeRange backend.TimeRange, page int) ([]*googlegithub.WorkflowRun, int, error) {
283+
func (client *Client) GetWorkflowRuns(ctx context.Context, owner, repo, workflow string, branch string, timeRange backend.TimeRange) ([]*googlegithub.WorkflowRun, error) {
284+
workflowRuns := []*googlegithub.WorkflowRun{}
285+
286+
page := 1
287+
for {
288+
if page == 0 {
289+
break
290+
}
291+
292+
workflowRunsPage, nextPage, err := client.getWorkflowRuns(ctx, owner, repo, workflow, branch, timeRange, page)
293+
if err != nil {
294+
return nil, fmt.Errorf("fetching workflow runs: %w", err)
295+
}
296+
297+
workflowRuns = append(workflowRuns, workflowRunsPage...)
298+
299+
page = nextPage
300+
}
301+
302+
return workflowRuns, nil
303+
}
304+
305+
func (client *Client) getWorkflowRuns(ctx context.Context, owner, repo, workflow string, branch string, timeRange backend.TimeRange, page int) ([]*googlegithub.WorkflowRun, int, error) {
284306
workflowID, _ := strconv.ParseInt(workflow, 10, 64)
285307

286308
workflowRuns := []*googlegithub.WorkflowRun{}
@@ -298,11 +320,13 @@ func (client *Client) getWorkflowRuns(ctx context.Context, owner, repo, workflow
298320
runs, response, err = client.restClient.Actions.ListWorkflowRunsByID(ctx, owner, repo, workflowID, &googlegithub.ListWorkflowRunsOptions{
299321
Created: created,
300322
ListOptions: googlegithub.ListOptions{Page: page, PerPage: 100},
323+
Branch: branch,
301324
})
302325
} else {
303326
runs, response, err = client.restClient.Actions.ListWorkflowRunsByFileName(ctx, owner, repo, workflow, &googlegithub.ListWorkflowRunsOptions{
304327
Created: created,
305328
ListOptions: googlegithub.ListOptions{Page: page, PerPage: 100},
329+
Branch: branch,
306330
})
307331
}
308332

pkg/github/datasource.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,18 @@ func (d *Datasource) HandleWorkflowUsageQuery(ctx context.Context, query *models
180180
return GetWorkflowUsage(ctx, d.client, opt, req.TimeRange)
181181
}
182182

183+
// HandleWorkflowRunsQuery is the query handler for listing workflow runs of a GitHub repository
184+
func (d *Datasource) HandleWorkflowRunsQuery(ctx context.Context, query *models.WorkflowRunsQuery, req backend.DataQuery) (dfutil.Framer, error) {
185+
opt := models.WorkflowRunsOptions{
186+
Repository: query.Repository,
187+
Owner: query.Owner,
188+
Workflow: query.Options.Workflow,
189+
Branch: query.Options.Branch,
190+
}
191+
192+
return GetWorkflowRuns(ctx, d.client, opt, req.TimeRange)
193+
}
194+
183195
// CheckHealth is the health check for GitHub
184196
func (d *Datasource) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
185197
_, err := GetAllRepositories(ctx, d.client, models.ListRepositoriesOptions{

pkg/github/query_handler.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ func GetQueryHandlers(s *QueryHandler) *datasource.QueryTypeMux {
5757
mux.HandleFunc(models.QueryTypeStargazers, s.HandleStargazers)
5858
mux.HandleFunc(models.QueryTypeWorkflows, s.HandleWorkflows)
5959
mux.HandleFunc(models.QueryTypeWorkflowUsage, s.HandleWorkflowUsage)
60+
mux.HandleFunc(models.QueryTypeWorkflowRuns, s.HandleWorkflowRuns)
6061

6162
return mux
6263
}
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// 🌟 This was machine generated. Do not edit. 🌟
2+
//
3+
// Frame[0] {
4+
// "typeVersion": [
5+
// 0,
6+
// 0
7+
// ],
8+
// "preferredVisualisationType": "table"
9+
// }
10+
// Name: workflow_run
11+
// Dimensions: 13 Fields by 2 Rows
12+
// +----------------+-----------------+-------------------+-----------------+-------------------------------+-------------------------------+-----------------+-----------------+-----------------+------------------+-----------------+-------------------+------------------+
13+
// | Name: id | Name: name | Name: head_branch | Name: head_sha | Name: created_at | Name: updated_at | Name: html_url | Name: url | Name: status | Name: conclusion | Name: event | Name: workflow_id | Name: run_number |
14+
// | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: |
15+
// | Type: []*int64 | Type: []*string | Type: []*string | Type: []*string | Type: []*time.Time | Type: []*time.Time | Type: []*string | Type: []*string | Type: []*string | Type: []*string | Type: []*string | Type: []*int64 | Type: []int64 |
16+
// +----------------+-----------------+-------------------+-----------------+-------------------------------+-------------------------------+-----------------+-----------------+-----------------+------------------+-----------------+-------------------+------------------+
17+
// | 2 | name_2 | head_branch_2 | head_sha_2 | 2013-02-03 00:00:00 +0000 UTC | 2013-02-04 00:00:00 +0000 UTC | html_url_2 | url_2 | status_2 | conclusion_2 | event_2 | 2 | 2 |
18+
// | 1 | name_1 | head_branch_1 | head_sha_1 | 2013-02-01 00:00:00 +0000 UTC | 2013-02-02 00:00:00 +0000 UTC | html_url_1 | url_1 | status_1 | conclusion_1 | event_1 | 1 | 1 |
19+
// +----------------+-----------------+-------------------+-----------------+-------------------------------+-------------------------------+-----------------+-----------------+-----------------+------------------+-----------------+-------------------+------------------+
20+
//
21+
//
22+
// 🌟 This was machine generated. Do not edit. 🌟
23+
{
24+
"status": 200,
25+
"frames": [
26+
{
27+
"schema": {
28+
"name": "workflow_run",
29+
"meta": {
30+
"typeVersion": [
31+
0,
32+
0
33+
],
34+
"preferredVisualisationType": "table"
35+
},
36+
"fields": [
37+
{
38+
"name": "id",
39+
"type": "number",
40+
"typeInfo": {
41+
"frame": "int64",
42+
"nullable": true
43+
}
44+
},
45+
{
46+
"name": "name",
47+
"type": "string",
48+
"typeInfo": {
49+
"frame": "string",
50+
"nullable": true
51+
}
52+
},
53+
{
54+
"name": "head_branch",
55+
"type": "string",
56+
"typeInfo": {
57+
"frame": "string",
58+
"nullable": true
59+
}
60+
},
61+
{
62+
"name": "head_sha",
63+
"type": "string",
64+
"typeInfo": {
65+
"frame": "string",
66+
"nullable": true
67+
}
68+
},
69+
{
70+
"name": "created_at",
71+
"type": "time",
72+
"typeInfo": {
73+
"frame": "time.Time",
74+
"nullable": true
75+
}
76+
},
77+
{
78+
"name": "updated_at",
79+
"type": "time",
80+
"typeInfo": {
81+
"frame": "time.Time",
82+
"nullable": true
83+
}
84+
},
85+
{
86+
"name": "html_url",
87+
"type": "string",
88+
"typeInfo": {
89+
"frame": "string",
90+
"nullable": true
91+
}
92+
},
93+
{
94+
"name": "url",
95+
"type": "string",
96+
"typeInfo": {
97+
"frame": "string",
98+
"nullable": true
99+
}
100+
},
101+
{
102+
"name": "status",
103+
"type": "string",
104+
"typeInfo": {
105+
"frame": "string",
106+
"nullable": true
107+
}
108+
},
109+
{
110+
"name": "conclusion",
111+
"type": "string",
112+
"typeInfo": {
113+
"frame": "string",
114+
"nullable": true
115+
}
116+
},
117+
{
118+
"name": "event",
119+
"type": "string",
120+
"typeInfo": {
121+
"frame": "string",
122+
"nullable": true
123+
}
124+
},
125+
{
126+
"name": "workflow_id",
127+
"type": "number",
128+
"typeInfo": {
129+
"frame": "int64",
130+
"nullable": true
131+
}
132+
},
133+
{
134+
"name": "run_number",
135+
"type": "number",
136+
"typeInfo": {
137+
"frame": "int64"
138+
}
139+
}
140+
]
141+
},
142+
"data": {
143+
"values": [
144+
[
145+
2,
146+
1
147+
],
148+
[
149+
"name_2",
150+
"name_1"
151+
],
152+
[
153+
"head_branch_2",
154+
"head_branch_1"
155+
],
156+
[
157+
"head_sha_2",
158+
"head_sha_1"
159+
],
160+
[
161+
1359849600000,
162+
1359676800000
163+
],
164+
[
165+
1359936000000,
166+
1359763200000
167+
],
168+
[
169+
"html_url_2",
170+
"html_url_1"
171+
],
172+
[
173+
"url_2",
174+
"url_1"
175+
],
176+
[
177+
"status_2",
178+
"status_1"
179+
],
180+
[
181+
"conclusion_2",
182+
"conclusion_1"
183+
],
184+
[
185+
"event_2",
186+
"event_1"
187+
],
188+
[
189+
2,
190+
1
191+
],
192+
[
193+
2,
194+
1
195+
]
196+
]
197+
}
198+
}
199+
]
200+
}

pkg/github/workflows.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,62 @@ func GetWorkflowUsage(ctx context.Context, client models.Client, opts models.Wor
185185

186186
return WorkflowUsageWrapper(data), nil
187187
}
188+
189+
// WorkflowRunsWrapper is a list of GitHub workflow runs
190+
type WorkflowRunsWrapper []*googlegithub.WorkflowRun
191+
192+
// Frames converts the list of workflow runs to a Grafana DataFrame
193+
func (workflowRuns WorkflowRunsWrapper) Frames() data.Frames {
194+
frame := data.NewFrame(
195+
"workflow_run",
196+
data.NewField("id", nil, []*int64{}),
197+
data.NewField("name", nil, []*string{}),
198+
data.NewField("head_branch", nil, []*string{}),
199+
data.NewField("head_sha", nil, []*string{}),
200+
data.NewField("created_at", nil, []*time.Time{}),
201+
data.NewField("updated_at", nil, []*time.Time{}),
202+
data.NewField("html_url", nil, []*string{}),
203+
data.NewField("url", nil, []*string{}),
204+
data.NewField("status", nil, []*string{}),
205+
data.NewField("conclusion", nil, []*string{}),
206+
data.NewField("event", nil, []*string{}),
207+
data.NewField("workflow_id", nil, []*int64{}),
208+
data.NewField("run_number", nil, []int64{}),
209+
)
210+
211+
for _, workflowRun := range workflowRuns {
212+
frame.InsertRow(
213+
0,
214+
workflowRun.ID,
215+
workflowRun.Name,
216+
workflowRun.HeadBranch,
217+
workflowRun.HeadSHA,
218+
workflowRun.CreatedAt.GetTime(),
219+
workflowRun.UpdatedAt.GetTime(),
220+
workflowRun.HTMLURL,
221+
workflowRun.URL,
222+
workflowRun.Status,
223+
workflowRun.Conclusion,
224+
workflowRun.Event,
225+
workflowRun.WorkflowID,
226+
int64(*workflowRun.RunNumber),
227+
)
228+
}
229+
230+
frame.Meta = &data.FrameMeta{PreferredVisualization: data.VisTypeTable}
231+
return data.Frames{frame}
232+
}
233+
234+
// GetWorkflowRuns gets all workflows runs for a GitHub repository and workflow
235+
func GetWorkflowRuns(ctx context.Context, client models.Client, opts models.WorkflowRunsOptions, timeRange backend.TimeRange) (WorkflowRunsWrapper, error) {
236+
if opts.Owner == "" || opts.Repository == "" {
237+
return nil, nil
238+
}
239+
240+
workflowRuns, err := client.GetWorkflowRuns(ctx, opts.Owner, opts.Repository, opts.Workflow, opts.Branch, timeRange)
241+
if err != nil {
242+
return nil, err
243+
}
244+
245+
return WorkflowRunsWrapper(workflowRuns), nil
246+
}

pkg/github/workflows_handler.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,19 @@ func (s *QueryHandler) HandleWorkflowUsage(ctx context.Context, req *backend.Que
3939
Responses: processQueries(ctx, req, s.handleWorkflowUsageQuery),
4040
}, nil
4141
}
42+
43+
func (s *QueryHandler) handleWorkflowRunsQuery(ctx context.Context, q backend.DataQuery) backend.DataResponse {
44+
query := &models.WorkflowRunsQuery{}
45+
if err := UnmarshalQuery(q.JSON, query); err != nil {
46+
return *err
47+
}
48+
49+
return dfutil.FrameResponseWithError(s.Datasource.HandleWorkflowRunsQuery(ctx, query, q))
50+
}
51+
52+
// HandleWorkflowRuns handles the plugin query for GitHub workflows
53+
func (s *QueryHandler) HandleWorkflowRuns(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
54+
return &backend.QueryDataResponse{
55+
Responses: processQueries(ctx, req, s.handleWorkflowRunsQuery),
56+
}, nil
57+
}

0 commit comments

Comments
 (0)