Skip to content

Commit d3991fa

Browse files
authored
feat(coderd): add parameter insights to template insights (#8656)
1 parent 2ed4530 commit d3991fa

File tree

16 files changed

+713
-37
lines changed

16 files changed

+713
-37
lines changed

coderd/apidoc/docs.go

+47
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

+47
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dbauthz/dbauthz.go

+19
Original file line numberDiff line numberDiff line change
@@ -1227,6 +1227,25 @@ func (q *querier) GetTemplateInsights(ctx context.Context, arg database.GetTempl
12271227
return q.db.GetTemplateInsights(ctx, arg)
12281228
}
12291229

1230+
func (q *querier) GetTemplateParameterInsights(ctx context.Context, arg database.GetTemplateParameterInsightsParams) ([]database.GetTemplateParameterInsightsRow, error) {
1231+
for _, templateID := range arg.TemplateIDs {
1232+
template, err := q.db.GetTemplateByID(ctx, templateID)
1233+
if err != nil {
1234+
return nil, err
1235+
}
1236+
1237+
if err := q.authorizeContext(ctx, rbac.ActionUpdate, template); err != nil {
1238+
return nil, err
1239+
}
1240+
}
1241+
if len(arg.TemplateIDs) == 0 {
1242+
if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceTemplate.All()); err != nil {
1243+
return nil, err
1244+
}
1245+
}
1246+
return q.db.GetTemplateParameterInsights(ctx, arg)
1247+
}
1248+
12301249
func (q *querier) GetTemplateVersionByID(ctx context.Context, tvid uuid.UUID) (database.TemplateVersion, error) {
12311250
tv, err := q.db.GetTemplateVersionByID(ctx, tvid)
12321251
if err != nil {

coderd/database/dbfake/dbfake.go

+109
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,21 @@ func isNotNull(v interface{}) bool {
596596
// these methods remain unimplemented in the FakeQuerier.
597597
var ErrUnimplemented = xerrors.New("unimplemented")
598598

599+
func uniqueSortedUUIDs(uuids []uuid.UUID) []uuid.UUID {
600+
set := make(map[uuid.UUID]struct{})
601+
for _, id := range uuids {
602+
set[id] = struct{}{}
603+
}
604+
unique := make([]uuid.UUID, 0, len(set))
605+
for id := range set {
606+
unique = append(unique, id)
607+
}
608+
slices.SortFunc(unique, func(a, b uuid.UUID) bool {
609+
return a.String() < b.String()
610+
})
611+
return unique
612+
}
613+
599614
func (*FakeQuerier) AcquireLock(_ context.Context, _ int64) error {
600615
return xerrors.New("AcquireLock must only be called within a transaction")
601616
}
@@ -2122,6 +2137,100 @@ func (q *FakeQuerier) GetTemplateInsights(_ context.Context, arg database.GetTem
21222137
return result, nil
21232138
}
21242139

2140+
func (q *FakeQuerier) GetTemplateParameterInsights(ctx context.Context, arg database.GetTemplateParameterInsightsParams) ([]database.GetTemplateParameterInsightsRow, error) {
2141+
err := validateDatabaseType(arg)
2142+
if err != nil {
2143+
return nil, err
2144+
}
2145+
2146+
q.mutex.RLock()
2147+
defer q.mutex.RUnlock()
2148+
2149+
// WITH latest_workspace_builds ...
2150+
latestWorkspaceBuilds := make(map[uuid.UUID]database.WorkspaceBuildTable)
2151+
for _, wb := range q.workspaceBuilds {
2152+
if wb.CreatedAt.Before(arg.StartTime) || wb.CreatedAt.Equal(arg.EndTime) || wb.CreatedAt.After(arg.EndTime) {
2153+
continue
2154+
}
2155+
if latestWorkspaceBuilds[wb.WorkspaceID].BuildNumber < wb.BuildNumber {
2156+
latestWorkspaceBuilds[wb.WorkspaceID] = wb
2157+
}
2158+
}
2159+
if len(arg.TemplateIDs) > 0 {
2160+
for wsID := range latestWorkspaceBuilds {
2161+
ws, err := q.getWorkspaceByIDNoLock(ctx, wsID)
2162+
if err != nil {
2163+
return nil, err
2164+
}
2165+
if slices.Contains(arg.TemplateIDs, ws.TemplateID) {
2166+
delete(latestWorkspaceBuilds, wsID)
2167+
}
2168+
}
2169+
}
2170+
// WITH unique_template_params ...
2171+
num := int64(0)
2172+
uniqueTemplateParams := make(map[string]*database.GetTemplateParameterInsightsRow)
2173+
uniqueTemplateParamWorkspaceBuildIDs := make(map[string][]uuid.UUID)
2174+
for _, wb := range latestWorkspaceBuilds {
2175+
tv, err := q.getTemplateVersionByIDNoLock(ctx, wb.TemplateVersionID)
2176+
if err != nil {
2177+
return nil, err
2178+
}
2179+
for _, tvp := range q.templateVersionParameters {
2180+
if tvp.TemplateVersionID != tv.ID {
2181+
continue
2182+
}
2183+
key := fmt.Sprintf("%s:%s:%s:%s", tvp.Name, tvp.DisplayName, tvp.Description, tvp.Options)
2184+
if _, ok := uniqueTemplateParams[key]; !ok {
2185+
num++
2186+
uniqueTemplateParams[key] = &database.GetTemplateParameterInsightsRow{
2187+
Num: num,
2188+
Name: tvp.Name,
2189+
DisplayName: tvp.DisplayName,
2190+
Description: tvp.Description,
2191+
Options: tvp.Options,
2192+
}
2193+
}
2194+
uniqueTemplateParams[key].TemplateIDs = append(uniqueTemplateParams[key].TemplateIDs, tv.TemplateID.UUID)
2195+
uniqueTemplateParamWorkspaceBuildIDs[key] = append(uniqueTemplateParamWorkspaceBuildIDs[key], wb.ID)
2196+
}
2197+
}
2198+
// SELECT ...
2199+
counts := make(map[string]map[string]int64)
2200+
for key, utp := range uniqueTemplateParams {
2201+
for _, wbp := range q.workspaceBuildParameters {
2202+
if !slices.Contains(uniqueTemplateParamWorkspaceBuildIDs[key], wbp.WorkspaceBuildID) {
2203+
continue
2204+
}
2205+
if wbp.Name != utp.Name {
2206+
continue
2207+
}
2208+
if counts[key] == nil {
2209+
counts[key] = make(map[string]int64)
2210+
}
2211+
counts[key][wbp.Value]++
2212+
}
2213+
}
2214+
2215+
var rows []database.GetTemplateParameterInsightsRow
2216+
for key, utp := range uniqueTemplateParams {
2217+
for value, count := range counts[key] {
2218+
rows = append(rows, database.GetTemplateParameterInsightsRow{
2219+
Num: utp.Num,
2220+
TemplateIDs: uniqueSortedUUIDs(utp.TemplateIDs),
2221+
Name: utp.Name,
2222+
DisplayName: utp.DisplayName,
2223+
Description: utp.Description,
2224+
Options: utp.Options,
2225+
Value: value,
2226+
Count: count,
2227+
})
2228+
}
2229+
}
2230+
2231+
return rows, nil
2232+
}
2233+
21252234
func (q *FakeQuerier) GetTemplateVersionByID(ctx context.Context, templateVersionID uuid.UUID) (database.TemplateVersion, error) {
21262235
q.mutex.RLock()
21272236
defer q.mutex.RUnlock()

coderd/database/dbmetrics/dbmetrics.go

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dbmock/dbmock.go

+15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/querier.go

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)