Skip to content

Commit 75b051c

Browse files
authored
feat: allow to group reports by reportid (#336)
* feat: allow to group reports by reportid Signed-off-by: Olivier Vernin <olivier@vernin.me> * chore: reduce function length Signed-off-by: Olivier Vernin <olivier@vernin.me> --------- Signed-off-by: Olivier Vernin <olivier@vernin.me>
1 parent 80e2337 commit 75b051c

3 files changed

Lines changed: 85 additions & 39 deletions

File tree

pkg/database/report.go

Lines changed: 64 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import (
99
"github.com/google/uuid"
1010
"github.com/jackc/pgx/v5/pgtype"
1111
"github.com/sirupsen/logrus"
12+
"github.com/stephenafamo/bob"
1213
"github.com/stephenafamo/bob/dialect/psql"
14+
"github.com/stephenafamo/bob/dialect/psql/dialect"
1315
"github.com/stephenafamo/bob/dialect/psql/dm"
1416
"github.com/stephenafamo/bob/dialect/psql/im"
1517
"github.com/stephenafamo/bob/dialect/psql/sm"
@@ -72,14 +74,31 @@ func SearchReport(ctx context.Context, id string) (*model.PipelineReport, error)
7274
return &report, nil
7375
}
7476

75-
// SearchLatestReport searches the latest reports according some parameters.
76-
func SearchLatestReport(ctx context.Context, scmID, sourceID, conditionID, targetID string, options ReportSearchOptions, limit, page int, startTime, endTime string) ([]SearchLatestReportData, int, error) {
77+
// SearchLatestReports searches the latest reports according some parameters.
78+
func SearchLatestReports(ctx context.Context, scmID, sourceID, conditionID, targetID string, options ReportSearchOptions, limit, page int, startTime, endTime string, latest bool) ([]SearchLatestReportData, int, error) {
7779
queryString := ""
7880
var args []any
7981

8082
query := psql.Select(
8183
sm.From("pipelineReports"),
82-
sm.Columns("id", "data", "created_at", "updated_at"),
84+
sm.Columns(
85+
"data -> 'ID'",
86+
"ID",
87+
"data -> 'PipelineID'",
88+
"data -> 'Result'",
89+
"data",
90+
"created_at",
91+
"updated_at"),
92+
)
93+
94+
if latest {
95+
query.Apply(
96+
sm.Distinct("data -> 'ID'"),
97+
sm.OrderBy("data -> 'ID'"),
98+
)
99+
}
100+
101+
query.Apply(
83102
sm.OrderBy(psql.Quote("updated_at")).Desc(),
84103
)
85104

@@ -93,45 +112,21 @@ func SearchLatestReport(ctx context.Context, scmID, sourceID, conditionID, targe
93112
}
94113

95114
if sourceID != "" {
96-
// Ensure sourceID is a valid UUID
97-
if _, err := uuid.Parse(sourceID); err != nil {
98-
return nil, 0, fmt.Errorf("parsing sourceID: %w", err)
115+
if err := applyResourceConfigFilter(&query, sourceID, configSourceType); err != nil {
116+
return nil, 0, err
99117
}
100-
101-
query.Apply(
102-
sm.Where(
103-
psql.Raw(`config_source_ids \? ?`, sourceID),
104-
),
105-
sm.Columns("config_source_ids"),
106-
)
107118
}
108119

109120
if conditionID != "" {
110-
// Ensure conditionID is a valid UUID
111-
if _, err := uuid.Parse(conditionID); err != nil {
112-
return nil, 0, fmt.Errorf("parsing conditionID: %w", err)
121+
if err := applyResourceConfigFilter(&query, conditionID, configConditionType); err != nil {
122+
return nil, 0, err
113123
}
114-
115-
query.Apply(
116-
sm.Columns("config_condition_ids"),
117-
sm.Where(
118-
psql.Raw(`config_condition_ids \? ?`, conditionID),
119-
),
120-
)
121124
}
122125

123126
if targetID != "" {
124-
// Ensure targetID is a valid UUID
125-
if _, err := uuid.Parse(targetID); err != nil {
126-
return nil, 0, fmt.Errorf("parsing targetID: %w", err)
127+
if err := applyResourceConfigFilter(&query, targetID, configTargetType); err != nil {
128+
return nil, 0, err
127129
}
128-
129-
query.Apply(
130-
sm.Columns("config_target_ids"),
131-
sm.Where(
132-
psql.Raw(`config_target_ids \? ?`, targetID),
133-
),
134-
)
135130
}
136131

137132
switch scmID {
@@ -255,13 +250,30 @@ func SearchLatestReport(ctx context.Context, scmID, sourceID, conditionID, targe
255250
filteredResources := pgtype.Hstore{}
256251

257252
if sourceID != "" || conditionID != "" || targetID != "" {
258-
err = rows.Scan(&p.ID, &p.Pipeline, &p.Created_at, &p.Updated_at, &filteredResources)
253+
err = rows.Scan(
254+
&p.ReportID,
255+
&p.ID,
256+
&p.PipelineID,
257+
&p.Result,
258+
&p.Pipeline,
259+
&p.Created_at,
260+
&p.Updated_at,
261+
&filteredResources,
262+
)
259263
if err != nil {
260264
return nil, 0, fmt.Errorf("parsing result: %s", err)
261265
}
262266

263267
} else {
264-
err = rows.Scan(&p.ID, &p.Pipeline, &p.Created_at, &p.Updated_at)
268+
err = rows.Scan(
269+
&p.ReportID,
270+
&p.ID,
271+
&p.PipelineID,
272+
&p.Result,
273+
&p.Pipeline,
274+
&p.Created_at,
275+
&p.Updated_at,
276+
)
265277
if err != nil {
266278
return nil, 0, fmt.Errorf("parsing result: %s", err)
267279
}
@@ -637,3 +649,20 @@ func SearchLatestReportByPipelineID(ctx context.Context, id string) (*model.Pipe
637649

638650
return &report, nil
639651
}
652+
653+
// applyResourceConfigFilters applies resource config filters to the given query.
654+
func applyResourceConfigFilter(query *bob.BaseQuery[*dialect.SelectQuery], id, kind string) error {
655+
656+
// Ensure resource id is a valid UUID
657+
if _, err := uuid.Parse(id); err != nil {
658+
return fmt.Errorf("parsing %sID: %w", kind, err)
659+
}
660+
661+
query.Apply(
662+
sm.Where(
663+
psql.Raw(fmt.Sprintf(`config_%s_ids \? ?`, kind), id),
664+
),
665+
sm.Columns(fmt.Sprintf("config_%s_ids", kind)),
666+
)
667+
return nil
668+
}

pkg/model/pipelinereport.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,16 @@ import (
1111
type PipelineReport struct {
1212
// ID is the unique identifier of the record in the database.
1313
ID uuid.UUID `json:",omitempty"`
14+
// Result represent the result of the pipeline execution.
15+
Result string `json:",omitempty"`
1416
// Pipeline represent the Updatecli pipeline report.
1517
Pipeline reports.Report `json:",omitempty"`
16-
// PipelineID represent the ID of the pipeline executed by Updatecli.
17-
// different execution of the same pipeline will have the same PipelineID.
18+
// ReportID represent the ID of the pipeline executed by Updatecli.
19+
// different execution of the same pipeline will have the same ReportID.
1820
// This value is coming from the pipeline report to improve the search of reports.
21+
ReportID string `json:",omitempty"`
22+
// PipelineID represent the unique identifier of the pipeline.
23+
// Several reports can be associated to the same PipelineID.
1924
PipelineID string `json:",omitempty"`
2025
// TargetScmIDs is a list of unique identifiers of the scm configuration associated with the database.
2126
TargetScmIDs []uuid.UUID `json:",omitempty"`

pkg/server/report_handlers.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"errors"
66
"log"
77
"net/http"
8+
"strconv"
89

910
"github.com/gin-gonic/gin"
1011
"github.com/jackc/pgx/v5"
@@ -122,6 +123,9 @@ func SearchPipelineReports(c *gin.Context) {
122123
// This is optional and can be used to filter reports by a specific end time
123124
// Time format is RFC3339: 2006-01-02T15:04:05Z07:00
124125
EndTime string `json:"end_time"`
126+
// Latest indicates whether to return only the latest report per pipeline ID
127+
// This is optional and defaults to false
128+
Latest bool `json:"latest"`
125129
}
126130

127131
queryParams := queryData{}
@@ -134,11 +138,12 @@ func SearchPipelineReports(c *gin.Context) {
134138
return
135139
}
136140

137-
dataset, totalCount, err := database.SearchLatestReport(
141+
dataset, totalCount, err := database.SearchLatestReports(
138142
c, queryParams.ScmID, queryParams.SourceID, queryParams.ConditionID,
139143
queryParams.TargetID, database.ReportSearchOptions{Days: monitoringDurationDays},
140144
queryParams.Limit, queryParams.Page,
141145
queryParams.StartTime, queryParams.EndTime,
146+
queryParams.Latest,
142147
)
143148
if err != nil {
144149
logrus.Errorf("searching for latest report: %s", err)
@@ -173,6 +178,12 @@ func ListPipelineReports(c *gin.Context) {
173178
scmID := queryParams.Get("scmid")
174179
startTime := queryParams.Get("start_time")
175180
endTime := queryParams.Get("end_time")
181+
lateststr := queryParams.Get("latest")
182+
183+
latest, err := strconv.ParseBool(lateststr)
184+
if err != nil {
185+
logrus.Warningf("ignoring latest param due to: %s", err)
186+
}
176187

177188
limit, page, err := getPaginationParamFromURLQuery(c)
178189
if err != nil {
@@ -183,12 +194,13 @@ func ListPipelineReports(c *gin.Context) {
183194
return
184195
}
185196

186-
dataset, totalCount, err := database.SearchLatestReport(
197+
dataset, totalCount, err := database.SearchLatestReports(
187198
c, scmID, "", "", "",
188199
database.ReportSearchOptions{Days: monitoringDurationDays},
189200
limit,
190201
page,
191202
startTime, endTime,
203+
latest,
192204
)
193205

194206
if err != nil {

0 commit comments

Comments
 (0)