From 6775a8678506af8973b17778870109ae88e1315c Mon Sep 17 00:00:00 2001 From: Kayla Washburn Date: Mon, 11 Dec 2023 10:09:51 -0700 Subject: [PATCH] chore: make `"users"."avatar_url"` `NOT NULL` (#11112) --- coderd/database/db2sdk/db2sdk.go | 2 +- coderd/database/dump.sql | 2 +- .../000176_not_null_users_avatar_url.down.sql | 3 ++ .../000176_not_null_users_avatar_url.up.sql | 7 ++++ coderd/database/models.go | 40 +++++++++---------- coderd/database/queries.sql.go | 34 ++++++++-------- coderd/insights.go | 4 +- coderd/templateversions.go | 2 +- coderd/userauth.go | 7 +--- enterprise/coderd/groups.go | 2 +- 10 files changed, 55 insertions(+), 48 deletions(-) create mode 100644 coderd/database/migrations/000176_not_null_users_avatar_url.down.sql create mode 100644 coderd/database/migrations/000176_not_null_users_avatar_url.up.sql diff --git a/coderd/database/db2sdk/db2sdk.go b/coderd/database/db2sdk/db2sdk.go index e17d5e32d8..f6de746c00 100644 --- a/coderd/database/db2sdk/db2sdk.go +++ b/coderd/database/db2sdk/db2sdk.go @@ -120,7 +120,7 @@ func User(user database.User, organizationIDs []uuid.UUID) codersdk.User { Status: codersdk.UserStatus(user.Status), OrganizationIDs: organizationIDs, Roles: make([]codersdk.Role, 0, len(user.RBACRoles)), - AvatarURL: user.AvatarURL.String, + AvatarURL: user.AvatarURL, LoginType: codersdk.LoginType(user.LoginType), } diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index a8b1626938..d742cf680f 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -746,7 +746,7 @@ CREATE TABLE users ( status user_status DEFAULT 'dormant'::user_status NOT NULL, rbac_roles text[] DEFAULT '{}'::text[] NOT NULL, login_type login_type DEFAULT 'password'::login_type NOT NULL, - avatar_url text, + avatar_url text DEFAULT ''::text NOT NULL, deleted boolean DEFAULT false NOT NULL, last_seen_at timestamp without time zone DEFAULT '0001-01-01 00:00:00'::timestamp without time zone NOT NULL, quiet_hours_schedule text DEFAULT ''::text NOT NULL, diff --git a/coderd/database/migrations/000176_not_null_users_avatar_url.down.sql b/coderd/database/migrations/000176_not_null_users_avatar_url.down.sql new file mode 100644 index 0000000000..af51d9df4f --- /dev/null +++ b/coderd/database/migrations/000176_not_null_users_avatar_url.down.sql @@ -0,0 +1,3 @@ +ALTER TABLE "users" + ALTER COLUMN "avatar_url" DROP NOT NULL, + ALTER COLUMN "avatar_url" DROP DEFAULT; diff --git a/coderd/database/migrations/000176_not_null_users_avatar_url.up.sql b/coderd/database/migrations/000176_not_null_users_avatar_url.up.sql new file mode 100644 index 0000000000..285eef7737 --- /dev/null +++ b/coderd/database/migrations/000176_not_null_users_avatar_url.up.sql @@ -0,0 +1,7 @@ +UPDATE "users" + SET "avatar_url" = '' + WHERE "avatar_url" IS NULL; + +ALTER TABLE "users" + ALTER COLUMN "avatar_url" SET NOT NULL, + ALTER COLUMN "avatar_url" SET DEFAULT ''; diff --git a/coderd/database/models.go b/coderd/database/models.go index 19d051aba0..a69c07bb2a 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1972,7 +1972,7 @@ type Template struct { AutostartBlockDaysOfWeek int16 `db:"autostart_block_days_of_week" json:"autostart_block_days_of_week"` RequireActiveVersion bool `db:"require_active_version" json:"require_active_version"` Deprecated string `db:"deprecated" json:"deprecated"` - CreatedByAvatarURL sql.NullString `db:"created_by_avatar_url" json:"created_by_avatar_url"` + CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"` CreatedByUsername string `db:"created_by_username" json:"created_by_username"` } @@ -2017,20 +2017,20 @@ type TemplateTable struct { // Joins in the username + avatar url of the created by user. type TemplateVersion struct { - ID uuid.UUID `db:"id" json:"id"` - TemplateID uuid.NullUUID `db:"template_id" json:"template_id"` - OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - Name string `db:"name" json:"name"` - Readme string `db:"readme" json:"readme"` - JobID uuid.UUID `db:"job_id" json:"job_id"` - CreatedBy uuid.UUID `db:"created_by" json:"created_by"` - ExternalAuthProviders []string `db:"external_auth_providers" json:"external_auth_providers"` - Message string `db:"message" json:"message"` - Archived bool `db:"archived" json:"archived"` - CreatedByAvatarURL sql.NullString `db:"created_by_avatar_url" json:"created_by_avatar_url"` - CreatedByUsername string `db:"created_by_username" json:"created_by_username"` + ID uuid.UUID `db:"id" json:"id"` + TemplateID uuid.NullUUID `db:"template_id" json:"template_id"` + OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + Name string `db:"name" json:"name"` + Readme string `db:"readme" json:"readme"` + JobID uuid.UUID `db:"job_id" json:"job_id"` + CreatedBy uuid.UUID `db:"created_by" json:"created_by"` + ExternalAuthProviders []string `db:"external_auth_providers" json:"external_auth_providers"` + Message string `db:"message" json:"message"` + Archived bool `db:"archived" json:"archived"` + CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"` + CreatedByUsername string `db:"created_by_username" json:"created_by_username"` } type TemplateVersionParameter struct { @@ -2114,7 +2114,7 @@ type User struct { Status UserStatus `db:"status" json:"status"` RBACRoles pq.StringArray `db:"rbac_roles" json:"rbac_roles"` LoginType LoginType `db:"login_type" json:"login_type"` - AvatarURL sql.NullString `db:"avatar_url" json:"avatar_url"` + AvatarURL string `db:"avatar_url" json:"avatar_url"` Deleted bool `db:"deleted" json:"deleted"` LastSeenAt time.Time `db:"last_seen_at" json:"last_seen_at"` // Daily (!) cron schedule (with optional CRON_TZ) signifying the start of the user's quiet hours. If empty, the default quiet hours on the instance is used instead. @@ -2140,9 +2140,9 @@ type UserLink struct { // Visible fields of users are allowed to be joined with other tables for including context of other resources. type VisibleUser struct { - ID uuid.UUID `db:"id" json:"id"` - Username string `db:"username" json:"username"` - AvatarURL sql.NullString `db:"avatar_url" json:"avatar_url"` + ID uuid.UUID `db:"id" json:"id"` + Username string `db:"username" json:"username"` + AvatarURL string `db:"avatar_url" json:"avatar_url"` } type Workspace struct { @@ -2325,7 +2325,7 @@ type WorkspaceBuild struct { Reason BuildReason `db:"reason" json:"reason"` DailyCost int32 `db:"daily_cost" json:"daily_cost"` MaxDeadline time.Time `db:"max_deadline" json:"max_deadline"` - InitiatorByAvatarUrl sql.NullString `db:"initiator_by_avatar_url" json:"initiator_by_avatar_url"` + InitiatorByAvatarUrl string `db:"initiator_by_avatar_url" json:"initiator_by_avatar_url"` InitiatorByUsername string `db:"initiator_by_username" json:"initiator_by_username"` } diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index a8931e0d29..0a19ca73a1 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -2325,11 +2325,11 @@ type GetUserActivityInsightsParams struct { } type GetUserActivityInsightsRow struct { - UserID uuid.UUID `db:"user_id" json:"user_id"` - Username string `db:"username" json:"username"` - AvatarURL sql.NullString `db:"avatar_url" json:"avatar_url"` - TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"` - UsageSeconds int64 `db:"usage_seconds" json:"usage_seconds"` + UserID uuid.UUID `db:"user_id" json:"user_id"` + Username string `db:"username" json:"username"` + AvatarURL string `db:"avatar_url" json:"avatar_url"` + TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"` + UsageSeconds int64 `db:"usage_seconds" json:"usage_seconds"` } // GetUserActivityInsights returns the ranking with top active users. @@ -2395,12 +2395,12 @@ type GetUserLatencyInsightsParams struct { } type GetUserLatencyInsightsRow struct { - UserID uuid.UUID `db:"user_id" json:"user_id"` - Username string `db:"username" json:"username"` - AvatarURL sql.NullString `db:"avatar_url" json:"avatar_url"` - TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"` - WorkspaceConnectionLatency50 float64 `db:"workspace_connection_latency_50" json:"workspace_connection_latency_50"` - WorkspaceConnectionLatency95 float64 `db:"workspace_connection_latency_95" json:"workspace_connection_latency_95"` + UserID uuid.UUID `db:"user_id" json:"user_id"` + Username string `db:"username" json:"username"` + AvatarURL string `db:"avatar_url" json:"avatar_url"` + TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"` + WorkspaceConnectionLatency50 float64 `db:"workspace_connection_latency_50" json:"workspace_connection_latency_50"` + WorkspaceConnectionLatency95 float64 `db:"workspace_connection_latency_95" json:"workspace_connection_latency_95"` } // GetUserLatencyInsights returns the median and 95th percentile connection @@ -7228,7 +7228,7 @@ type GetUsersRow struct { Status UserStatus `db:"status" json:"status"` RBACRoles pq.StringArray `db:"rbac_roles" json:"rbac_roles"` LoginType LoginType `db:"login_type" json:"login_type"` - AvatarURL sql.NullString `db:"avatar_url" json:"avatar_url"` + AvatarURL string `db:"avatar_url" json:"avatar_url"` Deleted bool `db:"deleted" json:"deleted"` LastSeenAt time.Time `db:"last_seen_at" json:"last_seen_at"` QuietHoursSchedule string `db:"quiet_hours_schedule" json:"quiet_hours_schedule"` @@ -7566,11 +7566,11 @@ WHERE ` type UpdateUserProfileParams struct { - ID uuid.UUID `db:"id" json:"id"` - Email string `db:"email" json:"email"` - Username string `db:"username" json:"username"` - AvatarURL sql.NullString `db:"avatar_url" json:"avatar_url"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + ID uuid.UUID `db:"id" json:"id"` + Email string `db:"email" json:"email"` + Username string `db:"username" json:"username"` + AvatarURL string `db:"avatar_url" json:"avatar_url"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` } func (q *sqlQuerier) UpdateUserProfile(ctx context.Context, arg UpdateUserProfileParams) (User, error) { diff --git a/coderd/insights.go b/coderd/insights.go index dfb7fddd39..bf8e45570c 100644 --- a/coderd/insights.go +++ b/coderd/insights.go @@ -121,7 +121,7 @@ func (api *API) insightsUserActivity(rw http.ResponseWriter, r *http.Request) { TemplateIDs: row.TemplateIDs, UserID: row.UserID, Username: row.Username, - AvatarURL: row.AvatarURL.String, + AvatarURL: row.AvatarURL, Seconds: row.UsageSeconds, }) } @@ -208,7 +208,7 @@ func (api *API) insightsUserLatency(rw http.ResponseWriter, r *http.Request) { TemplateIDs: row.TemplateIDs, UserID: row.UserID, Username: row.Username, - AvatarURL: row.AvatarURL.String, + AvatarURL: row.AvatarURL, LatencyMS: codersdk.ConnectionLatency{ P50: row.WorkspaceConnectionLatency50, P95: row.WorkspaceConnectionLatency95, diff --git a/coderd/templateversions.go b/coderd/templateversions.go index 3037a5ea95..4aa6bf7f63 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -1598,7 +1598,7 @@ func convertTemplateVersion(version database.TemplateVersion, job codersdk.Provi CreatedBy: codersdk.MinimalUser{ ID: version.CreatedBy, Username: version.CreatedByUsername, - AvatarURL: version.CreatedByAvatarURL.String, + AvatarURL: version.CreatedByAvatarURL, }, Archived: version.Archived, Warnings: warnings, diff --git a/coderd/userauth.go b/coderd/userauth.go index 796ea806d9..94fe821da7 100644 --- a/coderd/userauth.go +++ b/coderd/userauth.go @@ -1473,11 +1473,8 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C } needsUpdate := false - if user.AvatarURL.String != params.AvatarURL { - user.AvatarURL = sql.NullString{ - String: params.AvatarURL, - Valid: true, - } + if user.AvatarURL != params.AvatarURL { + user.AvatarURL = params.AvatarURL needsUpdate = true } diff --git a/enterprise/coderd/groups.go b/enterprise/coderd/groups.go index c209fd5a80..a681d27859 100644 --- a/enterprise/coderd/groups.go +++ b/enterprise/coderd/groups.go @@ -446,7 +446,7 @@ func convertUser(user database.User, organizationIDs []uuid.UUID) codersdk.User Status: codersdk.UserStatus(user.Status), OrganizationIDs: organizationIDs, Roles: make([]codersdk.Role, 0, len(user.RBACRoles)), - AvatarURL: user.AvatarURL.String, + AvatarURL: user.AvatarURL, LoginType: codersdk.LoginType(user.LoginType), }