@@ -13,6 +13,10 @@ import { getLogger } from '../shared/logger/logger'
13
13
import { showMessageWithUrl } from '../shared/utilities/messages'
14
14
import { onceChanged } from '../shared/utilities/functionUtils'
15
15
import { AuthAddConnection , AwsLoginWithBrowser } from '../shared/telemetry/telemetry.gen'
16
+ import { withTelemetryContext } from '../shared/telemetry/util'
17
+ import { AuthModifyConnection , telemetry } from '../shared/telemetry/telemetry'
18
+ import { asStringifiedStack } from '../shared/telemetry/spans'
19
+ import { getTelemetryReason , getTelemetryReasonDesc } from '../shared/errors'
16
20
17
21
/** Shows a warning message unless it is the same as the last one shown. */
18
22
const warnOnce = onceChanged ( ( s : string , url : string ) => {
@@ -197,17 +201,74 @@ export interface ProfileMetadata {
197
201
198
202
export type StoredProfile < T extends Profile = Profile > = T & { readonly metadata : ProfileMetadata }
199
203
204
+ function getTelemetryForProfile ( profile : StoredProfile < Profile > | undefined ) {
205
+ if ( ! profile ) {
206
+ return { }
207
+ }
208
+
209
+ let metadata : Partial < AuthModifyConnection > = {
210
+ connectionState : profile ?. metadata . connectionState ?? 'undefined' ,
211
+ }
212
+
213
+ if ( profile . type === 'sso' ) {
214
+ metadata = {
215
+ ...metadata ,
216
+ authScopes : profile . scopes ?. join ( ',' ) ,
217
+ credentialStartUrl : profile . startUrl ,
218
+ awsRegion : profile . ssoRegion ,
219
+ }
220
+ }
221
+
222
+ return metadata
223
+ }
224
+
225
+ const profileStoreClassName = 'ProfileStore'
200
226
export class ProfileStore {
227
+ // To de-dupe telemetry
228
+ private _prevGetProfile : { id : string ; connectionState : ProfileMetadata [ 'connectionState' ] } | undefined
229
+
201
230
public constructor ( private readonly memento : vscode . Memento ) { }
202
231
203
232
public getProfile ( id : string ) : StoredProfile | undefined {
204
233
return this . getData ( ) [ id ]
205
234
}
206
235
236
+ @withTelemetryContext ( { name : 'getProfileOrThrow' , class : profileStoreClassName } )
207
237
public getProfileOrThrow ( id : string ) : StoredProfile {
208
- const profile = this . getProfile ( id )
209
- if ( profile === undefined ) {
210
- throw new Error ( `Profile does not exist: ${ id } ` )
238
+ const metadata : AuthModifyConnection = {
239
+ action : 'getProfile' ,
240
+ id,
241
+ source : asStringifiedStack ( telemetry . getFunctionStack ( ) ) ,
242
+ }
243
+
244
+ let profile : StoredProfile < Profile > | undefined
245
+ try {
246
+ profile = this . getProfile ( id )
247
+ if ( profile === undefined ) {
248
+ throw new Error ( `Profile does not exist: ${ id } ` )
249
+ }
250
+ } catch ( err ) {
251
+ // Always emit failures
252
+ telemetry . auth_modifyConnection . emit ( {
253
+ ...metadata ,
254
+ result : 'Failed' ,
255
+ reason : getTelemetryReason ( err ) ,
256
+ reasonDesc : getTelemetryReasonDesc ( err ) ,
257
+ } )
258
+ throw err
259
+ }
260
+
261
+ // De-dupe metric on last id and connection state
262
+ if (
263
+ this . _prevGetProfile ?. id !== id ||
264
+ this . _prevGetProfile ?. connectionState !== profile . metadata . connectionState
265
+ ) {
266
+ telemetry . auth_modifyConnection . emit ( {
267
+ ...metadata ,
268
+ ...getTelemetryForProfile ( profile ) ,
269
+ result : 'Succeeded' ,
270
+ } )
271
+ this . _prevGetProfile = { id, connectionState : profile . metadata . connectionState }
211
272
}
212
273
213
274
return profile
@@ -219,17 +280,40 @@ export class ProfileStore {
219
280
220
281
public async addProfile ( id : string , profile : SsoProfile ) : Promise < StoredProfile < SsoProfile > >
221
282
public async addProfile ( id : string , profile : IamProfile ) : Promise < StoredProfile < IamProfile > >
283
+ @withTelemetryContext ( { name : 'addProfile' , class : profileStoreClassName } )
222
284
public async addProfile ( id : string , profile : Profile ) : Promise < StoredProfile > {
223
- return this . putProfile ( id , this . initMetadata ( profile ) )
285
+ return telemetry . auth_modifyConnection . run ( async ( span ) => {
286
+ span . record ( {
287
+ action : 'addProfile' ,
288
+ id,
289
+ source : asStringifiedStack ( telemetry . getFunctionStack ( ) ) ,
290
+ } )
291
+
292
+ const newProfile = this . initMetadata ( profile )
293
+ span . record ( getTelemetryForProfile ( newProfile ) )
294
+
295
+ return await this . putProfile ( id , newProfile )
296
+ } )
224
297
}
225
298
299
+ @withTelemetryContext ( { name : 'updateProfile' , class : profileStoreClassName } )
226
300
public async updateProfile ( id : string , profile : Profile ) : Promise < StoredProfile > {
227
- const oldProfile = this . getProfileOrThrow ( id )
228
- if ( oldProfile . type !== profile . type ) {
229
- throw new Error ( `Cannot change profile type from "${ oldProfile . type } " to "${ profile . type } "` )
230
- }
301
+ return telemetry . auth_modifyConnection . run ( async ( span ) => {
302
+ span . record ( {
303
+ action : 'updateProfile' ,
304
+ id,
305
+ source : asStringifiedStack ( telemetry . getFunctionStack ( ) ) ,
306
+ } )
307
+
308
+ const oldProfile = this . getProfileOrThrow ( id )
309
+ if ( oldProfile . type !== profile . type ) {
310
+ throw new Error ( `Cannot change profile type from "${ oldProfile . type } " to "${ profile . type } "` )
311
+ }
231
312
232
- return this . putProfile ( id , { ...oldProfile , ...profile } )
313
+ const newProfile = await this . putProfile ( id , { ...oldProfile , ...profile } )
314
+ span . record ( getTelemetryForProfile ( newProfile ) )
315
+ return newProfile
316
+ } )
233
317
}
234
318
235
319
public async updateMetadata ( id : string , metadata : ProfileMetadata ) : Promise < StoredProfile > {
@@ -238,11 +322,21 @@ export class ProfileStore {
238
322
return this . putProfile ( id , { ...profile , metadata : { ...profile . metadata , ...metadata } } )
239
323
}
240
324
325
+ @withTelemetryContext ( { name : 'deleteProfile' , class : profileStoreClassName } )
241
326
public async deleteProfile ( id : string ) : Promise < void > {
242
- const data = this . getData ( )
243
- delete ( data as Mutable < typeof data > ) [ id ]
244
-
245
- await this . updateData ( data )
327
+ return telemetry . auth_modifyConnection . run ( async ( span ) => {
328
+ span . record ( {
329
+ action : 'deleteProfile' ,
330
+ id,
331
+ source : asStringifiedStack ( telemetry . getFunctionStack ( ) ) ,
332
+ } )
333
+
334
+ const data = this . getData ( )
335
+ span . record ( getTelemetryForProfile ( data [ id ] ) )
336
+ delete ( data as Mutable < typeof data > ) [ id ]
337
+
338
+ await this . updateData ( data )
339
+ } )
246
340
}
247
341
248
342
public getCurrentProfileId ( ) : string | undefined {
@@ -395,11 +489,13 @@ export interface AwsConnection {
395
489
}
396
490
397
491
type Writeable < T > = { - readonly [ U in keyof T ] : T [ U ] }
398
- export type TelemetryMetadata = Partial < Writeable < AuthAddConnection | AwsLoginWithBrowser > >
492
+ export type TelemetryMetadata = Partial < Writeable < AuthAddConnection & AwsLoginWithBrowser & AuthModifyConnection > >
399
493
400
494
export async function getTelemetryMetadataForConn ( conn ?: Connection ) : Promise < TelemetryMetadata > {
401
495
if ( conn === undefined ) {
402
- return { }
496
+ return {
497
+ id : 'undefined' ,
498
+ }
403
499
}
404
500
405
501
if ( isSsoConnection ( conn ) ) {
0 commit comments