├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── DotnetToLambda.sln ├── LICENSE ├── README.md ├── docker-compose.yml ├── global.json └── src ├── DotnetToLambda.Api ├── DotnetToLambda.Api │ ├── Controllers │ │ └── BookingController.cs │ ├── DotnetToLambda.Api.csproj │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json └── DotnetToLambda.Core │ ├── DotnetToLambda.Core.csproj │ ├── Exceptions │ └── DuplicateRequestException.cs │ ├── Infrastructure │ ├── BookingContext.cs │ ├── BookingRepository.cs │ ├── CustomerService.cs │ ├── DatabaseConnection.cs │ └── Migrations │ │ ├── 20220131202551_InitialCreate.Designer.cs │ │ ├── 20220131202551_InitialCreate.cs │ │ └── BookingContextModelSnapshot.cs │ ├── Models │ ├── Booking.cs │ ├── BookingStatus.cs │ └── IBookingRepository.cs │ ├── Services │ └── ICustomerService.cs │ └── ViewModels │ ├── CancelBookingDTO.cs │ ├── ConfirmBookingDTO.cs │ └── ReserveBookingDTO.cs ├── infrastructure ├── README.md ├── cdk.json └── src │ ├── Infrastructure.sln │ └── Infrastructure │ ├── GlobalSuppressions.cs │ ├── Infrastructure.csproj │ ├── InfrastructureStack.cs │ └── Program.cs └── serverless ├── DotnetToLambda.Serverless.Config ├── DotnetToLambda.Serverless.Config.csproj └── ServerlessConfig.cs └── DotnetToLambda.Serverless ├── .gitignore ├── README.md ├── omnisharp.json ├── src ├── ApplyDatabaseMigrations │ ├── ApplyDatabaseMigrations.csproj │ ├── Function.cs │ └── aws-lambda-tools-defaults.json ├── CancelBooking │ ├── CancelBooking.csproj │ ├── Function.cs │ └── aws-lambda-tools-defaults.json ├── ConfirmBooking │ ├── ConfirmBooking.csproj │ ├── Function.cs │ └── aws-lambda-tools-defaults.json ├── ListForCustomer │ ├── Function.cs │ ├── ListForCustomer.csproj │ └── aws-lambda-tools-defaults.json ├── ReserveBooking │ ├── Function.cs │ ├── ReserveBooking.csproj │ └── aws-lambda-tools-defaults.json └── Retrieve │ ├── Function.cs │ ├── Retrieve.csproj │ └── aws-lambda-tools-defaults.json └── template.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/osx,node,linux,windows 3 | 4 | ### Linux ### 5 | *~ 6 | 7 | # temporary files which can be created if a process still has a handle open of a deleted file 8 | .fuse_hidden* 9 | 10 | # KDE directory preferences 11 | .directory 12 | 13 | # Linux trash folder which might appear on any partition or disk 14 | .Trash-* 15 | 16 | # .nfs files are created when an open file is removed but is still being accessed 17 | .nfs* 18 | 19 | *.dll 20 | .vs/* 21 | .idea/* 22 | **/bin/* 23 | **/obj/* 24 | 25 | ### Node ### 26 | # Logs 27 | logs 28 | *.log 29 | npm-debug.log* 30 | yarn-debug.log* 31 | yarn-error.log* 32 | 33 | # Runtime data 34 | pids 35 | *.pid 36 | *.seed 37 | *.pid.lock 38 | 39 | # Directory for instrumented libs generated by jscoverage/JSCover 40 | lib-cov 41 | 42 | # Coverage directory used by tools like istanbul 43 | coverage 44 | 45 | # nyc test coverage 46 | .nyc_output 47 | 48 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 49 | .grunt 50 | 51 | # Bower dependency directory (https://bower.io/) 52 | bower_components 53 | 54 | # node-waf configuration 55 | .lock-wscript 56 | 57 | # Compiled binary addons (http://nodejs.org/api/addons.html) 58 | build/Release 59 | 60 | # Dependency directories 61 | node_modules/ 62 | jspm_packages/ 63 | 64 | # Typescript v1 declaration files 65 | typings/ 66 | 67 | # Optional npm cache directory 68 | .npm 69 | 70 | # Optional eslint cache 71 | .eslintcache 72 | 73 | # Optional REPL history 74 | .node_repl_history 75 | 76 | # Output of 'npm pack' 77 | *.tgz 78 | 79 | # Yarn Integrity file 80 | .yarn-integrity 81 | 82 | # dotenv environment variables file 83 | .env 84 | 85 | 86 | ### OSX ### 87 | *.DS_Store 88 | .AppleDouble 89 | .LSOverride 90 | 91 | # Icon must end with two \r 92 | Icon 93 | 94 | # Thumbnails 95 | ._* 96 | 97 | # Files that might appear in the root of a volume 98 | .DocumentRevisions-V100 99 | .fseventsd 100 | .Spotlight-V100 101 | .TemporaryItems 102 | .Trashes 103 | .VolumeIcon.icns 104 | .com.apple.timemachine.donotpresent 105 | 106 | # Directories potentially created on remote AFP share 107 | .AppleDB 108 | .AppleDesktop 109 | Network Trash Folder 110 | Temporary Items 111 | .apdisk 112 | 113 | ### Windows ### 114 | # Windows thumbnail cache files 115 | Thumbs.db 116 | ehthumbs.db 117 | ehthumbs_vista.db 118 | 119 | # Folder config file 120 | Desktop.ini 121 | 122 | # Recycle Bin used on file shares 123 | $RECYCLE.BIN/ 124 | 125 | # Windows Installer files 126 | *.cab 127 | *.msi 128 | *.msm 129 | *.msp 130 | 131 | # Windows shortcuts 132 | *.lnk 133 | 134 | samconfig.toml 135 | .aws-sam 136 | output.txt 137 | response.json 138 | package-lock.json 139 | 140 | # End of https://www.gitignore.io/api/osx,node,linux,windows 141 | 142 | # CDK 143 | cdk.out 144 | dist/* 145 | /.idea/vcs.xml 146 | /.idea/modules.xml 147 | /.idea/aws.xml 148 | /.idea/misc.xml 149 | /.idea/serverless-patterns.iml 150 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /DotnetToLambda.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.32014.148 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DotnetToLambda.Api", "DotnetToLambda.Api", "{F381D164-BDA5-4857-B2DC-2641EBC8A452}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetToLambda.Api", "src\DotnetToLambda.Api\DotnetToLambda.Api\DotnetToLambda.Api.csproj", "{16B3ED90-4E6D-42F8-B5ED-FC9980CA9619}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetToLambda.Core", "src\DotnetToLambda.Api\DotnetToLambda.Core\DotnetToLambda.Core.csproj", "{189540AB-B87C-47B0-BE53-4CB6CB2D7B17}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DotnetToLambda.Serverless", "DotnetToLambda.Serverless", "{163A600C-DD7F-436D-A8B5-32F69D7041C5}" 13 | ProjectSection(SolutionItems) = preProject 14 | src\serverless\DotnetToLambda.Serverless\template.yaml = src\serverless\DotnetToLambda.Serverless\template.yaml 15 | EndProjectSection 16 | EndProject 17 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReserveBooking", "src\serverless\DotnetToLambda.Serverless\src\ReserveBooking\ReserveBooking.csproj", "{BB9CACF8-8420-42E6-9AF6-7E90360C48E7}" 18 | EndProject 19 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetToLambda.Serverless.Config", "src\serverless\DotnetToLambda.Serverless.Config\DotnetToLambda.Serverless.Config.csproj", "{0B4FC5F9-7251-4CC7-9DFC-14272C789D98}" 20 | EndProject 21 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CancelBooking", "src\serverless\DotnetToLambda.Serverless\src\CancelBooking\CancelBooking.csproj", "{FF0E54D0-4A7D-4865-B466-3BB0ABCF2D0E}" 22 | EndProject 23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConfirmBooking", "src\serverless\DotnetToLambda.Serverless\src\ConfirmBooking\ConfirmBooking.csproj", "{5B35C029-27CE-44ED-AFEF-A646F2CEC096}" 24 | EndProject 25 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ListForCustomer", "src\serverless\DotnetToLambda.Serverless\src\ListForCustomer\ListForCustomer.csproj", "{43C59108-87C5-4F4B-81D8-481CDE6C2D6D}" 26 | EndProject 27 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Retrieve", "src\serverless\DotnetToLambda.Serverless\src\Retrieve\Retrieve.csproj", "{1EB6729D-9846-441F-9C09-5F7CD563C0CE}" 28 | EndProject 29 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplyDatabaseMigrations", "src\serverless\DotnetToLambda.Serverless\src\ApplyDatabaseMigrations\ApplyDatabaseMigrations.csproj", "{3630969D-0CBF-443D-9894-4DAC15B9D947}" 30 | EndProject 31 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "infrastructure", "infrastructure", "{6A524563-A123-4814-85AD-BD64EBA01BDF}" 32 | EndProject 33 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "src\infrastructure\src\Infrastructure\Infrastructure.csproj", "{B4F8269C-8734-4F4D-8D17-E933EC07A054}" 34 | EndProject 35 | Global 36 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 37 | Debug|Any CPU = Debug|Any CPU 38 | Debug|x64 = Debug|x64 39 | Debug|x86 = Debug|x86 40 | Release|Any CPU = Release|Any CPU 41 | Release|x64 = Release|x64 42 | Release|x86 = Release|x86 43 | EndGlobalSection 44 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 45 | {16B3ED90-4E6D-42F8-B5ED-FC9980CA9619}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {16B3ED90-4E6D-42F8-B5ED-FC9980CA9619}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {16B3ED90-4E6D-42F8-B5ED-FC9980CA9619}.Debug|x64.ActiveCfg = Debug|Any CPU 48 | {16B3ED90-4E6D-42F8-B5ED-FC9980CA9619}.Debug|x64.Build.0 = Debug|Any CPU 49 | {16B3ED90-4E6D-42F8-B5ED-FC9980CA9619}.Debug|x86.ActiveCfg = Debug|Any CPU 50 | {16B3ED90-4E6D-42F8-B5ED-FC9980CA9619}.Debug|x86.Build.0 = Debug|Any CPU 51 | {16B3ED90-4E6D-42F8-B5ED-FC9980CA9619}.Release|Any CPU.ActiveCfg = Release|Any CPU 52 | {16B3ED90-4E6D-42F8-B5ED-FC9980CA9619}.Release|Any CPU.Build.0 = Release|Any CPU 53 | {16B3ED90-4E6D-42F8-B5ED-FC9980CA9619}.Release|x64.ActiveCfg = Release|Any CPU 54 | {16B3ED90-4E6D-42F8-B5ED-FC9980CA9619}.Release|x64.Build.0 = Release|Any CPU 55 | {16B3ED90-4E6D-42F8-B5ED-FC9980CA9619}.Release|x86.ActiveCfg = Release|Any CPU 56 | {16B3ED90-4E6D-42F8-B5ED-FC9980CA9619}.Release|x86.Build.0 = Release|Any CPU 57 | {189540AB-B87C-47B0-BE53-4CB6CB2D7B17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 58 | {189540AB-B87C-47B0-BE53-4CB6CB2D7B17}.Debug|Any CPU.Build.0 = Debug|Any CPU 59 | {189540AB-B87C-47B0-BE53-4CB6CB2D7B17}.Debug|x64.ActiveCfg = Debug|Any CPU 60 | {189540AB-B87C-47B0-BE53-4CB6CB2D7B17}.Debug|x64.Build.0 = Debug|Any CPU 61 | {189540AB-B87C-47B0-BE53-4CB6CB2D7B17}.Debug|x86.ActiveCfg = Debug|Any CPU 62 | {189540AB-B87C-47B0-BE53-4CB6CB2D7B17}.Debug|x86.Build.0 = Debug|Any CPU 63 | {189540AB-B87C-47B0-BE53-4CB6CB2D7B17}.Release|Any CPU.ActiveCfg = Release|Any CPU 64 | {189540AB-B87C-47B0-BE53-4CB6CB2D7B17}.Release|Any CPU.Build.0 = Release|Any CPU 65 | {189540AB-B87C-47B0-BE53-4CB6CB2D7B17}.Release|x64.ActiveCfg = Release|Any CPU 66 | {189540AB-B87C-47B0-BE53-4CB6CB2D7B17}.Release|x64.Build.0 = Release|Any CPU 67 | {189540AB-B87C-47B0-BE53-4CB6CB2D7B17}.Release|x86.ActiveCfg = Release|Any CPU 68 | {189540AB-B87C-47B0-BE53-4CB6CB2D7B17}.Release|x86.Build.0 = Release|Any CPU 69 | {BB9CACF8-8420-42E6-9AF6-7E90360C48E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 70 | {BB9CACF8-8420-42E6-9AF6-7E90360C48E7}.Debug|Any CPU.Build.0 = Debug|Any CPU 71 | {BB9CACF8-8420-42E6-9AF6-7E90360C48E7}.Debug|x64.ActiveCfg = Debug|Any CPU 72 | {BB9CACF8-8420-42E6-9AF6-7E90360C48E7}.Debug|x64.Build.0 = Debug|Any CPU 73 | {BB9CACF8-8420-42E6-9AF6-7E90360C48E7}.Debug|x86.ActiveCfg = Debug|Any CPU 74 | {BB9CACF8-8420-42E6-9AF6-7E90360C48E7}.Debug|x86.Build.0 = Debug|Any CPU 75 | {BB9CACF8-8420-42E6-9AF6-7E90360C48E7}.Release|Any CPU.ActiveCfg = Release|Any CPU 76 | {BB9CACF8-8420-42E6-9AF6-7E90360C48E7}.Release|Any CPU.Build.0 = Release|Any CPU 77 | {BB9CACF8-8420-42E6-9AF6-7E90360C48E7}.Release|x64.ActiveCfg = Release|Any CPU 78 | {BB9CACF8-8420-42E6-9AF6-7E90360C48E7}.Release|x64.Build.0 = Release|Any CPU 79 | {BB9CACF8-8420-42E6-9AF6-7E90360C48E7}.Release|x86.ActiveCfg = Release|Any CPU 80 | {BB9CACF8-8420-42E6-9AF6-7E90360C48E7}.Release|x86.Build.0 = Release|Any CPU 81 | {0B4FC5F9-7251-4CC7-9DFC-14272C789D98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 82 | {0B4FC5F9-7251-4CC7-9DFC-14272C789D98}.Debug|Any CPU.Build.0 = Debug|Any CPU 83 | {0B4FC5F9-7251-4CC7-9DFC-14272C789D98}.Debug|x64.ActiveCfg = Debug|Any CPU 84 | {0B4FC5F9-7251-4CC7-9DFC-14272C789D98}.Debug|x64.Build.0 = Debug|Any CPU 85 | {0B4FC5F9-7251-4CC7-9DFC-14272C789D98}.Debug|x86.ActiveCfg = Debug|Any CPU 86 | {0B4FC5F9-7251-4CC7-9DFC-14272C789D98}.Debug|x86.Build.0 = Debug|Any CPU 87 | {0B4FC5F9-7251-4CC7-9DFC-14272C789D98}.Release|Any CPU.ActiveCfg = Release|Any CPU 88 | {0B4FC5F9-7251-4CC7-9DFC-14272C789D98}.Release|Any CPU.Build.0 = Release|Any CPU 89 | {0B4FC5F9-7251-4CC7-9DFC-14272C789D98}.Release|x64.ActiveCfg = Release|Any CPU 90 | {0B4FC5F9-7251-4CC7-9DFC-14272C789D98}.Release|x64.Build.0 = Release|Any CPU 91 | {0B4FC5F9-7251-4CC7-9DFC-14272C789D98}.Release|x86.ActiveCfg = Release|Any CPU 92 | {0B4FC5F9-7251-4CC7-9DFC-14272C789D98}.Release|x86.Build.0 = Release|Any CPU 93 | {FF0E54D0-4A7D-4865-B466-3BB0ABCF2D0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 94 | {FF0E54D0-4A7D-4865-B466-3BB0ABCF2D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU 95 | {FF0E54D0-4A7D-4865-B466-3BB0ABCF2D0E}.Debug|x64.ActiveCfg = Debug|Any CPU 96 | {FF0E54D0-4A7D-4865-B466-3BB0ABCF2D0E}.Debug|x64.Build.0 = Debug|Any CPU 97 | {FF0E54D0-4A7D-4865-B466-3BB0ABCF2D0E}.Debug|x86.ActiveCfg = Debug|Any CPU 98 | {FF0E54D0-4A7D-4865-B466-3BB0ABCF2D0E}.Debug|x86.Build.0 = Debug|Any CPU 99 | {FF0E54D0-4A7D-4865-B466-3BB0ABCF2D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU 100 | {FF0E54D0-4A7D-4865-B466-3BB0ABCF2D0E}.Release|Any CPU.Build.0 = Release|Any CPU 101 | {FF0E54D0-4A7D-4865-B466-3BB0ABCF2D0E}.Release|x64.ActiveCfg = Release|Any CPU 102 | {FF0E54D0-4A7D-4865-B466-3BB0ABCF2D0E}.Release|x64.Build.0 = Release|Any CPU 103 | {FF0E54D0-4A7D-4865-B466-3BB0ABCF2D0E}.Release|x86.ActiveCfg = Release|Any CPU 104 | {FF0E54D0-4A7D-4865-B466-3BB0ABCF2D0E}.Release|x86.Build.0 = Release|Any CPU 105 | {5B35C029-27CE-44ED-AFEF-A646F2CEC096}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 106 | {5B35C029-27CE-44ED-AFEF-A646F2CEC096}.Debug|Any CPU.Build.0 = Debug|Any CPU 107 | {5B35C029-27CE-44ED-AFEF-A646F2CEC096}.Debug|x64.ActiveCfg = Debug|Any CPU 108 | {5B35C029-27CE-44ED-AFEF-A646F2CEC096}.Debug|x64.Build.0 = Debug|Any CPU 109 | {5B35C029-27CE-44ED-AFEF-A646F2CEC096}.Debug|x86.ActiveCfg = Debug|Any CPU 110 | {5B35C029-27CE-44ED-AFEF-A646F2CEC096}.Debug|x86.Build.0 = Debug|Any CPU 111 | {5B35C029-27CE-44ED-AFEF-A646F2CEC096}.Release|Any CPU.ActiveCfg = Release|Any CPU 112 | {5B35C029-27CE-44ED-AFEF-A646F2CEC096}.Release|Any CPU.Build.0 = Release|Any CPU 113 | {5B35C029-27CE-44ED-AFEF-A646F2CEC096}.Release|x64.ActiveCfg = Release|Any CPU 114 | {5B35C029-27CE-44ED-AFEF-A646F2CEC096}.Release|x64.Build.0 = Release|Any CPU 115 | {5B35C029-27CE-44ED-AFEF-A646F2CEC096}.Release|x86.ActiveCfg = Release|Any CPU 116 | {5B35C029-27CE-44ED-AFEF-A646F2CEC096}.Release|x86.Build.0 = Release|Any CPU 117 | {43C59108-87C5-4F4B-81D8-481CDE6C2D6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 118 | {43C59108-87C5-4F4B-81D8-481CDE6C2D6D}.Debug|Any CPU.Build.0 = Debug|Any CPU 119 | {43C59108-87C5-4F4B-81D8-481CDE6C2D6D}.Debug|x64.ActiveCfg = Debug|Any CPU 120 | {43C59108-87C5-4F4B-81D8-481CDE6C2D6D}.Debug|x64.Build.0 = Debug|Any CPU 121 | {43C59108-87C5-4F4B-81D8-481CDE6C2D6D}.Debug|x86.ActiveCfg = Debug|Any CPU 122 | {43C59108-87C5-4F4B-81D8-481CDE6C2D6D}.Debug|x86.Build.0 = Debug|Any CPU 123 | {43C59108-87C5-4F4B-81D8-481CDE6C2D6D}.Release|Any CPU.ActiveCfg = Release|Any CPU 124 | {43C59108-87C5-4F4B-81D8-481CDE6C2D6D}.Release|Any CPU.Build.0 = Release|Any CPU 125 | {43C59108-87C5-4F4B-81D8-481CDE6C2D6D}.Release|x64.ActiveCfg = Release|Any CPU 126 | {43C59108-87C5-4F4B-81D8-481CDE6C2D6D}.Release|x64.Build.0 = Release|Any CPU 127 | {43C59108-87C5-4F4B-81D8-481CDE6C2D6D}.Release|x86.ActiveCfg = Release|Any CPU 128 | {43C59108-87C5-4F4B-81D8-481CDE6C2D6D}.Release|x86.Build.0 = Release|Any CPU 129 | {1EB6729D-9846-441F-9C09-5F7CD563C0CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 130 | {1EB6729D-9846-441F-9C09-5F7CD563C0CE}.Debug|Any CPU.Build.0 = Debug|Any CPU 131 | {1EB6729D-9846-441F-9C09-5F7CD563C0CE}.Debug|x64.ActiveCfg = Debug|Any CPU 132 | {1EB6729D-9846-441F-9C09-5F7CD563C0CE}.Debug|x64.Build.0 = Debug|Any CPU 133 | {1EB6729D-9846-441F-9C09-5F7CD563C0CE}.Debug|x86.ActiveCfg = Debug|Any CPU 134 | {1EB6729D-9846-441F-9C09-5F7CD563C0CE}.Debug|x86.Build.0 = Debug|Any CPU 135 | {1EB6729D-9846-441F-9C09-5F7CD563C0CE}.Release|Any CPU.ActiveCfg = Release|Any CPU 136 | {1EB6729D-9846-441F-9C09-5F7CD563C0CE}.Release|Any CPU.Build.0 = Release|Any CPU 137 | {1EB6729D-9846-441F-9C09-5F7CD563C0CE}.Release|x64.ActiveCfg = Release|Any CPU 138 | {1EB6729D-9846-441F-9C09-5F7CD563C0CE}.Release|x64.Build.0 = Release|Any CPU 139 | {1EB6729D-9846-441F-9C09-5F7CD563C0CE}.Release|x86.ActiveCfg = Release|Any CPU 140 | {1EB6729D-9846-441F-9C09-5F7CD563C0CE}.Release|x86.Build.0 = Release|Any CPU 141 | {9BEB806D-2444-40FD-8871-E13174E291B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 142 | {9BEB806D-2444-40FD-8871-E13174E291B1}.Debug|Any CPU.Build.0 = Debug|Any CPU 143 | {9BEB806D-2444-40FD-8871-E13174E291B1}.Debug|x64.ActiveCfg = Debug|Any CPU 144 | {9BEB806D-2444-40FD-8871-E13174E291B1}.Debug|x64.Build.0 = Debug|Any CPU 145 | {9BEB806D-2444-40FD-8871-E13174E291B1}.Debug|x86.ActiveCfg = Debug|Any CPU 146 | {9BEB806D-2444-40FD-8871-E13174E291B1}.Debug|x86.Build.0 = Debug|Any CPU 147 | {9BEB806D-2444-40FD-8871-E13174E291B1}.Release|Any CPU.ActiveCfg = Release|Any CPU 148 | {9BEB806D-2444-40FD-8871-E13174E291B1}.Release|Any CPU.Build.0 = Release|Any CPU 149 | {9BEB806D-2444-40FD-8871-E13174E291B1}.Release|x64.ActiveCfg = Release|Any CPU 150 | {9BEB806D-2444-40FD-8871-E13174E291B1}.Release|x64.Build.0 = Release|Any CPU 151 | {9BEB806D-2444-40FD-8871-E13174E291B1}.Release|x86.ActiveCfg = Release|Any CPU 152 | {9BEB806D-2444-40FD-8871-E13174E291B1}.Release|x86.Build.0 = Release|Any CPU 153 | {3630969D-0CBF-443D-9894-4DAC15B9D947}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 154 | {3630969D-0CBF-443D-9894-4DAC15B9D947}.Debug|Any CPU.Build.0 = Debug|Any CPU 155 | {3630969D-0CBF-443D-9894-4DAC15B9D947}.Debug|x64.ActiveCfg = Debug|Any CPU 156 | {3630969D-0CBF-443D-9894-4DAC15B9D947}.Debug|x64.Build.0 = Debug|Any CPU 157 | {3630969D-0CBF-443D-9894-4DAC15B9D947}.Debug|x86.ActiveCfg = Debug|Any CPU 158 | {3630969D-0CBF-443D-9894-4DAC15B9D947}.Debug|x86.Build.0 = Debug|Any CPU 159 | {3630969D-0CBF-443D-9894-4DAC15B9D947}.Release|Any CPU.ActiveCfg = Release|Any CPU 160 | {3630969D-0CBF-443D-9894-4DAC15B9D947}.Release|Any CPU.Build.0 = Release|Any CPU 161 | {3630969D-0CBF-443D-9894-4DAC15B9D947}.Release|x64.ActiveCfg = Release|Any CPU 162 | {3630969D-0CBF-443D-9894-4DAC15B9D947}.Release|x64.Build.0 = Release|Any CPU 163 | {3630969D-0CBF-443D-9894-4DAC15B9D947}.Release|x86.ActiveCfg = Release|Any CPU 164 | {3630969D-0CBF-443D-9894-4DAC15B9D947}.Release|x86.Build.0 = Release|Any CPU 165 | {B4F8269C-8734-4F4D-8D17-E933EC07A054}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 166 | {B4F8269C-8734-4F4D-8D17-E933EC07A054}.Debug|Any CPU.Build.0 = Debug|Any CPU 167 | {B4F8269C-8734-4F4D-8D17-E933EC07A054}.Debug|x64.ActiveCfg = Debug|Any CPU 168 | {B4F8269C-8734-4F4D-8D17-E933EC07A054}.Debug|x64.Build.0 = Debug|Any CPU 169 | {B4F8269C-8734-4F4D-8D17-E933EC07A054}.Debug|x86.ActiveCfg = Debug|Any CPU 170 | {B4F8269C-8734-4F4D-8D17-E933EC07A054}.Debug|x86.Build.0 = Debug|Any CPU 171 | {B4F8269C-8734-4F4D-8D17-E933EC07A054}.Release|Any CPU.ActiveCfg = Release|Any CPU 172 | {B4F8269C-8734-4F4D-8D17-E933EC07A054}.Release|Any CPU.Build.0 = Release|Any CPU 173 | {B4F8269C-8734-4F4D-8D17-E933EC07A054}.Release|x64.ActiveCfg = Release|Any CPU 174 | {B4F8269C-8734-4F4D-8D17-E933EC07A054}.Release|x64.Build.0 = Release|Any CPU 175 | {B4F8269C-8734-4F4D-8D17-E933EC07A054}.Release|x86.ActiveCfg = Release|Any CPU 176 | {B4F8269C-8734-4F4D-8D17-E933EC07A054}.Release|x86.Build.0 = Release|Any CPU 177 | EndGlobalSection 178 | GlobalSection(SolutionProperties) = preSolution 179 | HideSolutionNode = FALSE 180 | EndGlobalSection 181 | GlobalSection(NestedProjects) = preSolution 182 | {16B3ED90-4E6D-42F8-B5ED-FC9980CA9619} = {F381D164-BDA5-4857-B2DC-2641EBC8A452} 183 | {189540AB-B87C-47B0-BE53-4CB6CB2D7B17} = {F381D164-BDA5-4857-B2DC-2641EBC8A452} 184 | {BB9CACF8-8420-42E6-9AF6-7E90360C48E7} = {163A600C-DD7F-436D-A8B5-32F69D7041C5} 185 | {0B4FC5F9-7251-4CC7-9DFC-14272C789D98} = {163A600C-DD7F-436D-A8B5-32F69D7041C5} 186 | {FF0E54D0-4A7D-4865-B466-3BB0ABCF2D0E} = {163A600C-DD7F-436D-A8B5-32F69D7041C5} 187 | {5B35C029-27CE-44ED-AFEF-A646F2CEC096} = {163A600C-DD7F-436D-A8B5-32F69D7041C5} 188 | {43C59108-87C5-4F4B-81D8-481CDE6C2D6D} = {163A600C-DD7F-436D-A8B5-32F69D7041C5} 189 | {1EB6729D-9846-441F-9C09-5F7CD563C0CE} = {163A600C-DD7F-436D-A8B5-32F69D7041C5} 190 | {9BEB806D-2444-40FD-8871-E13174E291B1} = {12000098-E55C-49C0-B224-2027315833DB} 191 | {3630969D-0CBF-443D-9894-4DAC15B9D947} = {163A600C-DD7F-436D-A8B5-32F69D7041C5} 192 | {B4F8269C-8734-4F4D-8D17-E933EC07A054} = {6A524563-A123-4814-85AD-BD64EBA01BDF} 193 | EndGlobalSection 194 | GlobalSection(ExtensibilityGlobals) = postSolution 195 | SolutionGuid = {49CA736E-8003-4685-A545-74E1A5B9471E} 196 | EndGlobalSection 197 | EndGlobal 198 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Migrating a .NET Core Web Api to Serverless Compute 2 | 3 | This repo contains sample code that is related to the AWS blog post here **link to be added** on migrating a .NET core Web Api to use Serverless compute. The API is a simple one that implements functionality for storing/retriving airline bookings. It interacts with: 4 | 5 | - A MySQL database 6 | - An external customer service API accessed over HTTP 7 | 8 | ## Local .NET Core API Execution 9 | 10 | The starting .NET Core Web API can be executed on your local machine using Docker. Follow the below steps to start the application: 11 | 12 | 1. Start the MySQL instance locally using docker-compose 13 | 14 | ``` bash 15 | docker-compose up -d 16 | ``` 17 | 18 | 2. Navigate into the DotnetToLambda.Api folder and start the API 19 | 20 | ``` bash 21 | cd .\src\DotnetToLambda.Api\DotnetToLambda.Api\ 22 | dotnet run 23 | ``` 24 | 25 | 3. Make a POST request to http://localhost:5000/booking/reserve with the below body: 26 | 27 | ```json 28 | { 29 | "OutboundFlightId": "1234567", 30 | "CustomerId": "customername", 31 | "ChargeId": "98765" 32 | } 33 | ``` 34 | 35 | 4. Make a GET request as per below to validate the API is online, you should receive back on record. 36 | ``` bash 37 | curl http://localhost:5000/booking/customer/customername 38 | ``` 39 | 40 | ## Serverless Deployment 41 | 42 | The AWS ready application is built in two parts that need to be deployed in order. 43 | 44 | ### 1. Infrastructure 45 | 46 | All required infrastructure (VPC, Subnets, RDS instances) are deployed using AWS CDK. To deploy: 47 | 48 | 1. Navigate into the infrastructure directory and deploy the CDK application 49 | ``` bash 50 | cd .\src\DotnetToLambda.Api\infrastructure\ 51 | cdk deploy 52 | ``` 53 | 54 | 2. Make a note of all the CDK outputs as these will be required to deploy the Serverless application 55 | 56 | ### 2. Booking API 57 | 58 | Secondly, the actual Booking API running on Lambda compute is deployed using the SAM Cli. 59 | 60 | 1. Navigate into the serverless application folder and build the sam application 61 | ``` bash 62 | cd .\src\serverless\DotnetToLambda.Serverless\ 63 | sam build 64 | ``` 65 | 2. Once the build is complete run the deploy command below, replacing the parameter values with the outputs taken from the CDK deployment 66 | ```bash 67 | 68 | sam deploy --parameter-overrides 'ParameterKey=SecretArn,ParameterValue= ParameterKey=PrivateSubnet1,ParameterValue= ParameterKey=PrivateSubnet2,ParameterValue= ParameterKey=SecurityGroup,ParameterValue=' 69 | 70 | ``` 71 | 72 | ## Security 73 | 74 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 75 | 76 | ## License 77 | 78 | This library is licensed under the MIT-0 License. See the LICENSE file. -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # Use root/example as user/password credentials 2 | version: '3.1' 3 | 4 | services: 5 | db: 6 | image: mysql 7 | command: --default-authentication-plugin=mysql_native_password 8 | restart: always 9 | environment: 10 | MYSQL_ROOT_PASSWORD: example 11 | ports: 12 | - "3306:3306" -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "3.1.416" 4 | } 5 | } -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Api/Controllers/BookingController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | using System.Threading.Tasks; 4 | using DotnetToLambda.Core.Exceptions; 5 | using DotnetToLambda.Core.ViewModels; 6 | using DotnetToLambda.Core.Models; 7 | using DotnetToLambda.Core.Services; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace DotnetToLambda.Api.Controllers 12 | { 13 | [ApiController] 14 | [Route("[controller]")] 15 | public class BookingController : ControllerBase 16 | { 17 | private readonly ILogger _logger; 18 | private readonly IBookingRepository _bookingRepository; 19 | private readonly ICustomerService _customerService; 20 | 21 | public BookingController(ILogger logger, 22 | IBookingRepository bookingRepository, 23 | ICustomerService customerService) 24 | { 25 | this._logger = logger; 26 | this._bookingRepository = bookingRepository; 27 | this._customerService = customerService; 28 | } 29 | 30 | /// 31 | /// HTTP GET endpoint to list all bookings for a customer. 32 | /// 33 | /// The customer id to list for. 34 | /// All for the given customer. 35 | [HttpGet("customer/{customerId}")] 36 | public async Task ListForCustomer(string customerId) 37 | { 38 | this._logger.LogInformation($"Received request to list bookings for {customerId}"); 39 | 40 | return this.Ok(await this._bookingRepository.ListForCustomer(customerId)); 41 | } 42 | 43 | /// 44 | /// HTTP GET endpoint to view a specific booking. 45 | /// 46 | /// The booking to retrieve. 47 | /// The booking. 48 | [HttpGet("booking/{bookingId}")] 49 | public async Task Retrieve(string bookingId) 50 | { 51 | this._logger.LogInformation($"Received request for booking {bookingId}"); 52 | 53 | var booking = await this._bookingRepository.Retrieve(bookingId); 54 | 55 | if (booking == null) 56 | { 57 | return this.NotFound(); 58 | } 59 | 60 | return this.Ok(booking); 61 | } 62 | 63 | /// 64 | /// HTTP POST endpoint to reserve a booking. 65 | /// 66 | /// The containing details of the reservation request. 67 | /// The created . 68 | [HttpPost("reserve")] 69 | public async Task Reserve ([FromBody] ReserveBookingDTO request) 70 | { 71 | this._logger.LogInformation("Received request to reserve a new booking:"); 72 | 73 | this._logger.LogInformation(JsonSerializer.Serialize(request)); 74 | 75 | if (!await this._customerService.CustomerExists(request.CustomerId)) 76 | { 77 | this._logger.LogWarning($"Customer {request.CustomerId} does not exist in the customer service"); 78 | return this.BadRequest(); 79 | } 80 | 81 | var booking = Booking.Create(Guid.NewGuid().ToString(), request.CustomerId, request.OutboundFlightId, request.ChargeId); 82 | 83 | this._logger.LogInformation($"Booking created with Id ${booking.BookingId}"); 84 | 85 | try 86 | { 87 | await this._bookingRepository.Add(booking); 88 | } 89 | catch (DuplicateRequestException ex) 90 | { 91 | return this.Ok(ex.DuplicateBooking); 92 | } 93 | 94 | this._logger.LogInformation("Booking added"); 95 | 96 | return this.Ok(booking); 97 | } 98 | 99 | /// 100 | /// HTTP POST endpoint to confirm a booking. 101 | /// 102 | /// The containing details of the reservation to be confirmed. 103 | /// The created . 104 | [HttpPost("confirm")] 105 | public async Task ConfirmBooking ([FromBody] ConfirmBookingDTO request) 106 | { 107 | this._logger.LogInformation("Received request to confirm a booking:"); 108 | 109 | this._logger.LogInformation(JsonSerializer.Serialize(request)); 110 | 111 | var existingBooking = await this._bookingRepository.Retrieve(request.BookingId); 112 | 113 | if (existingBooking == null) 114 | { 115 | return this.NotFound(); 116 | } 117 | 118 | this._logger.LogInformation($"Found booking, confirmed"); 119 | 120 | existingBooking.Confirm(); 121 | 122 | await this._bookingRepository.Update(existingBooking); 123 | 124 | this._logger.LogInformation("Booking updated"); 125 | 126 | return this.Ok(existingBooking); 127 | } 128 | 129 | /// 130 | /// HTTP PUT endpoint to cancel a booking. 131 | /// 132 | /// The containing details of the reservation to be cancelled. 133 | /// The created . 134 | [HttpPut("cancel")] 135 | public async Task CancelBooking ([FromBody] CancelBookingDTO request) 136 | { 137 | this._logger.LogInformation("Received request to cancel a booking:"); 138 | 139 | this._logger.LogInformation(JsonSerializer.Serialize(request)); 140 | 141 | var existingBooking = await this._bookingRepository.Retrieve(request.BookingId); 142 | 143 | if (existingBooking == null) 144 | { 145 | return this.NotFound(); 146 | } 147 | 148 | this._logger.LogInformation($"Found booking, cancelling"); 149 | 150 | existingBooking.Cancel(request.Reason); 151 | 152 | await this._bookingRepository.Update(existingBooking); 153 | 154 | this._logger.LogInformation("Booking updated"); 155 | 156 | return this.Ok(existingBooking); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Api/DotnetToLambda.Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Api/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace DotnetToLambda.Api 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Api/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:24916", 8 | "sslPort": 44370 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "weatherforecast", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "DotnetToLambda.Api": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "weatherforecast", 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Api/Startup.cs: -------------------------------------------------------------------------------- 1 | using DotnetToLambda.Core.Models; 2 | using DotnetToLambda.Core.Infrastructure; 3 | using DotnetToLambda.Core.Services; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.EntityFrameworkCore; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Hosting; 10 | 11 | namespace DotnetToLambda.Api 12 | { 13 | public class Startup 14 | { 15 | public Startup(IConfiguration configuration) 16 | { 17 | Configuration = configuration; 18 | } 19 | 20 | public IConfiguration Configuration { get; } 21 | 22 | // This method gets called by the runtime. Use this method to add services to the container. 23 | public void ConfigureServices(IServiceCollection services) 24 | { 25 | var databaseConnection = 26 | new DatabaseConnection(this.Configuration.GetConnectionString("DatabaseConnection")); 27 | 28 | services.AddSingleton(databaseConnection); 29 | 30 | services.AddDbContext(options => 31 | options.UseMySQL(databaseConnection.ToString())); 32 | 33 | services.AddTransient(); 34 | services.AddHttpClient(); 35 | 36 | services.AddControllers(); 37 | } 38 | 39 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 40 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 41 | { 42 | if (env.IsDevelopment()) 43 | { 44 | app.UseDeveloperExceptionPage(); 45 | } 46 | 47 | using (var serviceScope = app.ApplicationServices.CreateScope()) 48 | { 49 | var context = serviceScope.ServiceProvider.GetRequiredService(); 50 | context.Database.Migrate(); 51 | } 52 | 53 | app.UseRouting(); 54 | 55 | app.UseAuthorization(); 56 | 57 | app.UseEndpoints(endpoints => 58 | { 59 | endpoints.MapControllers(); 60 | }); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Api/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Api/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "CustomerApiEndpoint": "https://jsonplaceholder.typicode.com/users", 11 | "ConnectionStrings": { 12 | "DatabaseConnection": "server=localhost;port=3306;database=test;user=root;password=example;Persist Security Info=False;Connect Timeout=300" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Core/DotnetToLambda.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | all 13 | runtime; build; native; contentfiles; analyzers; buildtransitive 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Core/Exceptions/DuplicateRequestException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DotnetToLambda.Core.Models; 3 | 4 | namespace DotnetToLambda.Core.Exceptions 5 | { 6 | public class DuplicateRequestException : Exception 7 | { 8 | public DuplicateRequestException(Booking b) : base() 9 | { 10 | this.DuplicateBooking = b; 11 | } 12 | 13 | public Booking DuplicateBooking { get; private set; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Core/Infrastructure/BookingContext.cs: -------------------------------------------------------------------------------- 1 | using DotnetToLambda.Core.Models; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.Extensions.Configuration; 4 | 5 | namespace DotnetToLambda.Core.Infrastructure 6 | { 7 | public class BookingContext : DbContext 8 | { 9 | private readonly DatabaseConnection _connection; 10 | 11 | public BookingContext(DatabaseConnection connection) 12 | { 13 | this._connection = connection; 14 | } 15 | 16 | public DbSet Bookings { get; set; } 17 | 18 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 19 | { 20 | optionsBuilder.UseMySQL(this._connection.ToString()); 21 | } 22 | 23 | protected override void OnModelCreating(ModelBuilder modelBuilder) 24 | { 25 | modelBuilder.Entity().ToTable("Bookings"); 26 | 27 | modelBuilder.Entity() 28 | .HasKey(b => b.BookingId).HasName("PK_BookingId"); 29 | modelBuilder.Entity() 30 | .HasIndex(b => b.CustomerId) 31 | .HasName("UX_Booking_CustomerId"); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Core/Infrastructure/BookingRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using DotnetToLambda.Core.Exceptions; 6 | using DotnetToLambda.Core.Models; 7 | using Microsoft.EntityFrameworkCore; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace DotnetToLambda.Core.Infrastructure 11 | { 12 | public class BookingRepository : IBookingRepository 13 | { 14 | private readonly BookingContext _context; 15 | private readonly ILogger _logger; 16 | 17 | public BookingRepository(BookingContext context, ILogger logger) 18 | { 19 | _context = context; 20 | _logger = logger; 21 | } 22 | 23 | public async Task Add(Booking booking) 24 | { 25 | this._logger.LogInformation($"Attempting to add booking, first checking if duplicate exists for {booking.OutboundFlightId} and customer {booking.CustomerId}"); 26 | 27 | var existingBookingForFlight = await this._context.Bookings.FirstOrDefaultAsync(p => 28 | p.OutboundFlightId.Equals(booking.OutboundFlightId) && p.CustomerId.Equals(booking.CustomerId)); 29 | 30 | if (existingBookingForFlight != null) 31 | { 32 | this._logger.LogWarning("Duplicate request received for booking"); 33 | 34 | throw new DuplicateRequestException(existingBookingForFlight); 35 | } 36 | 37 | this._context.Bookings.Add(booking); 38 | 39 | await this._context.SaveChangesAsync(); 40 | } 41 | 42 | public async Task> ListForCustomer(string customerId) => 43 | await this._context.Bookings.Where(p => p.CustomerId.Equals(customerId)).ToListAsync(); 44 | 45 | public async Task Retrieve(string bookingId) => 46 | await this._context.Bookings.FirstOrDefaultAsync(p => p.BookingId == bookingId); 47 | 48 | public async Task Update(Booking booking) 49 | { 50 | this._context.Bookings.Update(booking); 51 | 52 | await this._context.SaveChangesAsync(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Core/Infrastructure/CustomerService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using System.Threading.Tasks; 4 | using DotnetToLambda.Core.Services; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.Logging; 7 | 8 | namespace DotnetToLambda.Core.Infrastructure 9 | { 10 | public class CustomerService : ICustomerService 11 | { 12 | private readonly IConfiguration _configuration; 13 | private readonly HttpClient _httpClient; 14 | private readonly ILogger _logger; 15 | private readonly Random _random; 16 | 17 | public CustomerService(IConfiguration configuration, HttpClient httpClient, ILogger logger) 18 | { 19 | this._configuration = configuration; 20 | this._httpClient = httpClient; 21 | this._logger = logger; 22 | this._random = new Random(DateTime.Now.Second); 23 | } 24 | 25 | public async Task CustomerExists(string customerIdentifier) 26 | { 27 | this._logger.LogInformation($"Checking if customer {customerIdentifier} exists"); 28 | 29 | var randomNumber = this._random.Next(0, 15); 30 | 31 | this._logger.LogInformation($"Using random user identifier {randomNumber}"); 32 | 33 | // Using JSON placeholder API's to simulate an external API call. The user endpoint returns data with 34 | // an integer between 1 and 10 and no results between 11 and 15. The random number generator is 35 | // to simulate external failures. 36 | var checkCustomerExists = 37 | await this._httpClient.GetAsync($"{this._configuration["CustomerApiEndpoint"]}/{randomNumber}"); 38 | 39 | if (!checkCustomerExists.IsSuccessStatusCode) 40 | { 41 | this._logger.LogError($"Failure calling customer API error"); 42 | 43 | return false; 44 | } 45 | 46 | var responseBody = await checkCustomerExists.Content.ReadAsStringAsync(); 47 | 48 | this._logger.LogInformation($"Customer API response is {responseBody}"); 49 | 50 | if (responseBody == "{}") 51 | { 52 | return false; 53 | } 54 | else 55 | { 56 | return true; 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Core/Infrastructure/DatabaseConnection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Text.Json.Serialization; 5 | 6 | namespace DotnetToLambda.Core.Infrastructure 7 | { 8 | public class DatabaseConnection 9 | { 10 | private string _connectionString; 11 | 12 | public DatabaseConnection() { } 13 | 14 | public DatabaseConnection(string fullConnectionString) 15 | { 16 | this._connectionString = fullConnectionString; 17 | } 18 | 19 | [JsonPropertyName("password")] 20 | public string Password { get; set; } 21 | 22 | [JsonPropertyName("engine")] 23 | public string Engine { get; set; } 24 | 25 | [JsonPropertyName("port")] 26 | public int Port { get; set; } 27 | 28 | [JsonPropertyName("dbInstanceIdentifier ")] 29 | public string DbInstanceIdentifier { get; set; } 30 | 31 | [JsonPropertyName("host")] 32 | public string Host { get; set; } 33 | 34 | [JsonPropertyName("username")] 35 | public string Username { get; set; } 36 | 37 | public override string ToString() 38 | { 39 | if (!string.IsNullOrEmpty(this._connectionString)) 40 | { 41 | return this._connectionString; 42 | } 43 | 44 | return 45 | $"server={this.Host};port={this.Port};database=BookingDB;user={this.Username};password={this.Password};Persist Security Info=False;Connect Timeout=300"; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Core/Infrastructure/Migrations/20220131202551_InitialCreate.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using DotnetToLambda.Core.Infrastructure; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | 9 | namespace DotnetToLambda.Core.Infrastructure.Migrations 10 | { 11 | [DbContext(typeof(BookingContext))] 12 | [Migration("20220131202551_InitialCreate")] 13 | partial class InitialCreate 14 | { 15 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 16 | { 17 | #pragma warning disable 612, 618 18 | modelBuilder 19 | .HasAnnotation("ProductVersion", "3.1.22") 20 | .HasAnnotation("Relational:MaxIdentifierLength", 64); 21 | 22 | modelBuilder.Entity("DotnetToLambda.Core.Booking", b => 23 | { 24 | b.Property("BookingId") 25 | .HasColumnType("varchar(767)"); 26 | 27 | b.Property("CancellationReason") 28 | .HasColumnType("text"); 29 | 30 | b.Property("ChargeId") 31 | .HasColumnType("text"); 32 | 33 | b.Property("ConfirmedOn") 34 | .HasColumnType("datetime"); 35 | 36 | b.Property("CustomerId") 37 | .HasColumnType("varchar(767)"); 38 | 39 | b.Property("OutboundFlightId") 40 | .HasColumnType("text"); 41 | 42 | b.Property("Status") 43 | .HasColumnType("int"); 44 | 45 | b.HasKey("BookingId") 46 | .HasName("PK_BookingId"); 47 | 48 | b.HasIndex("CustomerId") 49 | .HasName("UX_Booking_CustomerId"); 50 | 51 | b.ToTable("Bookings"); 52 | }); 53 | #pragma warning restore 612, 618 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Core/Infrastructure/Migrations/20220131202551_InitialCreate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | namespace DotnetToLambda.Core.Infrastructure.Migrations 5 | { 6 | public partial class InitialCreate : Migration 7 | { 8 | protected override void Up(MigrationBuilder migrationBuilder) 9 | { 10 | migrationBuilder.CreateTable( 11 | name: "Bookings", 12 | columns: table => new 13 | { 14 | BookingId = table.Column(nullable: false), 15 | OutboundFlightId = table.Column(nullable: true), 16 | CustomerId = table.Column(nullable: true), 17 | ChargeId = table.Column(nullable: true), 18 | Status = table.Column(nullable: false), 19 | ConfirmedOn = table.Column(nullable: true), 20 | CancellationReason = table.Column(nullable: true) 21 | }, 22 | constraints: table => 23 | { 24 | table.PrimaryKey("PK_BookingId", x => x.BookingId); 25 | }); 26 | 27 | migrationBuilder.CreateIndex( 28 | name: "UX_Booking_CustomerId", 29 | table: "Bookings", 30 | column: "CustomerId"); 31 | } 32 | 33 | protected override void Down(MigrationBuilder migrationBuilder) 34 | { 35 | migrationBuilder.DropTable( 36 | name: "Bookings"); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Core/Infrastructure/Migrations/BookingContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using DotnetToLambda.Core; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 7 | 8 | namespace DotnetToLambda.Core.Infrastructure.Migrations 9 | { 10 | [DbContext(typeof(BookingContext))] 11 | partial class BookingContextModelSnapshot : ModelSnapshot 12 | { 13 | protected override void BuildModel(ModelBuilder modelBuilder) 14 | { 15 | #pragma warning disable 612, 618 16 | modelBuilder 17 | .HasAnnotation("ProductVersion", "3.1.22") 18 | .HasAnnotation("Relational:MaxIdentifierLength", 64); 19 | 20 | modelBuilder.Entity("DotnetToLambda.Core.Booking", b => 21 | { 22 | b.Property("BookingId") 23 | .HasColumnType("varchar(767)"); 24 | 25 | b.Property("CancellationReason") 26 | .HasColumnType("text"); 27 | 28 | b.Property("ChargeId") 29 | .HasColumnType("text"); 30 | 31 | b.Property("ConfirmedOn") 32 | .HasColumnType("datetime"); 33 | 34 | b.Property("CustomerId") 35 | .HasColumnType("varchar(767)"); 36 | 37 | b.Property("OutboundFlightId") 38 | .HasColumnType("text"); 39 | 40 | b.Property("Status") 41 | .HasColumnType("int"); 42 | 43 | b.HasKey("BookingId") 44 | .HasName("PK_BookingId"); 45 | 46 | b.HasIndex("CustomerId") 47 | .HasName("UX_Booking_CustomerId"); 48 | 49 | b.ToTable("Bookings"); 50 | }); 51 | #pragma warning restore 612, 618 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Core/Models/Booking.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DotnetToLambda.Core.Models 6 | { 7 | public class Booking 8 | { 9 | private Booking() 10 | { 11 | this.Status = BookingStatus.Reserved; 12 | } 13 | 14 | public static Booking Create(string bookingId, string customerId, string outboundFlightId, string chargeId) 15 | { 16 | return new Booking() 17 | { 18 | BookingId = bookingId, 19 | CustomerId = customerId, 20 | OutboundFlightId = outboundFlightId, 21 | ChargeId = chargeId, 22 | }; 23 | } 24 | 25 | public string OutboundFlightId { get; private set; } 26 | 27 | public string CustomerId { get; private set; } 28 | 29 | public string ChargeId { get; private set; } 30 | 31 | public string BookingId { get; private set; } 32 | 33 | public BookingStatus Status { get; private set; } 34 | 35 | public DateTime? ConfirmedOn { get; private set; } 36 | 37 | public string CancellationReason { get; private set; } 38 | 39 | public void Confirm() 40 | { 41 | if (this.Status == BookingStatus.Cancelled) 42 | { 43 | return; 44 | } 45 | 46 | this.Status = BookingStatus.Confirmed; 47 | this.ConfirmedOn = DateTime.Now; 48 | } 49 | 50 | public void Cancel(string reason) 51 | { 52 | this.Status = BookingStatus.Cancelled; 53 | this.ConfirmedOn = DateTime.Now; 54 | this.CancellationReason = reason; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Core/Models/BookingStatus.cs: -------------------------------------------------------------------------------- 1 | namespace DotnetToLambda.Core.Models 2 | { 3 | public enum BookingStatus 4 | { 5 | Reserved, 6 | Confirmed, 7 | Cancelled 8 | } 9 | } -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Core/Models/IBookingRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace DotnetToLambda.Core.Models 7 | { 8 | public interface IBookingRepository 9 | { 10 | Task Add(Booking booking); 11 | 12 | Task> ListForCustomer(string customerId); 13 | 14 | Task Retrieve(string bookingId); 15 | 16 | Task Update(Booking booking); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Core/Services/ICustomerService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace DotnetToLambda.Core.Services 4 | { 5 | public interface ICustomerService 6 | { 7 | Task CustomerExists(string customerIdentifier); 8 | } 9 | } -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Core/ViewModels/CancelBookingDTO.cs: -------------------------------------------------------------------------------- 1 | namespace DotnetToLambda.Core.ViewModels 2 | { 3 | public class CancelBookingDTO 4 | { 5 | public string BookingId { get; set; } 6 | 7 | public string Reason { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Core/ViewModels/ConfirmBookingDTO.cs: -------------------------------------------------------------------------------- 1 | namespace DotnetToLambda.Core.ViewModels 2 | { 3 | public class ConfirmBookingDTO 4 | { 5 | public string BookingId { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/DotnetToLambda.Api/DotnetToLambda.Core/ViewModels/ReserveBookingDTO.cs: -------------------------------------------------------------------------------- 1 | namespace DotnetToLambda.Core.ViewModels 2 | { 3 | public class ReserveBookingDTO 4 | { 5 | public string OutboundFlightId { get; set; } 6 | 7 | public string CustomerId { get; set; } 8 | 9 | public string ChargeId { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/infrastructure/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your CDK C# project! 2 | 3 | This is a blank project for C# development with CDK. 4 | 5 | The `cdk.json` file tells the CDK Toolkit how to execute your app. 6 | 7 | It uses the [.NET Core CLI](https://docs.microsoft.com/dotnet/articles/core/) to compile and execute your project. 8 | 9 | ## Useful commands 10 | 11 | * `dotnet build src` compile this app 12 | * `cdk deploy` deploy this stack to your default AWS account/region 13 | * `cdk diff` compare deployed stack with current state 14 | * `cdk synth` emits the synthesized CloudFormation template -------------------------------------------------------------------------------- /src/infrastructure/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "dotnet run --project src/Infrastructure/Infrastructure.csproj", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "src/*/obj", 11 | "src/*/bin", 12 | "src/*.sln", 13 | "src/*/GlobalSuppressions.cs", 14 | "src/*/*.csproj" 15 | ] 16 | }, 17 | "context": { 18 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 19 | "@aws-cdk/core:stackRelativeExports": true, 20 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 21 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 22 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, 23 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 24 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 25 | "@aws-cdk/core:target-partitions": [ 26 | "aws", 27 | "aws-cn" 28 | ] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/infrastructure/src/Infrastructure.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "Infrastructure\Infrastructure.csproj", "{A8EE5D23-5E09-45C2-8BC7-694465AD222D}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Debug|x86 = Debug|x86 13 | Release|Any CPU = Release|Any CPU 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {A8EE5D23-5E09-45C2-8BC7-694465AD222D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {A8EE5D23-5E09-45C2-8BC7-694465AD222D}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {A8EE5D23-5E09-45C2-8BC7-694465AD222D}.Debug|x64.ActiveCfg = Debug|Any CPU 24 | {A8EE5D23-5E09-45C2-8BC7-694465AD222D}.Debug|x64.Build.0 = Debug|Any CPU 25 | {A8EE5D23-5E09-45C2-8BC7-694465AD222D}.Debug|x86.ActiveCfg = Debug|Any CPU 26 | {A8EE5D23-5E09-45C2-8BC7-694465AD222D}.Debug|x86.Build.0 = Debug|Any CPU 27 | {A8EE5D23-5E09-45C2-8BC7-694465AD222D}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {A8EE5D23-5E09-45C2-8BC7-694465AD222D}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {A8EE5D23-5E09-45C2-8BC7-694465AD222D}.Release|x64.ActiveCfg = Release|Any CPU 30 | {A8EE5D23-5E09-45C2-8BC7-694465AD222D}.Release|x64.Build.0 = Release|Any CPU 31 | {A8EE5D23-5E09-45C2-8BC7-694465AD222D}.Release|x86.ActiveCfg = Release|Any CPU 32 | {A8EE5D23-5E09-45C2-8BC7-694465AD222D}.Release|x86.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /src/infrastructure/src/Infrastructure/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Potential Code Quality Issues", "RECS0026:Possible unassigned object created by 'new'", Justification = "Constructs add themselves to the scope in which they are created")] 2 | -------------------------------------------------------------------------------- /src/infrastructure/src/Infrastructure/Infrastructure.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | Major 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/infrastructure/src/Infrastructure/InfrastructureStack.cs: -------------------------------------------------------------------------------- 1 | using Amazon.CDK; 2 | using Amazon.CDK.AWS.EC2; 3 | using Amazon.CDK.AWS.RDS; 4 | using Amazon.CDK.AWS.SSM; 5 | using Constructs; 6 | 7 | namespace Infrastructure 8 | { 9 | public class InfrastructureStack : Stack 10 | { 11 | internal InfrastructureStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) 12 | { 13 | var vpc = new Vpc(this, "DotnetToLambdaVpc", new VpcProps 14 | { 15 | Cidr = "10.0.0.0/16", 16 | MaxAzs = 2, 17 | SubnetConfiguration = new ISubnetConfiguration[] 18 | { 19 | new SubnetConfiguration 20 | { 21 | CidrMask = 24, 22 | SubnetType = SubnetType.PUBLIC, 23 | Name = "DotnetToLambdaPublicSubnet" 24 | }, 25 | new SubnetConfiguration 26 | { 27 | CidrMask = 24, 28 | SubnetType = SubnetType.PRIVATE_ISOLATED, 29 | Name = "DotnetToLambdaPrivateSubnet" 30 | }, 31 | new SubnetConfiguration 32 | { 33 | CidrMask = 28, 34 | SubnetType = SubnetType.PRIVATE_WITH_NAT, 35 | Name = "DotnetToLambdaPrivate" 36 | } 37 | }, 38 | NatGateways = 2, 39 | EnableDnsHostnames = true, 40 | EnableDnsSupport = true, 41 | }); 42 | 43 | const int dbPort = 3306; 44 | 45 | var db = new DatabaseInstance(this, "DB", new DatabaseInstanceProps 46 | { 47 | Vpc = vpc, 48 | VpcSubnets = new SubnetSelection {SubnetType = SubnetType.PRIVATE_ISOLATED}, 49 | Engine = DatabaseInstanceEngine.Mysql(new MySqlInstanceEngineProps() 50 | { 51 | Version = MysqlEngineVersion.VER_8_0_26 52 | }), 53 | InstanceType = InstanceType.Of(InstanceClass.BURSTABLE2, InstanceSize.MICRO), 54 | Port = dbPort, 55 | InstanceIdentifier = "DotnetToLambda", 56 | BackupRetention = Duration.Seconds(0) 57 | }); 58 | 59 | var privateSubnets = vpc.SelectSubnets(new SubnetSelection() 60 | { 61 | SubnetType = SubnetType.PRIVATE_ISOLATED 62 | }); 63 | 64 | var natEnabledPrivateSubnets = vpc.SelectSubnets(new SubnetSelection() 65 | { 66 | SubnetType = SubnetType.PRIVATE_WITH_NAT 67 | }); 68 | 69 | var dbSecurityGroup = db.Connections.SecurityGroups[0]; 70 | 71 | dbSecurityGroup.AddIngressRule(Peer.Ipv4(vpc.VpcCidrBlock), Port.Tcp(dbPort)); 72 | 73 | var parameter = new StringParameter(this, "dev-configuration", new StringParameterProps() 74 | { 75 | ParameterName = "dotnet-to-lambda-dev", 76 | StringValue = "{\"CustomerApiEndpoint\": \"https://jsonplaceholder.typicode.com/users\"}", 77 | DataType = ParameterDataType.TEXT, 78 | Tier = ParameterTier.STANDARD, 79 | Type = ParameterType.STRING, 80 | Description = "Dev configuration for dotnet to lambda", 81 | }); 82 | 83 | var subnet1 = new CfnOutput(this, "PrivateSubnet1", new CfnOutputProps() 84 | { 85 | ExportName = "PrivateSubnet1", 86 | Value = natEnabledPrivateSubnets.SubnetIds[0] 87 | }); 88 | 89 | var subnet2 = new CfnOutput(this, "PrivateSubnet2", new CfnOutputProps() 90 | { 91 | ExportName = "PrivateSubnet2", 92 | Value = natEnabledPrivateSubnets.SubnetIds[1] 93 | }); 94 | 95 | var dbSecurityGroupOutput = new CfnOutput(this, "SecurityGroup", new CfnOutputProps() 96 | { 97 | ExportName = "DbSecurityGroup", 98 | Value = dbSecurityGroup.SecurityGroupId 99 | }); 100 | 101 | var secretManager = new CfnOutput(this, "SecretArn", new CfnOutputProps() 102 | { 103 | ExportName = "DbConnectionStringSecret", 104 | Value = db.Secret.SecretArn 105 | }); 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /src/infrastructure/src/Infrastructure/Program.cs: -------------------------------------------------------------------------------- 1 | using Amazon.CDK; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Infrastructure 7 | { 8 | sealed class Program 9 | { 10 | public static void Main(string[] args) 11 | { 12 | var app = new App(); 13 | new InfrastructureStack(app, "InfrastructureStack", new StackProps 14 | { 15 | // If you don't specify 'env', this stack will be environment-agnostic. 16 | // Account/Region-dependent features and context lookups will not work, 17 | // but a single synthesized template can be deployed anywhere. 18 | 19 | // Uncomment the next block to specialize this stack for the AWS Account 20 | // and Region that are implied by the current CLI configuration. 21 | /* 22 | Env = new Amazon.CDK.Environment 23 | { 24 | Account = System.Environment.GetEnvironmentVariable("CDK_DEFAULT_ACCOUNT"), 25 | Region = System.Environment.GetEnvironmentVariable("CDK_DEFAULT_REGION"), 26 | } 27 | */ 28 | 29 | // Uncomment the next block if you know exactly what Account and Region you 30 | // want to deploy the stack to. 31 | /* 32 | Env = new Amazon.CDK.Environment 33 | { 34 | Account = "123456789012", 35 | Region = "us-east-1", 36 | } 37 | */ 38 | 39 | // For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html 40 | }); 41 | app.Synth(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless.Config/DotnetToLambda.Serverless.Config.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless.Config/ServerlessConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using System.Text.Json; 5 | using Amazon.SecretsManager; 6 | using Amazon.SecretsManager.Model; 7 | using Amazon.SimpleSystemsManagement; 8 | using Amazon.SimpleSystemsManagement.Model; 9 | using DotnetToLambda.Core.Infrastructure; 10 | using DotnetToLambda.Core.Models; 11 | using DotnetToLambda.Core.Services; 12 | using Microsoft.EntityFrameworkCore; 13 | using Microsoft.Extensions.Configuration; 14 | using Microsoft.Extensions.DependencyInjection; 15 | using Microsoft.Extensions.Logging; 16 | 17 | namespace DotnetToLambda.Serverless.Config 18 | { 19 | public static class ServerlessConfig 20 | { 21 | public static IServiceProvider Services { get; private set; } 22 | 23 | public static void ConfigureServices() 24 | { 25 | var client = new AmazonSecretsManagerClient(); 26 | 27 | var serviceCollection = new ServiceCollection(); 28 | 29 | var connectionDetails = LoadDatabaseSecret(client); 30 | 31 | serviceCollection.AddDbContext(options => 32 | options.UseMySQL(connectionDetails.ToString())); 33 | 34 | serviceCollection.AddHttpClient(); 35 | serviceCollection.AddTransient(); 36 | serviceCollection.AddSingleton(connectionDetails); 37 | serviceCollection.AddSingleton(LoadAppConfiguration()); 38 | 39 | serviceCollection.AddLogging(logging => 40 | { 41 | logging.AddLambdaLogger(); 42 | logging.SetMinimumLevel(LogLevel.Debug); 43 | }); 44 | 45 | Services = serviceCollection.BuildServiceProvider(); 46 | } 47 | 48 | private static DatabaseConnection LoadDatabaseSecret(AmazonSecretsManagerClient client) 49 | { 50 | var databaseConnectionSecret = client.GetSecretValueAsync(new GetSecretValueRequest() 51 | { 52 | SecretId = Environment.GetEnvironmentVariable("DATABASE_CONNECTION_SECRET_ID"), 53 | }).Result; 54 | 55 | return JsonSerializer 56 | .Deserialize(databaseConnectionSecret.SecretString); 57 | } 58 | 59 | private static IConfiguration LoadAppConfiguration() 60 | { 61 | var client = new AmazonSimpleSystemsManagementClient(); 62 | var param = client.GetParameterAsync(new GetParameterRequest() 63 | { 64 | Name = "dotnet-to-lambda-dev" 65 | }).Result; 66 | 67 | return new ConfigurationBuilder() 68 | .AddJsonStream(new MemoryStream(Encoding.ASCII.GetBytes(param.Parameter.Value))) 69 | .Build(); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/macos,linux,windows,sublimetext,dotsettings,visualstudio,visualstudiocode,sam 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos,linux,windows,sublimetext,dotsettings,visualstudio,visualstudiocode,sam 4 | 5 | ### DotSettings ### 6 | *.DotSettings 7 | 8 | ### Linux ### 9 | *~ 10 | 11 | # temporary files which can be created if a process still has a handle open of a deleted file 12 | .fuse_hidden* 13 | 14 | # KDE directory preferences 15 | .directory 16 | 17 | # Linux trash folder which might appear on any partition or disk 18 | .Trash-* 19 | 20 | # .nfs files are created when an open file is removed but is still being accessed 21 | .nfs* 22 | 23 | ### macOS ### 24 | # General 25 | .DS_Store 26 | .AppleDouble 27 | .LSOverride 28 | 29 | # Icon must end with two \r 30 | Icon 31 | 32 | 33 | # Thumbnails 34 | ._* 35 | 36 | # Files that might appear in the root of a volume 37 | .DocumentRevisions-V100 38 | .fseventsd 39 | .Spotlight-V100 40 | .TemporaryItems 41 | .Trashes 42 | .VolumeIcon.icns 43 | .com.apple.timemachine.donotpresent 44 | 45 | # Directories potentially created on remote AFP share 46 | .AppleDB 47 | .AppleDesktop 48 | Network Trash Folder 49 | Temporary Items 50 | .apdisk 51 | 52 | ### SAM ### 53 | # Ignore build directories for the AWS Serverless Application Model (SAM) 54 | # Info: https://aws.amazon.com/serverless/sam/ 55 | # Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html 56 | 57 | **/.aws-sam 58 | 59 | ### SublimeText ### 60 | # Cache files for Sublime Text 61 | *.tmlanguage.cache 62 | *.tmPreferences.cache 63 | *.stTheme.cache 64 | 65 | # Workspace files are user-specific 66 | *.sublime-workspace 67 | 68 | # Project files should be checked into the repository, unless a significant 69 | # proportion of contributors will probably not be using Sublime Text 70 | # *.sublime-project 71 | 72 | # SFTP configuration file 73 | sftp-config.json 74 | 75 | # Package control specific files 76 | Package Control.last-run 77 | Package Control.ca-list 78 | Package Control.ca-bundle 79 | Package Control.system-ca-bundle 80 | Package Control.cache/ 81 | Package Control.ca-certs/ 82 | Package Control.merged-ca-bundle 83 | Package Control.user-ca-bundle 84 | oscrypto-ca-bundle.crt 85 | bh_unicode_properties.cache 86 | 87 | # Sublime-github package stores a github token in this file 88 | # https://packagecontrol.io/packages/sublime-github 89 | GitHub.sublime-settings 90 | 91 | ### VisualStudioCode ### 92 | .vscode/* 93 | !.vscode/settings.json 94 | !.vscode/tasks.json 95 | !.vscode/launch.json 96 | !.vscode/extensions.json 97 | *.code-workspace 98 | 99 | ### VisualStudioCode Patch ### 100 | # Ignore all local history of files 101 | .history 102 | .ionide 103 | 104 | ### Windows ### 105 | # Windows thumbnail cache files 106 | Thumbs.db 107 | Thumbs.db:encryptable 108 | ehthumbs.db 109 | ehthumbs_vista.db 110 | 111 | # Dump file 112 | *.stackdump 113 | 114 | # Folder config file 115 | [Dd]esktop.ini 116 | 117 | # Recycle Bin used on file shares 118 | $RECYCLE.BIN/ 119 | 120 | # Windows Installer files 121 | *.cab 122 | *.msi 123 | *.msix 124 | *.msm 125 | *.msp 126 | 127 | # Windows shortcuts 128 | *.lnk 129 | 130 | ### VisualStudio ### 131 | ## Ignore Visual Studio temporary files, build results, and 132 | ## files generated by popular Visual Studio add-ons. 133 | ## 134 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 135 | 136 | # User-specific files 137 | *.rsuser 138 | *.suo 139 | *.user 140 | *.userosscache 141 | *.sln.docstates 142 | 143 | # User-specific files (MonoDevelop/Xamarin Studio) 144 | *.userprefs 145 | 146 | # Mono auto generated files 147 | mono_crash.* 148 | 149 | # Build results 150 | [Dd]ebug/ 151 | [Dd]ebugPublic/ 152 | [Rr]elease/ 153 | [Rr]eleases/ 154 | x64/ 155 | x86/ 156 | [Ww][Ii][Nn]32/ 157 | [Aa][Rr][Mm]/ 158 | [Aa][Rr][Mm]64/ 159 | bld/ 160 | [Bb]in/ 161 | [Oo]bj/ 162 | [Ll]og/ 163 | [Ll]ogs/ 164 | 165 | # Visual Studio 2015/2017 cache/options directory 166 | .vs/ 167 | # Uncomment if you have tasks that create the project's static files in wwwroot 168 | #wwwroot/ 169 | 170 | # Visual Studio 2017 auto generated files 171 | Generated\ Files/ 172 | 173 | # MSTest test Results 174 | [Tt]est[Rr]esult*/ 175 | [Bb]uild[Ll]og.* 176 | 177 | # NUnit 178 | *.VisualState.xml 179 | TestResult.xml 180 | nunit-*.xml 181 | 182 | # Build Results of an ATL Project 183 | [Dd]ebugPS/ 184 | [Rr]eleasePS/ 185 | dlldata.c 186 | 187 | # Benchmark Results 188 | BenchmarkDotNet.Artifacts/ 189 | 190 | # .NET Core 191 | project.lock.json 192 | project.fragment.lock.json 193 | artifacts/ 194 | 195 | # ASP.NET Scaffolding 196 | ScaffoldingReadMe.txt 197 | 198 | # StyleCop 199 | StyleCopReport.xml 200 | 201 | # Files built by Visual Studio 202 | *_i.c 203 | *_p.c 204 | *_h.h 205 | *.ilk 206 | *.meta 207 | *.obj 208 | *.iobj 209 | *.pch 210 | *.pdb 211 | *.ipdb 212 | *.pgc 213 | *.pgd 214 | *.rsp 215 | *.sbr 216 | *.tlb 217 | *.tli 218 | *.tlh 219 | *.tmp 220 | *.tmp_proj 221 | *_wpftmp.csproj 222 | *.log 223 | *.vspscc 224 | *.vssscc 225 | .builds 226 | *.pidb 227 | *.svclog 228 | *.scc 229 | 230 | # Chutzpah Test files 231 | _Chutzpah* 232 | 233 | # Visual C++ cache files 234 | ipch/ 235 | *.aps 236 | *.ncb 237 | *.opendb 238 | *.opensdf 239 | *.sdf 240 | *.cachefile 241 | *.VC.db 242 | *.VC.VC.opendb 243 | 244 | # Visual Studio profiler 245 | *.psess 246 | *.vsp 247 | *.vspx 248 | *.sap 249 | 250 | # Visual Studio Trace Files 251 | *.e2e 252 | 253 | # TFS 2012 Local Workspace 254 | $tf/ 255 | 256 | # Guidance Automation Toolkit 257 | *.gpState 258 | 259 | # ReSharper is a .NET coding add-in 260 | _ReSharper*/ 261 | *.[Rr]e[Ss]harper 262 | *.DotSettings.user 263 | 264 | # TeamCity is a build add-in 265 | _TeamCity* 266 | 267 | # DotCover is a Code Coverage Tool 268 | *.dotCover 269 | 270 | # AxoCover is a Code Coverage Tool 271 | .axoCover/* 272 | !.axoCover/settings.json 273 | 274 | # Coverlet is a free, cross platform Code Coverage Tool 275 | coverage*.[ji][sn][of][no] 276 | coverage*.xml 277 | 278 | # Visual Studio code coverage results 279 | *.coverage 280 | *.coveragexml 281 | 282 | # NCrunch 283 | _NCrunch_* 284 | .*crunch*.local.xml 285 | nCrunchTemp_* 286 | 287 | # MightyMoose 288 | *.mm.* 289 | AutoTest.Net/ 290 | 291 | # Web workbench (sass) 292 | .sass-cache/ 293 | 294 | # Installshield output folder 295 | [Ee]xpress/ 296 | 297 | # DocProject is a documentation generator add-in 298 | DocProject/buildhelp/ 299 | DocProject/Help/*.HxT 300 | DocProject/Help/*.HxC 301 | DocProject/Help/*.hhc 302 | DocProject/Help/*.hhk 303 | DocProject/Help/*.hhp 304 | DocProject/Help/Html2 305 | DocProject/Help/html 306 | 307 | # Click-Once directory 308 | publish/ 309 | 310 | # Publish Web Output 311 | *.[Pp]ublish.xml 312 | *.azurePubxml 313 | # Note: Comment the next line if you want to checkin your web deploy settings, 314 | # but database connection strings (with potential passwords) will be unencrypted 315 | *.pubxml 316 | *.publishproj 317 | 318 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 319 | # checkin your Azure Web App publish settings, but sensitive information contained 320 | # in these scripts will be unencrypted 321 | PublishScripts/ 322 | 323 | # NuGet Packages 324 | *.nupkg 325 | # NuGet Symbol Packages 326 | *.snupkg 327 | # The packages folder can be ignored because of Package Restore 328 | **/[Pp]ackages/* 329 | # except build/, which is used as an MSBuild target. 330 | !**/[Pp]ackages/build/ 331 | # Uncomment if necessary however generally it will be regenerated when needed 332 | #!**/[Pp]ackages/repositories.config 333 | # NuGet v3's project.json files produces more ignorable files 334 | *.nuget.props 335 | *.nuget.targets 336 | 337 | # Microsoft Azure Build Output 338 | csx/ 339 | *.build.csdef 340 | 341 | # Microsoft Azure Emulator 342 | ecf/ 343 | rcf/ 344 | 345 | # Windows Store app package directories and files 346 | AppPackages/ 347 | BundleArtifacts/ 348 | Package.StoreAssociation.xml 349 | _pkginfo.txt 350 | *.appx 351 | *.appxbundle 352 | *.appxupload 353 | 354 | # Visual Studio cache files 355 | # files ending in .cache can be ignored 356 | *.[Cc]ache 357 | # but keep track of directories ending in .cache 358 | !?*.[Cc]ache/ 359 | 360 | # Others 361 | ClientBin/ 362 | ~$* 363 | *.dbmdl 364 | *.dbproj.schemaview 365 | *.jfm 366 | *.pfx 367 | *.publishsettings 368 | orleans.codegen.cs 369 | 370 | # Including strong name files can present a security risk 371 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 372 | #*.snk 373 | 374 | # Since there are multiple workflows, uncomment next line to ignore bower_components 375 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 376 | #bower_components/ 377 | 378 | # RIA/Silverlight projects 379 | Generated_Code/ 380 | 381 | # Backup & report files from converting an old project file 382 | # to a newer Visual Studio version. Backup files are not needed, 383 | # because we have git ;-) 384 | _UpgradeReport_Files/ 385 | Backup*/ 386 | UpgradeLog*.XML 387 | UpgradeLog*.htm 388 | ServiceFabricBackup/ 389 | *.rptproj.bak 390 | 391 | # SQL Server files 392 | *.mdf 393 | *.ldf 394 | *.ndf 395 | 396 | # Business Intelligence projects 397 | *.rdl.data 398 | *.bim.layout 399 | *.bim_*.settings 400 | *.rptproj.rsuser 401 | *- [Bb]ackup.rdl 402 | *- [Bb]ackup ([0-9]).rdl 403 | *- [Bb]ackup ([0-9][0-9]).rdl 404 | 405 | # Microsoft Fakes 406 | FakesAssemblies/ 407 | 408 | # GhostDoc plugin setting file 409 | *.GhostDoc.xml 410 | 411 | # Node.js Tools for Visual Studio 412 | .ntvs_analysis.dat 413 | node_modules/ 414 | 415 | # Visual Studio 6 build log 416 | *.plg 417 | 418 | # Visual Studio 6 workspace options file 419 | *.opt 420 | 421 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 422 | *.vbw 423 | 424 | # Visual Studio LightSwitch build output 425 | **/*.HTMLClient/GeneratedArtifacts 426 | **/*.DesktopClient/GeneratedArtifacts 427 | **/*.DesktopClient/ModelManifest.xml 428 | **/*.Server/GeneratedArtifacts 429 | **/*.Server/ModelManifest.xml 430 | _Pvt_Extensions 431 | 432 | # Paket dependency manager 433 | .paket/paket.exe 434 | paket-files/ 435 | 436 | # FAKE - F# Make 437 | .fake/ 438 | 439 | # CodeRush personal settings 440 | .cr/personal 441 | 442 | # Python Tools for Visual Studio (PTVS) 443 | __pycache__/ 444 | *.pyc 445 | 446 | # Cake - Uncomment if you are using it 447 | # tools/** 448 | # !tools/packages.config 449 | 450 | # Tabs Studio 451 | *.tss 452 | 453 | # Telerik's JustMock configuration file 454 | *.jmconfig 455 | 456 | # BizTalk build output 457 | *.btp.cs 458 | *.btm.cs 459 | *.odx.cs 460 | *.xsd.cs 461 | 462 | # OpenCover UI analysis results 463 | OpenCover/ 464 | 465 | # Azure Stream Analytics local run output 466 | ASALocalRun/ 467 | 468 | # MSBuild Binary and Structured Log 469 | *.binlog 470 | 471 | # NVidia Nsight GPU debugger configuration file 472 | *.nvuser 473 | 474 | # MFractors (Xamarin productivity tool) working folder 475 | .mfractor/ 476 | 477 | # Local History for Visual Studio 478 | .localhistory/ 479 | 480 | # BeatPulse healthcheck temp database 481 | healthchecksdb 482 | 483 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 484 | MigrationBackup/ 485 | 486 | # Ionide (cross platform F# VS Code tools) working folder 487 | .ionide/ 488 | 489 | # Fody - auto-generated XML schema 490 | FodyWeavers.xsd 491 | 492 | ### VisualStudio Patch ### 493 | # Additional files built by Visual Studio 494 | *.tlog 495 | 496 | # End of https://www.toptal.com/developers/gitignore/api/macos,linux,windows,sublimetext,dotsettings,visualstudio,visualstudiocode,sam 497 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/README.md: -------------------------------------------------------------------------------- 1 | # DotnetToLambda.Serverless 2 | 3 | This project contains source code and supporting files for a serverless application that you can deploy with the SAM CLI. It includes the following files and folders. 4 | 5 | - src - Code for the application's Lambda function. 6 | - events - Invocation events that you can use to invoke the function. 7 | - test - Unit tests for the application code. 8 | - template.yaml - A template that defines the application's AWS resources. 9 | 10 | The application uses several AWS resources, including Lambda functions and an API Gateway API. These resources are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. 11 | 12 | If you prefer to use an integrated development environment (IDE) to build and test your application, you can use the AWS Toolkit. 13 | The AWS Toolkit is an open source plug-in for popular IDEs that uses the SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds a simplified step-through debugging experience for Lambda function code. See the following links to get started. 14 | 15 | * [CLion](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) 16 | * [GoLand](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) 17 | * [IntelliJ](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) 18 | * [WebStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) 19 | * [Rider](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) 20 | * [PhpStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) 21 | * [PyCharm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) 22 | * [RubyMine](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) 23 | * [DataGrip](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) 24 | * [VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html) 25 | * [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) 26 | 27 | ## Deploy the sample application 28 | 29 | The Serverless Application Model Command Line Interface (SAM CLI) is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. 30 | 31 | To use the SAM CLI, you need the following tools. 32 | 33 | * SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) 34 | * .NET Core - [Install .NET Core](https://www.microsoft.com/net/download) 35 | * Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community) 36 | 37 | To build and deploy your application for the first time, run the following in your shell: 38 | 39 | ```bash 40 | sam build 41 | sam deploy --guided 42 | ``` 43 | 44 | The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: 45 | 46 | * **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. 47 | * **AWS Region**: The AWS region you want to deploy your app to. 48 | * **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. 49 | * **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. 50 | * **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. 51 | 52 | You can find your API Gateway Endpoint URL in the output values displayed after deployment. 53 | 54 | ## Use the SAM CLI to build and test locally 55 | 56 | Build your application with the `sam build` command. 57 | 58 | ```bash 59 | DotnetToLambda.Serverless$ sam build 60 | ``` 61 | 62 | The SAM CLI installs dependencies defined in `src/HelloWorld.csproj`, creates a deployment package, and saves it in the `.aws-sam/build` folder. 63 | 64 | Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. 65 | 66 | Run functions locally and invoke them with the `sam local invoke` command. 67 | 68 | ```bash 69 | DotnetToLambda.Serverless$ sam local invoke HelloWorldFunction --event events/event.json 70 | ``` 71 | 72 | The SAM CLI can also emulate your application's API. Use the `sam local start-api` to run the API locally on port 3000. 73 | 74 | ```bash 75 | DotnetToLambda.Serverless$ sam local start-api 76 | DotnetToLambda.Serverless$ curl http://localhost:3000/ 77 | ``` 78 | 79 | The SAM CLI reads the application template to determine the API's routes and the functions that they invoke. The `Events` property on each function's definition includes the route and method for each path. 80 | 81 | ```yaml 82 | Events: 83 | HelloWorld: 84 | Type: Api 85 | Properties: 86 | Path: /hello 87 | Method: get 88 | ``` 89 | 90 | ## Add a resource to your application 91 | The application template uses AWS Serverless Application Model (AWS SAM) to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources such as functions, triggers, and APIs. For resources not included in [the SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use standard [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) resource types. 92 | 93 | ## Fetch, tail, and filter Lambda function logs 94 | 95 | To simplify troubleshooting, SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs generated by your deployed Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. 96 | 97 | `NOTE`: This command works for all AWS Lambda functions; not just the ones you deploy using SAM. 98 | 99 | ```bash 100 | DotnetToLambda.Serverless$ sam logs -n HelloWorldFunction --stack-name DotnetToLambda.Serverless --tail 101 | ``` 102 | 103 | You can find more information and examples about filtering Lambda function logs in the [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). 104 | 105 | ## Unit tests 106 | 107 | Tests are defined in the `test` folder in this project. 108 | 109 | ```bash 110 | DotnetToLambda.Serverless$ dotnet test test/HelloWorld.Test 111 | ``` 112 | 113 | ## Cleanup 114 | 115 | To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: 116 | 117 | ```bash 118 | aws cloudformation delete-stack --stack-name DotnetToLambda.Serverless 119 | ``` 120 | 121 | ## Resources 122 | 123 | See the [AWS SAM developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) for an introduction to SAM specification, the SAM CLI, and serverless application concepts. 124 | 125 | Next, you can use AWS Serverless Application Repository to deploy ready to use Apps that go beyond hello world samples and learn how authors developed their applications: [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) 126 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/omnisharp.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileOptions": { 3 | "excludeSearchPatterns": [ 4 | "**/bin/**/*", 5 | "**/obj/**/*" 6 | ] 7 | }, 8 | "msbuild": { 9 | "Platform": "rhel.7.2-x64" 10 | } 11 | } -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/ApplyDatabaseMigrations/ApplyDatabaseMigrations.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/ApplyDatabaseMigrations/Function.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Text.Json; 5 | using System.Threading.Tasks; 6 | 7 | using Amazon.Lambda.APIGatewayEvents; 8 | using Amazon.Lambda.Core; 9 | using DotnetToLambda.Core.ViewModels; 10 | using DotnetToLambda.Core.Models; 11 | using DotnetToLambda.Serverless.Config; 12 | using Microsoft.Extensions.DependencyInjection; 13 | using Microsoft.Extensions.Logging; 14 | using DotnetToLambda.Core.Infrastructure; 15 | using Microsoft.EntityFrameworkCore; 16 | using Microsoft.Extensions.Configuration; 17 | 18 | // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. 19 | [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] 20 | 21 | namespace ApplyDatabaseMigrations 22 | { 23 | public class Function 24 | { 25 | private readonly BookingContext _bookingContext; 26 | private readonly ILogger _logger; 27 | private readonly IConfiguration _configuration; 28 | private readonly DatabaseConnection _databaseConnection; 29 | 30 | public Function() 31 | { 32 | ServerlessConfig.ConfigureServices(); 33 | 34 | this._bookingContext= ServerlessConfig.Services.GetRequiredService(); 35 | this._logger = ServerlessConfig.Services.GetRequiredService>(); 36 | this._databaseConnection = ServerlessConfig.Services.GetRequiredService(); 37 | this._configuration = ServerlessConfig.Services.GetRequiredService(); 38 | } 39 | 40 | public void FunctionHandler(string input, ILambdaContext context) 41 | { 42 | this._logger.LogInformation("Starting database migration"); 43 | this._logger.LogInformation(this._databaseConnection.ToString()); 44 | this._logger.LogInformation(this._configuration["test"]); 45 | 46 | this._bookingContext.Database.Migrate(); 47 | 48 | this._logger.LogInformation("Migration applied"); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/ApplyDatabaseMigrations/aws-lambda-tools-defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "Information" : [ 3 | "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", 4 | "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", 5 | 6 | "dotnet lambda help", 7 | 8 | "All the command line options for the Lambda command can be specified in this file." 9 | ], 10 | 11 | "profile":"", 12 | "region" : "", 13 | "configuration": "Release", 14 | "framework" : "netcoreapp3.1", 15 | "function-runtime":"dotnetcore3.1", 16 | "function-memory-size" : 256, 17 | "function-timeout" : 30, 18 | "function-handler" : "HelloWorld::HelloWorld.Function::FunctionHandler" 19 | } 20 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/CancelBooking/CancelBooking.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/CancelBooking/Function.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Text.Json; 5 | using System.Threading.Tasks; 6 | 7 | using Amazon.Lambda.APIGatewayEvents; 8 | using Amazon.Lambda.Core; 9 | using DotnetToLambda.Core.ViewModels; 10 | using DotnetToLambda.Core.Models; 11 | using DotnetToLambda.Serverless.Config; 12 | using Microsoft.Extensions.Configuration; 13 | using Microsoft.Extensions.DependencyInjection; 14 | using Microsoft.Extensions.Logging; 15 | 16 | // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. 17 | [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] 18 | 19 | namespace CancelBooking 20 | { 21 | public class Function 22 | { 23 | private readonly IBookingRepository _bookingRepository; 24 | private readonly ILogger _logger; 25 | 26 | public Function() 27 | { 28 | ServerlessConfig.ConfigureServices(); 29 | 30 | this._bookingRepository = ServerlessConfig.Services.GetRequiredService(); 31 | this._logger = ServerlessConfig.Services.GetRequiredService>(); 32 | } 33 | 34 | public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context) 35 | { 36 | var request = JsonSerializer.Deserialize(apigProxyEvent.Body); 37 | 38 | this._logger.LogInformation("Received request to cancel a booking:"); 39 | 40 | this._logger.LogInformation(apigProxyEvent.Body); 41 | 42 | var existingBooking = await this._bookingRepository.Retrieve(request.BookingId); 43 | 44 | if (existingBooking == null) 45 | { 46 | return new APIGatewayProxyResponse 47 | { 48 | Body = JsonSerializer.Serialize(new 49 | { 50 | bookingId = request.BookingId, 51 | message = "Booking not found" 52 | }), 53 | StatusCode = 404, 54 | Headers = new Dictionary { { "Content-Type", "application/json" } } 55 | }; 56 | } 57 | 58 | this._logger.LogInformation($"Found booking, cancelling"); 59 | 60 | existingBooking.Cancel(request.Reason); 61 | 62 | await this._bookingRepository.Update(existingBooking); 63 | 64 | this._logger.LogInformation("Booking updated"); 65 | 66 | return new APIGatewayProxyResponse 67 | { 68 | Body = JsonSerializer.Serialize(existingBooking), 69 | StatusCode = 200, 70 | Headers = new Dictionary { { "Content-Type", "application/json" } } 71 | }; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/CancelBooking/aws-lambda-tools-defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "Information" : [ 3 | "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", 4 | "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", 5 | 6 | "dotnet lambda help", 7 | 8 | "All the command line options for the Lambda command can be specified in this file." 9 | ], 10 | 11 | "profile":"", 12 | "region" : "", 13 | "configuration": "Release", 14 | "framework" : "netcoreapp3.1", 15 | "function-runtime":"dotnetcore3.1", 16 | "function-memory-size" : 256, 17 | "function-timeout" : 30, 18 | "function-handler" : "HelloWorld::HelloWorld.Function::FunctionHandler" 19 | } 20 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/ConfirmBooking/ConfirmBooking.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/ConfirmBooking/Function.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Text.Json; 5 | using System.Threading.Tasks; 6 | 7 | using Amazon.Lambda.APIGatewayEvents; 8 | using Amazon.Lambda.Core; 9 | using DotnetToLambda.Core.ViewModels; 10 | using DotnetToLambda.Core.Models; 11 | using DotnetToLambda.Serverless.Config; 12 | using Microsoft.Extensions.DependencyInjection; 13 | using Microsoft.Extensions.Logging; 14 | 15 | // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. 16 | [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] 17 | 18 | namespace ConfirmBooking 19 | { 20 | public class Function 21 | { 22 | private readonly IBookingRepository _bookingRepository; 23 | private readonly ILogger _logger; 24 | 25 | public Function() 26 | { 27 | ServerlessConfig.ConfigureServices(); 28 | 29 | this._bookingRepository = ServerlessConfig.Services.GetRequiredService(); 30 | this._logger = ServerlessConfig.Services.GetRequiredService>(); 31 | } 32 | 33 | public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context) 34 | { 35 | var request = JsonSerializer.Deserialize(apigProxyEvent.Body); 36 | 37 | this._logger.LogInformation("Received request to confirm a booking:"); 38 | 39 | this._logger.LogInformation(JsonSerializer.Serialize(request)); 40 | 41 | var existingBooking = await this._bookingRepository.Retrieve(request.BookingId); 42 | 43 | if (existingBooking == null) 44 | { 45 | return new APIGatewayProxyResponse 46 | { 47 | Body = JsonSerializer.Serialize(new 48 | { 49 | bookingId = request.BookingId, 50 | message = "Booking not found" 51 | }), 52 | StatusCode = 404, 53 | Headers = new Dictionary { { "Content-Type", "application/json" } } 54 | }; 55 | } 56 | 57 | this._logger.LogInformation($"Found booking, confirmed"); 58 | 59 | existingBooking.Confirm(); 60 | 61 | await this._bookingRepository.Update(existingBooking); 62 | 63 | this._logger.LogInformation("Booking updated"); 64 | 65 | return new APIGatewayProxyResponse 66 | { 67 | Body = JsonSerializer.Serialize(existingBooking), 68 | StatusCode = 200, 69 | Headers = new Dictionary { { "Content-Type", "application/json" } } 70 | }; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/ConfirmBooking/aws-lambda-tools-defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "Information" : [ 3 | "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", 4 | "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", 5 | 6 | "dotnet lambda help", 7 | 8 | "All the command line options for the Lambda command can be specified in this file." 9 | ], 10 | 11 | "profile":"", 12 | "region" : "", 13 | "configuration": "Release", 14 | "framework" : "netcoreapp3.1", 15 | "function-runtime":"dotnetcore3.1", 16 | "function-memory-size" : 256, 17 | "function-timeout" : 30, 18 | "function-handler" : "HelloWorld::HelloWorld.Function::FunctionHandler" 19 | } 20 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/ListForCustomer/Function.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Text.Json; 5 | using System.Threading.Tasks; 6 | 7 | using Amazon.Lambda.APIGatewayEvents; 8 | using Amazon.Lambda.Core; 9 | using DotnetToLambda.Core.ViewModels; 10 | using DotnetToLambda.Core.Models; 11 | using DotnetToLambda.Serverless.Config; 12 | using Microsoft.Extensions.DependencyInjection; 13 | using Microsoft.Extensions.Logging; 14 | 15 | // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. 16 | [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] 17 | 18 | namespace ListForCustomer 19 | { 20 | public class Function 21 | { 22 | private readonly IBookingRepository _bookingRepository; 23 | private readonly ILogger _logger; 24 | 25 | public Function() 26 | { 27 | ServerlessConfig.ConfigureServices(); 28 | 29 | this._bookingRepository = ServerlessConfig.Services.GetRequiredService(); 30 | this._logger = ServerlessConfig.Services.GetRequiredService>(); 31 | } 32 | 33 | public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context) 34 | { 35 | if (!apigProxyEvent.PathParameters.ContainsKey("customerId")) 36 | { 37 | return new APIGatewayProxyResponse 38 | { 39 | StatusCode = 400, 40 | Headers = new Dictionary { { "Content-Type", "application/json" } } 41 | }; 42 | } 43 | 44 | var customerId = apigProxyEvent.PathParameters["customerId"]; 45 | 46 | this._logger.LogInformation($"Received request to list bookings for: {customerId}"); 47 | 48 | var customerBookings = await this._bookingRepository.ListForCustomer(customerId); 49 | 50 | return new APIGatewayProxyResponse 51 | { 52 | Body = JsonSerializer.Serialize(customerBookings), 53 | StatusCode = 200, 54 | Headers = new Dictionary { { "Content-Type", "application/json" } } 55 | }; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/ListForCustomer/ListForCustomer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/ListForCustomer/aws-lambda-tools-defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "Information" : [ 3 | "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", 4 | "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", 5 | 6 | "dotnet lambda help", 7 | 8 | "All the command line options for the Lambda command can be specified in this file." 9 | ], 10 | 11 | "profile":"", 12 | "region" : "", 13 | "configuration": "Release", 14 | "framework" : "netcoreapp3.1", 15 | "function-runtime":"dotnetcore3.1", 16 | "function-memory-size" : 256, 17 | "function-timeout" : 30, 18 | "function-handler" : "HelloWorld::HelloWorld.Function::FunctionHandler" 19 | } 20 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/ReserveBooking/Function.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Text.Json; 5 | using System.Threading.Tasks; 6 | 7 | using Amazon.Lambda.APIGatewayEvents; 8 | using Amazon.Lambda.Core; 9 | using DotnetToLambda.Core.Exceptions; 10 | using DotnetToLambda.Core.ViewModels; 11 | using DotnetToLambda.Core.Models; 12 | using DotnetToLambda.Core.Services; 13 | using DotnetToLambda.Serverless.Config; 14 | using Microsoft.Extensions.DependencyInjection; 15 | using Microsoft.Extensions.Logging; 16 | 17 | // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. 18 | [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] 19 | 20 | namespace ReserveBooking 21 | { 22 | public class Function 23 | { 24 | private readonly IBookingRepository _bookingRepository; 25 | private readonly ILogger _logger; 26 | private readonly ICustomerService _customerService; 27 | 28 | public Function() 29 | { 30 | ServerlessConfig.ConfigureServices(); 31 | 32 | this._bookingRepository = ServerlessConfig.Services.GetRequiredService(); 33 | this._logger = ServerlessConfig.Services.GetRequiredService>(); 34 | this._customerService = ServerlessConfig.Services.GetRequiredService(); 35 | } 36 | 37 | public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context) 38 | { 39 | var request = JsonSerializer.Deserialize(apigProxyEvent.Body); 40 | 41 | this._logger.LogInformation("Received request to reserve a new booking:"); 42 | 43 | this._logger.LogInformation(JsonSerializer.Serialize(request)); 44 | 45 | if (!await this._customerService.CustomerExists(request.CustomerId)) 46 | { 47 | this._logger.LogWarning($"Customer {request.CustomerId} does not exist in the customer service"); 48 | 49 | return new APIGatewayProxyResponse 50 | { 51 | Body = "{\"message\": \"Customer not found\"}", 52 | StatusCode = 400, 53 | Headers = new Dictionary { { "Content-Type", "application/json" } } 54 | }; 55 | } 56 | 57 | var booking = Booking.Create(Guid.NewGuid().ToString(), request.CustomerId, request.OutboundFlightId, request.ChargeId); 58 | 59 | this._logger.LogInformation($"Booking created with Id ${booking.BookingId}"); 60 | 61 | try 62 | { 63 | await this._bookingRepository.Add(booking); 64 | } 65 | catch (DuplicateRequestException ex) 66 | { 67 | return new APIGatewayProxyResponse 68 | { 69 | Body = JsonSerializer.Serialize(ex.DuplicateBooking), 70 | StatusCode = 200, 71 | Headers = new Dictionary { { "Content-Type", "application/json" } } 72 | }; 73 | } 74 | 75 | this._logger.LogInformation("Booking added"); 76 | 77 | return new APIGatewayProxyResponse 78 | { 79 | Body = JsonSerializer.Serialize(booking), 80 | StatusCode = 200, 81 | Headers = new Dictionary { { "Content-Type", "application/json" } } 82 | }; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/ReserveBooking/ReserveBooking.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/ReserveBooking/aws-lambda-tools-defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "Information" : [ 3 | "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", 4 | "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", 5 | 6 | "dotnet lambda help", 7 | 8 | "All the command line options for the Lambda command can be specified in this file." 9 | ], 10 | 11 | "profile":"", 12 | "region" : "", 13 | "configuration": "Release", 14 | "framework" : "netcoreapp3.1", 15 | "function-runtime":"dotnetcore3.1", 16 | "function-memory-size" : 256, 17 | "function-timeout" : 30, 18 | "function-handler" : "HelloWorld::HelloWorld.Function::FunctionHandler" 19 | } 20 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/Retrieve/Function.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Text.Json; 5 | using System.Threading.Tasks; 6 | 7 | using Amazon.Lambda.APIGatewayEvents; 8 | using Amazon.Lambda.Core; 9 | using DotnetToLambda.Core.ViewModels; 10 | using DotnetToLambda.Core.Models; 11 | using DotnetToLambda.Serverless.Config; 12 | using Microsoft.Extensions.DependencyInjection; 13 | using Microsoft.Extensions.Logging; 14 | 15 | // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. 16 | [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] 17 | 18 | namespace Retrieve 19 | { 20 | public class Function 21 | { 22 | private readonly IBookingRepository _bookingRepository; 23 | private readonly ILogger _logger; 24 | 25 | public Function() 26 | { 27 | ServerlessConfig.ConfigureServices(); 28 | 29 | this._bookingRepository = ServerlessConfig.Services.GetRequiredService(); 30 | this._logger = ServerlessConfig.Services.GetRequiredService>(); 31 | } 32 | 33 | public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context) 34 | { 35 | if (!apigProxyEvent.PathParameters.ContainsKey("bookingId")) 36 | { 37 | return new APIGatewayProxyResponse 38 | { 39 | StatusCode = 400, 40 | Headers = new Dictionary { { "Content-Type", "application/json" } } 41 | }; 42 | } 43 | 44 | var bookingId = apigProxyEvent.PathParameters["bookingId"]; 45 | 46 | this._logger.LogInformation($"Received request for booking {bookingId}"); 47 | 48 | var booking = await this._bookingRepository.Retrieve(bookingId); 49 | 50 | if (booking == null) 51 | { 52 | return new APIGatewayProxyResponse 53 | { 54 | Body = JsonSerializer.Serialize(new 55 | { 56 | bookingId = bookingId, 57 | message = "Booking not found" 58 | }), 59 | StatusCode = 404, 60 | Headers = new Dictionary { { "Content-Type", "application/json" } } 61 | }; 62 | } 63 | 64 | return new APIGatewayProxyResponse 65 | { 66 | Body = JsonSerializer.Serialize(booking), 67 | StatusCode = 200, 68 | Headers = new Dictionary { { "Content-Type", "application/json" } } 69 | }; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/Retrieve/Retrieve.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/src/Retrieve/aws-lambda-tools-defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "Information" : [ 3 | "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", 4 | "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", 5 | 6 | "dotnet lambda help", 7 | 8 | "All the command line options for the Lambda command can be specified in this file." 9 | ], 10 | 11 | "profile":"", 12 | "region" : "", 13 | "configuration": "Release", 14 | "framework" : "netcoreapp3.1", 15 | "function-runtime":"dotnetcore3.1", 16 | "function-memory-size" : 256, 17 | "function-timeout" : 30, 18 | "function-handler" : "HelloWorld::HelloWorld.Function::FunctionHandler" 19 | } 20 | -------------------------------------------------------------------------------- /src/serverless/DotnetToLambda.Serverless/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | 4 | Globals: 5 | Function: 6 | Timeout: 30 7 | Runtime: dotnetcore3.1 8 | Architectures: 9 | - x86_64 10 | MemorySize: 256 11 | Environment: 12 | Variables: 13 | SERVICE: bookings 14 | DATABASE_CONNECTION_SECRET_ID: !Ref SecretArn 15 | VpcConfig: 16 | SubnetIds: 17 | - !Ref PrivateSubnet1 18 | - !Ref PrivateSubnet2 19 | SecurityGroupIds: 20 | - !Ref SecurityGroup 21 | AutoPublishAlias: live 22 | DeploymentPreference: 23 | Type: Canary10Percent5Minutes 24 | 25 | Parameters: 26 | EnvironmentName: 27 | Description: An environment name that is prefixed to resource names 28 | Type: String 29 | Default: dotnet-to-lambda 30 | SecretArn: 31 | Description: 'Required. Database secret ARN' 32 | Type: 'String' 33 | PrivateSubnet1: 34 | Description: 'Required. Private subnet 1. Output from cdk deploy' 35 | Type: 'String' 36 | PrivateSubnet2: 37 | Description: 'Required. Private subnet 2. Output from cdk deploy' 38 | Type: 'String' 39 | SecurityGroup: 40 | Description: 'Required. Security group. Output from cdk deploy' 41 | Type: 'String' 42 | 43 | Resources: 44 | ReserveBookingFunction: 45 | Type: AWS::Serverless::Function 46 | Properties: 47 | CodeUri: ./src/ReserveBooking/ 48 | Handler: ReserveBooking::ReserveBooking.Function::FunctionHandler 49 | Policies: 50 | - AWSSecretsManagerGetSecretValuePolicy: 51 | SecretArn: !Ref SecretArn 52 | - SSMParameterReadPolicy: 53 | ParameterName: dotnet-to-lambda-dev 54 | Events: 55 | ReserveBookingApi: 56 | Type: Api 57 | Properties: 58 | Path: /booking/reserve 59 | Method: post 60 | 61 | CancelBookingFunction: 62 | Type: AWS::Serverless::Function 63 | Properties: 64 | CodeUri: ./src/CancelBooking/ 65 | Handler: CancelBooking::CancelBooking.Function::FunctionHandler 66 | Policies: 67 | - AWSSecretsManagerGetSecretValuePolicy: 68 | SecretArn: !Ref SecretArn 69 | - SSMParameterReadPolicy: 70 | ParameterName: dotnet-to-lambda-dev 71 | Events: 72 | CancelBookingApi: 73 | Type: Api 74 | Properties: 75 | Path: /booking/cancel 76 | Method: put 77 | 78 | ConfirmBookingFunction: 79 | Type: AWS::Serverless::Function 80 | Properties: 81 | CodeUri: ./src/ConfirmBooking/ 82 | Handler: ConfirmBooking::ConfirmBooking.Function::FunctionHandler 83 | Policies: 84 | - AWSSecretsManagerGetSecretValuePolicy: 85 | SecretArn: !Ref SecretArn 86 | - SSMParameterReadPolicy: 87 | ParameterName: dotnet-to-lambda-dev 88 | Events: 89 | ConfirmBookingApi: 90 | Type: Api 91 | Properties: 92 | Path: /booking/confirm 93 | Method: post 94 | 95 | ListForCustomerFunction: 96 | Type: AWS::Serverless::Function 97 | Properties: 98 | CodeUri: ./src/ListForCustomer/ 99 | Handler: ListForCustomer::ListForCustomer.Function::FunctionHandler 100 | Policies: 101 | - AWSSecretsManagerGetSecretValuePolicy: 102 | SecretArn: !Ref SecretArn 103 | - SSMParameterReadPolicy: 104 | ParameterName: dotnet-to-lambda-dev 105 | Events: 106 | ListForCustomerApi: 107 | Type: Api 108 | Properties: 109 | Path: /customer/{customerId} 110 | Method: get 111 | 112 | RetrieveFunction: 113 | Type: AWS::Serverless::Function 114 | Properties: 115 | CodeUri: ./src/Retrieve/ 116 | Handler: Retrieve::Retrieve.Function::FunctionHandler 117 | Policies: 118 | - AWSSecretsManagerGetSecretValuePolicy: 119 | SecretArn: !Ref SecretArn 120 | - SSMParameterReadPolicy: 121 | ParameterName: dotnet-to-lambda-dev 122 | Events: 123 | RetrieveApi: 124 | Type: Api 125 | Properties: 126 | Path: /booking/{bookingId} 127 | Method: get 128 | 129 | ApplyDatabaseMigrationsFunction: 130 | Type: AWS::Serverless::Function 131 | Properties: 132 | CodeUri: ./src/ApplyDatabaseMigrations/ 133 | Handler: ApplyDatabaseMigrations::ApplyDatabaseMigrations.Function::FunctionHandler 134 | Policies: 135 | - AWSSecretsManagerGetSecretValuePolicy: 136 | SecretArn: !Ref SecretArn 137 | - SSMParameterReadPolicy: 138 | ParameterName: dotnet-to-lambda-dev 139 | 140 | Outputs: 141 | BookingApi: 142 | Description: "API Gateway endpoint URL for Prod stage for reserving bookings" 143 | Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod" 144 | --------------------------------------------------------------------------------