Skip to content

Commit 629360c

Browse files
committed
resolved merge conflicts
2 parents 7bd38a0 + d0ed107 commit 629360c

File tree

8 files changed

+111
-37
lines changed

8 files changed

+111
-37
lines changed

agent/agent.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -456,8 +456,8 @@ func (a *agent) handleReconnectingPTY(ctx context.Context, rawID string, conn ne
456456

457457
// The ID format is referenced in conn.go.
458458
// <uuid>:<height>:<width>
459-
idParts := strings.Split(rawID, ":")
460-
if len(idParts) != 3 {
459+
idParts := strings.SplitN(rawID, ":", 4)
460+
if len(idParts) != 4 {
461461
a.logger.Warn(ctx, "client sent invalid id format", slog.F("raw-id", rawID))
462462
return
463463
}
@@ -489,7 +489,7 @@ func (a *agent) handleReconnectingPTY(ctx context.Context, rawID string, conn ne
489489
}
490490
} else {
491491
// Empty command will default to the users shell!
492-
cmd, err := a.createCommand(ctx, "", nil)
492+
cmd, err := a.createCommand(ctx, idParts[3], nil)
493493
if err != nil {
494494
a.logger.Warn(ctx, "create reconnecting pty command", slog.Error(err))
495495
return

agent/agent_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ func TestAgent(t *testing.T) {
221221

222222
conn := setupAgent(t, agent.Metadata{}, 0)
223223
id := uuid.NewString()
224-
netConn, err := conn.ReconnectingPTY(id, 100, 100)
224+
netConn, err := conn.ReconnectingPTY(id, 100, 100, "/bin/bash")
225225
require.NoError(t, err)
226226
bufRead := bufio.NewReader(netConn)
227227

@@ -259,7 +259,7 @@ func TestAgent(t *testing.T) {
259259
expectLine(matchEchoOutput)
260260

261261
_ = netConn.Close()
262-
netConn, err = conn.ReconnectingPTY(id, 100, 100)
262+
netConn, err = conn.ReconnectingPTY(id, 100, 100, "/bin/bash")
263263
require.NoError(t, err)
264264
bufRead = bufio.NewReader(netConn)
265265

agent/conn.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ type Conn struct {
3434

3535
// ReconnectingPTY returns a connection serving a TTY that can
3636
// be reconnected to via ID.
37-
func (c *Conn) ReconnectingPTY(id string, height, width uint16) (net.Conn, error) {
38-
channel, err := c.CreateChannel(context.Background(), fmt.Sprintf("%s:%d:%d", id, height, width), &peer.ChannelOptions{
37+
//
38+
// The command is optional and defaults to start a shell.
39+
func (c *Conn) ReconnectingPTY(id string, height, width uint16, command string) (net.Conn, error) {
40+
channel, err := c.CreateChannel(context.Background(), fmt.Sprintf("%s:%d:%d:%s", id, height, width, command), &peer.ChannelOptions{
3941
Protocol: ProtocolReconnectingPTY,
4042
})
4143
if err != nil {

coderd/workspaceagents.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) {
381381
return
382382
}
383383
defer agentConn.Close()
384-
ptNetConn, err := agentConn.ReconnectingPTY(reconnect.String(), uint16(height), uint16(width))
384+
ptNetConn, err := agentConn.ReconnectingPTY(reconnect.String(), uint16(height), uint16(width), "")
385385
if err != nil {
386386
_ = conn.Close(websocket.StatusInternalError, httpapi.WebsocketCloseSprintf("dial: %s", err))
387387
return

site/src/components/UserDropdown/UserDropdown.stories.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Box from "@material-ui/core/Box"
22
import { Story } from "@storybook/react"
3+
import { MockUser } from "../../testHelpers/entities"
34
import { UserDropdown, UserDropdownProps } from "./UsersDropdown"
45

56
export default {
@@ -16,9 +17,9 @@ const Template: Story<UserDropdownProps> = (args: UserDropdownProps) => (
1617
</Box>
1718
)
1819

19-
export const Example = Template.bind({})
20-
Example.args = {
21-
user: { id: "1", username: "CathyCoder", email: "cathy@coder.com", created_at: "dawn" },
20+
export const ExampleNoRoles = Template.bind({})
21+
ExampleNoRoles.args = {
22+
user: MockUser,
2223
onSignOut: () => {
2324
return Promise.resolve()
2425
},

site/src/components/UserDropdown/UserDropdown.test.tsx

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { screen } from "@testing-library/react"
2-
import { MockUser } from "../../testHelpers/entities"
2+
import { MockAdminRole, MockMemberRole, MockUser } from "../../testHelpers/entities"
33
import { render } from "../../testHelpers/renderHelpers"
44
import { Language, UserDropdown, UserDropdownProps } from "./UsersDropdown"
55

@@ -32,6 +32,36 @@ describe("UserDropdown", () => {
3232
})
3333

3434
describe("when the menu is open", () => {
35+
it("displays the user's roles", async () => {
36+
await renderAndClick()
37+
38+
expect(screen.getByText(MockAdminRole.display_name)).toBeDefined()
39+
expect(screen.getByText(MockMemberRole.display_name)).toBeDefined()
40+
})
41+
42+
it("has the correct link for the documentation item", async () => {
43+
process.env.CODER_VERSION = "v0.5.4"
44+
await renderAndClick()
45+
46+
const link = screen.getByText(Language.docsLabel).closest("a")
47+
if (!link) {
48+
throw new Error("Anchor tag not found for the documentation menu item")
49+
}
50+
51+
expect(link.getAttribute("href")).toBe(`https://github.com/coder/coder/tree/${process.env.CODER_VERSION}/docs`)
52+
})
53+
54+
it("has the correct link for the account item", async () => {
55+
await renderAndClick()
56+
57+
const link = screen.getByText(Language.accountLabel).closest("a")
58+
if (!link) {
59+
throw new Error("Anchor tag not found for the account menu item")
60+
}
61+
62+
expect(link.getAttribute("href")).toBe("/settings/account")
63+
})
64+
3565
describe("and sign out is clicked", () => {
3666
it("calls the onSignOut function", async () => {
3767
const onSignOut = jest.fn()
@@ -41,27 +71,4 @@ describe("UserDropdown", () => {
4171
})
4272
})
4373
})
44-
45-
it("has the correct link for the documentation item", async () => {
46-
process.env.CODER_VERSION = "v0.5.4"
47-
await renderAndClick()
48-
49-
const link = screen.getByText(Language.docsLabel).closest("a")
50-
if (!link) {
51-
throw new Error("Anchor tag not found for the documentation menu item")
52-
}
53-
54-
expect(link.getAttribute("href")).toBe(`https://github.com/coder/coder/tree/${process.env.CODER_VERSION}/docs`)
55-
})
56-
57-
it("has the correct link for the account item", async () => {
58-
await renderAndClick()
59-
60-
const link = screen.getByText(Language.accountLabel).closest("a")
61-
if (!link) {
62-
throw new Error("Anchor tag not found for the account menu item")
63-
}
64-
65-
expect(link.getAttribute("href")).toBe("/settings/account")
66-
})
6774
})
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Story } from "@storybook/react"
2+
import React from "react"
3+
import { MockUser } from "../../testHelpers/entities"
4+
import { UserProfileCard, UserProfileCardProps } from "./UserProfileCard"
5+
6+
export default {
7+
title: "components/UserDropdown",
8+
component: UserProfileCard,
9+
argTypes: {
10+
onSignOut: { action: "Sign Out" },
11+
},
12+
}
13+
14+
const Template: Story<UserProfileCardProps> = (args: UserProfileCardProps) => <UserProfileCard {...args} />
15+
16+
export const ExampleNoRoles = Template.bind({})
17+
ExampleNoRoles.args = {
18+
user: {
19+
...MockUser,
20+
roles: [],
21+
},
22+
}
23+
24+
export const ExampleOneRole = Template.bind({})
25+
ExampleOneRole.args = {
26+
user: {
27+
...MockUser,
28+
roles: [{ name: "member", display_name: "Member" }],
29+
},
30+
}
31+
32+
export const ExampleThreeRoles = Template.bind({})
33+
ExampleThreeRoles.args = {
34+
user: {
35+
...MockUser,
36+
roles: [
37+
{ name: "admin", display_name: "Admin" },
38+
{ name: "member", display_name: "Member" },
39+
{ name: "auditor", display_name: "Auditor" },
40+
],
41+
},
42+
}

site/src/components/UserProfileCard/UserProfileCard.tsx

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import Chip from "@material-ui/core/Chip"
12
import { makeStyles } from "@material-ui/core/styles"
23
import Typography from "@material-ui/core/Typography"
34
import { FC } from "react"
45
import * as TypesGen from "../../api/typesGenerated"
6+
import { Role } from "../../api/typesGenerated"
57
import { UserAvatar } from "../UserAvatar/UserAvatar"
68

7-
interface UserProfileCardProps {
9+
export interface UserProfileCardProps {
810
user: TypesGen.User
911
}
1012

@@ -18,6 +20,13 @@ export const UserProfileCard: FC<UserProfileCardProps> = ({ user }) => {
1820
</div>
1921
<Typography className={styles.userName}>{user.username}</Typography>
2022
<Typography className={styles.userEmail}>{user.email}</Typography>
23+
<ul className={styles.chipContainer}>
24+
{user.roles.map((role: Role) => (
25+
<li key={role.name} className={styles.chipStyles}>
26+
<Chip classes={{ root: styles.chipRoot }} label={role.display_name} />
27+
</li>
28+
))}
29+
</ul>
2130
</div>
2231
)
2332
}
@@ -52,6 +61,19 @@ const useStyles = makeStyles((theme) => ({
5261
fontSize: 14,
5362
letterSpacing: 0.2,
5463
color: theme.palette.text.secondary,
55-
marginBottom: theme.spacing(1.5),
64+
},
65+
chipContainer: {
66+
display: "flex",
67+
justifyContent: "center",
68+
flexWrap: "wrap",
69+
listStyle: "none",
70+
margin: "0",
71+
padding: `${theme.spacing(1.5)}px ${theme.spacing(2.75)}px`,
72+
},
73+
chipStyles: {
74+
margin: theme.spacing(0.5),
75+
},
76+
chipRoot: {
77+
backgroundColor: "#7057FF",
5678
},
5779
}))

0 commit comments

Comments
 (0)