Skip to content

Commit fa414d5

Browse files
authored
chore(parameters): update AWS SDK client type (#3768)
1 parent 64f5cf2 commit fa414d5

File tree

7 files changed

+62
-113
lines changed

7 files changed

+62
-113
lines changed

docs/utilities/parameters.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ All caching logic is handled by the `BaseProvider`, and provided that the return
298298
Here's an example of implementing a custom parameter store using an external service like HashiCorp Vault, a widely popular key-value secret storage.
299299

300300
=== "Provider usage"
301-
```typescript hl_lines="5-8 12-16"
301+
```typescript hl_lines="12"
302302
--8<-- "examples/snippets/parameters/customProviderVaultUsage.ts"
303303
```
304304

examples/snippets/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
"@middy/core": "^4.7.0",
4141
"aws-sdk": "^2.1692.0",
4242
"aws-sdk-client-mock": "^4.1.0",
43-
"hashi-vault-js": "^0.4.16",
4443
"zod": "^3.24.2"
4544
}
4645
}

examples/snippets/parameters/customProviderVault.ts

+34-35
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,31 @@
11
import { BaseProvider } from '@aws-lambda-powertools/parameters/base';
22
import { GetParameterError } from '@aws-lambda-powertools/parameters/errors';
3-
import Vault from 'hashi-vault-js';
43
import type {
54
HashiCorpVaultGetOptions,
65
HashiCorpVaultProviderOptions,
76
} from './customProviderVaultTypes.js';
87

98
class HashiCorpVaultProvider extends BaseProvider {
10-
public client: Vault;
9+
readonly #baseUrl: string;
1110
readonly #token: string;
11+
readonly #rootPath?: string;
12+
readonly #timeout: number;
13+
readonly #abortController: AbortController;
1214

1315
/**
1416
* It initializes the HashiCorpVaultProvider class.
1517
*
16-
* @param {HashiCorpVaultProviderOptions} config - The configuration object.
18+
* @param config - The configuration object.
1719
*/
1820
public constructor(config: HashiCorpVaultProviderOptions) {
1921
super({});
2022

21-
const { url, token, clientConfig, vaultClient } = config;
22-
if (vaultClient) {
23-
if (vaultClient instanceof Vault) {
24-
this.client = vaultClient;
25-
} else {
26-
throw Error('Not a valid Vault client provided');
27-
}
28-
} else {
29-
const config = {
30-
baseUrl: url,
31-
...(clientConfig ?? {
32-
timeout: 10000,
33-
rootPath: '',
34-
}),
35-
};
36-
this.client = new Vault(config);
37-
}
23+
const { url, token, rootPath, timeout } = config;
24+
this.#baseUrl = url;
25+
this.#rootPath = rootPath ?? 'secret';
26+
this.#timeout = timeout ?? 5000;
3827
this.#token = token;
28+
this.#abortController = new AbortController();
3929
}
4030

4131
/**
@@ -46,8 +36,8 @@ class HashiCorpVaultProvider extends BaseProvider {
4636
* * `forceFetch` - Whether to always fetch a new value from the store regardless if already available in cache
4737
* * `sdkOptions` - Extra options to pass to the HashiCorp Vault SDK, e.g. `mount` or `version`
4838
*
49-
* @param {string} name - The name of the secret
50-
* @param {HashiCorpVaultGetOptions} options - Options to customize the retrieval of the secret
39+
* @param name - The name of the secret
40+
* @param options - Options to customize the retrieval of the secret
5141
*/
5242
public async get<T extends Record<string, unknown>>(
5343
name: string,
@@ -68,27 +58,36 @@ class HashiCorpVaultProvider extends BaseProvider {
6858
/**
6959
* Retrieve a secret from HashiCorp Vault.
7060
*
71-
* @param {string} name - The name of the secret
72-
* @param {HashiCorpVaultGetOptions} options - Options to customize the retrieval of the secret
61+
* @param name - The name of the secret
62+
* @param options - Options to customize the retrieval of the secret
7363
*/
7464
protected async _get(
7565
name: string,
7666
options?: HashiCorpVaultGetOptions
7767
): Promise<Record<string, unknown>> {
78-
const mount = options?.sdkOptions?.mount ?? 'secret';
79-
const version = options?.sdkOptions?.version;
68+
const { sdkOptions } = options ?? {};
69+
const mount = sdkOptions?.mount ?? this.#rootPath;
70+
const version = sdkOptions?.version
71+
? `?version=${sdkOptions?.version}`
72+
: '';
8073

81-
const response = await this.client.readKVSecret(
82-
this.#token,
83-
name,
84-
version,
85-
mount
86-
);
74+
setTimeout(() => {
75+
this.#abortController.abort();
76+
}, this.#timeout);
8777

88-
if (response.isVaultError) {
89-
throw response;
78+
const res = await fetch(
79+
`${this.#baseUrl}/${mount}/data/${name}${version}`,
80+
{
81+
headers: { 'X-Vault-Token': this.#token },
82+
method: 'GET',
83+
signal: this.#abortController.signal,
84+
}
85+
);
86+
if (!res.ok) {
87+
throw new GetParameterError(`Failed to fetch secret ${res.statusText}`);
9088
}
91-
return response.data;
89+
const response = await res.json();
90+
return response.data.data;
9291
}
9392

9493
/**

examples/snippets/parameters/customProviderVaultTypes.ts

+12-45
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import type { GetOptionsInterface } from '@aws-lambda-powertools/parameters/base/types';
2-
import type Vault from 'hashi-vault-js';
32

43
/**
5-
* Base interface for HashiCorpVaultProviderOptions.
6-
* @interface
4+
* Options for the HashiCorpVaultProvider class constructor.
5+
*
6+
* @param {string} url - Indicate the server name/IP, port and API version for the Vault instance, all paths are relative to this one.
7+
* @param {string} token - The Vault token to use for authentication.
8+
*
79
*/
8-
interface HashiCorpVaultProviderOptionsBase {
10+
interface HashiCorpVaultProviderOptions {
911
/**
1012
* Indicate the server name/IP, port and API version for the Vault instance, all paths are relative to this one.
1113
* @example 'https://vault.example.com:8200/v1'
@@ -15,53 +17,18 @@ interface HashiCorpVaultProviderOptionsBase {
1517
* The Vault token to use for authentication.
1618
*/
1719
token: string;
18-
}
19-
20-
/**
21-
* Interface for HashiCorpVaultProviderOptions with clientConfig property.
22-
* @interface
23-
*/
24-
interface HashiCorpVaultProviderOptionsWithClientConfig
25-
extends HashiCorpVaultProviderOptionsBase {
2620
/**
27-
* Optional configuration to pass during client initialization to customize the `hashi-vault-js` client.
21+
* The root path to use for the secret engine. Defaults to `secret`.
2822
*/
29-
clientConfig?: unknown;
23+
rootPath?: string;
3024
/**
31-
* This property should never be passed.
25+
* The timeout in milliseconds for the HTTP requests. Defaults to `5000`.
26+
* @example 10000
27+
* @default 5000
3228
*/
33-
vaultClient?: never;
29+
timeout?: number;
3430
}
3531

36-
/**
37-
* Interface for HashiCorpVaultProviderOptions with vaultClient property.
38-
*
39-
* @interface
40-
*/
41-
interface HashiCorpVaultProviderOptionsWithClientInstance
42-
extends HashiCorpVaultProviderOptionsBase {
43-
/**
44-
* Optional `hashi-vault-js` client to pass during HashiCorpVaultProvider class instantiation. If not provided, a new client will be created.
45-
*/
46-
vaultClient?: Vault;
47-
/**
48-
* This property should never be passed.
49-
*/
50-
clientConfig: never;
51-
}
52-
53-
/**
54-
* Options for the HashiCorpVaultProvider class constructor.
55-
*
56-
* @param {string} url - Indicate the server name/IP, port and API version for the Vault instance, all paths are relative to this one.
57-
* @param {string} token - The Vault token to use for authentication.
58-
* @param {Vault.VaultConfig} [clientConfig] - Optional configuration to pass during client initialization, e.g. timeout. Mutually exclusive with vaultClient.
59-
* @param {Vault} [vaultClient] - Optional `hashi-vault-js` client to pass during HashiCorpVaultProvider class instantiation. Mutually exclusive with clientConfig.
60-
*/
61-
type HashiCorpVaultProviderOptions =
62-
| HashiCorpVaultProviderOptionsWithClientConfig
63-
| HashiCorpVaultProviderOptionsWithClientInstance;
64-
6532
type HashiCorpVaultReadKVSecretOptions = {
6633
/**
6734
* The mount point of the secret engine to use. Defaults to `secret`.
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
11
import { Logger } from '@aws-lambda-powertools/logger';
22
import { HashiCorpVaultProvider } from './customProviderVault.js';
33

4-
const logger = new Logger({ logLevel: 'DEBUG' });
4+
const logger = new Logger({ serviceName: 'serverless-airline' });
55
const secretsProvider = new HashiCorpVaultProvider({
66
url: 'https://vault.example.com:8200/v1',
77
token: process.env.ROOT_TOKEN ?? '',
8+
rootPath: 'kv',
89
});
910

10-
try {
11-
// Retrieve a secret from HashiCorp Vault
12-
const secret = await secretsProvider.get<{ foo: 'string' }>('my-secret', {
13-
sdkOptions: {
14-
mount: 'secrets',
15-
},
16-
});
17-
if (!secret) {
18-
throw new Error('Secret not found');
19-
}
20-
logger.debug('Secret retrieved!');
21-
} catch (error) {
22-
if (error instanceof Error) {
23-
logger.error(error.message, error);
24-
}
25-
}
11+
// Retrieve a secret from HashiCorp Vault
12+
const secret = await secretsProvider.get<{ foo: 'string' }>('my-secret');
13+
14+
const res = await fetch('https://example.com/api', {
15+
method: 'POST',
16+
headers: {
17+
'Content-Type': 'application/json',
18+
Authorization: `Bearer ${secret?.foo}`,
19+
},
20+
body: JSON.stringify({ data: 'example' }),
21+
});
22+
logger.debug('res status', { status: res.status });

package-lock.json

-13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/parameters/src/types/BaseProvider.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ type BaseProviderConstructorOptions = {
1717
*
1818
* If the `awsSdkV3Client` is not provided, this will be used to create a new client.
1919
*/
20-
awsSdkV3ClientPrototype: new (
20+
awsSdkV3ClientPrototype?: new (
2121
config?: unknown
2222
) => unknown;
2323
};

0 commit comments

Comments
 (0)