Skip to content

feat: add total users insight #15486

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Remove temporarily defunct sdk functions
  • Loading branch information
SasSwart committed Nov 26, 2024
commit 6fb880c6d3680c81d5672b648018363733722cfa
105 changes: 0 additions & 105 deletions coderd/insights_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2351,108 +2351,3 @@ func TestGenericInsights_RBAC(t *testing.T) {
})
}
}

func TestTotalUsersInsight(t *testing.T) {
t.Parallel()

t.Run("Success", func(t *testing.T) {
t.Parallel()

client, db := coderdtest.NewWithDatabase(t, nil)
coderdtest.CreateFirstUser(t, client)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
t.Cleanup(cancel)

// Given: a deployment with many users
dbgen.User(t, db, database.User{
Email: "user1@coder.com",
Username: "user1",
CreatedAt: daysAgo(2),
})
dbgen.User(t, db, database.User{
Email: "user2@coder.com",
Username: "user2",
CreatedAt: daysAgo(2),
})
dbgen.User(t, db, database.User{
Email: "user3@coder.com",
Username: "user3",
CreatedAt: daysAgo(1),
})
dbgen.User(t, db, database.User{
Email: "user4@coder.com",
Username: "user4",
CreatedAt: daysAgo(0),
})

// When: requesting the accumulated total of users by day
now := time.Now().UTC().Truncate(time.Hour).Add(time.Hour)
res, err := client.TotalUsersInsight(ctx, codersdk.TotalUsersInsightRequest{
StartTime: daysAgo(2),
EndTime: now,
})
require.NoError(t, err)

// Then: expect the correct number of dates and the growth of the accumulated total of users
require.Len(t, res, 3, "expect 3 dates of data")

// First day
require.Equal(t, res[0].Date, daysAgo(2).Format(time.DateOnly))
require.Equal(t, res[0].Total, uint64(2))

// Second day
require.Equal(t, res[1].Date, daysAgo(1).Format(time.DateOnly))
require.Equal(t, res[1].Total, uint64(3))

// Third day
require.Equal(t, res[2].Date, daysAgo(0).Format(time.DateOnly))
require.Equal(t, res[2].Total, uint64(5))
})

t.Run("DatesWithNoUserRegistration", func(t *testing.T) {
t.Parallel()

// Given: a deployment with no users getting created every day
client, db := coderdtest.NewWithDatabase(t, nil)
coderdtest.CreateFirstUser(t, client)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
t.Cleanup(cancel)
dbgen.User(t, db, database.User{
Email: "user1@coder.com",
Username: "user1",
CreatedAt: daysAgo(2),
})
dbgen.User(t, db, database.User{
Email: "user2@coder.com",
Username: "user2",
CreatedAt: daysAgo(1),
})

// When: requesting the accumulated total of users for dates with no users created
now := time.Now().UTC().Truncate(time.Hour).Add(time.Hour)
res, err := client.TotalUsersInsight(ctx, codersdk.TotalUsersInsightRequest{
StartTime: daysAgo(2).Truncate(24 * time.Hour),
EndTime: now,
})
require.NoError(t, err)

// Then: expect the correct number of dates. If there are no users created, the total should be 0.
require.Len(t, res, 3, "expect 3 dates of data")

// First day
require.Equal(t, res[0].Date, daysAgo(2).Format(time.DateOnly))
require.Equal(t, res[0].Total, uint64(1))

// Second day - no users created so should contain the same total as the previous day
require.Equal(t, res[1].Date, daysAgo(1).Format(time.DateOnly))
require.Equal(t, res[1].Total, uint64(2))

// Third day
require.Equal(t, res[2].Date, daysAgo(0).Format(time.DateOnly))
require.Equal(t, res[2].Total, uint64(3))
})
}

func daysAgo(days int) time.Time {
return time.Now().UTC().AddDate(0, 0, -days).Truncate(24 * time.Hour)
}
2 changes: 2 additions & 0 deletions coderd/workspaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,8 @@ func createWorkspace(
// Update audit log's organization
auditReq.UpdateOrganizationID(template.OrganizationID)

// TODO (SasSwart): api.Authorize below is already done above. This seems redundant.
// Should we remove this?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that does look redundant. Let's remove it in a separate PR.

// Do this upfront to save work. If this fails, the rest of the work
// would be wasted.
if !api.Authorize(r, policy.ActionCreate,
Expand Down
29 changes: 0 additions & 29 deletions codersdk/insights.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,32 +282,3 @@ func (c *Client) TemplateInsights(ctx context.Context, req TemplateInsightsReque
var result TemplateInsightsResponse
return result, json.NewDecoder(resp.Body).Decode(&result)
}

type TotalUsersInsightRequest struct {
StartTime time.Time `json:"start_time" format:"date-time"`
EndTime time.Time `json:"end_time" format:"date-time"`
}
type TotalUserByDate struct {
Date string `json:"date"`
Total uint64 `json:"total"`
}
type TotalUsersInsightResponse []TotalUserByDate

func (c *Client) TotalUsersInsight(ctx context.Context, req TotalUsersInsightRequest) (TotalUsersInsightResponse, error) {
qp := url.Values{}
qp.Add("start_time", req.StartTime.Format(insightsTimeLayout))
qp.Add("end_time", req.EndTime.Format(insightsTimeLayout))

reqURL := fmt.Sprintf("/api/v2/insights/total-users?%s", qp.Encode())
resp, err := c.Request(ctx, http.MethodGet, reqURL, nil)
if err != nil {
return nil, xerrors.Errorf("make request: %w", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, ReadBodyAsError(resp)
}
var result TotalUsersInsightResponse
return result, json.NewDecoder(resp.Body).Decode(&result)
}
15 changes: 0 additions & 15 deletions site/src/api/typesGenerated.ts

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

Loading