ssm-cache 2.9__tar.gz → 3.0.0__tar.gz

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.
Files changed (42) hide show
  1. ssm_cache-3.0.0/LICENSE +21 -0
  2. ssm_cache-3.0.0/PKG-INFO +467 -0
  3. ssm_cache-3.0.0/README.md +434 -0
  4. ssm_cache-3.0.0/pyproject.toml +95 -0
  5. ssm_cache-3.0.0/ssm_cache/__init__.py +21 -0
  6. ssm_cache-3.0.0/ssm_cache/exceptions.py +13 -0
  7. ssm_cache-3.0.0/ssm_cache/filters.py +91 -0
  8. ssm_cache-3.0.0/ssm_cache/groups.py +121 -0
  9. ssm_cache-3.0.0/ssm_cache/parameters.py +133 -0
  10. ssm_cache-3.0.0/ssm_cache/py.typed +0 -0
  11. ssm_cache-3.0.0/ssm_cache/refreshable.py +180 -0
  12. ssm_cache-3.0.0/ssm_cache/utils.py +20 -0
  13. ssm_cache-3.0.0/ssm_cache.egg-info/PKG-INFO +467 -0
  14. ssm_cache-3.0.0/ssm_cache.egg-info/SOURCES.txt +23 -0
  15. ssm_cache-3.0.0/ssm_cache.egg-info/requires.txt +12 -0
  16. {ssm-cache-2.9 → ssm_cache-3.0.0}/ssm_cache.egg-info/top_level.txt +1 -1
  17. ssm_cache-3.0.0/tests/test_filters.py +165 -0
  18. ssm_cache-3.0.0/tests/test_groups.py +279 -0
  19. ssm_cache-3.0.0/tests/test_parameters.py +105 -0
  20. ssm_cache-3.0.0/tests/test_refreshable.py +229 -0
  21. ssm_cache-3.0.0/tests/test_secrets.py +64 -0
  22. ssm_cache-3.0.0/tests/test_utils.py +46 -0
  23. ssm_cache-3.0.0/tests/test_versioning.py +169 -0
  24. ssm-cache-2.9/PKG-INFO +0 -385
  25. ssm-cache-2.9/README.md +0 -372
  26. ssm-cache-2.9/setup.py +0 -21
  27. ssm-cache-2.9/ssm_cache/__init__.py +0 -9
  28. ssm-cache-2.9/ssm_cache/cache.py +0 -350
  29. ssm-cache-2.9/ssm_cache/filters.py +0 -85
  30. ssm-cache-2.9/ssm_cache.egg-info/PKG-INFO +0 -385
  31. ssm-cache-2.9/ssm_cache.egg-info/SOURCES.txt +0 -18
  32. ssm-cache-2.9/ssm_cache.egg-info/requires.txt +0 -2
  33. ssm-cache-2.9/tests/__init__.py +0 -65
  34. ssm-cache-2.9/tests/cache_test.py +0 -244
  35. ssm-cache-2.9/tests/decorator_test.py +0 -146
  36. ssm-cache-2.9/tests/filters_test.py +0 -255
  37. ssm-cache-2.9/tests/hierarchy_test.py +0 -233
  38. ssm-cache-2.9/tests/override_test.py +0 -98
  39. ssm-cache-2.9/tests/secrets_test.py +0 -84
  40. ssm-cache-2.9/tests/versioning_test.py +0 -185
  41. {ssm-cache-2.9 → ssm_cache-3.0.0}/setup.cfg +0 -0
  42. {ssm-cache-2.9 → ssm_cache-3.0.0}/ssm_cache.egg-info/dependency_links.txt +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Alex Casalboni
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,467 @@
1
+ Metadata-Version: 2.4
2
+ Name: ssm-cache
3
+ Version: 3.0.0
4
+ Summary: AWS System Manager Parameter Store caching client for Python
5
+ Author-email: Alex Casalboni <alex@alexcasalboni.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/alexcasalboni/ssm-cache-python
8
+ Project-URL: Repository, https://github.com/alexcasalboni/ssm-cache-python
9
+ Project-URL: Bug Tracker, https://github.com/alexcasalboni/ssm-cache-python/issues
10
+ Keywords: aws,amazon-web-services,aws-lambda,aws-ssm,parameter-store
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: boto3>=1.5.0
22
+ Requires-Dist: botocore>=1.7.12
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=8.3; extra == "dev"
25
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
26
+ Requires-Dist: moto>=4.0; extra == "dev"
27
+ Requires-Dist: freezegun>=1.5.5; extra == "dev"
28
+ Requires-Dist: placebo>=0.10.0; extra == "dev"
29
+ Requires-Dist: ruff>=0.15; extra == "dev"
30
+ Requires-Dist: mypy>=1.11; extra == "dev"
31
+ Requires-Dist: coveralls>=4.0; extra == "dev"
32
+ Dynamic: license-file
33
+
34
+ AWS System Manager Parameter Store Caching Client for Python
35
+ ==========================================================
36
+
37
+ [![CI](https://github.com/alexcasalboni/ssm-cache-python/actions/workflows/ci.yml/badge.svg)](https://github.com/alexcasalboni/ssm-cache-python/actions/workflows/ci.yml)
38
+ [![Coverage Status](https://coveralls.io/repos/github/alexcasalboni/ssm-cache-python/badge.svg)](https://coveralls.io/github/alexcasalboni/ssm-cache-python)
39
+ [![PyPI version](https://badge.fury.io/py/ssm-cache.svg)](https://badge.fury.io/py/ssm-cache)
40
+ [![Python versions](https://img.shields.io/pypi/pyversions/ssm-cache.svg)](https://pypi.org/project/ssm-cache/)
41
+ [![GitHub license](https://img.shields.io/github/license/alexcasalboni/ssm-cache-python.svg)](https://github.com/alexcasalboni/ssm-cache-python/blob/master/LICENSE)
42
+ [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://GitHub.com/alexcasalboni/ssm-cache-python/graphs/commit-activity)
43
+ [![GitHub issues](https://img.shields.io/github/issues/alexcasalboni/ssm-cache-python.svg)](https://github.com/alexcasalboni/ssm-cache-python/issues)
44
+ [![GitHub stars](https://img.shields.io/github/stars/alexcasalboni/ssm-cache-python.svg)](https://github.com/alexcasalboni/ssm-cache-python/stargazers)
45
+
46
+ This module wraps the AWS Parameter Store and adds a caching and grouping layer with max-age invalidation.
47
+
48
+ You can use this module with AWS Lambda to read and refresh parameters and secrets. Your IAM role will require `ssm:GetParameters` permissions (optionally, also `kms:Decrypt` if you use `SecureString` params).
49
+
50
+ ## Package structure
51
+
52
+ The library is split into focused modules — import from the top-level `ssm_cache` package for everyday use, or from a specific submodule when you need internals (e.g. for testing or extension).
53
+
54
+ ```
55
+ ssm_cache/
56
+ ├── __init__.py # public re-exports + __version__
57
+ ├── exceptions.py # InvalidParameterError, InvalidPathError, InvalidVersionError
58
+ ├── filters.py # SSMFilter, SSMFilterType, SSMFilterKeyId, …
59
+ ├── groups.py # SSMParameterGroup
60
+ ├── parameters.py # SSMParameter, SecretsManagerParameter
61
+ ├── refreshable.py # Refreshable base class (caching, refresh_on_error, set_ssm_client)
62
+ └── utils.py # utcnow(), batch()
63
+ ```
64
+
65
+ The version is available at runtime:
66
+
67
+ ```python
68
+ import ssm_cache
69
+ print(ssm_cache.__version__) # e.g. "3.0.0"
70
+ ```
71
+
72
+ ## How to install
73
+
74
+ ```bash
75
+ pip install ssm-cache
76
+ ```
77
+
78
+ Dev and test dependencies are declared as an optional group in `pyproject.toml` and can be installed with:
79
+
80
+ ```bash
81
+ pip install "ssm-cache[dev]"
82
+ # or, from a local clone:
83
+ pip install -e ".[dev]"
84
+ ```
85
+
86
+ ## How to use it
87
+
88
+ ### Simplest use case
89
+
90
+ A single parameter, configured by name.
91
+
92
+ ```python
93
+ from ssm_cache import SSMParameter
94
+ param = SSMParameter('my_param_name')
95
+ value = param.value
96
+ ```
97
+
98
+ ### With cache invalidation
99
+
100
+ You can configure the `max_age` in seconds, after which the values will be automatically refreshed.
101
+
102
+ ```python
103
+ from ssm_cache import SSMParameter
104
+ param_1 = SSMParameter('param_1', max_age=300) # 5 min
105
+ value_1 = param_1.value
106
+
107
+ param_2 = SSMParameter('param_2', max_age=3600) # 1 hour
108
+ value_2 = param_2.value
109
+ ```
110
+
111
+ ### With multiple parameters
112
+
113
+ You can configure more than one parameter to be fetched, cached, and decrypted as a group.
114
+
115
+ ```python
116
+ from ssm_cache import SSMParameterGroup
117
+ group = SSMParameterGroup(max_age=300)
118
+ param_1 = group.parameter('param_1')
119
+ param_2 = group.parameter('param_2')
120
+
121
+ value_1 = param_1.value
122
+ value_2 = param_2.value
123
+ ```
124
+
125
+ ### With hierarchical parameters
126
+
127
+ You can fetch and cache a group of parameters under a given prefix. Optionally, the group itself can have its own base path.
128
+
129
+ ```python
130
+ from ssm_cache import SSMParameterGroup
131
+ group = SSMParameterGroup(base_path="/Foo")
132
+ foo_bar = group.parameter('/Bar') # fetches /Foo/Bar
133
+ baz_params = group.parameters('/Baz') # fetches /Foo/Baz/1, /Foo/Baz/2, …
134
+
135
+ assert len(group) == 3
136
+ ```
137
+
138
+ `group.parameters(...)` can be called multiple times. When caching is enabled, the group's expiry is anchored to the earliest `parameters()` call, so all prefixes age out together.
139
+
140
+ #### Hierarchical parameters and filters
141
+
142
+ Filter by parameter `Type` or KMS `KeyId`, either with a raw dict or a typed class (which validates values before the API call).
143
+
144
+ ```python
145
+ from ssm_cache import SSMParameterGroup
146
+ from ssm_cache.filters import SSMFilterType, SSMFilterKeyId
147
+
148
+ group = SSMParameterGroup()
149
+
150
+ # raw dict
151
+ params = group.parameters(
152
+ path="/Foo/Bar",
153
+ filters=[{'Key': 'Type', 'Option': 'Equals', 'Values': ['StringList']}],
154
+ )
155
+
156
+ # typed class — validates allowed values before calling the API
157
+ params = group.parameters(
158
+ path="/Foo/Bar",
159
+ filters=[SSMFilterType().value('StringList')],
160
+ )
161
+
162
+ # KeyId filter, begins-with
163
+ params = group.parameters(
164
+ path="/Foo/Bar",
165
+ filters=[SSMFilterKeyId('BeginsWith').value('alias/')],
166
+ )
167
+ ```
168
+
169
+ #### Non-recursive fetch
170
+
171
+ ```python
172
+ from ssm_cache import SSMParameterGroup
173
+ group = SSMParameterGroup()
174
+
175
+ # fetches /Foo/1, /Foo/2 but NOT /Foo/Bar/1
176
+ params = group.parameters(path="/Foo", recursive=False)
177
+ ```
178
+
179
+ ### With StringList parameters
180
+
181
+ `StringList` parameters are automatically split on commas and returned as Python lists.
182
+
183
+ ```python
184
+ from ssm_cache import SSMParameter
185
+ # "my_twitter_api_keys" is a StringList (four comma-separated values)
186
+ twitter_params = SSMParameter('my_twitter_api_keys')
187
+ key, secret, access_token, access_token_secret = twitter_params.value
188
+ ```
189
+
190
+ ### Explicit refresh
191
+
192
+ Force a refresh on a parameter or group at any time. When a parameter belongs to a group, refreshing it refreshes the whole group.
193
+
194
+ ```python
195
+ from ssm_cache import SSMParameter
196
+ param = SSMParameter('my_param_name')
197
+ value = param.value
198
+ param.refresh()
199
+ new_value = param.value
200
+ ```
201
+
202
+ ```python
203
+ from ssm_cache import SSMParameterGroup
204
+ group = SSMParameterGroup()
205
+ param_1 = group.parameter('param_1')
206
+ param_2 = group.parameter('param_2')
207
+
208
+ value_1 = param_1.value
209
+ value_2 = param_2.value
210
+
211
+ group.refresh() # refreshes all params in the group
212
+ param_1.refresh() # also refreshes the whole group
213
+ ```
214
+
215
+ ### Without decryption
216
+
217
+ Decryption is enabled by default. Disable it explicitly for `SSMParameter` or `SSMParameterGroup`.
218
+
219
+ ```python
220
+ from ssm_cache import SSMParameter
221
+ param = SSMParameter('my_param_name', with_decryption=False)
222
+ value = param.value
223
+ ```
224
+
225
+ ### AWS Secrets Manager integration
226
+
227
+ `SecretsManagerParameter` provides the same interface as `SSMParameter` and transparently accesses Secrets Manager values via the SSM parameter path `/aws/reference/secretsmanager/<name>`.
228
+
229
+ ```python
230
+ from ssm_cache import SecretsManagerParameter
231
+ secret = SecretsManagerParameter('my_secret_name')
232
+ value = secret.value
233
+ ```
234
+
235
+ Secrets can be mixed with regular parameters inside a `SSMParameterGroup`. No group base path is applied to secrets.
236
+
237
+ ```python
238
+ from ssm_cache import SSMParameterGroup
239
+ group = SSMParameterGroup()
240
+ param = group.parameter('my_param')
241
+ secret = group.secret('my_secret')
242
+
243
+ param_value = param.value
244
+ secret_value = secret.value
245
+ ```
246
+
247
+ Passing a name that starts with `/` raises `InvalidParameterError` immediately, since that would be ambiguous with a raw SSM path.
248
+
249
+ ### Versioning support
250
+
251
+ SSM Parameter Store supports [version selectors](https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-paramstore-versions.html). Omitting the version always fetches the latest.
252
+
253
+ ```python
254
+ from ssm_cache import SSMParameter
255
+
256
+ # always fetches the latest version
257
+ param = SSMParameter('my_param_name')
258
+ print(param.version) # int
259
+
260
+ # pinned to version 2 — refresh() will NOT advance to a newer version
261
+ param_v2 = SSMParameter('my_param_name:2')
262
+ value = param_v2.value
263
+ ```
264
+
265
+ ## Usage with AWS Lambda
266
+
267
+ Parameters and secrets are initialised once outside the handler, so the cache persists across warm invocations.
268
+
269
+ ```python
270
+ from ssm_cache import SSMParameter, SecretsManagerParameter
271
+
272
+ param = SSMParameter('my_param_name', max_age=300)
273
+ secret = SecretsManagerParameter('my_secret_name', max_age=300)
274
+
275
+ def lambda_handler(event, context):
276
+ dbname = param.value
277
+ password = secret.value
278
+ return f'Hello from Lambda with dbname {dbname}'
279
+ ```
280
+
281
+ ## Complex invalidation based on signals
282
+
283
+ Explicitly call `refresh()` when an application-level error signals that a cached value is stale.
284
+
285
+ ```python
286
+ from ssm_cache import SSMParameter
287
+ from my_db_lib import Client, InvalidCredentials # pseudo-code
288
+
289
+ param = SSMParameter('my_db_password')
290
+ my_db_client = Client(password=param.value)
291
+
292
+ def read_record(is_retry=False):
293
+ try:
294
+ return my_db_client.read_record()
295
+ except InvalidCredentials:
296
+ if not is_retry:
297
+ param.refresh()
298
+ my_db_client = Client(password=param.value)
299
+ return read_record(is_retry=True)
300
+
301
+ def lambda_handler(event, context):
302
+ return {'record': read_record()}
303
+ ```
304
+
305
+ ## Decorator utility
306
+
307
+ `refresh_on_error` codifies the retry pattern above as a decorator on any `SSMParameter` or `SSMParameterGroup` instance.
308
+
309
+ ```python
310
+ from ssm_cache import SSMParameter
311
+ from my_db_lib import Client, InvalidCredentials # pseudo-code
312
+
313
+ param = SSMParameter('my_db_password')
314
+ my_db_client = Client(password=param.value)
315
+
316
+ def on_error_callback():
317
+ my_db_client = Client(password=param.value)
318
+
319
+ @param.refresh_on_error(InvalidCredentials, on_error_callback)
320
+ def read_record(is_retry=False):
321
+ return my_db_client.read_record()
322
+
323
+ def lambda_handler(event, context):
324
+ return {'record': read_record()}
325
+ ```
326
+
327
+ `refresh_on_error` accepts:
328
+
329
+ | Argument | Default | Description |
330
+ |---|---|---|
331
+ | `error_class` | `Exception` | Exception type to intercept |
332
+ | `error_callback` | `None` | Called after refresh, before retry |
333
+ | `retry_argument` | `"is_retry"` | Kwarg name injected on retry |
334
+
335
+ ## Replacing the SSM client
336
+
337
+ `set_ssm_client` lives on `Refreshable`, the base class shared by `SSMParameter` and `SSMParameterGroup`. Call it on whichever class you want to override, or on `Refreshable` directly to affect all subclasses at once.
338
+
339
+ ```python
340
+ from ssm_cache.refreshable import Refreshable
341
+
342
+ # affects SSMParameter, SSMParameterGroup, and any subclass
343
+ Refreshable.set_ssm_client(my_custom_client)
344
+ ```
345
+
346
+ The replacement object must implement two methods: `get_parameters` and `get_parameters_by_path`.
347
+
348
+ A common use case is injecting a [Placebo](https://github.com/garnaat/placebo) client for offline or unit testing:
349
+
350
+ ```python
351
+ import boto3, placebo
352
+ from ssm_cache.refreshable import Refreshable
353
+
354
+ session = boto3.Session()
355
+ pill = placebo.attach(session, data_path='/path/to/responses')
356
+ pill.playback()
357
+
358
+ Refreshable.set_ssm_client(session.client('ssm'))
359
+ ```
360
+
361
+ ## How to contribute
362
+
363
+ Clone the repo and install all dev dependencies in one step:
364
+
365
+ ```bash
366
+ git clone https://github.com/alexcasalboni/ssm-cache-python.git
367
+ cd ssm-cache-python
368
+ python -m venv env
369
+ source env/bin/activate
370
+ pip install -e ".[dev]"
371
+ ```
372
+
373
+ ### Running the tests
374
+
375
+ ```bash
376
+ pytest
377
+ ```
378
+
379
+ With coverage:
380
+
381
+ ```bash
382
+ pytest --cov=ssm_cache --cov-report=term-missing
383
+ ```
384
+
385
+ HTML coverage report:
386
+
387
+ ```bash
388
+ pytest --cov=ssm_cache --cov-report=html
389
+ open htmlcov/index.html
390
+ ```
391
+
392
+ ### Linting and formatting
393
+
394
+ The project uses [ruff](https://docs.astral.sh/ruff/) for both linting and formatting.
395
+
396
+ Check for lint violations:
397
+
398
+ ```bash
399
+ ruff check .
400
+ ```
401
+
402
+ Auto-fix all fixable violations:
403
+
404
+ ```bash
405
+ ruff check --fix .
406
+ ```
407
+
408
+ Check formatting:
409
+
410
+ ```bash
411
+ ruff format --check .
412
+ ```
413
+
414
+ Apply formatting:
415
+
416
+ ```bash
417
+ ruff format .
418
+ ```
419
+
420
+ The CI `lint` job runs both `ruff format --check` and `ruff check` on every push and pull request before the test matrix starts. A failing lint check blocks the test run.
421
+
422
+ Opening a PR triggers the GitHub Actions CI matrix across Python 3.10–3.14 and uploads coverage to Coveralls automatically.
423
+
424
+ ### Test layout
425
+
426
+ Test files mirror the package modules:
427
+
428
+ | Test file | Covers |
429
+ |---|---|
430
+ | `tests/test_utils.py` | `ssm_cache.utils` — `utcnow`, `batch` |
431
+ | `tests/test_refreshable.py` | `ssm_cache.refreshable` — `Refreshable`, `refresh_on_error`, `set_ssm_client` |
432
+ | `tests/test_parameters.py` | `ssm_cache.parameters` — `SSMParameter` |
433
+ | `tests/test_groups.py` | `ssm_cache.groups` — `SSMParameterGroup`, hierarchy |
434
+ | `tests/test_filters.py` | `ssm_cache.filters` — `SSMFilter` and subclasses |
435
+ | `tests/test_secrets.py` | `SecretsManagerParameter` |
436
+ | `tests/test_versioning.py` | Versioning and version pinning (placebo-backed) |
437
+
438
+ ## What's new?
439
+
440
+ * **version 3.0.0**:
441
+ * dropped support for Python <3.10 (3.8 and 3.9 are end-of-life)
442
+ * Python 3.10–3.14 supported and tested
443
+ * fully type-annotated; ships a `py.typed` marker (PEP 561) and is type-checked with mypy
444
+ * split monolithic `cache.py` into logically grouped modules (`exceptions`, `filters`, `groups`, `parameters`, `refreshable`, `utils`)
445
+ * `__version__` added to package
446
+ * `pyproject.toml` replaces `setup.py` and `requirements*.txt`
447
+ * `set_ssm_client` promoted to `Refreshable` base class
448
+ * `ParameterNotFound` ClientError now normalised to `InvalidParameterError`
449
+ * test suite restructured to mirror package layout
450
+ * ruff replaces pylint for linting and formatting
451
+ * migrated from Travis to GitHub Actions
452
+ * **version 2.10**: exclude tests folder from site-packages
453
+ * **version 2.9**: bugfix, versioning support, tests with Python 3.7
454
+ * **version 2.8**: bugfix, new tests, fixed Travis build config
455
+ * **version 2.7**: support for AWS Secrets Manager integration
456
+ * **version 2.5**: hierarchical parameters, filters, and non-recursiveness support
457
+ * **version 2.3**: StringList parameters support (auto-conversion)
458
+ * **version 2.2**: client replacement and boto3/botocore minimum requirements
459
+ * **version 2.1**: group refresh bugfix
460
+ * **version 2.0**: new interface, `SSMParameterGroup` support
461
+ * **version 1.3**: Python3 support
462
+ * **version 1.0**: initial release
463
+
464
+ ## References and articles
465
+
466
+ * [You should use SSM Parameter Store over Lambda env variables](https://hackernoon.com/you-should-use-ssm-parameter-store-over-lambda-env-variables-5197fc6ea45b) by Yan Cui (similar Node.js implementation)
467
+ * [AWS System Manager Parameter Store doc](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-paramstore.html)