1
1
<template >
2
- <div class =" auth-form container-background border-common" id = " builder-id-form " >
2
+ <div class =" auth-form container-background border-common" >
3
3
<div >
4
4
<FormTitle :isConnected =" isConnected" >AWS Builder ID</FormTitle >
5
5
6
6
<div v-if =" stage === 'START'" >
7
7
<div class =" form-section" >
8
- <div class =" sub-text -color" >
9
- {{ getDescription() }}
8
+ <div class =" auth-form-description form-description -color" >
9
+ {{ description }}
10
10
<a :href =" signUpUrl" v-on:click =" emitUiClick('auth_learnMoreBuilderId')" >Learn more.</a >
11
11
</div >
12
12
</div >
13
13
14
14
<div class =" form-section" >
15
15
<button v-on:click =" startSignIn()" >{{ submitButtonText }}</button >
16
- <div class =" small -description error-text" >{{ error }}</div >
16
+ <div class =" form -description-color input-description-small error-text" >{{ error }}</div >
17
17
</div >
18
18
</div >
19
19
@@ -45,6 +45,7 @@ import { WebviewClientFactory } from '../../../../webviews/client'
45
45
import { AuthError } from ' ../types'
46
46
import { FeatureId } from ' ../../../../shared/telemetry/telemetry.gen'
47
47
import { AuthForm } from ' ./shared.vue'
48
+ import { CredentialSourceId } from ' ../../../../shared/telemetry/telemetry.gen'
48
49
49
50
const client = WebviewClientFactory .create <AuthWebview >()
50
51
@@ -68,64 +69,45 @@ export default defineComponent({
68
69
builderIdCode: ' ' ,
69
70
name: this .state .name ,
70
71
error: ' ' as string ,
71
- signUpUrl: ' ' as string ,
72
+ signUpUrl: this . state . getSignUpUrl () ,
72
73
submitButtonText: ' ' as string ,
74
+ description: this .state .getDescription (),
73
75
}
74
76
},
75
77
async created() {
76
- this .signUpUrl = this .getSignUpUrl ()
77
- await this .update (' created' )
78
+ await this .emitUpdate (' created' )
78
79
},
79
80
methods: {
80
81
async startSignIn() {
82
+ await this .state .startAuthFormInteraction ()
83
+
84
+ // update UI to show a pending state
81
85
this .stage = ' WAITING_ON_USER'
82
- client .startAuthFormInteraction (this .state .featureType , ' awsId' )
83
- const authError = await this .state .startBuilderIdSetup ()
84
-
85
- if (authError ) {
86
- this .error = authError .text
87
- this .stage = await this .state .stage ()
88
-
89
- client .failedAuthAttempt ({
90
- authType: ' awsId' ,
91
- featureType: this .state .featureType ,
92
- reason: authError .id ,
93
- })
86
+
87
+ const wasSuccessful = await this .state .startBuilderIdSetup ()
88
+ if (wasSuccessful ) {
89
+ await this .emitUpdate (' signIn' )
94
90
} else {
95
- client .successfulAuthAttempt ({
96
- featureType: this .state .featureType ,
97
- authType: ' awsId' ,
98
- })
99
- await this .update (' signIn' )
91
+ await this .updateForm ()
100
92
}
101
93
},
102
- async update(cause ? : ConnectionUpdateCause ) {
103
- await this .updateSubmitButtonText ()
94
+ /** Updates the content of the form using the state data */
95
+ async updateForm() {
96
+ this .error = this .state .error
104
97
this .stage = await this .state .stage ()
98
+ this .submitButtonText = await this .state .getSubmitButtonText ()
105
99
this .isConnected = await this .state .isAuthConnected ()
100
+ },
101
+ async emitUpdate(cause ? : ConnectionUpdateCause ) {
102
+ await this .updateForm ()
106
103
this .emitAuthConnectionUpdated ({ id: this .state .id , isConnected: this .isConnected , cause })
107
104
},
108
105
async signout() {
109
106
await this .state .signout ()
110
- client .emitUiClick (this .state .uiClickSignout )
111
- this .update (' signOut' )
107
+ this .emitUpdate (' signOut' )
112
108
},
113
109
showNodeInView() {
114
110
this .state .showNodeInView ()
115
- client .emitUiClick (this .state .uiClickOpenId )
116
- },
117
- getSignUpUrl() {
118
- return this .state .getSignUpUrl ()
119
- },
120
- getDescription() {
121
- return this .state .getDescription ()
122
- },
123
- async updateSubmitButtonText() {
124
- if (! (await isBuilderIdConnected ())) {
125
- this .submitButtonText = ' Sign up or Sign in'
126
- } else {
127
- this .submitButtonText = ` Connect AWS Builder ID with ${this .state .name } `
128
- }
129
111
},
130
112
},
131
113
})
@@ -135,6 +117,7 @@ export default defineComponent({
135
117
*/
136
118
abstract class BaseBuilderIdState implements AuthForm {
137
119
protected _stage: BuilderIdStage = ' START'
120
+ #error: string = ' '
138
121
139
122
abstract get name(): string
140
123
abstract get id(): AuthFormId
@@ -143,13 +126,30 @@ abstract class BaseBuilderIdState implements AuthForm {
143
126
abstract get featureType(): FeatureId
144
127
protected abstract _startBuilderIdSetup(): Promise <AuthError | undefined >
145
128
abstract isAuthConnected(): Promise <boolean >
146
- abstract showNodeInView(): Promise <void >
147
-
148
- protected constructor () {}
129
+ abstract _showNodeInView(): Promise <void >
130
+ abstract isConnectionExists(): Promise <boolean >
131
+
132
+ /**
133
+ * Starts the Builder ID setup.
134
+ *
135
+ * Returns true if was successful.
136
+ */
137
+ async startBuilderIdSetup(): Promise <boolean > {
138
+ this .#error = ' '
139
+
140
+ const authError = await this ._startBuilderIdSetup ()
141
+
142
+ if (authError ) {
143
+ this .#error = authError .text
144
+ client .failedAuthAttempt (this .id , {
145
+ reason: authError .id ,
146
+ })
147
+ } else {
148
+ this .#error = ' '
149
+ client .successfulAuthAttempt (this .id )
150
+ }
149
151
150
- async startBuilderIdSetup(): Promise <AuthError | undefined > {
151
- this ._stage = ' WAITING_ON_USER'
152
- return this ._startBuilderIdSetup ()
152
+ return authError === undefined
153
153
}
154
154
155
155
async stage(): Promise <BuilderIdStage > {
@@ -160,6 +160,40 @@ abstract class BaseBuilderIdState implements AuthForm {
160
160
161
161
async signout(): Promise <void > {
162
162
await client .signoutBuilderId ()
163
+ client .emitUiClick (this .uiClickSignout )
164
+ }
165
+
166
+ get authType(): CredentialSourceId {
167
+ return ' awsId'
168
+ }
169
+
170
+ get error(): string {
171
+ return this .#error
172
+ }
173
+
174
+ /**
175
+ * In the scenario a Builder ID is already connected,
176
+ * we want to change the submit button text for all unconnected
177
+ * Builder IDs to something else since they are not techincally
178
+ * signing in again, but instead adding scopes.
179
+ */
180
+ async getSubmitButtonText(): Promise <string > {
181
+ if (! (await this .anyBuilderIdConnected ())) {
182
+ return ' Sign up or Sign in'
183
+ } else {
184
+ return ` Connect AWS Builder ID with ${this .name } `
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Returns true if any Builder Id is connected
190
+ */
191
+ private async anyBuilderIdConnected(): Promise <boolean > {
192
+ const results = await Promise .all ([
193
+ CodeWhispererBuilderIdState .instance .isAuthConnected (),
194
+ CodeCatalystBuilderIdState .instance .isAuthConnected (),
195
+ ])
196
+ return results .some (isConnected => isConnected )
163
197
}
164
198
165
199
getSignUpUrl(): string {
@@ -169,6 +203,15 @@ abstract class BaseBuilderIdState implements AuthForm {
169
203
getDescription(): string {
170
204
return ' With AWS Builder ID, sign in for free without an AWS account.'
171
205
}
206
+
207
+ startAuthFormInteraction() {
208
+ return client .startAuthFormInteraction (this .featureType , this .authType )
209
+ }
210
+
211
+ showNodeInView() {
212
+ this ._showNodeInView ()
213
+ client .emitUiClick (this .uiClickOpenId )
214
+ }
172
215
}
173
216
174
217
export class CodeWhispererBuilderIdState extends BaseBuilderIdState {
@@ -196,20 +239,26 @@ export class CodeWhispererBuilderIdState extends BaseBuilderIdState {
196
239
return client .isCodeWhispererBuilderIdConnected ()
197
240
}
198
241
242
+ override isConnectionExists(): Promise <boolean > {
243
+ return client .hasBuilderId (' codewhisperer' )
244
+ }
245
+
199
246
protected override _startBuilderIdSetup(): Promise <AuthError | undefined > {
200
247
return client .startCodeWhispererBuilderIdSetup ()
201
248
}
202
249
203
- override showNodeInView (): Promise <void > {
250
+ override _showNodeInView (): Promise <void > {
204
251
return client .showCodeWhispererNode ()
205
252
}
206
253
207
254
override getSignUpUrl(): string {
208
255
return ' https://docs.aws.amazon.com/codewhisperer/latest/userguide/whisper-setup-indv-devs.html'
209
256
}
210
257
258
+ private constructor () {
259
+ super ()
260
+ }
211
261
static #instance: CodeWhispererBuilderIdState | undefined
212
-
213
262
static get instance(): CodeWhispererBuilderIdState {
214
263
return (this .#instance ?? = new CodeWhispererBuilderIdState ())
215
264
}
@@ -240,46 +289,36 @@ export class CodeCatalystBuilderIdState extends BaseBuilderIdState {
240
289
return client .isCodeCatalystBuilderIdConnected ()
241
290
}
242
291
292
+ override isConnectionExists(): Promise <boolean > {
293
+ return client .hasBuilderId (' codecatalyst' )
294
+ }
295
+
243
296
protected override _startBuilderIdSetup(): Promise <AuthError | undefined > {
244
297
return client .startCodeCatalystBuilderIdSetup ()
245
298
}
246
299
247
- override showNodeInView (): Promise <void > {
300
+ override _showNodeInView (): Promise <void > {
248
301
return client .showCodeCatalystNode ()
249
302
}
250
303
251
- static #instance: CodeCatalystBuilderIdState | undefined
252
-
253
- static get instance(): CodeCatalystBuilderIdState {
254
- return (this .#instance ?? = new CodeCatalystBuilderIdState ())
255
- }
256
-
257
304
override getDescription(): string {
258
305
return ' You must have an existing CodeCatalyst Space connected to your AWS Builder ID.'
259
306
}
260
307
261
308
override getSignUpUrl(): string {
262
309
return ' https://aws.amazon.com/codecatalyst/'
263
310
}
264
- }
265
311
266
- /**
267
- * Returns true if any Builder Id is connected
268
- */
269
- export async function isBuilderIdConnected(): Promise <boolean > {
270
- const results = await Promise .all ([
271
- CodeWhispererBuilderIdState .instance .isAuthConnected (),
272
- CodeCatalystBuilderIdState .instance .isAuthConnected (),
273
- ])
274
- return results .some (isConnected => isConnected )
312
+ private constructor () {
313
+ super ()
314
+ }
315
+ static #instance: CodeCatalystBuilderIdState | undefined
316
+ static get instance(): CodeCatalystBuilderIdState {
317
+ return (this .#instance ?? = new CodeCatalystBuilderIdState ())
318
+ }
275
319
}
276
320
</script >
277
321
<style >
278
322
@import ' ./sharedAuthForms.css' ;
279
323
@import ' ../shared.css' ;
280
-
281
- #builder-id-form {
282
- width : 300px ;
283
- height : fit-content ;
284
- }
285
324
</style >
0 commit comments