Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@ lint/helm:
# All files generated by the database should be added here, and this can be used
# as a target for jobs that need to run after the database is generated.
DB_GEN_FILES := \
coderd/database/dump.sql \
coderd/database/querier.go \
coderd/database/unique_constraint.go \
coderd/database/dbmem/dbmem.go \
Expand All @@ -519,8 +520,6 @@ GEN_FILES := \
provisionersdk/proto/provisioner.pb.go \
provisionerd/proto/provisionerd.pb.go \
vpn/vpn.pb.go \
coderd/database/dump.sql \
$(DB_GEN_FILES) \
site/src/api/typesGenerated.ts \
coderd/rbac/object_gen.go \
codersdk/rbacresources_gen.go \
Expand All @@ -540,7 +539,7 @@ GEN_FILES := \
coderd/database/pubsub/psmock/psmock.go

# all gen targets should be added here and to gen/mark-fresh
gen: $(GEN_FILES)
gen: gen/db $(GEN_FILES)
.PHONY: gen

gen/db: $(DB_GEN_FILES)
Expand Down
61 changes: 61 additions & 0 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,7 @@ func New(options *Options) *API {
r.Use(apiKeyMiddleware)
r.Get("/daus", api.deploymentDAUs)
r.Get("/user-activity", api.insightsUserActivity)
r.Get("/user-status-counts-over-time", api.insightsUserStatusCountsOverTime)
r.Get("/user-latency", api.insightsUserLatency)
r.Get("/templates", api.insightsTemplates)
})
Expand Down
7 changes: 7 additions & 0 deletions coderd/database/dbauthz/dbauthz.go
Original file line number Diff line number Diff line change
Expand Up @@ -2413,6 +2413,13 @@ func (q *querier) GetUserNotificationPreferences(ctx context.Context, userID uui
return q.db.GetUserNotificationPreferences(ctx, userID)
}

func (q *querier) GetUserStatusCountsOverTime(ctx context.Context, arg database.GetUserStatusCountsOverTimeParams) ([]database.GetUserStatusCountsOverTimeRow, error) {
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceUser); err != nil {
return nil, err
}
return q.db.GetUserStatusCountsOverTime(ctx, arg)
}

func (q *querier) GetUserWorkspaceBuildParameters(ctx context.Context, params database.GetUserWorkspaceBuildParametersParams) ([]database.GetUserWorkspaceBuildParametersRow, error) {
u, err := q.db.GetUserByID(ctx, params.OwnerID)
if err != nil {
Expand Down
6 changes: 6 additions & 0 deletions coderd/database/dbauthz/dbauthz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1490,6 +1490,12 @@ func (s *MethodTestSuite) TestUser() {
rbac.ResourceTemplate.InOrg(orgID), policy.ActionRead,
)
}))
s.Run("GetUserStatusCountsOverTime", s.Subtest(func(db database.Store, check *expects) {
check.Args(database.GetUserStatusCountsOverTimeParams{
StartTime: time.Now().Add(-time.Hour * 24 * 30),
EndTime: time.Now(),
}).Asserts(rbac.ResourceUser, policy.ActionRead)
}))
}

func (s *MethodTestSuite) TestWorkspace() {
Expand Down
56 changes: 56 additions & 0 deletions coderd/database/dbmem/dbmem.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func New() database.Store {
customRoles: make([]database.CustomRole, 0),
locks: map[int64]struct{}{},
runtimeConfig: map[string]string{},
userStatusChanges: make([]database.UserStatusChange, 0),
},
}
// Always start with a default org. Matching migration 198.
Expand Down Expand Up @@ -256,6 +257,7 @@ type data struct {
lastLicenseID int32
defaultProxyDisplayName string
defaultProxyIconURL string
userStatusChanges []database.UserStatusChange
}

func tryPercentile(fs []float64, p float64) float64 {
Expand Down Expand Up @@ -5664,6 +5666,42 @@ func (q *FakeQuerier) GetUserNotificationPreferences(_ context.Context, userID u
return out, nil
}

func (q *FakeQuerier) GetUserStatusCountsOverTime(_ context.Context, arg database.GetUserStatusCountsOverTimeParams) ([]database.GetUserStatusCountsOverTimeRow, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()

err := validateDatabaseType(arg)
if err != nil {
return nil, err
}

result := make([]database.GetUserStatusCountsOverTimeRow, 0)
for _, change := range q.userStatusChanges {
if change.ChangedAt.Before(arg.StartTime) || change.ChangedAt.After(arg.EndTime) {
continue
}
date := time.Date(change.ChangedAt.Year(), change.ChangedAt.Month(), change.ChangedAt.Day(), 0, 0, 0, 0, time.UTC)
if !slices.ContainsFunc(result, func(r database.GetUserStatusCountsOverTimeRow) bool {
return r.Status == change.NewStatus && r.Date.Equal(date)
}) {
result = append(result, database.GetUserStatusCountsOverTimeRow{
Status: change.NewStatus,
Date: date,
Count: 1,
})
} else {
for i, r := range result {
if r.Status == change.NewStatus && r.Date.Equal(date) {
result[i].Count++
break
}
}
}
}

return result, nil
}

func (q *FakeQuerier) GetUserWorkspaceBuildParameters(_ context.Context, params database.GetUserWorkspaceBuildParametersParams) ([]database.GetUserWorkspaceBuildParametersRow, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
Expand Down Expand Up @@ -8016,6 +8054,12 @@ func (q *FakeQuerier) InsertUser(_ context.Context, arg database.InsertUserParam
sort.Slice(q.users, func(i, j int) bool {
return q.users[i].CreatedAt.Before(q.users[j].CreatedAt)
})

q.userStatusChanges = append(q.userStatusChanges, database.UserStatusChange{
UserID: user.ID,
NewStatus: user.Status,
ChangedAt: user.UpdatedAt,
})
return user, nil
}

Expand Down Expand Up @@ -9052,12 +9096,18 @@ func (q *FakeQuerier) UpdateInactiveUsersToDormant(_ context.Context, params dat
Username: user.Username,
LastSeenAt: user.LastSeenAt,
})
q.userStatusChanges = append(q.userStatusChanges, database.UserStatusChange{
UserID: user.ID,
NewStatus: database.UserStatusDormant,
ChangedAt: params.UpdatedAt,
})
}
}

if len(updated) == 0 {
return nil, sql.ErrNoRows
}

return updated, nil
}

Expand Down Expand Up @@ -9858,6 +9908,12 @@ func (q *FakeQuerier) UpdateUserStatus(_ context.Context, arg database.UpdateUse
user.Status = arg.Status
user.UpdatedAt = arg.UpdatedAt
q.users[index] = user

q.userStatusChanges = append(q.userStatusChanges, database.UserStatusChange{
UserID: user.ID,
NewStatus: user.Status,
ChangedAt: user.UpdatedAt,
})
return user, nil
}
return database.User{}, sql.ErrNoRows
Expand Down
7 changes: 7 additions & 0 deletions coderd/database/dbmetrics/querymetrics.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions coderd/database/dbmock/dbmock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading