@middy/ssm 5.0.0-alpha.0 → 5.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,8 +19,9 @@
19
19
  <a href="https://snyk.io/test/github/middyjs/middy">
20
20
  <img src="https://snyk.io/test/github/middyjs/middy/badge.svg" alt="Known Vulnerabilities" data-canonical-src="https://snyk.io/test/github/middyjs/middy" style="max-width:100%;">
21
21
  </a>
22
- <a href="https://lgtm.com/projects/g/middyjs/middy/context:javascript">
23
- <img src="https://img.shields.io/lgtm/grade/javascript/g/middyjs/middy.svg?logo=lgtm&logoWidth=18" alt="Language grade: JavaScript" style="max-width:100%;">
22
+ <a href="https://github.com/middyjs/middy/actions/workflows/sast.yml">
23
+ <img src="https://github.com/middyjs/middy/actions/workflows/sast.yml/badge.svg
24
+ ?branch=main&event=push" alt="CodeQL" style="max-width:100%;">
24
25
  </a>
25
26
  <a href="https://bestpractices.coreinfrastructure.org/projects/5280">
26
27
  <img src="https://bestpractices.coreinfrastructure.org/projects/5280/badge" alt="Core Infrastructure Initiative (CII) Best Practices" style="max-width:100%;">
package/index.d.ts CHANGED
@@ -2,27 +2,39 @@ import middy from '@middy/core'
2
2
  import { Options as MiddyOptions } from '@middy/util'
3
3
  import { Context as LambdaContext } from 'aws-lambda'
4
4
  import { SSMClient, SSMClientConfig } from '@aws-sdk/client-ssm'
5
- import { JsonValue } from 'type-fest'
6
5
 
7
- interface Options<AwsSSMClient = SSMClient>
8
- extends MiddyOptions<AwsSSMClient, SSMClientConfig> {}
6
+ export type ParamType<T> = string & { __returnType?: T }
7
+ export declare function ssmParam<T> (path: string): ParamType<T>
9
8
 
10
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
11
- type ExtractSingles<T> = T extends `/${infer _}` ? never : T
12
-
13
- export type Context<TOptions extends Options | undefined> = TOptions extends {
14
- setToContext: true
9
+ export interface SSMOptions<AwsSSMClient = SSMClient>
10
+ extends Omit<MiddyOptions<AwsSSMClient, SSMClientConfig>, 'fetchData'> {
11
+ fetchData?: { [key: string]: string | ParamType<unknown> }
15
12
  }
16
- ? LambdaContext &
17
- Record<ExtractSingles<keyof TOptions['fetchData']>, JsonValue> &
18
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
19
- (keyof TOptions['fetchData'] extends `${infer _P}/${infer _S}`
20
- ? Record<string, JsonValue>
21
- : unknown)
13
+
14
+ export type Context<TOptions extends SSMOptions | undefined> =
15
+ TOptions extends { setToContext: true }
16
+ ? TOptions extends { fetchData: infer TFetchData }
17
+ ? LambdaContext & {
18
+ [Key in keyof TFetchData]: TFetchData[Key] extends ParamType<infer T>
19
+ ? T
20
+ : unknown
21
+ }
22
+ : never
22
23
  : LambdaContext
23
24
 
24
- declare function ssm<TOptions extends Options> (
25
+ export type Internal<TOptions extends SSMOptions | undefined> =
26
+ TOptions extends SSMOptions
27
+ ? TOptions extends { fetchData: infer TFetchData }
28
+ ? {
29
+ [Key in keyof TFetchData]: TFetchData[Key] extends ParamType<infer T>
30
+ ? T
31
+ : unknown
32
+ }
33
+ : {}
34
+ : {}
35
+
36
+ declare function ssm<TOptions extends SSMOptions> (
25
37
  options?: TOptions
26
- ): middy.MiddlewareObj<unknown, any, Error, Context<TOptions>>
38
+ ): middy.MiddlewareObj<unknown, any, Error, Context<TOptions>, Internal<TOptions>>
27
39
 
28
40
  export default ssm
package/index.js CHANGED
@@ -9,6 +9,7 @@ const defaults = {
9
9
  fetchData: {},
10
10
  disablePrefetch: false,
11
11
  cacheKey: 'ssm',
12
+ cacheKeyExpiry: {},
12
13
  cacheExpiry: -1,
13
14
  setToContext: false
14
15
  };
@@ -28,21 +29,27 @@ const ssmMiddleware = (opts = {})=>{
28
29
  let batchReq = null;
29
30
  let batchInternalKeys = [];
30
31
  let batchFetchKeys = [];
32
+ const namedKeys = [];
31
33
  const internalKeys = Object.keys(options.fetchData);
32
34
  const fetchKeys = Object.values(options.fetchData);
33
- for (const [idx, internalKey] of internalKeys.entries()){
35
+ for (const internalKey of internalKeys){
34
36
  if (cachedValues[internalKey]) continue;
37
+ if (options.fetchData[internalKey].substr(-1) === '/') continue; // Skip path passed in
38
+ namedKeys.push(internalKey);
39
+ }
40
+ for (const [idx, internalKey] of namedKeys.entries()){
35
41
  const fetchKey = options.fetchData[internalKey];
36
- if (fetchKey.substr(-1) === '/') continue;
37
42
  batchInternalKeys.push(internalKey);
38
43
  batchFetchKeys.push(fetchKey);
39
- if ((!idx || (idx + 1) % awsRequestLimit !== 0) && !(idx + 1 === internalKeys.length)) {
44
+ // from the first to the batch size skip, unless it's the last entry
45
+ if ((!idx || (idx + 1) % awsRequestLimit !== 0) && !(idx + 1 === namedKeys.length)) {
40
46
  continue;
41
47
  }
42
48
  batchReq = client.send(new GetParametersCommand({
43
49
  Names: batchFetchKeys,
44
50
  WithDecryption: true
45
51
  })).then((resp)=>{
52
+ // Don't sanitize key, mapped to set value in options
46
53
  return Object.assign(...(resp.InvalidParameters ?? []).map((fetchKey)=>{
47
54
  return {
48
55
  [fetchKey]: new Promise(()=>{
@@ -50,7 +57,11 @@ const ssmMiddleware = (opts = {})=>{
50
57
  const value = getCache(options.cacheKey).value ?? {};
51
58
  value[internalKey] = undefined;
52
59
  modifyCache(options.cacheKey, value);
53
- throw new Error('[ssm] InvalidParameter ' + fetchKey);
60
+ throw new Error('InvalidParameter ' + fetchKey, {
61
+ cause: {
62
+ package: '@middy/ssm'
63
+ }
64
+ });
54
65
  })
55
66
  };
56
67
  }), ...(resp.Parameters ?? []).map((param)=>{
@@ -80,7 +91,7 @@ const ssmMiddleware = (opts = {})=>{
80
91
  for(const internalKey in options.fetchData){
81
92
  if (cachedValues[internalKey]) continue;
82
93
  const fetchKey = options.fetchData[internalKey];
83
- if (fetchKey.substr(-1) !== '/') continue;
94
+ if (fetchKey.substr(-1) !== '/') continue; // Skip not path passed in
84
95
  values[internalKey] = fetchPath(fetchKey).catch((e)=>{
85
96
  const value = getCache(options.cacheKey).value ?? {};
86
97
  value[internalKey] = undefined;
@@ -112,26 +123,29 @@ const ssmMiddleware = (opts = {})=>{
112
123
  }
113
124
  return jsonSafeParse(param.Value);
114
125
  };
115
- let prefetch, client;
126
+ let client;
116
127
  if (canPrefetch(options)) {
117
128
  client = createPrefetchClient(options);
118
- prefetch = processCache(options, fetch);
129
+ processCache(options, fetch);
119
130
  }
120
131
  const ssmMiddlewareBefore = async (request)=>{
121
132
  if (!client) {
122
133
  client = await createClient(options, request);
123
134
  }
124
- const { value } = prefetch ?? processCache(options, fetch, request);
135
+ const { value } = processCache(options, fetch, request);
125
136
  Object.assign(request.internal, value);
126
137
  if (options.setToContext) {
127
138
  const data = await getInternal(Object.keys(options.fetchData), request);
128
139
  Object.assign(request.context, data);
129
140
  }
130
- prefetch = null;
131
141
  };
132
142
  return {
133
143
  before: ssmMiddlewareBefore
134
144
  };
135
145
  };
136
146
  export default ssmMiddleware;
147
+ // used for TS type inference (see index.d.ts)
148
+ export function ssmParam(name) {
149
+ return name;
150
+ }
137
151
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@middy/ssm",
3
- "version": "5.0.0-alpha.0",
3
+ "version": "5.0.0-alpha.2",
4
4
  "description": "SSM (EC2 Systems Manager) parameters middleware for the middy framework",
5
5
  "type": "module",
6
6
  "engines": {
@@ -10,24 +10,18 @@
10
10
  "publishConfig": {
11
11
  "access": "public"
12
12
  },
13
- "main": "./index.cjs",
14
13
  "module": "./index.js",
15
14
  "exports": {
16
15
  ".": {
17
16
  "import": {
18
17
  "types": "./index.d.ts",
19
18
  "default": "./index.js"
20
- },
21
- "require": {
22
- "types": "./index.d.ts",
23
- "default": "./index.cjs"
24
19
  }
25
20
  }
26
21
  },
27
22
  "types": "index.d.ts",
28
23
  "files": [
29
24
  "index.js",
30
- "index.cjs",
31
25
  "index.d.ts"
32
26
  ],
33
27
  "scripts": {
@@ -66,14 +60,13 @@
66
60
  "url": "https://github.com/sponsors/willfarrell"
67
61
  },
68
62
  "dependencies": {
69
- "@middy/util": "5.0.0-alpha.0"
63
+ "@middy/util": "5.0.0-alpha.2"
70
64
  },
71
65
  "devDependencies": {
72
66
  "@aws-sdk/client-ssm": "^3.0.0",
73
- "@middy/core": "5.0.0-alpha.0",
67
+ "@middy/core": "5.0.0-alpha.2",
74
68
  "@types/aws-lambda": "^8.10.101",
75
- "aws-xray-sdk": "^3.3.3",
76
- "type-fest": "^3.0.0"
69
+ "aws-xray-sdk": "^3.3.3"
77
70
  },
78
- "gitHead": "08c35e3dba9efdad0b86666ce206ce302cc65d07"
71
+ "gitHead": "ebce8d5df8783077fa49ba62ee9be20e8486a7f1"
79
72
  }
package/index.cjs DELETED
@@ -1,145 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(module, "exports", {
6
- enumerable: true,
7
- get: ()=>_default
8
- });
9
- const _util = require("@middy/util");
10
- const _clientSsm = require("@aws-sdk/client-ssm");
11
- const awsRequestLimit = 10;
12
- const defaults = {
13
- AwsClient: _clientSsm.SSMClient,
14
- awsClientOptions: {},
15
- awsClientAssumeRole: undefined,
16
- awsClientCapture: undefined,
17
- fetchData: {},
18
- disablePrefetch: false,
19
- cacheKey: 'ssm',
20
- cacheExpiry: -1,
21
- setToContext: false
22
- };
23
- const ssmMiddleware = (opts = {})=>{
24
- const options = {
25
- ...defaults,
26
- ...opts
27
- };
28
- const fetch = (request, cachedValues)=>{
29
- return {
30
- ...fetchSingle(request, cachedValues),
31
- ...fetchByPath(request, cachedValues)
32
- };
33
- };
34
- const fetchSingle = (request, cachedValues = {})=>{
35
- const values = {};
36
- let batchReq = null;
37
- let batchInternalKeys = [];
38
- let batchFetchKeys = [];
39
- const internalKeys = Object.keys(options.fetchData);
40
- const fetchKeys = Object.values(options.fetchData);
41
- for (const [idx, internalKey] of internalKeys.entries()){
42
- if (cachedValues[internalKey]) continue;
43
- const fetchKey = options.fetchData[internalKey];
44
- if (fetchKey.substr(-1) === '/') continue;
45
- batchInternalKeys.push(internalKey);
46
- batchFetchKeys.push(fetchKey);
47
- if ((!idx || (idx + 1) % awsRequestLimit !== 0) && !(idx + 1 === internalKeys.length)) {
48
- continue;
49
- }
50
- batchReq = client.send(new _clientSsm.GetParametersCommand({
51
- Names: batchFetchKeys,
52
- WithDecryption: true
53
- })).then((resp)=>{
54
- return Object.assign(...(resp.InvalidParameters ?? []).map((fetchKey)=>{
55
- return {
56
- [fetchKey]: new Promise(()=>{
57
- const internalKey = internalKeys[fetchKeys.indexOf(fetchKey)];
58
- const value = (0, _util.getCache)(options.cacheKey).value ?? {};
59
- value[internalKey] = undefined;
60
- (0, _util.modifyCache)(options.cacheKey, value);
61
- throw new Error('[ssm] InvalidParameter ' + fetchKey);
62
- })
63
- };
64
- }), ...(resp.Parameters ?? []).map((param)=>{
65
- return {
66
- [param.Name]: parseValue(param)
67
- };
68
- }));
69
- }).catch((e)=>{
70
- const value = (0, _util.getCache)(options.cacheKey).value ?? {};
71
- value[internalKey] = undefined;
72
- (0, _util.modifyCache)(options.cacheKey, value);
73
- throw e;
74
- });
75
- for (const internalKey of batchInternalKeys){
76
- values[internalKey] = batchReq.then((params)=>{
77
- return params[options.fetchData[internalKey]];
78
- });
79
- }
80
- batchInternalKeys = [];
81
- batchFetchKeys = [];
82
- batchReq = null;
83
- }
84
- return values;
85
- };
86
- const fetchByPath = (request, cachedValues = {})=>{
87
- const values = {};
88
- for(const internalKey in options.fetchData){
89
- if (cachedValues[internalKey]) continue;
90
- const fetchKey = options.fetchData[internalKey];
91
- if (fetchKey.substr(-1) !== '/') continue;
92
- values[internalKey] = fetchPath(fetchKey).catch((e)=>{
93
- const value = (0, _util.getCache)(options.cacheKey).value ?? {};
94
- value[internalKey] = undefined;
95
- (0, _util.modifyCache)(options.cacheKey, value);
96
- throw e;
97
- });
98
- }
99
- return values;
100
- };
101
- const fetchPath = (path, nextToken, values = {})=>{
102
- return client.send(new _clientSsm.GetParametersByPathCommand({
103
- Path: path,
104
- NextToken: nextToken,
105
- Recursive: true,
106
- WithDecryption: true
107
- })).then((resp)=>{
108
- Object.assign(values, ...resp.Parameters.map((param)=>{
109
- return {
110
- [(0, _util.sanitizeKey)(param.Name.replace(path, ''))]: parseValue(param)
111
- };
112
- }));
113
- if (resp.NextToken) return fetchPath(path, resp.NextToken, values);
114
- return values;
115
- });
116
- };
117
- const parseValue = (param)=>{
118
- if (param.Type === 'StringList') {
119
- return param.Value.split(',');
120
- }
121
- return (0, _util.jsonSafeParse)(param.Value);
122
- };
123
- let prefetch, client;
124
- if ((0, _util.canPrefetch)(options)) {
125
- client = (0, _util.createPrefetchClient)(options);
126
- prefetch = (0, _util.processCache)(options, fetch);
127
- }
128
- const ssmMiddlewareBefore = async (request)=>{
129
- if (!client) {
130
- client = await (0, _util.createClient)(options, request);
131
- }
132
- const { value } = prefetch ?? (0, _util.processCache)(options, fetch, request);
133
- Object.assign(request.internal, value);
134
- if (options.setToContext) {
135
- const data = await (0, _util.getInternal)(Object.keys(options.fetchData), request);
136
- Object.assign(request.context, data);
137
- }
138
- prefetch = null;
139
- };
140
- return {
141
- before: ssmMiddlewareBefore
142
- };
143
- };
144
- const _default = ssmMiddleware;
145
-