fix!: omit name, avatar_url and last_seen_at from responses when empty (#18005)

User name, avatar URL, and last seen at, are not required fields so they
can be empty. Instead of returning the 0 values from Go, we want to make
it more agnostic, and omit them when they are empty. This make the docs
and usage way clearer for consumers.
This commit is contained in:
Bruno Quaresma
2025-05-23 11:35:05 -03:00
committed by GitHub
parent 96f69b8e13
commit 94c129c03d
11 changed files with 21 additions and 25 deletions

View File

@ -24,7 +24,6 @@
"workspace_name": "test-workspace",
"workspace_owner_id": "==========[first user ID]===========",
"workspace_owner_name": "testuser",
"workspace_owner_avatar_url": "",
"template_version_id": "============[version ID]============",
"template_version_name": "===========[version name]===========",
"build_number": 1,

View File

@ -2,7 +2,6 @@
{
"id": "==========[first user ID]===========",
"username": "testuser",
"avatar_url": "",
"name": "Test User",
"email": "testuser@coder.com",
"created_at": "====[timestamp]=====",
@ -23,8 +22,6 @@
{
"id": "==========[second user ID]==========",
"username": "testuser2",
"avatar_url": "",
"name": "",
"email": "testuser2@coder.com",
"created_at": "====[timestamp]=====",
"updated_at": "====[timestamp]=====",

View File

@ -74,8 +74,8 @@ type OrganizationMember struct {
type OrganizationMemberWithUserData struct {
Username string `table:"username,default_sort" json:"username"`
Name string `table:"name" json:"name"`
AvatarURL string `json:"avatar_url"`
Name string `table:"name" json:"name,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"`
Email string `json:"email"`
GlobalRoles []SlimRole `json:"global_roles"`
OrganizationMember `table:"m,recursive_inline"`

View File

@ -40,7 +40,7 @@ type UsersRequest struct {
type MinimalUser struct {
ID uuid.UUID `json:"id" validate:"required" table:"id" format:"uuid"`
Username string `json:"username" validate:"required" table:"username,default_sort"`
AvatarURL string `json:"avatar_url" format:"uri"`
AvatarURL string `json:"avatar_url,omitempty" format:"uri"`
}
// ReducedUser omits role and organization information. Roles are deduced from
@ -49,11 +49,11 @@ type MinimalUser struct {
// required by the frontend.
type ReducedUser struct {
MinimalUser `table:"m,recursive_inline"`
Name string `json:"name"`
Name string `json:"name,omitempty"`
Email string `json:"email" validate:"required" table:"email" format:"email"`
CreatedAt time.Time `json:"created_at" validate:"required" table:"created at" format:"date-time"`
UpdatedAt time.Time `json:"updated_at" table:"updated at" format:"date-time"`
LastSeenAt time.Time `json:"last_seen_at" format:"date-time"`
LastSeenAt time.Time `json:"last_seen_at,omitempty" format:"date-time"`
Status UserStatus `json:"status" table:"status" enums:"active,suspended"`
LoginType LoginType `json:"login_type"`

View File

@ -58,7 +58,7 @@ type WorkspaceBuild struct {
WorkspaceName string `json:"workspace_name"`
WorkspaceOwnerID uuid.UUID `json:"workspace_owner_id" format:"uuid"`
WorkspaceOwnerName string `json:"workspace_owner_name"`
WorkspaceOwnerAvatarURL string `json:"workspace_owner_avatar_url"`
WorkspaceOwnerAvatarURL string `json:"workspace_owner_avatar_url,omitempty"`
TemplateVersionID uuid.UUID `json:"template_version_id" format:"uuid"`
TemplateVersionName string `json:"template_version_name"`
BuildNumber int32 `json:"build_number"`

View File

@ -1366,7 +1366,7 @@ export interface MinimalOrganization {
export interface MinimalUser {
readonly id: string;
readonly username: string;
readonly avatar_url: string;
readonly avatar_url?: string;
}
// From netcheck/netcheck.go
@ -1639,8 +1639,8 @@ export interface OrganizationMember {
// From codersdk/organizations.go
export interface OrganizationMemberWithUserData extends OrganizationMember {
readonly username: string;
readonly name: string;
readonly avatar_url: string;
readonly name?: string;
readonly avatar_url?: string;
readonly email: string;
readonly global_roles: readonly SlimRole[];
}
@ -2253,11 +2253,11 @@ export interface RateLimitConfig {
// From codersdk/users.go
export interface ReducedUser extends MinimalUser {
readonly name: string;
readonly name?: string;
readonly email: string;
readonly created_at: string;
readonly updated_at: string;
readonly last_seen_at: string;
readonly last_seen_at?: string;
readonly status: UserStatus;
readonly login_type: LoginType;
readonly theme_preference?: string;
@ -3609,7 +3609,7 @@ export interface WorkspaceBuild {
readonly workspace_name: string;
readonly workspace_owner_id: string;
readonly workspace_owner_name: string;
readonly workspace_owner_avatar_url: string;
readonly workspace_owner_avatar_url?: string;
readonly template_version_id: string;
readonly template_version_name: string;
readonly build_number: number;

View File

@ -20,7 +20,7 @@ import { prepareQuery } from "utils/filters";
// The common properties between users and org members that we need.
export type SelectedUser = {
avatar_url: string;
avatar_url?: string;
email: string;
username: string;
};

View File

@ -62,7 +62,7 @@ const ChatLanding: FC = () => {
textAlign: "center",
}}
>
Good evening, {user?.name.split(" ")[0]}
Good evening, {(user.name ?? user.username).split(" ")[0]}
</h1>
<p
css={{

View File

@ -13,7 +13,7 @@ describe("AccountForm", () => {
// Given
const mockInitialValues: UpdateUserProfileRequest = {
username: MockUserMember.username,
name: MockUserMember.name,
name: MockUserMember.name ?? MockUserMember.username,
};
// When
@ -44,7 +44,7 @@ describe("AccountForm", () => {
// Given
const mockInitialValues: UpdateUserProfileRequest = {
username: MockUserMember.username,
name: MockUserMember.name,
name: MockUserMember.name ?? MockUserMember.username,
};
// When

View File

@ -29,7 +29,7 @@ const AccountPage: FC = () => {
email={me.email}
updateProfileError={updateProfileError}
isLoading={isUpdatingProfile}
initialValues={{ username: me.username, name: me.name }}
initialValues={{ username: me.username, name: me.name ?? "" }}
onSubmit={updateProfile}
/>
</Section>

View File

@ -539,8 +539,8 @@ export const MockOrganizationMember: TypesGen.OrganizationMemberWithUserData = {
user_id: MockUserOwner.id,
username: MockUserOwner.username,
email: MockUserOwner.email,
created_at: "",
updated_at: "",
updated_at: "2025-05-22T17:51:49.49745Z",
created_at: "2025-05-22T17:51:49.497449Z",
name: MockUserOwner.name,
avatar_url: MockUserOwner.avatar_url,
global_roles: MockUserOwner.roles,
@ -553,8 +553,8 @@ export const MockOrganizationMember2: TypesGen.OrganizationMemberWithUserData =
user_id: MockUserMember.id,
username: MockUserMember.username,
email: MockUserMember.email,
created_at: "",
updated_at: "",
updated_at: "2025-05-22T17:51:49.49745Z",
created_at: "2025-05-22T17:51:49.497449Z",
name: MockUserMember.name,
avatar_url: MockUserMember.avatar_url,
global_roles: MockUserMember.roles,