@@ -14,7 +14,7 @@ import { SDK } from './sdk';
14
14
import { callTrace , traceMemberMethods } from './tracing' ;
15
15
import { formatErrorMessage } from '../../util' ;
16
16
import { IO , type IoHelper } from '../io/private' ;
17
- import { Mode , PluginHost } from '../plugin' ;
17
+ import { PluginHost , Mode } from '../plugin' ;
18
18
import { AuthenticationError } from '../toolkit-error' ;
19
19
20
20
export type AssumeRoleAdditionalOptions = Partial < Omit < AssumeRoleCommandInput , 'ExternalId' | 'RoleArn' > > ;
@@ -44,6 +44,13 @@ export interface SdkProviderOptions {
44
44
* The logger for sdk calls.
45
45
*/
46
46
readonly logger ?: Logger ;
47
+
48
+ /**
49
+ * The plugin host to use
50
+ *
51
+ * @default - an empty plugin host
52
+ */
53
+ readonly pluginHost ?: PluginHost ;
47
54
}
48
55
49
56
/**
@@ -136,7 +143,7 @@ export class SdkProvider {
136
143
137
144
const region = await builder . region ( options . profile ) ;
138
145
const requestHandler = await builder . requestHandlerBuilder ( options . httpOptions ) ;
139
- return new SdkProvider ( credentialProvider , region , requestHandler , options . ioHelper , options . logger ) ;
146
+ return new SdkProvider ( credentialProvider , region , requestHandler , options . pluginHost ?? new PluginHost ( ) , options . ioHelper , options . logger ) ;
140
147
}
141
148
142
149
private readonly plugins ;
@@ -148,10 +155,11 @@ export class SdkProvider {
148
155
*/
149
156
public readonly defaultRegion : string ,
150
157
private readonly requestHandler : NodeHttpHandlerOptions = { } ,
158
+ pluginHost : PluginHost ,
151
159
private readonly ioHelper : IoHelper ,
152
160
private readonly logger ?: Logger ,
153
161
) {
154
- this . plugins = new CredentialPlugins ( PluginHost . instance , ioHelper ) ;
162
+ this . plugins = new CredentialPlugins ( pluginHost , ioHelper ) ;
155
163
}
156
164
157
165
/**
@@ -183,7 +191,7 @@ export class SdkProvider {
183
191
184
192
// Our current credentials must be valid and not expired. Confirm that before we get into doing
185
193
// actual CloudFormation calls, which might take a long time to hang.
186
- const sdk = new SDK ( baseCreds . credentials , env . region , this . requestHandler , this . ioHelper , this . logger ) ;
194
+ const sdk = this . _makeSdk ( baseCreds . credentials , env . region ) ;
187
195
await sdk . validateCredentials ( ) ;
188
196
return { sdk, didAssumeRole : false } ;
189
197
}
@@ -216,7 +224,7 @@ export class SdkProvider {
216
224
`${ fmtObtainedCredentials ( baseCreds ) } could not be used to assume '${ options . assumeRoleArn } ', but are for the right account. Proceeding anyway.` ,
217
225
) ) ;
218
226
return {
219
- sdk : new SDK ( baseCreds . credentials , env . region , this . requestHandler , this . ioHelper , this . logger ) ,
227
+ sdk : this . _makeSdk ( baseCreds . credentials , env . region ) ,
220
228
didAssumeRole : false ,
221
229
} ;
222
230
}
@@ -236,7 +244,7 @@ export class SdkProvider {
236
244
if ( baseCreds . source === 'none' ) {
237
245
return undefined ;
238
246
}
239
- return ( await new SDK ( baseCreds . credentials , env . region , this . requestHandler , this . ioHelper , this . logger ) . currentAccount ( ) ) . partition ;
247
+ return ( await this . _makeSdk ( baseCreds . credentials , env . region ) . currentAccount ( ) ) . partition ;
240
248
}
241
249
242
250
/**
@@ -282,7 +290,7 @@ export class SdkProvider {
282
290
public async defaultAccount ( ) : Promise < Account | undefined > {
283
291
return cached ( this , CACHED_ACCOUNT , async ( ) => {
284
292
try {
285
- return await new SDK ( this . defaultCredentialProvider , this . defaultRegion , this . requestHandler , this . ioHelper , this . logger ) . currentAccount ( ) ;
293
+ return await this . _makeSdk ( this . defaultCredentialProvider , this . defaultRegion ) . currentAccount ( ) ;
286
294
} catch ( e : any ) {
287
295
// Treat 'ExpiredToken' specially. This is a common situation that people may find themselves in, and
288
296
// they are complaining about if we fail 'cdk synth' on them. We loudly complain in order to show that
@@ -384,7 +392,7 @@ export class SdkProvider {
384
392
// Call the provider at least once here, to catch an error if it occurs
385
393
await credentials ( ) ;
386
394
387
- return new SDK ( credentials , region , this . requestHandler , this . ioHelper , this . logger ) ;
395
+ return this . _makeSdk ( credentials , region ) ;
388
396
} catch ( err : any ) {
389
397
if ( err . name === 'ExpiredToken' ) {
390
398
throw err ;
@@ -402,6 +410,29 @@ export class SdkProvider {
402
410
) ;
403
411
}
404
412
}
413
+
414
+ /**
415
+ * Factory function that creates a new SDK instance
416
+ *
417
+ * This is a function here, instead of all the places where this is used creating a `new SDK`
418
+ * instance, so that it is trivial to mock from tests.
419
+ *
420
+ * Use like this:
421
+ *
422
+ * ```ts
423
+ * const mockSdk = jest.spyOn(SdkProvider.prototype, '_makeSdk').mockReturnValue(new MockSdk());
424
+ * // ...
425
+ * mockSdk.mockRestore();
426
+ * ```
427
+ *
428
+ * @internal
429
+ */
430
+ public _makeSdk (
431
+ credProvider : AwsCredentialIdentityProvider ,
432
+ region : string ,
433
+ ) {
434
+ return new SDK ( credProvider , region , this . requestHandler , this . ioHelper , this . logger ) ;
435
+ }
405
436
}
406
437
407
438
/**
0 commit comments