Skip to content

Commit a5d39ad

Browse files
refactor: Extract ssh logic from auth service (#5670)
* refactor: Extract ssh logic from auth service * Update site/src/i18n/en/userSettingsPage.json Co-authored-by: Kira Pilot <kira@coder.com> Co-authored-by: Kira Pilot <kira@coder.com>
1 parent 8e4af79 commit a5d39ad

File tree

5 files changed

+146
-133
lines changed

5 files changed

+146
-133
lines changed
+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
2-
"securityUpdateSuccessMessage": "Updated password."
2+
"securityUpdateSuccessMessage": "Updated password.",
3+
"sshRegenerateSuccessMessage": "SSH Key regenerated successfully."
34
}

site/src/pages/UserSettingsPage/SSHKeysPage/SSHKeysPage.test.tsx

+7-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import {
55
MockGitSSHKey,
66
renderWithAuth,
77
} from "../../../testHelpers/renderHelpers"
8-
import { Language as authXServiceLanguage } from "../../../xServices/auth/authXService"
98
import { Language as SSHKeysPageLanguage, SSHKeysPage } from "./SSHKeysPage"
109
import { Language as SSHKeysPageViewLanguage } from "./SSHKeysPageView"
10+
import { i18n } from "i18n"
11+
12+
const { t } = i18n
1113

1214
describe("SSH keys Page", () => {
1315
it("shows the SSH key", async () => {
@@ -52,7 +54,10 @@ describe("SSH keys Page", () => {
5254
fireEvent.click(confirmButton)
5355

5456
// Check if the success message is displayed
55-
await screen.findByText(authXServiceLanguage.successRegenerateSSHKey)
57+
const successMessage = t("sshRegenerateSuccessMessage", {
58+
ns: "userSettingsPage",
59+
})
60+
await screen.findByText(successMessage)
5661

5762
// Check if the API was called correctly
5863
expect(API.regenerateUserSSHKey).toBeCalledTimes(1)

site/src/pages/UserSettingsPage/SSHKeysPage/SSHKeysPage.tsx

+12-20
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { useActor } from "@xstate/react"
2-
import { useContext, useEffect, PropsWithChildren, FC } from "react"
1+
import { useMachine } from "@xstate/react"
2+
import { PropsWithChildren, FC } from "react"
3+
import { sshKeyMachine } from "xServices/sshKey/sshKeyXService"
34
import { ConfirmDialog } from "../../../components/Dialogs/ConfirmDialog/ConfirmDialog"
45
import { Section } from "../../../components/SettingsLayout/Section"
5-
import { XServiceContext } from "../../../xServices/StateContext"
66
import { SSHKeysPageView } from "./SSHKeysPageView"
77

88
export const Language = {
@@ -15,19 +15,13 @@ export const Language = {
1515
}
1616

1717
export const SSHKeysPage: FC<PropsWithChildren<unknown>> = () => {
18-
const xServices = useContext(XServiceContext)
19-
const [authState, authSend] = useActor(xServices.authXService)
20-
const { sshKey, getSSHKeyError, regenerateSSHKeyError } = authState.context
21-
22-
useEffect(() => {
23-
authSend({ type: "GET_SSH_KEY" })
24-
}, [authSend])
25-
26-
const isLoading = authState.matches("signedIn.ssh.gettingSSHKey")
27-
const hasLoaded = authState.matches("signedIn.ssh.loaded")
18+
const [sshState, sshSend] = useMachine(sshKeyMachine)
19+
const isLoading = sshState.matches("gettingSSHKey")
20+
const hasLoaded = sshState.matches("loaded")
21+
const { getSSHKeyError, regenerateSSHKeyError, sshKey } = sshState.context
2822

2923
const onRegenerateClick = () => {
30-
authSend({ type: "REGENERATE_SSH_KEY" })
24+
sshSend({ type: "REGENERATE_SSH_KEY" })
3125
}
3226

3327
return (
@@ -46,17 +40,15 @@ export const SSHKeysPage: FC<PropsWithChildren<unknown>> = () => {
4640
<ConfirmDialog
4741
type="delete"
4842
hideCancel={false}
49-
open={authState.matches("signedIn.ssh.loaded.confirmSSHKeyRegenerate")}
50-
confirmLoading={authState.matches(
51-
"signedIn.ssh.loaded.regeneratingSSHKey",
52-
)}
43+
open={sshState.matches("confirmSSHKeyRegenerate")}
44+
confirmLoading={sshState.matches("regeneratingSSHKey")}
5345
title={Language.regenerateDialogTitle}
5446
confirmText={Language.confirmLabel}
5547
onConfirm={() => {
56-
authSend({ type: "CONFIRM_REGENERATE_SSH_KEY" })
48+
sshSend({ type: "CONFIRM_REGENERATE_SSH_KEY" })
5749
}}
5850
onClose={() => {
59-
authSend({ type: "CANCEL_REGENERATE_SSH_KEY" })
51+
sshSend({ type: "CANCEL_REGENERATE_SSH_KEY" })
6052
}}
6153
description={<>{Language.regenerateDialogMessage}</>}
6254
/>

site/src/xServices/auth/authXService.ts

-110
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { displaySuccess } from "../../components/GlobalSnackbar/utils"
55

66
export const Language = {
77
successProfileUpdate: "Updated settings.",
8-
successRegenerateSSHKey: "SSH Key regenerated successfully",
98
}
109

1110
export const checks = {
@@ -83,20 +82,12 @@ export interface AuthContext {
8382
methods?: TypesGen.AuthMethods
8483
permissions?: Permissions
8584
checkPermissionsError?: Error | unknown
86-
// SSH
87-
sshKey?: TypesGen.GitSSHKey
88-
getSSHKeyError?: Error | unknown
89-
regenerateSSHKeyError?: Error | unknown
9085
}
9186

9287
export type AuthEvent =
9388
| { type: "SIGN_OUT" }
9489
| { type: "SIGN_IN"; email: string; password: string }
9590
| { type: "UPDATE_PROFILE"; data: TypesGen.UpdateUserProfileRequest }
96-
| { type: "GET_SSH_KEY" }
97-
| { type: "REGENERATE_SSH_KEY" }
98-
| { type: "CONFIRM_REGENERATE_SSH_KEY" }
99-
| { type: "CANCEL_REGENERATE_SSH_KEY" }
10091
| { type: "GET_AUTH_METHODS" }
10192

10293
export const authMachine =
@@ -128,12 +119,6 @@ export const authMachine =
128119
checkPermissions: {
129120
data: TypesGen.AuthorizationResponse
130121
}
131-
getSSHKey: {
132-
data: TypesGen.GitSSHKey
133-
}
134-
regenerateSSHKey: {
135-
data: TypesGen.GitSSHKey
136-
}
137122
hasFirstUser: {
138123
data: boolean
139124
}
@@ -279,79 +264,6 @@ export const authMachine =
279264
},
280265
},
281266
},
282-
ssh: {
283-
initial: "idle",
284-
states: {
285-
idle: {
286-
on: {
287-
GET_SSH_KEY: {
288-
target: "gettingSSHKey",
289-
},
290-
},
291-
},
292-
gettingSSHKey: {
293-
entry: "clearGetSSHKeyError",
294-
invoke: {
295-
src: "getSSHKey",
296-
onDone: [
297-
{
298-
actions: "assignSSHKey",
299-
target: "loaded",
300-
},
301-
],
302-
onError: [
303-
{
304-
actions: "assignGetSSHKeyError",
305-
target: "idle",
306-
},
307-
],
308-
},
309-
},
310-
loaded: {
311-
initial: "idle",
312-
states: {
313-
idle: {
314-
on: {
315-
REGENERATE_SSH_KEY: {
316-
target: "confirmSSHKeyRegenerate",
317-
},
318-
},
319-
},
320-
confirmSSHKeyRegenerate: {
321-
on: {
322-
CANCEL_REGENERATE_SSH_KEY: {
323-
target: "idle",
324-
},
325-
CONFIRM_REGENERATE_SSH_KEY: {
326-
target: "regeneratingSSHKey",
327-
},
328-
},
329-
},
330-
regeneratingSSHKey: {
331-
entry: "clearRegenerateSSHKeyError",
332-
invoke: {
333-
src: "regenerateSSHKey",
334-
onDone: [
335-
{
336-
actions: [
337-
"assignSSHKey",
338-
"notifySuccessSSHKeyRegenerated",
339-
],
340-
target: "idle",
341-
},
342-
],
343-
onError: [
344-
{
345-
actions: "assignRegenerateSSHKeyError",
346-
target: "idle",
347-
},
348-
],
349-
},
350-
},
351-
},
352-
},
353-
},
354-
},
355267
methods: {
356268
initial: "idle",
357269
states: {
@@ -480,9 +392,6 @@ export const authMachine =
480392
checks: permissionsToCheck,
481393
})
482394
},
483-
// SSH
484-
getSSHKey: () => API.getUserSSHKey(),
485-
regenerateSSHKey: () => API.regenerateUserSSHKey(),
486395
// First user
487396
hasFirstUser: () => API.hasFirstUser(),
488397
},
@@ -538,25 +447,6 @@ export const authMachine =
538447
clearGetPermissionsError: assign({
539448
checkPermissionsError: (_) => undefined,
540449
}),
541-
// SSH
542-
assignSSHKey: assign({
543-
sshKey: (_, event) => event.data,
544-
}),
545-
assignGetSSHKeyError: assign({
546-
getSSHKeyError: (_, event) => event.data,
547-
}),
548-
clearGetSSHKeyError: assign({
549-
getSSHKeyError: (_) => undefined,
550-
}),
551-
assignRegenerateSSHKeyError: assign({
552-
regenerateSSHKeyError: (_, event) => event.data,
553-
}),
554-
clearRegenerateSSHKeyError: assign({
555-
regenerateSSHKeyError: (_) => undefined,
556-
}),
557-
notifySuccessSSHKeyRegenerated: () => {
558-
displaySuccess(Language.successRegenerateSSHKey)
559-
},
560450
redirect: (_, { data }) => {
561451
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- data can be undefined
562452
if (!data) {
+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { getUserSSHKey, regenerateUserSSHKey } from "api/api"
2+
import { GitSSHKey } from "api/typesGenerated"
3+
import { displaySuccess } from "components/GlobalSnackbar/utils"
4+
import { createMachine, assign } from "xstate"
5+
import { i18n } from "i18n"
6+
7+
const { t } = i18n
8+
9+
interface Context {
10+
sshKey?: GitSSHKey
11+
getSSHKeyError?: unknown
12+
regenerateSSHKeyError?: unknown
13+
}
14+
15+
type Events =
16+
| { type: "REGENERATE_SSH_KEY" }
17+
| { type: "CONFIRM_REGENERATE_SSH_KEY" }
18+
| { type: "CANCEL_REGENERATE_SSH_KEY" }
19+
20+
export const sshKeyMachine = createMachine(
21+
{
22+
id: "sshKeyState",
23+
predictableActionArguments: true,
24+
schema: {
25+
context: {} as Context,
26+
events: {} as Events,
27+
services: {} as {
28+
getSSHKey: {
29+
data: GitSSHKey
30+
}
31+
regenerateSSHKey: {
32+
data: GitSSHKey
33+
}
34+
},
35+
},
36+
tsTypes: {} as import("./sshKeyXService.typegen").Typegen0,
37+
initial: "gettingSSHKey",
38+
states: {
39+
gettingSSHKey: {
40+
entry: "clearGetSSHKeyError",
41+
invoke: {
42+
src: "getSSHKey",
43+
onDone: [
44+
{
45+
actions: "assignSSHKey",
46+
target: "loaded",
47+
},
48+
],
49+
onError: [
50+
{
51+
actions: "assignGetSSHKeyError",
52+
target: "notLoaded",
53+
},
54+
],
55+
},
56+
},
57+
notLoaded: {
58+
type: "final",
59+
},
60+
loaded: {
61+
on: {
62+
REGENERATE_SSH_KEY: {
63+
target: "confirmSSHKeyRegenerate",
64+
},
65+
},
66+
},
67+
confirmSSHKeyRegenerate: {
68+
on: {
69+
CANCEL_REGENERATE_SSH_KEY: {
70+
target: "loaded",
71+
},
72+
CONFIRM_REGENERATE_SSH_KEY: {
73+
target: "regeneratingSSHKey",
74+
},
75+
},
76+
},
77+
regeneratingSSHKey: {
78+
entry: "clearRegenerateSSHKeyError",
79+
invoke: {
80+
src: "regenerateSSHKey",
81+
onDone: [
82+
{
83+
actions: ["assignSSHKey", "notifySuccessSSHKeyRegenerated"],
84+
target: "loaded",
85+
},
86+
],
87+
onError: [
88+
{
89+
actions: "assignRegenerateSSHKeyError",
90+
target: "loaded",
91+
},
92+
],
93+
},
94+
},
95+
},
96+
},
97+
{
98+
services: {
99+
getSSHKey: () => getUserSSHKey(),
100+
regenerateSSHKey: () => regenerateUserSSHKey(),
101+
},
102+
actions: {
103+
assignSSHKey: assign({
104+
sshKey: (_, { data }) => data,
105+
}),
106+
assignGetSSHKeyError: assign({
107+
getSSHKeyError: (_, { data }) => data,
108+
}),
109+
clearGetSSHKeyError: assign({
110+
getSSHKeyError: (_) => undefined,
111+
}),
112+
assignRegenerateSSHKeyError: assign({
113+
regenerateSSHKeyError: (_, { data }) => data,
114+
}),
115+
clearRegenerateSSHKeyError: assign({
116+
regenerateSSHKeyError: (_) => undefined,
117+
}),
118+
notifySuccessSSHKeyRegenerated: () => {
119+
displaySuccess(
120+
t("sshRegenerateSuccessMessage", { ns: "userSettingsPage" }),
121+
)
122+
},
123+
},
124+
},
125+
)

0 commit comments

Comments
 (0)