Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat(site): display user avatar#11893

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
johnstcn merged 9 commits intomainfrombq/replace-user-icon-by-avatar
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
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
2 changes: 2 additions & 0 deletionscli/testdata/coder_list_--output_json.golden
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@
"updated_at": "[timestamp]",
"owner_id": "[first user ID]",
"owner_name": "testuser",
"owner_avatar_url": "",
"organization_id": "[first org ID]",
"template_id": "[template ID]",
"template_name": "test-template",
Expand All@@ -21,6 +22,7 @@
"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,
Expand Down
6 changes: 6 additions & 0 deletionscoderd/apidoc/docs.go
View file
Open in desktop

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

6 changes: 6 additions & 0 deletionscoderd/apidoc/swagger.json
View file
Open in desktop

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

24 changes: 14 additions & 10 deletionscoderd/httpmw/organizationparam.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -63,12 +63,13 @@ func ExtractOrganizationParam(db database.Store) func(http.Handler) http.Handler
}
}

// OrganizationMember is the database object plus the Username. Including the Username in this
// middleware is preferable to a join at the SQL layer so that we can keep the autogenerated
// database types as they are.
// OrganizationMember is the database object plus the Username and Avatar URL. Including these
//in themiddleware is preferable to a join at the SQL layer so that we can keep the
//autogenerateddatabase types as they are.
type OrganizationMember struct {
database.OrganizationMember
Username string
Username string
AvatarURL string
}

// ExtractOrganizationMemberParam grabs a user membership from the "organization" and "user" URL parameter.
Expand DownExpand Up@@ -107,14 +108,17 @@ func ExtractOrganizationMemberParam(db database.Store) func(http.Handler) http.H

ctx = context.WithValue(ctx, organizationMemberParamContextKey{}, OrganizationMember{
OrganizationMember: organizationMember,
// Here we're making one exception to the rule about not leaking data about the user
// to the API handler, which is to include the username. If the caller has permission
// to read the OrganizationMember, then we're explicitly saying here that they also
// have permission to see the member's username, which is itself uncontroversial.
// Here we're making two exceptions to the rule about not leaking data about the user
// to the API handler, which is to include the username and avatar URL.
// If the caller has permission to read the OrganizationMember, then we're explicitly
// saying here that they also have permission to see the member's username and avatar.
// This is OK!
//
// API handlers need this information for audit logging and returning the owner's
// username in response to creating a workspace.
Username: user.Username,
// username in response to creating a workspace. Additionally, the frontend consumes
// the Avatar URL and this allows the FE to avoid an extra request.
Username: user.Username,
AvatarURL: user.AvatarURL,
})
next.ServeHTTP(rw, r.WithContext(ctx))
})
Expand Down
33 changes: 31 additions & 2 deletionscoderd/httpmw/organizationparam_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,14 +8,17 @@ import (

"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbgen"
"github.com/coder/coder/v2/coderd/database/dbmem"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/coderd/httpmw"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/testutil"
)

func TestOrganizationParam(t *testing.T) {
Expand DownExpand Up@@ -139,6 +142,7 @@ func TestOrganizationParam(t *testing.T) {
t.Run("Success", func(t *testing.T) {
t.Parallel()
var (
ctx = testutil.Context(t, testutil.WaitShort)
db = dbmem.New()
rw = httptest.NewRecorder()
r, user = setupAuthentication(db)
Expand All@@ -148,7 +152,14 @@ func TestOrganizationParam(t *testing.T) {
_ = dbgen.OrganizationMember(t, db, database.OrganizationMember{
OrganizationID: organization.ID,
UserID: user.ID,
Roles: []string{rbac.RoleOrgMember(organization.ID)},
})
_, err := db.UpdateUserRoles(ctx, database.UpdateUserRolesParams{
ID: user.ID,
GrantedRoles: []string{rbac.RoleTemplateAdmin()},
})
require.NoError(t, err)

chi.RouteContext(r.Context()).URLParams.Add("organization", organization.ID.String())
chi.RouteContext(r.Context()).URLParams.Add("user", user.ID.String())
rtr.Use(
Expand All@@ -161,9 +172,27 @@ func TestOrganizationParam(t *testing.T) {
httpmw.ExtractOrganizationMemberParam(db),
)
rtr.Get("/", func(rw http.ResponseWriter, r *http.Request) {
_ = httpmw.OrganizationParam(r)
_ = httpmw.OrganizationMemberParam(r)
org := httpmw.OrganizationParam(r)
assert.NotZero(t, org)
assert.NotZero(t, org.CreatedAt)
// assert.NotZero(t, org.Description) // not supported
assert.NotZero(t, org.ID)
assert.NotEmpty(t, org.Name)
orgMem := httpmw.OrganizationMemberParam(r)
rw.WriteHeader(http.StatusOK)
assert.NotZero(t, orgMem)
assert.NotZero(t, orgMem.CreatedAt)
assert.NotZero(t, orgMem.UpdatedAt)
assert.Equal(t, org.ID, orgMem.OrganizationID)
assert.Equal(t, user.ID, orgMem.UserID)
assert.Equal(t, user.Username, orgMem.Username)
assert.Equal(t, user.AvatarURL, orgMem.AvatarURL)
assert.NotEmpty(t, orgMem.Roles)
assert.NotZero(t, orgMem.OrganizationMember)
assert.NotEmpty(t, orgMem.OrganizationMember.CreatedAt)
assert.NotEmpty(t, orgMem.OrganizationMember.UpdatedAt)
assert.NotEmpty(t, orgMem.OrganizationMember.UserID)
assert.NotEmpty(t, orgMem.OrganizationMember.Roles)
})
rtr.ServeHTTP(rw, r)
res := rw.Result()
Expand Down
6 changes: 3 additions & 3 deletionscoderd/users.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1271,13 +1271,13 @@ func userOrganizationIDs(ctx context.Context, api *API, user database.User) ([]u
return member.OrganizationIDs, nil
}

funcusernameWithID(id uuid.UUID, users []database.User) (string, bool) {
funcuserByID(id uuid.UUID, users []database.User) (database.User, bool) {
for _, user := range users {
if id == user.ID {
return user.Username, true
return user, true
}
}
return"", false
returndatabase.User{}, false
}

func convertAPIKey(k database.APIKey) codersdk.APIKey {
Expand Down
65 changes: 35 additions & 30 deletionscoderd/workspacebuilds.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,7 +69,7 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) {
})
return
}
ownerName, ok :=usernameWithID(workspace.OwnerID, data.users)
owner, ok :=userByID(workspace.OwnerID, data.users)
if !ok {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error converting workspace build.",
Expand All@@ -82,7 +82,8 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) {
workspaceBuild,
workspace,
data.jobs[0],
ownerName,
owner.Username,
owner.AvatarURL,
data.resources,
data.metadata,
data.agents,
Expand DownExpand Up@@ -283,7 +284,7 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ
})
return
}
ownerName, ok :=usernameWithID(workspace.OwnerID, data.users)
owner, ok :=userByID(workspace.OwnerID, data.users)
if !ok {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error converting workspace build.",
Expand All@@ -296,7 +297,8 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ
workspaceBuild,
workspace,
data.jobs[0],
ownerName,
owner.Username,
owner.AvatarURL,
data.resources,
data.metadata,
data.agents,
Expand DownExpand Up@@ -416,7 +418,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
})
return
}
ownerName, exists :=usernameWithID(workspace.OwnerID, users)
owner, exists :=userByID(workspace.OwnerID, users)
if !exists {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error converting workspace build.",
Expand All@@ -432,7 +434,8 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
ProvisionerJob: *provisionerJob,
QueuePosition: 0,
},
ownerName,
owner.Username,
owner.AvatarURL,
[]database.WorkspaceResource{},
[]database.WorkspaceResourceMetadatum{},
[]database.WorkspaceAgent{},
Expand DownExpand Up@@ -833,7 +836,7 @@ func (api *API) convertWorkspaceBuilds(
if !exists {
return nil, xerrors.New("template version not found")
}
ownerName, exists :=usernameWithID(workspace.OwnerID, users)
owner, exists :=userByID(workspace.OwnerID, users)
if !exists {
return nil, xerrors.Errorf("owner not found for workspace: %q", workspace.Name)
}
Expand All@@ -842,7 +845,8 @@ func (api *API) convertWorkspaceBuilds(
build,
workspace,
job,
ownerName,
owner.Username,
owner.AvatarURL,
workspaceResources,
resourceMetadata,
resourceAgents,
Expand All@@ -865,7 +869,7 @@ func (api *API) convertWorkspaceBuild(
build database.WorkspaceBuild,
workspace database.Workspace,
job database.GetProvisionerJobsByIDsWithQueuePositionRow,
ownerName string,
username, avatarURL string,
workspaceResources []database.WorkspaceResource,
resourceMetadata []database.WorkspaceResourceMetadatum,
resourceAgents []database.WorkspaceAgent,
Expand DownExpand Up@@ -909,7 +913,7 @@ func (api *API) convertWorkspaceBuild(
scripts := scriptsByAgentID[agent.ID]
logSources := logSourcesByAgentID[agent.ID]
apiAgent, err := db2sdk.WorkspaceAgent(
api.DERPMap(), *api.TailnetCoordinator.Load(), agent, db2sdk.Apps(apps, agent,ownerName, workspace), convertScripts(scripts), convertLogSources(logSources), api.AgentInactiveDisconnectTimeout,
api.DERPMap(), *api.TailnetCoordinator.Load(), agent, db2sdk.Apps(apps, agent,username, workspace), convertScripts(scripts), convertLogSources(logSources), api.AgentInactiveDisconnectTimeout,
api.DeploymentValues.AgentFallbackTroubleshootingURL.String(),
)
if err != nil {
Expand All@@ -923,26 +927,27 @@ func (api *API) convertWorkspaceBuild(
apiJob := convertProvisionerJob(job)
transition := codersdk.WorkspaceTransition(build.Transition)
return codersdk.WorkspaceBuild{
ID: build.ID,
CreatedAt: build.CreatedAt,
UpdatedAt: build.UpdatedAt,
WorkspaceOwnerID: workspace.OwnerID,
WorkspaceOwnerName: ownerName,
WorkspaceID: build.WorkspaceID,
WorkspaceName: workspace.Name,
TemplateVersionID: build.TemplateVersionID,
TemplateVersionName: templateVersion.Name,
BuildNumber: build.BuildNumber,
Transition: transition,
InitiatorID: build.InitiatorID,
InitiatorUsername: build.InitiatorByUsername,
Job: apiJob,
Deadline: codersdk.NewNullTime(build.Deadline, !build.Deadline.IsZero()),
MaxDeadline: codersdk.NewNullTime(build.MaxDeadline, !build.MaxDeadline.IsZero()),
Reason: codersdk.BuildReason(build.Reason),
Resources: apiResources,
Status: convertWorkspaceStatus(apiJob.Status, transition),
DailyCost: build.DailyCost,
ID: build.ID,
CreatedAt: build.CreatedAt,
UpdatedAt: build.UpdatedAt,
WorkspaceOwnerID: workspace.OwnerID,
WorkspaceOwnerName: username,
WorkspaceOwnerAvatarURL: avatarURL,
WorkspaceID: build.WorkspaceID,
WorkspaceName: workspace.Name,
TemplateVersionID: build.TemplateVersionID,
TemplateVersionName: templateVersion.Name,
BuildNumber: build.BuildNumber,
Transition: transition,
InitiatorID: build.InitiatorID,
InitiatorUsername: build.InitiatorByUsername,
Job: apiJob,
Deadline: codersdk.NewNullTime(build.Deadline, !build.Deadline.IsZero()),
MaxDeadline: codersdk.NewNullTime(build.MaxDeadline, !build.MaxDeadline.IsZero()),
Reason: codersdk.BuildReason(build.Reason),
Resources: apiResources,
Status: convertWorkspaceStatus(apiJob.Status, transition),
DailyCost: build.DailyCost,
}, nil
}

Expand Down
18 changes: 17 additions & 1 deletioncoderd/workspacebuilds_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,6 +21,7 @@ import (
"github.com/coder/coder/v2/coderd/audit"
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbauthz"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/codersdk"
Expand All@@ -37,12 +38,23 @@ func TestWorkspaceBuild(t *testing.T) {
propagation.Baggage{},
),
)
ctx := testutil.Context(t, testutil.WaitShort)
auditor := audit.NewMock()
client:= coderdtest.New(t, &coderdtest.Options{
client, db:= coderdtest.NewWithDatabase(t, &coderdtest.Options{
IncludeProvisionerDaemon: true,
Auditor: auditor,
})
user := coderdtest.CreateFirstUser(t, client)
//nolint:gocritic // testing
up, err := db.UpdateUserProfile(dbauthz.AsSystemRestricted(ctx), database.UpdateUserProfileParams{
ID: user.UserID,
Email: coderdtest.FirstUserParams.Email,
Username: coderdtest.FirstUserParams.Username,
Name: "Admin",
AvatarURL: client.URL.String(),
UpdatedAt: dbtime.Now(),
})
require.NoError(t, err)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
Expand All@@ -57,6 +69,10 @@ func TestWorkspaceBuild(t *testing.T) {
assert.Equal(t, logs[0].Ip.IPNet.IP.String(), "127.0.0.1") &&
assert.Equal(t, logs[1].Ip.IPNet.IP.String(), "127.0.0.1")
}, testutil.WaitShort, testutil.IntervalFast)
wb, err := client.WorkspaceBuild(testutil.Context(t, testutil.WaitShort), workspace.LatestBuild.ID)
require.NoError(t, err)
require.Equal(t, up.Username, wb.WorkspaceOwnerName)
require.Equal(t, up.AvatarURL, wb.WorkspaceOwnerAvatarURL)
}

func TestWorkspaceBuildByBuildNumber(t *testing.T) {
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp