Repository: mbonig/state-machine (language: python)

Issue:
File extension change request
For convenient usage in Visual Studio Code [there is a suggestion](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) from AWS to use *.json.asl file extension. Currently with *.workflow.json extension Visual Studio Code can not define it automatically correctly as ASL file.


Discussion / hints:

Fix patch:
diff --git a/API.md b/API.md
index 7ee3d99..fefdcbd 100644
--- a/API.md
+++ b/API.md
@@ -709,10 +709,6 @@ Specifies whether Amazon X-Ray tracing is enabled for this state machine.
 
 ### StepFunctionsAutoDiscoverOptions <a name="StepFunctionsAutoDiscoverOptions" id="@matthewbonig/state-machine.StepFunctionsAutoDiscoverOptions"></a>
 
-For future use.
-
-No properties, yet.
-
 #### Initializer <a name="Initializer" id="@matthewbonig/state-machine.StepFunctionsAutoDiscoverOptions.Initializer"></a>
 
 ```typescript
@@ -721,6 +717,26 @@ import { StepFunctionsAutoDiscoverOptions } from '@matthewbonig/state-machine'
 const stepFunctionsAutoDiscoverOptions: StepFunctionsAutoDiscoverOptions = { ... }
 ```
 
+#### Properties <a name="Properties" id="Properties"></a>
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| <code><a href="#@matthewbonig/state-machine.StepFunctionsAutoDiscoverOptions.property.extension">extension</a></code> | <code>string</code> | An optional extension to use for discovering state machine files. |
+
+---
+
+##### `extension`<sup>Optional</sup> <a name="extension" id="@matthewbonig/state-machine.StepFunctionsAutoDiscoverOptions.property.extension"></a>
+
+```typescript
+public readonly extension: string;
+```
+
+- *Type:* string
+- *Default:* '.workflow.json' (JSON_STEPFUNCTION_EXT)
+
+An optional extension to use for discovering state machine files.
+
+---
 
 ## Classes <a name="Classes" id="Classes"></a>
 
diff --git a/README.md b/README.md
index c887424..a6b781c 100644
--- a/README.md
+++ b/README.md
@@ -72,6 +72,29 @@ export class SomeStack extends Stack {
 > :warning: **The interfaces and constructs generated here are NOT jsii compliant (they use Partials and Omits) and cannot be 
 compiled by jsii into other languages. If you plan to distribute any libraries you cannot use this.**
 
+### Alternative Extensions
+
+There is an optional parameter, `extension` that you can pass to have it search for alternative extensions.
+AWS recommends that ASL definition files have a `.asl.json` extension, which will be picked up by some IDE
+tools. This extension was recommended after initial development of this component. Therefore, the default is
+to use the original extension. But, you can override this by passing a different extension to the
+AutoDiscover's constructor options. There are two constants defined, `JSON_STEPFUNCTION_EXT` and `AWS_RECOMMENDED_JSON_EXT` that you can use.
+
+```js
+// ...
+const { StepFunctionsAutoDiscover, AWS_RECOMMENDED_EXT } = require('@matthewbonig/state-machine');
+
+const project = new awscdk.AwsCdkTypeScriptApp({
+  // ...,
+  deps: [
+    // ...,
+    '@matthewbonig/state-machine',
+  ]
+});
+
+new StepFunctionsAutoDiscover(project, { extension: AWS_RECOMMENDED_JSON_EXT });
+```
+
 ### Examples
 
 ```ts
@@ -156,8 +179,6 @@ new StateMachine(stack, 'Test', {
   });
 ```
 
-
-
 For Python, be sure to use a context manager when opening your JSON file.  
 - You do not need to `str()` the dictionary object you supply as your `definition` prop.  
 - Elements of your override path **do** need to be strings.
@@ -180,6 +201,7 @@ state_machine = StateMachine(
     },
   })
 ```
+
 In this example, the ASL has a state called 'Read database credentials secret' and the SecretId parameter is overridden with a 
 CDK generated value.
 Future changes can be done by editing, debugging, and testing the state machine directly in the Workflow Studio.
diff --git a/src/StepFunctionsAutoDiscover.ts b/src/StepFunctionsAutoDiscover.ts
index ffcee90..69cba65 100644
--- a/src/StepFunctionsAutoDiscover.ts
+++ b/src/StepFunctionsAutoDiscover.ts
@@ -8,13 +8,23 @@ import { AwsCdkDeps } from 'projen/lib/awscdk/awscdk-deps';
 import { AutoDiscoverBase } from 'projen/lib/cdk';
 import { buildStateType } from './BuildStateType';
 
-const JSON_STEPFUNCTION_EXT = '.workflow.json';
+/**
+ * The original extension used to search for ASL files.
+ */
+export const JSON_STEPFUNCTION_EXT = '.workflow.json';
 
 /**
- * For future use. No properties, yet.
+ * The AWS-recommended extension for ASL files.
  */
-export interface StepFunctionsAutoDiscoverOptions {
+export const AWS_RECOMMENDED_JSON_EXT = '.json.asl';
 
+export interface StepFunctionsAutoDiscoverOptions {
+  /**
+   * An optional extension to use for discovering state machine files.
+   *
+   * @default '.workflow.json' (JSON_STEPFUNCTION_EXT)
+   */
+  readonly extension?: string;
 }
 
 /**
@@ -33,20 +43,28 @@ export interface StepFunctionsAutoDiscoverOptions {
  */
 export class StepFunctionsAutoDiscover extends AutoDiscoverBase {
   constructor(project: AwsCdkTypeScriptApp, _options?: StepFunctionsAutoDiscoverOptions) {
+    if (_options?.extension) {
+      if (!_options.extension.startsWith('.')) {
+        throw new Error('extension must start with a .');
+      }
+    }
+    let extension = _options?.extension || JSON_STEPFUNCTION_EXT;
     super(project, {
-      extension: JSON_STEPFUNCTION_EXT,
+      extension: extension,
       projectdir: project.srcdir,
     });
     for (const entrypoint of this.entrypoints) {
       new StepFunctionsStateMachine(this.project, {
         workflowAsl: entrypoint,
         cdkDeps: project.cdkDeps,
+        extension,
       });
     }
   }
 }
 
 export interface StepFunctionsStateMachineOptions {
+  readonly extension: string;
   readonly constructFile?: string;
   readonly workflowAsl: string;
   readonly constructName?: string;
@@ -60,20 +78,21 @@ export class StepFunctionsStateMachine extends Component {
   constructor(project: Project, options: StepFunctionsStateMachineOptions) {
     super(project);
 
+    const extension = options.extension ?? JSON_STEPFUNCTION_EXT;
     const workflowAsl = options.workflowAsl;
 
     if (
-      !workflowAsl.endsWith(JSON_STEPFUNCTION_EXT)
+      !workflowAsl.endsWith(extension)
     ) {
       throw new Error(
-        `${workflowAsl} must have a ${JSON_STEPFUNCTION_EXT} extension`,
+        `${workflowAsl} must have a ${extension} extension`,
       );
     }
     const basePath = join(
       dirname(workflowAsl),
       basename(
         workflowAsl,
-        JSON_STEPFUNCTION_EXT,
+        extension,
       ),
     );
     const constructFile = options.constructFile ?? `${basePath}-statemachine.ts`;


Test patch:
diff --git a/test/StateMachineAutoDiscover.test.ts b/test/StateMachineAutoDiscover.test.ts
index 3e632ea..dc9ca3b 100644
--- a/test/StateMachineAutoDiscover.test.ts
+++ b/test/StateMachineAutoDiscover.test.ts
@@ -5,38 +5,54 @@ import * as path from 'path';
 import { AwsCdkTypeScriptApp } from 'projen/lib/awscdk';
 import { synthSnapshot } from 'projen/lib/util/synth';
 import { StepFunctionsAutoDiscover } from '../src';
+import { AWS_RECOMMENDED_JSON_EXT, JSON_STEPFUNCTION_EXT } from '../src/StepFunctionsAutoDiscover';
+
+
+function setupTestProject(testFile: string, extension: string = JSON_STEPFUNCTION_EXT, srcFile: string = 'test.workflow.json') {
+  const tempDir = mkdtempSync(path.join(os.tmpdir(), 'test2'));
+  const project = new AwsCdkTypeScriptApp({
+    name: 'test',
+    defaultReleaseBranch: 'main',
+    cdkVersion: '2.53.0',
+    outdir: tempDir,
+  });
+
+  // add a test file...
+  fs.mkdirSync(path.join(project.outdir, project.srcdir));
+  fs.copyFileSync(
+    path.join(__dirname, 'step-functions', testFile),
+    path.join(project.outdir, project.srcdir, srcFile),
+  );
+  new StepFunctionsAutoDiscover(project, { extension: extension });
+  return project;
+}
 
 
 describe('AutoDiscover', () => {
 
-  function setupTestProject(testFile: string) {
-    const tempDir = mkdtempSync(path.join(os.tmpdir(), 'test2'));
-    const project = new AwsCdkTypeScriptApp({
-      name: 'test',
-      defaultReleaseBranch: 'main',
-      cdkVersion: '2.53.0',
-      outdir: tempDir,
-    });
-
-    // add a test file...
-    fs.mkdirSync(path.join(project.outdir, project.srcdir));
-    fs.copyFileSync(
-      path.join(__dirname, 'step-functions', testFile),
-      path.join(project.outdir, project.srcdir, 'test.workflow.json'),
-    );
-    new StepFunctionsAutoDiscover(project);
-    return project;
-  }
-
-  test('test 1', () => {
+
+  test('simple case', () => {
     const project = setupTestProject('test.workflow.json');
     const snap = synthSnapshot(project);
     expect(snap['src/test-statemachine.ts']).toMatchSnapshot();
   });
 
-  test('test 2', async () => {
+  test('more complex case', async () => {
     const project = setupTestProject('test2.workflow.json');
     const snap = synthSnapshot(project);
     expect(snap['src/test-statemachine.ts']).toMatchSnapshot();
   });
 });
+
+describe('Extension parameter', () => {
+  test('takes another extension', async () => {
+    const project = setupTestProject('test.json.asl', AWS_RECOMMENDED_JSON_EXT, 'test.json.asl');
+    const snap = synthSnapshot(project);
+    expect(snap['src/test-statemachine.ts']).toMatchSnapshot();
+  });
+
+  test('fails when extension does not start with a .', () => {
+    expect(() => setupTestProject('test.json.asl', 'asdfasdf', 'test.json.asl')).toThrow('extension must start with a .');
+  });
+});
+
diff --git a/test/__snapshots__/StateMachineAutoDiscover.test.ts.snap b/test/__snapshots__/StateMachineAutoDiscover.test.ts.snap
index bb47448..772c162 100644
--- a/test/__snapshots__/StateMachineAutoDiscover.test.ts.snap
+++ b/test/__snapshots__/StateMachineAutoDiscover.test.ts.snap
@@ -1,6 +1,139 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`AutoDiscover test 1 1`] = `
+exports[`AutoDiscover more complex case 1`] = `
+"// ~~ Generated by projen. To modify, edit .projenrc.js and run \\"npx projen\\".
+import { StateMachine, StateMachineProps } from '@matthewbonig/state-machine';
+import { Construct } from 'constructs';
+import fs from 'fs';
+import path from 'path';
+export interface TestStateMachineOverrides {
+  readonly 'Get Pod To Execute On': Partial<{
+    'Type': string;
+    'Resource': string;
+    'Parameters': Partial<{
+      'FunctionName': string;
+      'Payload': Partial<{
+        'commands': Partial<[string,string,string,string]>;
+      }>;
+    }>;
+    'Retry': Partial<[Partial<{
+      'ErrorEquals': Partial<[string,string,string]>;
+      'IntervalSeconds': number;
+      'MaxAttempts': number;
+      'BackoffRate': number;
+    }>]>;
+    'ResultSelector': Partial<{
+      'belle.$': string;
+    }>;
+    'ResultPath': string;
+    'Next': string;
+    'Catch': Partial<[Partial<{
+      'ErrorEquals': Partial<[string]>;
+      'Next': string;
+      'ResultPath': string;
+    }>]>;
+  }>;
+  readonly 'Generate Commands': Partial<{
+    'Type': string;
+    'Resource': string;
+    'Parameters': Partial<{
+      'Payload.$': string;
+      'FunctionName': string;
+    }>;
+    'Retry': Partial<[Partial<{
+      'ErrorEquals': Partial<[string,string,string]>;
+      'IntervalSeconds': number;
+      'MaxAttempts': number;
+      'BackoffRate': number;
+    }>]>;
+    'ResultSelector': Partial<{
+      'commands.$': string;
+    }>;
+    'ResultPath': string;
+    'Next': string;
+    'Catch': Partial<[Partial<{
+      'ErrorEquals': Partial<[string]>;
+      'Next': string;
+      'ResultPath': string;
+    }>]>;
+  }>;
+  readonly 'Run Command': Partial<{
+    'Type': string;
+    'Resource': string;
+    'Parameters': Partial<{
+      'Payload': Partial<{
+        'commands.$': string;
+      }>;
+      'FunctionName': string;
+    }>;
+    'Retry': Partial<[Partial<{
+      'ErrorEquals': Partial<[string,string,string]>;
+      'IntervalSeconds': number;
+      'MaxAttempts': number;
+      'BackoffRate': number;
+    }>]>;
+    'ResultPath': string;
+    'Next': string;
+    'Catch': Partial<[Partial<{
+      'ErrorEquals': Partial<[string]>;
+      'Next': string;
+      'ResultPath': string;
+    }>]>;
+  }>;
+  readonly 'Notify Slack': Partial<{
+    'Type': string;
+    'Resource': string;
+    'Parameters': Partial<{
+      'Payload.$': string;
+      'FunctionName': string;
+    }>;
+    'Retry': Partial<[Partial<{
+      'ErrorEquals': Partial<[string,string,string]>;
+      'IntervalSeconds': number;
+      'MaxAttempts': number;
+      'BackoffRate': number;
+    }>]>;
+    'ResultPath': any;
+    'End': Partial<{
+    }>;
+    'Catch': Partial<[Partial<{
+      'ErrorEquals': Partial<[string]>;
+      'Next': string;
+      'ResultPath': string;
+    }>]>;
+  }>;
+  readonly 'Notify Slack of Error': Partial<{
+    'Type': string;
+    'Resource': string;
+    'Parameters': Partial<{
+      'Payload.$': string;
+      'FunctionName': string;
+    }>;
+    'Retry': Partial<[Partial<{
+      'ErrorEquals': Partial<[string,string,string]>;
+      'IntervalSeconds': number;
+      'MaxAttempts': number;
+      'BackoffRate': number;
+    }>]>;
+    'End': Partial<{
+    }>;
+    'ResultPath': any;
+  }>;
+}
+export interface TestStateMachineProps extends Omit<StateMachineProps, 'overrides' | 'definition'> {
+  readonly overrides: Partial<TestStateMachineOverrides>;
+}
+export class TestStateMachine extends StateMachine {
+  constructor(scope: Construct, id: string, props: TestStateMachineProps) {
+    super(scope, id, {
+      ...props,
+      definition: JSON.parse(fs.readFileSync(path.join(__dirname, 'test.workflow.json')).toString()),
+    });
+  }
+}"
+`;
+
+exports[`AutoDiscover simple case 1`] = `
 "// ~~ Generated by projen. To modify, edit .projenrc.js and run \\"npx projen\\".
 import { StateMachine, StateMachineProps } from '@matthewbonig/state-machine';
 import { Construct } from 'constructs';
@@ -277,20 +410,76 @@ export class TestStateMachine extends StateMachine {
 }"
 `;
 
-exports[`AutoDiscover test 2 1`] = `
+exports[`Extension parameter takes another extension 1`] = `
 "// ~~ Generated by projen. To modify, edit .projenrc.js and run \\"npx projen\\".
 import { StateMachine, StateMachineProps } from '@matthewbonig/state-machine';
 import { Construct } from 'constructs';
 import fs from 'fs';
 import path from 'path';
 export interface TestStateMachineOverrides {
-  readonly 'Get Pod To Execute On': Partial<{
+  readonly 'Which database?': Partial<{
+    'Type': string;
+    'Choices': Partial<[Partial<{
+      'Variable': string;
+      'StringMatches': string;
+      'Next': string;
+    }>,Partial<{
+      'Variable': string;
+      'StringMatches': string;
+      'Next': string;
+    }>,Partial<{
+      'Variable': string;
+      'StringMatches': string;
+      'Next': string;
+    }>]>;
+  }>;
+  readonly 'Get belle secret': Partial<{
+    'Type': string;
+    'Next': string;
+    'Parameters': Partial<{
+      'SecretId': string;
+    }>;
+    'Resource': string;
+    'ResultPath': string;
+  }>;
+  readonly 'Parse belle secret value': Partial<{
+    'Type': string;
+    'Parameters': Partial<{
+      'ParsedValue.$': string;
+    }>;
+    'ResultPath': string;
+    'Next': string;
+  }>;
+  readonly 'Create new database username and password': Partial<{
+    'Type': string;
+    'Resource': string;
+    'Parameters': Partial<{
+      'Payload.$': string;
+      'FunctionName': string;
+    }>;
+    'Retry': Partial<[Partial<{
+      'ErrorEquals': Partial<[string,string,string]>;
+      'IntervalSeconds': number;
+      'MaxAttempts': number;
+      'BackoffRate': number;
+    }>]>;
+    'ResultPath': string;
+    'Comment': string;
+    'Next': string;
+    'ResultSelector': Partial<{
+      'username.$': string;
+      'password.$': string;
+      'otsUrl.$': string;
+    }>;
+  }>;
+  readonly 'Create user on the database': Partial<{
     'Type': string;
     'Resource': string;
     'Parameters': Partial<{
       'FunctionName': string;
       'Payload': Partial<{
-        'commands': Partial<[string,string,string,string]>;
+        'script.$': string;
+        'databaseName.$': string;
       }>;
     }>;
     'Retry': Partial<[Partial<{
@@ -299,18 +488,49 @@ export interface TestStateMachineOverrides {
       'MaxAttempts': number;
       'BackoffRate': number;
     }>]>;
-    'ResultSelector': Partial<{
-      'belle.$': string;
+    'ResultPath': string;
+    'Next': string;
+  }>;
+  readonly 'Send credentials to user': Partial<{
+    'Type': string;
+    'Parameters': Partial<{
+      'Destination': Partial<{
+        'ToAddresses.$': string;
+      }>;
+      'Message': Partial<{
+        'Body': Partial<{
+          'Text': Partial<{
+            'Data.$': string;
+          }>;
+        }>;
+        'Subject': Partial<{
+          'Data': string;
+        }>;
+      }>;
+      'Source': string;
     }>;
+    'Resource': string;
+    'End': Partial<{
+    }>;
+  }>;
+  readonly 'Get engines secret': Partial<{
+    'Type': string;
+    'Parameters': Partial<{
+      'SecretId': string;
+    }>;
+    'Resource': string;
     'ResultPath': string;
     'Next': string;
-    'Catch': Partial<[Partial<{
-      'ErrorEquals': Partial<[string]>;
-      'Next': string;
-      'ResultPath': string;
-    }>]>;
   }>;
-  readonly 'Generate Commands': Partial<{
+  readonly 'Parse engines secret value': Partial<{
+    'Type': string;
+    'Parameters': Partial<{
+      'ParsedValue.$': string;
+    }>;
+    'ResultPath': string;
+    'Next': string;
+  }>;
+  readonly 'Create new database username and password for engines': Partial<{
     'Type': string;
     'Resource': string;
     'Parameters': Partial<{
@@ -323,25 +543,24 @@ export interface TestStateMachineOverrides {
       'MaxAttempts': number;
       'BackoffRate': number;
     }>]>;
+    'ResultPath': string;
+    'Comment': string;
     'ResultSelector': Partial<{
-      'commands.$': string;
+      'username.$': string;
+      'password.$': string;
+      'otsUrl.$': string;
     }>;
-    'ResultPath': string;
     'Next': string;
-    'Catch': Partial<[Partial<{
-      'ErrorEquals': Partial<[string]>;
-      'Next': string;
-      'ResultPath': string;
-    }>]>;
   }>;
-  readonly 'Run Command': Partial<{
+  readonly 'Create user on jasmine': Partial<{
     'Type': string;
     'Resource': string;
     'Parameters': Partial<{
+      'FunctionName': string;
       'Payload': Partial<{
-        'commands.$': string;
+        'script.$': string;
+        'databaseName.$': string;
       }>;
-      'FunctionName': string;
     }>;
     'Retry': Partial<[Partial<{
       'ErrorEquals': Partial<[string,string,string]>;
@@ -351,13 +570,47 @@ export interface TestStateMachineOverrides {
     }>]>;
     'ResultPath': string;
     'Next': string;
-    'Catch': Partial<[Partial<{
-      'ErrorEquals': Partial<[string]>;
-      'Next': string;
-      'ResultPath': string;
-    }>]>;
   }>;
-  readonly 'Notify Slack': Partial<{
+  readonly 'Send credentials to jasmine user': Partial<{
+    'Type': string;
+    'Parameters': Partial<{
+      'Destination': Partial<{
+        'ToAddresses.$': string;
+      }>;
+      'Message': Partial<{
+        'Body': Partial<{
+          'Text': Partial<{
+            'Data.$': string;
+          }>;
+        }>;
+        'Subject': Partial<{
+          'Data': string;
+        }>;
+      }>;
+      'Source': string;
+    }>;
+    'Resource': string;
+    'End': Partial<{
+    }>;
+  }>;
+  readonly 'Get anna secret': Partial<{
+    'Type': string;
+    'Next': string;
+    'Parameters': Partial<{
+      'SecretId': string;
+    }>;
+    'Resource': string;
+    'ResultPath': string;
+  }>;
+  readonly 'Parse anna secret value': Partial<{
+    'Type': string;
+    'Parameters': Partial<{
+      'ParsedValue.$': string;
+    }>;
+    'ResultPath': string;
+    'Next': string;
+  }>;
+  readonly 'Create new database username and password for anna': Partial<{
     'Type': string;
     'Resource': string;
     'Parameters': Partial<{
@@ -370,21 +623,24 @@ export interface TestStateMachineOverrides {
       'MaxAttempts': number;
       'BackoffRate': number;
     }>]>;
-    'ResultPath': any;
-    'End': Partial<{
+    'ResultPath': string;
+    'Comment': string;
+    'Next': string;
+    'ResultSelector': Partial<{
+      'username.$': string;
+      'password.$': string;
+      'otsUrl.$': string;
     }>;
-    'Catch': Partial<[Partial<{
-      'ErrorEquals': Partial<[string]>;
-      'Next': string;
-      'ResultPath': string;
-    }>]>;
   }>;
-  readonly 'Notify Slack of Error': Partial<{
+  readonly 'Create user on the anna database': Partial<{
     'Type': string;
     'Resource': string;
     'Parameters': Partial<{
-      'Payload.$': string;
       'FunctionName': string;
+      'Payload': Partial<{
+        'script.$': string;
+        'databaseName.$': string;
+      }>;
     }>;
     'Retry': Partial<[Partial<{
       'ErrorEquals': Partial<[string,string,string]>;
@@ -392,9 +648,30 @@ export interface TestStateMachineOverrides {
       'MaxAttempts': number;
       'BackoffRate': number;
     }>]>;
+    'ResultPath': string;
+    'Next': string;
+  }>;
+  readonly 'Send credentials to user for anna': Partial<{
+    'Type': string;
+    'Parameters': Partial<{
+      'Destination': Partial<{
+        'ToAddresses.$': string;
+      }>;
+      'Message': Partial<{
+        'Body': Partial<{
+          'Text': Partial<{
+            'Data.$': string;
+          }>;
+        }>;
+        'Subject': Partial<{
+          'Data': string;
+        }>;
+      }>;
+      'Source': string;
+    }>;
+    'Resource': string;
     'End': Partial<{
     }>;
-    'ResultPath': any;
   }>;
 }
 export interface TestStateMachineProps extends Omit<StateMachineProps, 'overrides' | 'definition'> {
@@ -404,7 +681,7 @@ export class TestStateMachine extends StateMachine {
   constructor(scope: Construct, id: string, props: TestStateMachineProps) {
     super(scope, id, {
       ...props,
-      definition: JSON.parse(fs.readFileSync(path.join(__dirname, 'test.workflow.json')).toString()),
+      definition: JSON.parse(fs.readFileSync(path.join(__dirname, 'test.json.asl')).toString()),
     });
   }
 }"
diff --git a/test/step-functions/test.json.asl b/test/step-functions/test.json.asl
new file mode 100644
index 0000000..c11a1ac
--- /dev/null
+++ b/test/step-functions/test.json.asl
@@ -0,0 +1,300 @@
+{
+  "StartAt": "Which database?",
+  "States": {
+    "Which database?": {
+      "Type": "Choice",
+      "Choices": [
+        {
+          "Variable": "$.databaseName",
+          "StringMatches": "belle*",
+          "Next": "Get belle secret"
+        },
+        {
+          "Variable": "$.databaseName",
+          "StringMatches": "jasmine*",
+          "Next": "Get engines secret"
+        },
+        {
+          "Variable": "$.databaseName",
+          "StringMatches": "anna*",
+          "Next": "Get anna secret"
+        }
+      ]
+    },
+    "Get belle secret": {
+      "Type": "Task",
+      "Next": "Parse belle secret value",
+      "Parameters": {
+        "SecretId": "qa2/belle-sql-secrets"
+      },
+      "Resource": "arn:aws:states:::aws-sdk:secretsmanager:getSecretValue",
+      "ResultPath": "$.databaseSecret"
+    },
+    "Parse belle secret value": {
+      "Type": "Pass",
+      "Parameters": {
+        "ParsedValue.$": "States.StringToJson($.databaseSecret.SecretString)"
+      },
+      "ResultPath": "$.databaseSecret",
+      "Next": "Create new database username and password"
+    },
+    "Create new database username and password": {
+      "Type": "Task",
+      "Resource": "arn:aws:states:::lambda:invoke",
+      "Parameters": {
+        "Payload.$": "$",
+        "FunctionName": "arn:aws:lambda:us-east-1:754664436462:function:qa2-Database-UserDatabaseCreatorPersonalUsernameGe-dgk0cAkQs0VW"
+      },
+      "Retry": [
+        {
+          "ErrorEquals": [
+            "Lambda.ServiceException",
+            "Lambda.AWSLambdaException",
+            "Lambda.SdkClientException"
+          ],
+          "IntervalSeconds": 2,
+          "MaxAttempts": 6,
+          "BackoffRate": 2
+        }
+      ],
+      "ResultPath": "$.newCreds",
+      "Comment": "returns back {username,password}",
+      "Next": "Create user on the database",
+      "ResultSelector": {
+        "username.$": "$.Payload.username",
+        "password.$": "$.Payload.password",
+        "otsUrl.$": "$.Payload.otsUrl"
+      }
+    },
+    "Create user on the database": {
+      "Type": "Task",
+      "Resource": "arn:aws:states:::lambda:invoke",
+      "Parameters": {
+        "FunctionName": "arn:aws:lambda:us-east-1:754664436462:function:qa2-Database-Databaseadhocsingl2E02F587-yZxuEpPiDCUc",
+        "Payload": {
+          "script.$": "States.Format('CREATE ROLE {} LOGIN PASSWORD \\'{}\\'; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public to {}; GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public to {}; GRANT {} to {};', $.newCreds.username, $.newCreds.password, $.newCreds.username, $.newCreds.username, $.databaseSecret.ParsedValue.SQL_USER,$.newCreds.username)",
+          "databaseName.$": "$.databaseSecret.ParsedValue.SQL_DATABASE"
+        }
+      },
+      "Retry": [
+        {
+          "ErrorEquals": [
+            "Lambda.ServiceException",
+            "Lambda.AWSLambdaException",
+            "Lambda.SdkClientException"
+          ],
+          "IntervalSeconds": 2,
+          "MaxAttempts": 6,
+          "BackoffRate": 2
+        }
+      ],
+      "ResultPath": "$.databaseResults",
+      "Next": "Send credentials to user"
+    },
+    "Send credentials to user": {
+      "Type": "Task",
+      "Parameters": {
+        "Destination": {
+          "ToAddresses.$": "States.Array($.email)"
+        },
+        "Message": {
+          "Body": {
+            "Text": {
+              "Data.$": "States.Format('New credentials were generated for you: \n\ndatabase: {}\nusername: {}\npassword: {} \nhost: {}', $.databaseSecret.ParsedValue.SQL_DATABASE, $.newCreds.username, $.newCreds.otsUrl, $.databaseSecret.ParsedValue.SQL_HOST)"
+            }
+          },
+          "Subject": {
+            "Data": "New database access credentials have been created for you!"
+          }
+        },
+        "Source": "devops@momnt.com"
+      },
+      "Resource": "arn:aws:states:::aws-sdk:ses:sendEmail",
+      "End": true
+    },
+    "Get engines secret": {
+      "Type": "Task",
+      "Parameters": {
+        "SecretId": "qa2/engines-secrets"
+      },
+      "Resource": "arn:aws:states:::aws-sdk:secretsmanager:getSecretValue",
+      "ResultPath": "$.databaseSecret",
+      "Next": "Parse engines secret value"
+    },
+    "Parse engines secret value": {
+      "Type": "Pass",
+      "Parameters": {
+        "ParsedValue.$": "States.StringToJson($.databaseSecret.SecretString)"
+      },
+      "ResultPath": "$.databaseSecret",
+      "Next": "Create new database username and password for engines"
+    },
+    "Create new database username and password for engines": {
+      "Type": "Task",
+      "Resource": "arn:aws:states:::lambda:invoke",
+      "Parameters": {
+        "Payload.$": "$",
+        "FunctionName": "arn:aws:lambda:us-east-1:754664436462:function:qa1-Database-UserDatabaseCreatorPersonalUsernameGe-dmIs8HXvJAYc"
+      },
+      "Retry": [
+        {
+          "ErrorEquals": [
+            "Lambda.ServiceException",
+            "Lambda.AWSLambdaException",
+            "Lambda.SdkClientException"
+          ],
+          "IntervalSeconds": 2,
+          "MaxAttempts": 6,
+          "BackoffRate": 2
+        }
+      ],
+      "ResultPath": "$.newCreds",
+      "Comment": "returns back {username,password}",
+      "ResultSelector": {
+        "username.$": "$.Payload.username",
+        "password.$": "$.Payload.password",
+        "otsUrl.$": "$.Payload.otsUrl"
+      },
+      "Next": "Create user on jasmine"
+    },
+    "Create user on jasmine": {
+      "Type": "Task",
+      "Resource": "arn:aws:states:::lambda:invoke",
+      "Parameters": {
+        "FunctionName": "arn:aws:lambda:us-east-1:754664436462:function:qa2-Database-Databaseadhocsingl2E02F587-yZxuEpPiDCUc",
+        "Payload": {
+          "script.$": "States.Format('CREATE ROLE {} LOGIN PASSWORD \\'{}\\'; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA engine_rules to {}; GRANT ALL PRIVILEGES ON SCHEMA engine_rules to {}; GRANT {} to {};', $.newCreds.username, $.newCreds.password, $.newCreds.username, $.newCreds.username, $.databaseSecret.ParsedValue.PGUSER, $.newCreds.username)",
+          "databaseName.$": "$.databaseSecret.ParsedValue.PGDATABASE"
+        }
+      },
+      "Retry": [
+        {
+          "ErrorEquals": [
+            "Lambda.ServiceException",
+            "Lambda.AWSLambdaException",
+            "Lambda.SdkClientException"
+          ],
+          "IntervalSeconds": 2,
+          "MaxAttempts": 6,
+          "BackoffRate": 2
+        }
+      ],
+      "ResultPath": "$.databaseResults",
+      "Next": "Send credentials to jasmine user"
+    },
+    "Send credentials to jasmine user": {
+      "Type": "Task",
+      "Parameters": {
+        "Destination": {
+          "ToAddresses.$": "States.Array($.email)"
+        },
+        "Message": {
+          "Body": {
+            "Text": {
+              "Data.$": "States.Format('New credentials were generated for you: \n\ndatabase: {}\nusername: {}\npassword: {} \nhost: {}', $.databaseSecret.ParsedValue.PGDATABASE, $.newCreds.username, $.newCreds.otsUrl, $.databaseSecret.ParsedValue.PGHOST)"
+            }
+          },
+          "Subject": {
+            "Data": "New database access credentials have been created for you!"
+          }
+        },
+        "Source": "devops@momnt.com"
+      },
+      "Resource": "arn:aws:states:::aws-sdk:ses:sendEmail",
+      "End": true
+    },
+
+    "Get anna secret": {
+      "Type": "Task",
+      "Next": "Parse anna secret value",
+      "Parameters": {
+        "SecretId": "qa2/anna-sql-secrets"
+      },
+      "Resource": "arn:aws:states:::aws-sdk:secretsmanager:getSecretValue",
+      "ResultPath": "$.databaseSecret"
+    },
+    "Parse anna secret value": {
+      "Type": "Pass",
+      "Parameters": {
+        "ParsedValue.$": "States.StringToJson($.databaseSecret.SecretString)"
+      },
+      "ResultPath": "$.databaseSecret",
+      "Next": "Create new database username and password for anna"
+    },
+    "Create new database username and password for anna": {
+      "Type": "Task",
+      "Resource": "arn:aws:states:::lambda:invoke",
+      "Parameters": {
+        "Payload.$": "$",
+        "FunctionName": "arn:aws:lambda:us-east-1:754664436462:function:qa2-Database-UserDatabaseCreatorPersonalUsernameGe-dgk0cAkQs0VW"
+      },
+      "Retry": [
+        {
+          "ErrorEquals": [
+            "Lambda.ServiceException",
+            "Lambda.AWSLambdaException",
+            "Lambda.SdkClientException"
+          ],
+          "IntervalSeconds": 2,
+          "MaxAttempts": 6,
+          "BackoffRate": 2
+        }
+      ],
+      "ResultPath": "$.newCreds",
+      "Comment": "returns back {username,password}",
+      "Next": "Create user on the anna database",
+      "ResultSelector": {
+        "username.$": "$.Payload.username",
+        "password.$": "$.Payload.password",
+        "otsUrl.$": "$.Payload.otsUrl"
+      }
+    },
+    "Create user on the anna database": {
+      "Type": "Task",
+      "Resource": "arn:aws:states:::lambda:invoke",
+      "Parameters": {
+        "FunctionName": "arn:aws:lambda:us-east-1:754664436462:function:qa2-Database-Databaseadhocsingl2E02F587-yZxuEpPiDCUc",
+        "Payload": {
+          "script.$": "States.Format('CREATE ROLE {} LOGIN PASSWORD \\'{}\\'; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public to {}; GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public to {}; GRANT {} to {};', $.newCreds.username, $.newCreds.password, $.newCreds.username, $.newCreds.username, $.databaseSecret.ParsedValue.PGUSER, $.newCreds.username)",
+          "databaseName.$": "$.databaseSecret.ParsedValue.PGDATABASE"
+        }
+      },
+      "Retry": [
+        {
+          "ErrorEquals": [
+            "Lambda.ServiceException",
+            "Lambda.AWSLambdaException",
+            "Lambda.SdkClientException"
+          ],
+          "IntervalSeconds": 2,
+          "MaxAttempts": 6,
+          "BackoffRate": 2
+        }
+      ],
+      "ResultPath": "$.databaseResults",
+      "Next": "Send credentials to user for anna"
+    },
+    "Send credentials to user for anna": {
+      "Type": "Task",
+      "Parameters": {
+        "Destination": {
+          "ToAddresses.$": "States.Array($.email)"
+        },
+        "Message": {
+          "Body": {
+            "Text": {
+              "Data.$": "States.Format('New credentials were generated for you: \n\ndatabase: {}\nusername: {}\npassword: {} \nhost: {}', $.databaseSecret.ParsedValue.PGDATABASE, $.newCreds.username, $.newCreds.otsUrl, $.databaseSecret.ParsedValue.PGHOST)"
+            }
+          },
+          "Subject": {
+            "Data": "New database access credentials have been created for you!"
+          }
+        },
+        "Source": "devops@momnt.com"
+      },
+      "Resource": "arn:aws:states:::aws-sdk:ses:sendEmail",
+      "End": true
+    }
+  },
+  "Comment": "must provide:\nusername  (will be prefix)\ndatabaseName (jasmine or belle, don't have to have full name, but should be able to?)\nemail (the email address to send results to)"
+}