@bitblit/ratchet-aws 6.0.146-alpha → 6.0.147-alpha

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 (103) hide show
  1. package/package.json +5 -4
  2. package/src/batch/aws-batch-background-processor.spec.ts +22 -0
  3. package/src/batch/aws-batch-background-processor.ts +71 -0
  4. package/src/batch/aws-batch-ratchet.spec.ts +42 -0
  5. package/src/batch/aws-batch-ratchet.ts +70 -0
  6. package/src/build/ratchet-aws-info.ts +19 -0
  7. package/src/cache/memory-storage-provider.ts +39 -0
  8. package/src/cache/simple-cache-object-wrapper.ts +11 -0
  9. package/src/cache/simple-cache-read-options.ts +9 -0
  10. package/src/cache/simple-cache-storage-provider.ts +15 -0
  11. package/src/cache/simple-cache.spec.ts +42 -0
  12. package/src/cache/simple-cache.ts +81 -0
  13. package/src/cloudwatch/cloud-watch-log-group-ratchet.spec.ts +26 -0
  14. package/src/cloudwatch/cloud-watch-log-group-ratchet.ts +105 -0
  15. package/src/cloudwatch/cloud-watch-logs-ratchet.spec.ts +123 -0
  16. package/src/cloudwatch/cloud-watch-logs-ratchet.ts +232 -0
  17. package/src/cloudwatch/cloud-watch-metrics-ratchet.spec.ts +30 -0
  18. package/src/cloudwatch/cloud-watch-metrics-ratchet.ts +98 -0
  19. package/src/dao/example-prototype-dao-item.ts +8 -0
  20. package/src/dao/memory-prototype-dao-provider.ts +16 -0
  21. package/src/dao/prototype-dao-config.ts +8 -0
  22. package/src/dao/prototype-dao-db.ts +4 -0
  23. package/src/dao/prototype-dao-provider.ts +6 -0
  24. package/src/dao/prototype-dao.spec.ts +33 -0
  25. package/src/dao/prototype-dao.ts +110 -0
  26. package/src/dao/s3-simple-dao.ts +96 -0
  27. package/src/dao/simple-dao-item.ts +13 -0
  28. package/src/dynamodb/dynamo-ratchet-like.ts +61 -0
  29. package/src/dynamodb/dynamo-ratchet.spec.ts +206 -0
  30. package/src/dynamodb/dynamo-ratchet.ts +850 -0
  31. package/src/dynamodb/dynamo-table-ratchet.spec.ts +23 -0
  32. package/src/dynamodb/dynamo-table-ratchet.ts +189 -0
  33. package/src/dynamodb/hash-spreader.spec.ts +22 -0
  34. package/src/dynamodb/hash-spreader.ts +89 -0
  35. package/src/dynamodb/impl/dynamo-db-storage-provider.spec.ts +60 -0
  36. package/src/dynamodb/impl/dynamo-db-storage-provider.ts +140 -0
  37. package/src/dynamodb/impl/dynamo-db-sync-lock.spec.ts +41 -0
  38. package/src/dynamodb/impl/dynamo-db-sync-lock.ts +78 -0
  39. package/src/dynamodb/impl/dynamo-expiring-code-provider.ts +31 -0
  40. package/src/dynamodb/impl/dynamo-runtime-parameter-provider.spec.ts +65 -0
  41. package/src/dynamodb/impl/dynamo-runtime-parameter-provider.ts +44 -0
  42. package/src/ec2/ec2-ratchet.spec.ts +45 -0
  43. package/src/ec2/ec2-ratchet.ts +169 -0
  44. package/src/ecr/ecr-unused-image-cleaner-options.ts +9 -0
  45. package/src/ecr/ecr-unused-image-cleaner-output.ts +8 -0
  46. package/src/ecr/ecr-unused-image-cleaner-repository-output.ts +10 -0
  47. package/src/ecr/ecr-unused-image-cleaner.spec.ts +40 -0
  48. package/src/ecr/ecr-unused-image-cleaner.ts +183 -0
  49. package/src/ecr/retained-image-descriptor.ts +7 -0
  50. package/src/ecr/retained-image-reason.ts +4 -0
  51. package/src/ecr/used-image-finder.ts +6 -0
  52. package/src/ecr/used-image-finders/aws-batch-used-image-finder.ts +40 -0
  53. package/src/ecr/used-image-finders/lambda-used-image-finder.ts +51 -0
  54. package/src/environment/cascade-environment-service-provider.ts +28 -0
  55. package/src/environment/env-var-environment-service-provider.ts +36 -0
  56. package/src/environment/environment-service-config.ts +7 -0
  57. package/src/environment/environment-service-provider.ts +7 -0
  58. package/src/environment/environment-service.spec.ts +41 -0
  59. package/src/environment/environment-service.ts +89 -0
  60. package/src/environment/fixed-environment-service-provider.ts +26 -0
  61. package/src/environment/ssm-environment-service-provider.spec.ts +18 -0
  62. package/src/environment/ssm-environment-service-provider.ts +71 -0
  63. package/src/expiring-code/expiring-code-params.ts +7 -0
  64. package/src/expiring-code/expiring-code-provider.ts +6 -0
  65. package/src/expiring-code/expiring-code-ratchet.spec.ts +10 -0
  66. package/src/expiring-code/expiring-code-ratchet.ts +44 -0
  67. package/src/expiring-code/expiring-code.ts +6 -0
  68. package/src/iam/aws-credentials-ratchet.ts +25 -0
  69. package/src/lambda/lambda-event-detector.ts +55 -0
  70. package/src/lambda/lambda-event-type-guards.ts +38 -0
  71. package/src/model/cloud-watch-metrics-minute-level-dynamo-count-request.ts +18 -0
  72. package/src/model/dynamo-count-result.ts +8 -0
  73. package/src/route53/route-53-ratchet.ts +77 -0
  74. package/src/runtime-parameter/cached-stored-runtime-parameter.ts +5 -0
  75. package/src/runtime-parameter/global-variable-override-runtime-parameter-provider.spec.ts +41 -0
  76. package/src/runtime-parameter/global-variable-override-runtime-parameter-provider.ts +82 -0
  77. package/src/runtime-parameter/memory-runtime-parameter-provider.ts +42 -0
  78. package/src/runtime-parameter/runtime-parameter-provider.ts +12 -0
  79. package/src/runtime-parameter/runtime-parameter-ratchet.spec.ts +53 -0
  80. package/src/runtime-parameter/runtime-parameter-ratchet.ts +84 -0
  81. package/src/runtime-parameter/stored-runtime-parameter.ts +6 -0
  82. package/src/s3/expanded-file-children.ts +5 -0
  83. package/src/s3/impl/s3-environment-service-provider.ts +41 -0
  84. package/src/s3/impl/s3-expiring-code-provider.spec.ts +63 -0
  85. package/src/s3/impl/s3-expiring-code-provider.ts +71 -0
  86. package/src/s3/impl/s3-prototype-dao-provider.spec.ts +45 -0
  87. package/src/s3/impl/s3-prototype-dao-provider.ts +37 -0
  88. package/src/s3/impl/s3-remote-file-tracking-provider-options.ts +6 -0
  89. package/src/s3/impl/s3-remote-file-tracking-provider.spec.ts +67 -0
  90. package/src/s3/impl/s3-remote-file-tracking-provider.ts +157 -0
  91. package/src/s3/impl/s3-storage-provider.spec.ts +32 -0
  92. package/src/s3/impl/s3-storage-provider.ts +60 -0
  93. package/src/s3/s3-cache-ratchet-like.ts +64 -0
  94. package/src/s3/s3-cache-ratchet.spec.ts +150 -0
  95. package/src/s3/s3-cache-ratchet.ts +476 -0
  96. package/src/s3/s3-location-sync-ratchet.ts +207 -0
  97. package/src/s3/s3-ratchet.spec.ts +26 -0
  98. package/src/s3/s3-ratchet.ts +26 -0
  99. package/src/ses/ses-mail-sending-provider.ts +85 -0
  100. package/src/sns/sns-ratchet.spec.ts +24 -0
  101. package/src/sns/sns-ratchet.ts +52 -0
  102. package/src/sync-lock/memory-sync-lock.ts +48 -0
  103. package/src/sync-lock/sync-lock-provider.ts +5 -0
@@ -0,0 +1,51 @@
1
+ import { UsedImageFinder } from '../used-image-finder.js';
2
+ import { RequireRatchet } from '@bitblit/ratchet-common/lang/require-ratchet';
3
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
4
+ import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet';
5
+ import {
6
+ FunctionConfiguration,
7
+ GetFunctionCommand,
8
+ GetFunctionCommandOutput,
9
+ LambdaClient,
10
+ ListFunctionsCommand,
11
+ ListFunctionsCommandInput,
12
+ } from '@aws-sdk/client-lambda';
13
+ import { ListFunctionsResponse } from '@aws-sdk/client-lambda/dist-types/models';
14
+
15
+ export class LambdaUsedImageFinder implements UsedImageFinder {
16
+ constructor(private lambda: LambdaClient) {
17
+ RequireRatchet.notNullOrUndefined(lambda, 'lambda');
18
+ }
19
+
20
+ public async findUsedImageUris(): Promise<string[]> {
21
+ const rval: Set<string> = new Set<string>();
22
+ const fns: FunctionConfiguration[] = await this.fetchFunctions();
23
+ Logger.info('Found %d functions', fns.length);
24
+ for (const fn of fns) {
25
+ //for (let i = 0; i < fns.length; i++) {
26
+ if (fn.PackageType === 'Image') {
27
+ const out: GetFunctionCommandOutput = await this.lambda.send(new GetFunctionCommand({ FunctionName: fn.FunctionName }));
28
+ if (out.Code.RepositoryType === 'ECR' && out.Code.ImageUri) {
29
+ rval.add(out.Code.ImageUri);
30
+ }
31
+ } else {
32
+ Logger.info('Skipping zip packaged function: %s', fn.FunctionName);
33
+ }
34
+ }
35
+ return Array.from(rval);
36
+ }
37
+
38
+ public async fetchFunctions(): Promise<FunctionConfiguration[]> {
39
+ let rval: any[] = [];
40
+ const cmd: ListFunctionsCommandInput = {};
41
+ let resp: ListFunctionsResponse = null;
42
+
43
+ do {
44
+ resp = await this.lambda.send(new ListFunctionsCommand(cmd));
45
+ rval = rval.concat(resp.Functions);
46
+ cmd.Marker = resp.NextMarker;
47
+ } while (StringRatchet.trimToNull(cmd.Marker));
48
+
49
+ return rval;
50
+ }
51
+ }
@@ -0,0 +1,28 @@
1
+ import { RequireRatchet } from '@bitblit/ratchet-common/lang/require-ratchet';
2
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
3
+ import { EnvironmentServiceProvider } from './environment-service-provider.js';
4
+
5
+ /**
6
+ * Reads a series of providers, returning the first non-null, non-error
7
+ */
8
+ export class CascadeEnvironmentServiceProvider<T> implements EnvironmentServiceProvider<T> {
9
+ public constructor(private providers: EnvironmentServiceProvider<T>[]) {
10
+ RequireRatchet.notNullOrUndefined(providers);
11
+ RequireRatchet.true(providers.length > 0);
12
+ }
13
+
14
+ public async fetchConfig(name: string): Promise<T> {
15
+ Logger.silly('CascadeEnvironmentServiceProvider fetch for %s', name);
16
+ let rval: T = null;
17
+ for (let i = 0; i < this.providers.length && !rval; i++) {
18
+ try {
19
+ rval = await this.providers[i].fetchConfig(name);
20
+ } catch (err) {
21
+ Logger.error('Provider %d failed - trying next : %s', i, err, err);
22
+ rval = null;
23
+ }
24
+ }
25
+
26
+ return rval;
27
+ }
28
+ }
@@ -0,0 +1,36 @@
1
+ import { RequireRatchet } from '@bitblit/ratchet-common/lang/require-ratchet';
2
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
3
+ import { ErrorRatchet } from '@bitblit/ratchet-common/lang/error-ratchet';
4
+ import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet';
5
+ import { EnvironmentServiceProvider } from './environment-service-provider.js';
6
+
7
+ /**
8
+ * Service for reading environmental variables
9
+ * Also hides the decryption detail from higher up servicess
10
+ */
11
+ export class EnvVarEnvironmentServiceProvider<T> implements EnvironmentServiceProvider<T> {
12
+ public constructor(private envVarName: string) {
13
+ RequireRatchet.notNullOrUndefined(envVarName);
14
+ }
15
+
16
+ public async fetchConfig(): Promise<T> {
17
+ Logger.silly('EnvVarEnvironmentServiceProvider fetch for %s', this.envVarName);
18
+
19
+ let rval: T = null;
20
+ const src: Record<string, any> = process ? process.env : global ? global : {};
21
+ const toParse: string = StringRatchet.trimToNull(src[this.envVarName]);
22
+
23
+ // If we reach here with a string result, try to parse it
24
+ if (toParse) {
25
+ try {
26
+ rval = JSON.parse(toParse);
27
+ } catch (err) {
28
+ Logger.error('Failed to read env - null or invalid JSON : %s : %s', err, toParse, err);
29
+ throw err;
30
+ }
31
+ } else {
32
+ ErrorRatchet.throwFormattedErr('Could not find env var with name : %s', this.envVarName);
33
+ }
34
+ return rval;
35
+ }
36
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Configuration of environmental server
3
+ */
4
+ export interface EnvironmentServiceConfig {
5
+ maxRetries: number;
6
+ backoffMultiplierMS: number;
7
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Classes implementing EnvironmentServiceProvider offer the ability to fetch a configuration
3
+ * object from _somewhere_
4
+ */
5
+ export interface EnvironmentServiceProvider<T> {
6
+ fetchConfig(name: string): Promise<T>;
7
+ }
@@ -0,0 +1,41 @@
1
+ import { EnvironmentService } from './environment-service.js';
2
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
3
+ import { FixedEnvironmentServiceProvider } from './fixed-environment-service-provider.js';
4
+ import { describe, expect, test } from 'vitest';
5
+
6
+ const fixed: FixedEnvironmentServiceProvider<any> = FixedEnvironmentServiceProvider.fromRecord<any>({ a: 'b', c: 5 });
7
+
8
+ describe('#environmentService', function () {
9
+ test('should throw exception on missing environment values', async () => {
10
+ try {
11
+ const es = new EnvironmentService<any>(fixed);
12
+ const _vals: any = await es.getConfig('i_do_not_exist');
13
+ this.bail('Should not have returned a value');
14
+ } catch (err) {
15
+ expect(err).toBeTruthy();
16
+ Logger.info('Success - threw %s', err);
17
+ }
18
+ });
19
+
20
+ test('should find a valid value', async () => {
21
+ const es = new EnvironmentService<any>(fixed);
22
+ const vals: any = await es.getConfig('c');
23
+ expect(vals).toBeTruthy();
24
+ });
25
+ /*
26
+ test.skip('should load config from s3', async () => {
27
+ Logger.setLevel(LoggerLevelName.silly);
28
+ const bucket: string = 'xxx';
29
+ const path: string = 'yyy';
30
+
31
+ const es: EnvironmentService<any> = new EnvironmentService<any>(
32
+ new S3EnvironmentServiceProvider({ bucketName: bucket, region: 'us-east-1' })
33
+ );
34
+ const vals: any = await es.getConfig(path);
35
+ const vals1: any = await es.getConfig(path);
36
+ const vals2: any = await es.getConfig(path);
37
+ expect(vals).toBeTruthy();
38
+ });
39
+
40
+ */
41
+ });
@@ -0,0 +1,89 @@
1
+ import { RequireRatchet } from '@bitblit/ratchet-common/lang/require-ratchet';
2
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
3
+ import { ErrorRatchet } from '@bitblit/ratchet-common/lang/error-ratchet';
4
+ import { PromiseRatchet } from '@bitblit/ratchet-common/lang/promise-ratchet';
5
+ import { EnvironmentServiceProvider } from './environment-service-provider.js';
6
+ import { EnvironmentServiceConfig } from './environment-service-config.js';
7
+
8
+ /**
9
+ * Wraps up a EnvironmentServiceProvider and provides caching and retry-on-failure logic
10
+ */
11
+ export class EnvironmentService<T> {
12
+ private readPromiseCache = new Map<string, Promise<any>>();
13
+
14
+ public static defaultEnvironmentServiceConfig(): EnvironmentServiceConfig {
15
+ const rval: EnvironmentServiceConfig = {
16
+ maxRetries: 3,
17
+ backoffMultiplierMS: 500,
18
+ };
19
+ return rval;
20
+ }
21
+
22
+ constructor(
23
+ private provider: EnvironmentServiceProvider<T>,
24
+ private cfg: EnvironmentServiceConfig = EnvironmentService.defaultEnvironmentServiceConfig(),
25
+ ) {
26
+ RequireRatchet.notNullOrUndefined(provider);
27
+ RequireRatchet.notNullOrUndefined(cfg);
28
+ }
29
+
30
+ public async getConfig(name: string): Promise<T> {
31
+ Logger.silly('EnvService:Request to read config %s', name);
32
+ if (!this.readPromiseCache.has(name)) {
33
+ Logger.silly('EnvService: Nothing in cache - adding');
34
+ this.readPromiseCache.set(name, this.getConfigUncached(name));
35
+ }
36
+
37
+ return this.readPromiseCache.get(name);
38
+ }
39
+
40
+ public async fetchConfigValueByPath<T>(cfgName: string, path: string[]): Promise<T> {
41
+ RequireRatchet.notNullUndefinedOrOnlyWhitespaceString(cfgName);
42
+ RequireRatchet.notNullUndefinedOrEmptyArray(path);
43
+
44
+ const env: any = await this.getConfig(cfgName);
45
+ if (!env) {
46
+ throw ErrorRatchet.fErr('No config found with name ', cfgName);
47
+ }
48
+ let rval: any = env;
49
+ for (const p of path) {
50
+ rval = rval ? rval[p] : null;
51
+ }
52
+ if (!rval) {
53
+ throw ErrorRatchet.fErr('No value found in %s for path %s', cfgName, path);
54
+ }
55
+ return rval;
56
+ }
57
+
58
+ private async getConfigUncached(name: string): Promise<T> {
59
+ let tryCount: number = 1;
60
+ let rval: T = null;
61
+
62
+ while (!rval && tryCount < this.cfg.maxRetries) {
63
+ tryCount++;
64
+ Logger.silly('Attempting fetch of %s', name);
65
+ try {
66
+ rval = await this.provider.fetchConfig(name);
67
+ } catch (err) {
68
+ const waitMS: number = tryCount * this.cfg.backoffMultiplierMS;
69
+ Logger.info(
70
+ 'Error attempting to fetch config %s (try %d of %d, waiting %s MS): %s',
71
+ name,
72
+ tryCount,
73
+ this.cfg.maxRetries,
74
+ waitMS,
75
+ err,
76
+ err,
77
+ );
78
+ await PromiseRatchet.wait(waitMS);
79
+ }
80
+ }
81
+
82
+ if (!rval) {
83
+ ErrorRatchet.throwFormattedErr('Was unable to fetch config %s even after %d retries', name, this.cfg.maxRetries);
84
+ }
85
+ return rval;
86
+ }
87
+
88
+
89
+ }
@@ -0,0 +1,26 @@
1
+ import { RequireRatchet } from '@bitblit/ratchet-common/lang/require-ratchet';
2
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
3
+ import { EnvironmentServiceProvider } from './environment-service-provider.js';
4
+
5
+ /**
6
+ * Forces in a single object as the environment
7
+ */
8
+ export class FixedEnvironmentServiceProvider<T> implements EnvironmentServiceProvider<T> {
9
+ public constructor(private value: Map<string, T>) {
10
+ RequireRatchet.notNullOrUndefined(value);
11
+ }
12
+
13
+ public static fromRecord<T>(record: Record<string, T>): FixedEnvironmentServiceProvider<T> {
14
+ const m: Map<string, T> = new Map<string, T>();
15
+ Object.keys(record).forEach((k) => {
16
+ m.set(k, record[k]);
17
+ });
18
+ return new FixedEnvironmentServiceProvider<T>(m);
19
+ }
20
+
21
+ public async fetchConfig(name: string): Promise<T> {
22
+ Logger.silly('FixedEnvironmentServiceProvider fetch for %s', name);
23
+ const rval: T = this.value.get(name);
24
+ return rval;
25
+ }
26
+ }
@@ -0,0 +1,18 @@
1
+ import { EnvironmentService } from './environment-service.js';
2
+ import { describe, expect, test } from 'vitest';
3
+ import { SsmEnvironmentServiceProvider } from './ssm-environment-service-provider.js';
4
+ import { AwsCredentialsRatchet } from '../iam/aws-credentials-ratchet';
5
+
6
+ describe('#ssmEnvironmentService', function () {
7
+ test.skip('should throw exception on missing environment values', async () => {
8
+ const profile: string = 'profile'; //pluma';
9
+ const config: string = 'Configuration'; //Pluma
10
+ AwsCredentialsRatchet.applySetProfileEnvironmentalVariable(profile);
11
+ const environmentService: EnvironmentService<any> = new EnvironmentService<any>(new SsmEnvironmentServiceProvider(), {
12
+ maxRetries: 5,
13
+ backoffMultiplierMS: 500,
14
+ });
15
+ const cfg: any = await environmentService.getConfig(config);
16
+ expect(cfg).not.toBeNull;
17
+ });
18
+ });
@@ -0,0 +1,71 @@
1
+ import { RequireRatchet } from '@bitblit/ratchet-common/lang/require-ratchet';
2
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
3
+ import { ErrorRatchet } from '@bitblit/ratchet-common/lang/error-ratchet';
4
+ import { PromiseRatchet } from '@bitblit/ratchet-common/lang/promise-ratchet';
5
+ import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet';
6
+ import { GetParameterCommand, GetParameterCommandOutput, ParameterNotFound, SSMClient } from '@aws-sdk/client-ssm';
7
+ import { EnvironmentServiceProvider } from './environment-service-provider.js';
8
+
9
+ /**
10
+ * Service for reading environmental variables
11
+ * Also hides the decryption detail from higher up services
12
+ */
13
+ export class SsmEnvironmentServiceProvider<T> implements EnvironmentServiceProvider<T> {
14
+ private ssm: SSMClient;
15
+ public constructor(
16
+ private region = 'us-east-1',
17
+ private ssmEncrypted = true,
18
+ ) {
19
+ RequireRatchet.notNullOrUndefined(region);
20
+ RequireRatchet.notNullOrUndefined(ssmEncrypted);
21
+ this.ssm = new SSMClient({ region: this.region });
22
+ }
23
+
24
+ public async fetchConfig(name: string): Promise<T> {
25
+ Logger.silly('SsmEnvironmentServiceProvider fetch for %s', name);
26
+ const params = {
27
+ Name: name /* required */,
28
+ WithDecryption: this.ssmEncrypted,
29
+ };
30
+
31
+ let rval: T = null;
32
+ let toParse: string = null;
33
+ try {
34
+ const value: GetParameterCommandOutput = await this.ssm.send(new GetParameterCommand(params));
35
+ toParse = StringRatchet.trimToNull(value?.Parameter?.Value);
36
+ } catch (err) {
37
+ if (err instanceof ParameterNotFound) {
38
+ const errMsg: string = Logger.warn('AWS could not find parameter %s in region %s - are you using the right AWS key?', name, this.region
39
+
40
+ );
41
+ throw new Error(errMsg);
42
+ } else if (err.name === 'CredentialsProviderError') {
43
+ Logger.warn('Token is expired - if on SSO cli try "aws sso login"');
44
+ throw err;
45
+ } else if ((ErrorRatchet.safeStringifyErr(err) || '').toLowerCase().indexOf('throttl') > -1) {
46
+ // CAW 2023-10-26 : Ok, so in AWS Sdk v3 there is no ThrottlingException for SSM (although there
47
+ // are for other services like Route53...) so maybe they just don't throw this any more but that
48
+ // seems unlikely so I'm putting this in just as belt-and-suspenders... if they really never throttle
49
+ // any more then this will just never get called
50
+ Logger.warn('Throttled while trying to read parameters - waiting 1 second before allowing retry');
51
+ await PromiseRatchet.wait(1_000);
52
+ } else {
53
+ Logger.error('Final environment fetch error (cannot retry) : %s', err, err);
54
+ throw err;
55
+ }
56
+ }
57
+
58
+ // If we reach here with a string result, try to parse it
59
+ if (toParse) {
60
+ try {
61
+ rval = JSON.parse(toParse);
62
+ } catch (err) {
63
+ Logger.error('Failed to read env - null or invalid JSON : %s : %s', err, toParse, err);
64
+ throw err;
65
+ }
66
+ } else {
67
+ throw ErrorRatchet.fErr('Could not find system parameter with name : %s in this account', name);
68
+ }
69
+ return rval;
70
+ }
71
+ }
@@ -0,0 +1,7 @@
1
+ export interface ExpiringCodeParams {
2
+ context: string;
3
+ timeToLiveSeconds: number;
4
+ tags?: string[];
5
+ length: number;
6
+ alphabet: string;
7
+ }
@@ -0,0 +1,6 @@
1
+ import { ExpiringCode } from './expiring-code.js';
2
+
3
+ export interface ExpiringCodeProvider {
4
+ storeCode(code: ExpiringCode): Promise<boolean>;
5
+ checkCode(code: string, context: string, deleteOnMatch?: boolean): Promise<boolean>;
6
+ }
@@ -0,0 +1,10 @@
1
+ import { ExpiringCodeRatchet } from './expiring-code-ratchet.js';
2
+ import { ExpiringCode } from './expiring-code.js';
3
+ import { describe, expect, test } from 'vitest';
4
+
5
+ describe('#ExpiringCodeRatchet', () => {
6
+ test('Should generate valid codes', async () => {
7
+ const output: ExpiringCode = ExpiringCodeRatchet.generateCode({ context: 'test', length: 5, timeToLiveSeconds: 10, alphabet: 'A' });
8
+ expect(output.code).toEqual('AAAAA');
9
+ });
10
+ });
@@ -0,0 +1,44 @@
1
+ import { RequireRatchet } from '@bitblit/ratchet-common/lang/require-ratchet';
2
+ import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet';
3
+ import { ExpiringCodeProvider } from './expiring-code-provider.js';
4
+ import { ExpiringCodeParams } from './expiring-code-params.js';
5
+ import { ExpiringCode } from './expiring-code.js';
6
+
7
+ /**
8
+ * Supports creating and checking single use codes
9
+ */
10
+ export class ExpiringCodeRatchet {
11
+ constructor(private provider: ExpiringCodeProvider) {}
12
+
13
+ public static generateCode(params: ExpiringCodeParams): ExpiringCode {
14
+ RequireRatchet.notNullOrUndefined(params, 'params');
15
+ RequireRatchet.notNullOrUndefined(params.context, 'params.context');
16
+ RequireRatchet.notNullOrUndefined(params.length, 'params.length');
17
+ RequireRatchet.notNullOrUndefined(params.alphabet, 'params.alphabet');
18
+
19
+ let code: string = '';
20
+
21
+ while (code.length < params.length) {
22
+ code += params.alphabet.charAt(Math.floor(params.alphabet.length * Math.random()));
23
+ }
24
+
25
+ const rval: ExpiringCode = {
26
+ code: code,
27
+ context: params.context,
28
+ tags: params.tags,
29
+ expiresEpochMS: Date.now() + params.timeToLiveSeconds * 1000,
30
+ };
31
+ return rval;
32
+ }
33
+
34
+ public async createNewCode(params: ExpiringCodeParams): Promise<ExpiringCode> {
35
+ const value: ExpiringCode = ExpiringCodeRatchet.generateCode(params);
36
+ const rval: boolean = await this.provider.storeCode(value);
37
+ return rval ? value : null;
38
+ }
39
+
40
+ public async checkCode(code: string, context: string, deleteOnMatch?: boolean): Promise<boolean> {
41
+ const rval: boolean = await this.provider.checkCode(StringRatchet.trimToEmpty(code), StringRatchet.trimToEmpty(context), deleteOnMatch);
42
+ return rval;
43
+ }
44
+ }
@@ -0,0 +1,6 @@
1
+ export interface ExpiringCode {
2
+ code: string;
3
+ context: string;
4
+ expiresEpochMS: number;
5
+ tags?: string[];
6
+ }
@@ -0,0 +1,25 @@
1
+ import { ErrorRatchet } from '@bitblit/ratchet-common/lang/error-ratchet';
2
+ import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet';
3
+
4
+ /**
5
+ * Ratchet for manipulating credentials
6
+ *
7
+ * Mainly here so I don't have to remember certain specific AWS environmental variable names
8
+ */
9
+ export class AwsCredentialsRatchet {
10
+ // Empty constructor prevents instantiation
11
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
12
+ private constructor() {}
13
+
14
+ public static applySetProfileEnvironmentalVariable(newProfile: string): void {
15
+ if (process.env) {
16
+ if (StringRatchet.trimToNull(newProfile)) {
17
+ process.env['AWS_PROFILE'] = newProfile;
18
+ } else {
19
+ ErrorRatchet.throwFormattedErr('Cannot set profile to null/empty string');
20
+ }
21
+ } else {
22
+ ErrorRatchet.throwFormattedErr('Cannot set profile - not in a node environment - process missing');
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,55 @@
1
+ // Simple switch to see what kind of event I am looking at
2
+
3
+ export class LambdaEventDetector {
4
+ public static isValidCronEvent(event: any): boolean {
5
+ return event && event.source == 'aws.events' && event.resources && event.resources.length > 0;
6
+ }
7
+
8
+ public static isValidSnsEvent(event: any): boolean {
9
+ return event && event.Records && event.Records.length > 0 && event.Records[0].EventSource == 'aws:sns';
10
+ }
11
+
12
+ public static isValidSqsEvent(event: any): boolean {
13
+ return event && event.Records && event.Records.length > 0 && event.Records[0].eventSource == 'aws:sqs';
14
+ }
15
+
16
+ public static isValidDynamoDBEvent(event: any): boolean {
17
+ return event && event.Records && event.Records.length > 0 && event.Records[0].eventSource == 'aws:dynamodb';
18
+ }
19
+
20
+ public static isValidS3Event(event: any): boolean {
21
+ return event && event.Records && event.Records.length > 0 && event.Records[0].eventSource == 'aws:s3';
22
+ }
23
+
24
+ public static isValidApiGatewayV2WithRequestContextEvent(event: any): boolean {
25
+ return event && event.rawPath && event.requestContext && event.routeKey;
26
+ }
27
+
28
+ public static isValidApiGatewayEvent(event: any): boolean {
29
+ return event && event.httpMethod && event.path && event.requestContext;
30
+ }
31
+
32
+ public static isValidApiGatewayAuthorizerEvent(event: any): boolean {
33
+ return event && event.authorizationToken && event.methodArn;
34
+ }
35
+
36
+ public static isSingleCronEvent(event: any): boolean {
37
+ return this.isValidCronEvent(event) && LambdaEventDetector.isSingleEntryEvent(event, 'resources');
38
+ }
39
+
40
+ public static isSingleSnsEvent(event: any): boolean {
41
+ return this.isValidSnsEvent(event) && LambdaEventDetector.isSingleEntryEvent(event);
42
+ }
43
+
44
+ public static isSingleDynamoDBEvent(event: any): boolean {
45
+ return this.isValidDynamoDBEvent(event) && LambdaEventDetector.isSingleEntryEvent(event);
46
+ }
47
+
48
+ public static isSingleS3Event(event: any): boolean {
49
+ return this.isValidS3Event(event) && LambdaEventDetector.isSingleEntryEvent(event);
50
+ }
51
+
52
+ public static isSingleEntryEvent(event: any, entryName: string = 'Records'): boolean {
53
+ return event && event[entryName] && event[entryName] instanceof Array && event[entryName].length === 1;
54
+ }
55
+ }
@@ -0,0 +1,38 @@
1
+ // Simple switch to see what kind of event I am looking at
2
+
3
+ import { APIGatewayEvent, APIGatewayProxyEventV2, DynamoDBStreamEvent, S3Event, ScheduledEvent, SNSEvent, SQSEvent } from 'aws-lambda';
4
+ import { LambdaEventDetector } from './lambda-event-detector.js';
5
+
6
+ export class LambdaEventTypeGuards {
7
+ public static isValidCronEvent(event: any): event is ScheduledEvent {
8
+ return LambdaEventDetector.isValidCronEvent(event);
9
+ }
10
+
11
+ public static isValidSqsEvent(event: any): event is SQSEvent {
12
+ return LambdaEventDetector.isValidSqsEvent(event);
13
+ }
14
+
15
+ public static isValidSnsEvent(event: any): event is SNSEvent {
16
+ return LambdaEventDetector.isValidSnsEvent(event);
17
+ }
18
+
19
+ public static isValidDynamoDBEvent(event: any): event is DynamoDBStreamEvent {
20
+ return LambdaEventDetector.isValidDynamoDBEvent(event);
21
+ }
22
+
23
+ public static isValidS3Event(event: any): event is S3Event {
24
+ return LambdaEventDetector.isValidS3Event(event);
25
+ }
26
+
27
+ public static isValidApiGatewayV2WithRequestContextEvent(event: any): event is APIGatewayProxyEventV2 {
28
+ return LambdaEventDetector.isValidApiGatewayV2WithRequestContextEvent(event);
29
+ }
30
+
31
+ public static isValidApiGatewayEvent(event: any): event is APIGatewayEvent {
32
+ return LambdaEventDetector.isValidApiGatewayEvent(event);
33
+ }
34
+
35
+ public static isValidApiGatewayAuthorizerEvent(event: any): boolean {
36
+ return LambdaEventDetector.isValidApiGatewayAuthorizerEvent(event);
37
+ }
38
+ }
@@ -0,0 +1,18 @@
1
+ /*
2
+ Service for interacting with cloudwatch
3
+ */
4
+
5
+ import { KeyValue } from '@bitblit/ratchet-common/lang/key-value';
6
+ import { DynamoRatchet } from '../dynamodb/dynamo-ratchet.js';
7
+ import { QueryCommandInput, ScanCommandInput } from '@aws-sdk/lib-dynamodb';
8
+
9
+ export interface CloudWatchMetricsMinuteLevelDynamoCountRequest {
10
+ dynamoRatchet: DynamoRatchet;
11
+ query: QueryCommandInput;
12
+ scan: ScanCommandInput;
13
+
14
+ minuteUTC: string; // Format yyyy-MM-dd HH:mm
15
+ namespace: string;
16
+ metric: string;
17
+ dims: KeyValue<any>[];
18
+ }
@@ -0,0 +1,8 @@
1
+ /*
2
+ Strong typing for results from dynamo db count queries
3
+ */
4
+ export interface DynamoCountResult {
5
+ count: number;
6
+ scannedCount: number;
7
+ pages: number;
8
+ }