From 6827e0221eee6adf75fa6e14a0f0ea2d1410dd38 Mon Sep 17 00:00:00 2001 From: Renato Golia Date: Mon, 31 Oct 2022 14:40:57 +0100 Subject: [PATCH] Preparation for 5.0 (#24) --- AWSLambdaSharpTemplate.sln | 132 +++--- README.md | 144 +++++- appveyor.yml | 2 + samples/EventFunction/EventFunction.csproj | 2 +- samples/EventFunction/Function.cs | 63 ++- .../aws-lambda-tools-defaults.json | 2 +- samples/RequestResponseFunction/Function.cs | 63 ++- .../RequestResponseFunction.csproj | 2 +- .../aws-lambda-tools-defaults.json | 2 +- samples/SnsEventFunction/Function.cs | 67 ++- .../SnsEventFunction/SnsEventFunction.csproj | 2 +- .../aws-lambda-tools-defaults.json | 2 +- .../Function.cs | 69 ++- .../SnsEventFunctionWithParallelism.csproj | 2 +- .../aws-lambda-tools-defaults.json | 4 +- samples/SqsEventFunction/Function.cs | 61 +++ .../SqsEventFunction/SqsEventFunction.csproj | 19 + .../aws-lambda-tools-defaults.json | 21 + .../Function.cs | 61 +++ .../SqsEventFunctionWithParallelism.csproj | 19 + .../aws-lambda-tools-defaults.json | 21 + .../INotificationHandler.cs | 19 +- .../INotificationSerializer.cs | 33 +- .../Kralizek.Lambda.Template.Sns.csproj | 1 + .../ParallelSnsEventHandler.cs | 97 ++-- .../ServiceCollectionExtensions.cs | 157 ++++--- .../SnsEventHandler.cs | 68 +-- .../IMessageHandler.cs | 15 +- .../IMessageSerializer.cs | 33 +- .../Kralizek.Lambda.Template.Sqs.csproj | 1 + .../ParallelSqsEventHandler.cs | 95 ++-- .../ServiceCollectionExtensions.cs | 157 ++++--- .../SqsEventHandler.cs | 65 +-- .../AsyncExtensions.cs | 45 +- src/Kralizek.Lambda.Template/EventFunction.cs | 65 ++- src/Kralizek.Lambda.Template/Function.cs | 89 ++-- .../IExecutionEnvironment.cs | 69 ++- .../Kralizek.Lambda.Template.csproj | 1 + .../RequestResponseFunction.cs | 72 ++- .../EmptyEventFunction.csproj | 5 +- .../content/EmptyEventFunction/Function.cs | 33 +- .../EmptyEventFunction/StringEventHandler.cs | 25 +- .../aws-lambda-tools-defaults.json | 4 +- .../EmptyRequestResponseFunction.csproj | 5 +- .../EmptyRequestResponseFunction/Function.cs | 31 +- .../ToUpperStringRequestResponseHandler.cs | 27 +- .../aws-lambda-tools-defaults.json | 4 +- .../content/RichEventFunction/Function.cs | 67 ++- .../RichEventFunction.csproj | 16 +- .../RichEventFunction/StringEventHandler.cs | 29 +- .../aws-lambda-tools-defaults.json | 4 +- .../RichRequestResponseFunction/Function.cs | 65 ++- .../RichRequestResponseFunction.csproj | 16 +- .../ToUpperStringRequestResponseHandler.cs | 27 +- .../aws-lambda-tools-defaults.json | 4 +- .../content/SnsEventFunction/Function.cs | 31 +- .../content/SnsEventFunction/Notification.cs | 21 +- .../SnsEventFunction/NotificationHandler.cs | 25 +- .../SnsEventFunction/SnsEventFunction.csproj | 7 +- .../aws-lambda-tools-defaults.json | 4 +- .../content/SqsEventFunction/Function.cs | 31 +- .../SqsEventFunction/SqsEventFunction.csproj | 7 +- .../content/SqsEventFunction/TestMessage.cs | 21 +- .../SqsEventFunction/TestMessageHandler.cs | 25 +- .../aws-lambda-tools-defaults.json | 4 +- .../CustomSerializerTests.cs | 149 +++--- .../EventFunctionDisposalTests.cs | 89 ++-- .../EventFunctionTests.cs | 84 ++-- .../RequestResponseFunctionDisposalTests.cs | 89 ++-- .../RequestResponseFunctionTests.cs | 84 ++-- .../Sns/ParallelSnsEventHandlerTests.cs | 440 +++++++++--------- .../Sns/ServiceCollectionExtensionsTests.cs | 131 +++--- .../Sns/SnsEventHandlerDisposalTests.cs | 175 ++++--- .../Sns/SnsEventHandlerTests.cs | 249 +++++----- .../Sns/TestNotification.cs | 15 +- .../Sqs/ServiceCollectionExtensionsTests.cs | 172 +++---- .../Sqs/SqsEventHandlerDisposalTests.cs | 169 ++++--- .../Sqs/SqsEventHandlerTests.cs | 238 +++++----- .../Sqs/SqsForEachAsyncEventHandlerTests.cs | 381 ++++++++------- .../Sqs/TestNotification.cs | 15 +- 80 files changed, 2704 insertions(+), 2156 deletions(-) create mode 100644 samples/SqsEventFunction/Function.cs create mode 100644 samples/SqsEventFunction/SqsEventFunction.csproj create mode 100644 samples/SqsEventFunction/aws-lambda-tools-defaults.json create mode 100644 samples/SqsEventFunctionWithParallelism/Function.cs create mode 100644 samples/SqsEventFunctionWithParallelism/SqsEventFunctionWithParallelism.csproj create mode 100644 samples/SqsEventFunctionWithParallelism/aws-lambda-tools-defaults.json diff --git a/AWSLambdaSharpTemplate.sln b/AWSLambdaSharpTemplate.sln index 27dad59..6ad29d2 100644 --- a/AWSLambdaSharpTemplate.sln +++ b/AWSLambdaSharpTemplate.sln @@ -1,82 +1,94 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29926.136 -MinimumVisualStudioVersion = 15.0.26124.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{42BE6308-5AC6-40B3-96DA-1FF015AF533E}" + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{3D496921-7E86-44BE-AF72-9786A924052A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kralizek.Lambda.Template", "src\Kralizek.Lambda.Template\Kralizek.Lambda.Template.csproj", "{9DDBE0B9-6C3C-4A87-89B2-D4BA074BE90E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventFunction", "samples\EventFunction\EventFunction.csproj", "{42D6C3FB-246F-4CF8-9B72-3431FF78C845}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{A973E126-E744-44E4-A27C-6F63993AC642}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RequestResponseFunction", "samples\RequestResponseFunction\RequestResponseFunction.csproj", "{71875B94-7313-4F3B-BC05-996DD51A7314}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Lambda.Template", "tests\Tests.Lambda.Template\Tests.Lambda.Template.csproj", "{D0263D83-E1A0-46EB-9B82-9793A4D89590}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SnsEventFunction", "samples\SnsEventFunction\SnsEventFunction.csproj", "{BDAC13DE-DF28-4C36-8857-463963C202C4}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{B9F8C8E0-047D-4F38-8F7D-E64F39E51988}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SnsEventFunctionWithParallelism", "samples\SnsEventFunctionWithParallelism\SnsEventFunctionWithParallelism.csproj", "{E93EC39F-7934-46CE-8FD1-6D882576D66D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventFunction", "samples\EventFunction\EventFunction.csproj", "{3783641F-A1E9-42A7-A7EE-99B8E8F4DB08}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqsEventFunction", "samples\SqsEventFunction\SqsEventFunction.csproj", "{DF86D670-8E2F-40B8-899E-E544B50C4024}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RequestResponseFunction", "samples\RequestResponseFunction\RequestResponseFunction.csproj", "{9FAE7942-AAB6-4A33-8751-F7973F928858}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqsEventFunctionWithParallelism", "samples\SqsEventFunctionWithParallelism\SqsEventFunctionWithParallelism.csproj", "{EC8A27C9-363C-434A-BEBE-0955E1BB40FC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kralizek.Lambda.Template.Sns", "src\Kralizek.Lambda.Template.Sns\Kralizek.Lambda.Template.Sns.csproj", "{8DFE9840-FEF4-4E39-89FD-80E968E3C057}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{AF4300F6-9636-4925-B8D5-F98E1BDC5EF5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SnsEventFunction", "samples\SnsEventFunction\SnsEventFunction.csproj", "{08E76E38-D49D-4C15-8349-FC23EB37F0DF}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kralizek.Lambda.Template", "src\Kralizek.Lambda.Template\Kralizek.Lambda.Template.csproj", "{D092A193-823E-4205-872B-E3060BC3CF1A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kralizek.Lambda.Template.Sqs", "src\Kralizek.Lambda.Template.Sqs\Kralizek.Lambda.Template.Sqs.csproj", "{282BE189-9DE6-4A03-B5A4-ADBF4C45D21B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kralizek.Lambda.Template.Sns", "src\Kralizek.Lambda.Template.Sns\Kralizek.Lambda.Template.Sns.csproj", "{5574076F-7D6D-4AD2-9231-76A18D2A85D7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SnsEventFunctionWithParallelism", "samples\SnsEventFunctionWithParallelism\SnsEventFunctionWithParallelism.csproj", "{3E986948-21D5-4035-AE12-F669FA488D17}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kralizek.Lambda.Template.Sqs", "src\Kralizek.Lambda.Template.Sqs\Kralizek.Lambda.Template.Sqs.csproj", "{F9D3C337-5936-4BCB-BA2F-03A9E7C148DD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7025A861-EB1B-4B7C-A193-33590F6863BD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Lambda.Template", "tests\Tests.Lambda.Template\Tests.Lambda.Template.csproj", "{518662EF-B287-4F24-9833-EABBC6CF6A82}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9DDBE0B9-6C3C-4A87-89B2-D4BA074BE90E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9DDBE0B9-6C3C-4A87-89B2-D4BA074BE90E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9DDBE0B9-6C3C-4A87-89B2-D4BA074BE90E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9DDBE0B9-6C3C-4A87-89B2-D4BA074BE90E}.Release|Any CPU.Build.0 = Release|Any CPU - {D0263D83-E1A0-46EB-9B82-9793A4D89590}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D0263D83-E1A0-46EB-9B82-9793A4D89590}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D0263D83-E1A0-46EB-9B82-9793A4D89590}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D0263D83-E1A0-46EB-9B82-9793A4D89590}.Release|Any CPU.Build.0 = Release|Any CPU - {3783641F-A1E9-42A7-A7EE-99B8E8F4DB08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3783641F-A1E9-42A7-A7EE-99B8E8F4DB08}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3783641F-A1E9-42A7-A7EE-99B8E8F4DB08}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3783641F-A1E9-42A7-A7EE-99B8E8F4DB08}.Release|Any CPU.Build.0 = Release|Any CPU - {9FAE7942-AAB6-4A33-8751-F7973F928858}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9FAE7942-AAB6-4A33-8751-F7973F928858}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9FAE7942-AAB6-4A33-8751-F7973F928858}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9FAE7942-AAB6-4A33-8751-F7973F928858}.Release|Any CPU.Build.0 = Release|Any CPU - {8DFE9840-FEF4-4E39-89FD-80E968E3C057}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8DFE9840-FEF4-4E39-89FD-80E968E3C057}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8DFE9840-FEF4-4E39-89FD-80E968E3C057}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8DFE9840-FEF4-4E39-89FD-80E968E3C057}.Release|Any CPU.Build.0 = Release|Any CPU - {08E76E38-D49D-4C15-8349-FC23EB37F0DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {08E76E38-D49D-4C15-8349-FC23EB37F0DF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {08E76E38-D49D-4C15-8349-FC23EB37F0DF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {08E76E38-D49D-4C15-8349-FC23EB37F0DF}.Release|Any CPU.Build.0 = Release|Any CPU - {282BE189-9DE6-4A03-B5A4-ADBF4C45D21B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {282BE189-9DE6-4A03-B5A4-ADBF4C45D21B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {282BE189-9DE6-4A03-B5A4-ADBF4C45D21B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {282BE189-9DE6-4A03-B5A4-ADBF4C45D21B}.Release|Any CPU.Build.0 = Release|Any CPU - {3E986948-21D5-4035-AE12-F669FA488D17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3E986948-21D5-4035-AE12-F669FA488D17}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E986948-21D5-4035-AE12-F669FA488D17}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3E986948-21D5-4035-AE12-F669FA488D17}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {9DDBE0B9-6C3C-4A87-89B2-D4BA074BE90E} = {42BE6308-5AC6-40B3-96DA-1FF015AF533E} - {D0263D83-E1A0-46EB-9B82-9793A4D89590} = {A973E126-E744-44E4-A27C-6F63993AC642} - {3783641F-A1E9-42A7-A7EE-99B8E8F4DB08} = {B9F8C8E0-047D-4F38-8F7D-E64F39E51988} - {9FAE7942-AAB6-4A33-8751-F7973F928858} = {B9F8C8E0-047D-4F38-8F7D-E64F39E51988} - {8DFE9840-FEF4-4E39-89FD-80E968E3C057} = {42BE6308-5AC6-40B3-96DA-1FF015AF533E} - {08E76E38-D49D-4C15-8349-FC23EB37F0DF} = {B9F8C8E0-047D-4F38-8F7D-E64F39E51988} - {282BE189-9DE6-4A03-B5A4-ADBF4C45D21B} = {42BE6308-5AC6-40B3-96DA-1FF015AF533E} - {3E986948-21D5-4035-AE12-F669FA488D17} = {B9F8C8E0-047D-4F38-8F7D-E64F39E51988} + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {42D6C3FB-246F-4CF8-9B72-3431FF78C845}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42D6C3FB-246F-4CF8-9B72-3431FF78C845}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42D6C3FB-246F-4CF8-9B72-3431FF78C845}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42D6C3FB-246F-4CF8-9B72-3431FF78C845}.Release|Any CPU.Build.0 = Release|Any CPU + {71875B94-7313-4F3B-BC05-996DD51A7314}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71875B94-7313-4F3B-BC05-996DD51A7314}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71875B94-7313-4F3B-BC05-996DD51A7314}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71875B94-7313-4F3B-BC05-996DD51A7314}.Release|Any CPU.Build.0 = Release|Any CPU + {BDAC13DE-DF28-4C36-8857-463963C202C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BDAC13DE-DF28-4C36-8857-463963C202C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BDAC13DE-DF28-4C36-8857-463963C202C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BDAC13DE-DF28-4C36-8857-463963C202C4}.Release|Any CPU.Build.0 = Release|Any CPU + {E93EC39F-7934-46CE-8FD1-6D882576D66D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E93EC39F-7934-46CE-8FD1-6D882576D66D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E93EC39F-7934-46CE-8FD1-6D882576D66D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E93EC39F-7934-46CE-8FD1-6D882576D66D}.Release|Any CPU.Build.0 = Release|Any CPU + {DF86D670-8E2F-40B8-899E-E544B50C4024}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF86D670-8E2F-40B8-899E-E544B50C4024}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF86D670-8E2F-40B8-899E-E544B50C4024}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF86D670-8E2F-40B8-899E-E544B50C4024}.Release|Any CPU.Build.0 = Release|Any CPU + {EC8A27C9-363C-434A-BEBE-0955E1BB40FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC8A27C9-363C-434A-BEBE-0955E1BB40FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC8A27C9-363C-434A-BEBE-0955E1BB40FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC8A27C9-363C-434A-BEBE-0955E1BB40FC}.Release|Any CPU.Build.0 = Release|Any CPU + {D092A193-823E-4205-872B-E3060BC3CF1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D092A193-823E-4205-872B-E3060BC3CF1A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D092A193-823E-4205-872B-E3060BC3CF1A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D092A193-823E-4205-872B-E3060BC3CF1A}.Release|Any CPU.Build.0 = Release|Any CPU + {5574076F-7D6D-4AD2-9231-76A18D2A85D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5574076F-7D6D-4AD2-9231-76A18D2A85D7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5574076F-7D6D-4AD2-9231-76A18D2A85D7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5574076F-7D6D-4AD2-9231-76A18D2A85D7}.Release|Any CPU.Build.0 = Release|Any CPU + {F9D3C337-5936-4BCB-BA2F-03A9E7C148DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F9D3C337-5936-4BCB-BA2F-03A9E7C148DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F9D3C337-5936-4BCB-BA2F-03A9E7C148DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F9D3C337-5936-4BCB-BA2F-03A9E7C148DD}.Release|Any CPU.Build.0 = Release|Any CPU + {518662EF-B287-4F24-9833-EABBC6CF6A82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {518662EF-B287-4F24-9833-EABBC6CF6A82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {518662EF-B287-4F24-9833-EABBC6CF6A82}.Release|Any CPU.ActiveCfg = Release|Any CPU + {518662EF-B287-4F24-9833-EABBC6CF6A82}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {CE8B1DF0-D1A8-43E0-A6D5-305B4EF727B5} + GlobalSection(NestedProjects) = preSolution + {42D6C3FB-246F-4CF8-9B72-3431FF78C845} = {3D496921-7E86-44BE-AF72-9786A924052A} + {71875B94-7313-4F3B-BC05-996DD51A7314} = {3D496921-7E86-44BE-AF72-9786A924052A} + {BDAC13DE-DF28-4C36-8857-463963C202C4} = {3D496921-7E86-44BE-AF72-9786A924052A} + {E93EC39F-7934-46CE-8FD1-6D882576D66D} = {3D496921-7E86-44BE-AF72-9786A924052A} + {DF86D670-8E2F-40B8-899E-E544B50C4024} = {3D496921-7E86-44BE-AF72-9786A924052A} + {EC8A27C9-363C-434A-BEBE-0955E1BB40FC} = {3D496921-7E86-44BE-AF72-9786A924052A} + {D092A193-823E-4205-872B-E3060BC3CF1A} = {AF4300F6-9636-4925-B8D5-F98E1BDC5EF5} + {5574076F-7D6D-4AD2-9231-76A18D2A85D7} = {AF4300F6-9636-4925-B8D5-F98E1BDC5EF5} + {F9D3C337-5936-4BCB-BA2F-03A9E7C148DD} = {AF4300F6-9636-4925-B8D5-F98E1BDC5EF5} + {518662EF-B287-4F24-9833-EABBC6CF6A82} = {7025A861-EB1B-4B7C-A193-33590F6863BD} EndGlobalSection EndGlobal diff --git a/README.md b/README.md index c459827..034882e 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ Unless specific use cases, `TOutput` should always be your type, never `Task` to be registered in `ConfigureServices`. This is the signature of `IRequestResponseHandler` + ```csharp public interface IRequestResponseHandler { @@ -66,40 +67,140 @@ public interface IRequestResponseHandler An Event function represents a function that is not expected to produce any value. -To create an Event function, simply change your class so that it inherits from `EventFunction` where `TInput`is a class representing the incoming data. +To create an Event function, simply change your class so that it inherits from `EventFunction` where `TInput` is a class representing the incoming data. An Event function requires an handler that implements `IEventHandler` to be registered in `ConfigureServices`. This is the signature of `IEventHandler` + ```csharp public interface IEventHandler { Task HandleAsync(TInput input, ILambdaContext context); } ``` + [Here](https://github.com/Kralizek/AWSLambdaSharpTemplate/tree/master/samples/EventFunction) you can find a sample that shows an Event function that accepts an input string and logs it into CloudWatch logs. -## Custom serializers +The library offers special support for two classes of Event functions: functions that handle SQS and SNS messages. + +### Event functions handling SNS notifications + +AWS Lambda functions can be used to handle SNS notifications. + +Since all SNS notifications have the same structure, the package `Kralizek.Lambda.Template.Sns` can be used to speed up the development of functions that handle SNS notifications. + +To do so, create a class that implements the interface `INotificationHandler`, then change the `ConfigureServices` method to look like the following snippet. -You can pass a custom serializer to the Handler registration to override the default behavior of parsing all messages as JSON. ```csharp +protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) +{ + services.UseNotificationHandler(); +} +``` + +#### Parallel execution of SNS notifications + +Since a single SNS request can contain multiple messages, you can specify that you want all messages to be processed in parallel by changing the `ConfigureServices` method like in the snippet below. -public class CustomSerializer : ISerializer +```csharp +protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) { - public T Deserialize(string input) - { - // do some fancy 'serializing' - return JsonSerializer.Deserialize(input); - } + services.UseNotificationHandler().WithParallelExecution(maxDegreeOfParallelism: 4); } +``` -// Function.cs -private class DummyFunction : EventFunction +You can use the `maxDegreeOfParallelism` parameter to specify the amount of parallel executions that you desire. By default, the amount of logical processors available is used. + +```csharp +protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) { - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) - { - services.UseSqsHandler(serializer: new CustomSerializer()); - } + services.UseNotificationHandler().WithParallelExecution(); +} +``` + +#### Custom serialization of SNS messages + +Since each SNS message contains the actual payload as encoded a string, a custom serializer can be specified to replace the default JSON serializer. + +To do so, create an implementation of the interface `INotificationSerializer` and register it in the `ConfigureServices` method. + +```csharp +public class MyCustomSerializer : INotificationSerializer +{ + public TMessage? Deserialize(string input) + { + // implement your deserialization strategy here + } +} +``` + +```csharp +protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) +{ + services.UseNotificationHandler(); + + services.UseCustomNotificationSerializer(); +} +``` + +### Event functions handling SQS messages + +AWS Lambda functions can be used to handle SQS messages. + +Since all SQS messages have the same structure, the package `Kralizek.Lambda.Template.Sqs` can be used to speed up the development of functions that handle SQS messages. + +To do so, create a class that implements the interface `IMessageHandler`, then change the `ConfigureServices` method to look like the following snippet. + +```csharp +protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) +{ + services.UseQueueMessageHandler(); +} +``` + +#### Parallel execution of SQS messages + +Since a single SQS message can contain multiple messages, you can specify that you want all messages to be processed in parallel by changing the `ConfigureServices` method like in the snippet below. + +```csharp +protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) +{ + services.UseQueueMessageHandler().WithParallelExecution(maxDegreeOfParallelism: 4); +} +``` + +You can use the `maxDegreeOfParallelism` parameter to specify the amount of parallel executions that you desire. By default, the amount of logical processors available is used. + +```csharp +protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) +{ + services.UseQueueMessageHandler().WithParallelExecution(); +} +``` + +#### Custom serialization of SQS messages + +Since each SQS message contains the actual payload as encoded a string, a custom serializer can be specified to replace the default JSON serializer. + +To do so, create an implementation of the interface `IMessageSerializer` and register it in the `ConfigureServices` method. + +```csharp +public class MyCustomSerializer : IMessageSerializer +{ + public TMessage? Deserialize(string input) + { + // implement your deserialization strategy here + } +} +``` + +```csharp +protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) +{ + services.UseQueueMessageHandler() + + services.UseCustomMessageSerializer(); } ``` @@ -121,8 +222,11 @@ Here is a list of all the available templates |Lambda Empty RequestResponse Function|lambda-template-requestresponse-empty|Creates a RequestResponse function with no extra setup| |Lambda Boilerplate Event Function|lambda-template-event-boilerplate|Creates an Event function with some boilerplate added| |Lambda Boilerplate RequestResponse Function|lambda-template-requestresponse-boilerplate|Creates a RequestResponse function with some boilerplate added| +|Lambda SNS Handler Function|lambda-template-sns-event|Creates a function to handle SNS notifications| +|Lambda SQS Handler Function|lambda-template-sqs-event|Creates a function to handle SQS messages| All the templates support the following parameters + * `--name|-n` Name of the project. It is also used as name of the function * `--role` Name of the IAM role the function will use when being executed * `--region` Name of the AWS region where the function will be installed @@ -133,6 +237,7 @@ All the templates support the following parameters The empty templates are created with just the minimum required dependencies. These include: + * `Amazon.Lambda.Core` * `Amazon.Lambda.Serialization.SystemTextJson` * `Kralizek.Lambda.Template` @@ -148,10 +253,11 @@ The boilerplate templates are an enriched version of the empty templates. They c Besides the basic dependencies of the empty templates, the boilerplate templates have some extra dependencies. The extra dependencies are: -* `Amazon.Lambda.Serialization.SystemTextJson` is used to push logs into AWS CloudWatch -* `Kralizek.Extensions.Logging` contains several helper methods for better logging -* `Microsoft.Extensions.Configuration.EnvironmentVariables` used to load configuration values from environment variables -* `Microsoft.Extensions.Configuration.Json` used to load configuration values from json files + +* `Amazon.Lambda.Logging.AspNetCore` is used to push logs into AWS CloudWatch +* `Microsoft.Extensions.Configuration.EnvironmentVariables` is used to load configuration values from environment variables +* `Microsoft.Extensions.Configuration.Json` is used to load configuration values from json files +* `Microsoft.Extensions.Logging.Configuration` is used to load logging configuration from the configuration subsystem ## Tools diff --git a/appveyor.yml b/appveyor.yml index b67cd7a..b1ea595 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,6 +13,8 @@ only_commits: files: - src/ - templates/ + - samples/ + - tests/ nuget: account_feed: false diff --git a/samples/EventFunction/EventFunction.csproj b/samples/EventFunction/EventFunction.csproj index 4a5e395..880b3d8 100644 --- a/samples/EventFunction/EventFunction.csproj +++ b/samples/EventFunction/EventFunction.csproj @@ -3,7 +3,7 @@ net6.0 true - Lambda + enable diff --git a/samples/EventFunction/Function.cs b/samples/EventFunction/Function.cs index e021050..c58a5e1 100644 --- a/samples/EventFunction/Function.cs +++ b/samples/EventFunction/Function.cs @@ -8,46 +8,45 @@ // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] -namespace EventFunction +namespace EventFunction; + +public class Function : EventFunction { - public class Function : EventFunction + protected override void Configure(IConfigurationBuilder builder) { - protected override void Configure(IConfigurationBuilder builder) - { - builder.AddEnvironmentVariables(); - } - - protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) - { - logging.AddConfiguration(Configuration.GetSection("Logging")); + builder.AddEnvironmentVariables(); + } - logging.AddLambdaLogger(new LambdaLoggerOptions - { - IncludeCategory = true, - IncludeLogLevel = true, - IncludeNewline = true - }); - } + protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) + { + logging.AddConfiguration(Configuration.GetSection("Logging")); - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) + logging.AddLambdaLogger(new LambdaLoggerOptions { - RegisterHandler(services); - } + IncludeCategory = true, + IncludeLogLevel = true, + IncludeNewline = true + }); } - public class EventHandler : IEventHandler + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) { - private readonly ILogger _logger; + RegisterHandler(services); + } +} - public EventHandler(ILogger logger) - { - _logger = logger; - } +public class EventHandler : IEventHandler +{ + private readonly ILogger _logger; - public Task HandleAsync(string input, ILambdaContext context) - { - _logger.LogInformation(input); - return Task.CompletedTask; - } + public EventHandler(ILogger logger) + { + _logger = logger; } -} + + public Task HandleAsync(string? input, ILambdaContext context) + { + _logger.LogInformation("Input: {Input}", input); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/samples/EventFunction/aws-lambda-tools-defaults.json b/samples/EventFunction/aws-lambda-tools-defaults.json index 981ac10..1f3cdc9 100644 --- a/samples/EventFunction/aws-lambda-tools-defaults.json +++ b/samples/EventFunction/aws-lambda-tools-defaults.json @@ -11,7 +11,7 @@ "profile": "", "region": "eu-west-1", "configuration": "Release", - "framework": "netcoreapp2.1", + "framework": "net6.0", "function-runtime": "dotnetcore2.1", "function-name": "test-lambda-template", "function-memory-size": 256, diff --git a/samples/RequestResponseFunction/Function.cs b/samples/RequestResponseFunction/Function.cs index 04854d8..8159277 100644 --- a/samples/RequestResponseFunction/Function.cs +++ b/samples/RequestResponseFunction/Function.cs @@ -9,46 +9,45 @@ // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] -namespace RequestResponseFunction +namespace RequestResponseFunction; + +public class Function : RequestResponseFunction { - public class Function : RequestResponseFunction + protected override void Configure(IConfigurationBuilder builder) { - protected override void Configure(IConfigurationBuilder builder) - { - builder.AddEnvironmentVariables(); - } - - protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) - { - logging.AddConfiguration(Configuration.GetSection("Logging")); + builder.AddEnvironmentVariables(); + } - logging.AddLambdaLogger(new LambdaLoggerOptions - { - IncludeCategory = true, - IncludeLogLevel = true, - IncludeNewline = true - }); - } + protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) + { + logging.AddConfiguration(Configuration.GetSection("Logging")); - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) + logging.AddLambdaLogger(new LambdaLoggerOptions { - RegisterHandler(services); - } + IncludeCategory = true, + IncludeLogLevel = true, + IncludeNewline = true + }); } - public class Handler : IRequestResponseHandler + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) { - private readonly ILogger _logger; + RegisterHandler(services); + } +} - public Handler(ILogger logger) - { - _logger = logger; - } +public class Handler : IRequestResponseHandler +{ + private readonly ILogger _logger; - public Task HandleAsync(string input, ILambdaContext context) - { - _logger.LogInformation(input); - return Task.FromResult(input?.ToUpper()); - } + public Handler(ILogger logger) + { + _logger = logger; } -} + + public Task HandleAsync(string? input, ILambdaContext context) + { + _logger.LogInformation("Input: {Input}", input); + return Task.FromResult(input?.ToUpper() ?? string.Empty); + } +} \ No newline at end of file diff --git a/samples/RequestResponseFunction/RequestResponseFunction.csproj b/samples/RequestResponseFunction/RequestResponseFunction.csproj index 4a5e395..880b3d8 100644 --- a/samples/RequestResponseFunction/RequestResponseFunction.csproj +++ b/samples/RequestResponseFunction/RequestResponseFunction.csproj @@ -3,7 +3,7 @@ net6.0 true - Lambda + enable diff --git a/samples/RequestResponseFunction/aws-lambda-tools-defaults.json b/samples/RequestResponseFunction/aws-lambda-tools-defaults.json index c53491e..07eefef 100644 --- a/samples/RequestResponseFunction/aws-lambda-tools-defaults.json +++ b/samples/RequestResponseFunction/aws-lambda-tools-defaults.json @@ -11,7 +11,7 @@ "profile": "", "region": "eu-west-1", "configuration": "Release", - "framework": "netcoreapp2.1", + "framework": "net6.0", "function-runtime": "dotnetcore2.1", "function-name": "test-lambda-template", "function-memory-size": 256, diff --git a/samples/SnsEventFunction/Function.cs b/samples/SnsEventFunction/Function.cs index 7722891..9e79905 100644 --- a/samples/SnsEventFunction/Function.cs +++ b/samples/SnsEventFunction/Function.cs @@ -10,52 +10,51 @@ [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] -namespace SnsEventFunction +namespace SnsEventFunction; + +public class Function : EventFunction { - public class Function : EventFunction + protected override void Configure(IConfigurationBuilder builder) { - protected override void Configure(IConfigurationBuilder builder) - { - builder.AddEnvironmentVariables(); - } - - protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) - { - logging.AddConfiguration(Configuration.GetSection("Logging")); + builder.AddEnvironmentVariables(); + } - logging.AddLambdaLogger(new LambdaLoggerOptions - { - IncludeCategory = true, - IncludeLogLevel = true, - IncludeNewline = true - }); - } + protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) + { + logging.AddConfiguration(Configuration.GetSection("Logging")); - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) + logging.AddLambdaLogger(new LambdaLoggerOptions { - services.UseNotificationHandler(); - } + IncludeCategory = true, + IncludeLogLevel = true, + IncludeNewline = true + }); } - public class CustomNotification + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) { - public string Message { get; set; } + services.UseNotificationHandler(); } +} - public class CustomNotificationHandler : INotificationHandler - { - private readonly ILogger _logger; +public class CustomNotification +{ + public string? Message { get; set; } +} - public CustomNotificationHandler(ILogger logger) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } +public class CustomNotificationHandler : INotificationHandler +{ + private readonly ILogger _logger; - public Task HandleAsync(CustomNotification notification, ILambdaContext context) - { - _logger.LogInformation($"Handling notification: {notification.Message}"); + public CustomNotificationHandler(ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public Task HandleAsync(CustomNotification? notification, ILambdaContext context) + { + _logger.LogInformation("Handling notification: {Message}", notification?.Message); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/samples/SnsEventFunction/SnsEventFunction.csproj b/samples/SnsEventFunction/SnsEventFunction.csproj index 032cdb6..346dbd2 100644 --- a/samples/SnsEventFunction/SnsEventFunction.csproj +++ b/samples/SnsEventFunction/SnsEventFunction.csproj @@ -3,7 +3,7 @@ net6.0 true - Lambda + enable diff --git a/samples/SnsEventFunction/aws-lambda-tools-defaults.json b/samples/SnsEventFunction/aws-lambda-tools-defaults.json index 9662d22..71b2e53 100644 --- a/samples/SnsEventFunction/aws-lambda-tools-defaults.json +++ b/samples/SnsEventFunction/aws-lambda-tools-defaults.json @@ -11,7 +11,7 @@ "profile":"RG", "region" : "eu-west-1", "configuration" : "Release", - "framework": "netcoreapp2.1", + "framework": "net6.0", "function-runtime": "dotnetcore2.1", "function-name": "test-lambda-template", "function-memory-size" : 256, diff --git a/samples/SnsEventFunctionWithParallelism/Function.cs b/samples/SnsEventFunctionWithParallelism/Function.cs index b45aaa8..0aa326c 100644 --- a/samples/SnsEventFunctionWithParallelism/Function.cs +++ b/samples/SnsEventFunctionWithParallelism/Function.cs @@ -10,52 +10,51 @@ [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] -namespace SnsEventFunction +namespace SnsEventFunction; + +public class Function : EventFunction { - public class Function : EventFunction + protected override void Configure(IConfigurationBuilder builder) { - protected override void Configure(IConfigurationBuilder builder) - { - builder.AddEnvironmentVariables(); - } - - protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) - { - logging.AddConfiguration(Configuration.GetSection("Logging")); + builder.AddEnvironmentVariables(); + } - logging.AddLambdaLogger(new LambdaLoggerOptions - { - IncludeCategory = true, - IncludeLogLevel = true, - IncludeNewline = true - }); - } + protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) + { + logging.AddConfiguration(Configuration.GetSection("Logging")); - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) + logging.AddLambdaLogger(new LambdaLoggerOptions { - services.UseNotificationHandler().WithParallelExecution(maxDegreeOfParallelism: 4); - } + IncludeCategory = true, + IncludeLogLevel = true, + IncludeNewline = true + }); } - public class CustomNotification + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) { - public string Message { get; set; } + services.UseNotificationHandler().WithParallelExecution(maxDegreeOfParallelism: 4); } +} - public class CustomNotificationHandler : INotificationHandler - { - private readonly ILogger _logger; +public class CustomNotification +{ + public string? Message { get; set; } +} - public CustomNotificationHandler(ILogger logger) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } +public class CustomNotificationHandler : INotificationHandler +{ + private readonly ILogger _logger; - public Task HandleAsync(CustomNotification notification, ILambdaContext context) - { - _logger.LogInformation($"Handling notification: {notification.Message}"); + public CustomNotificationHandler(ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public Task HandleAsync(CustomNotification? notification, ILambdaContext context) + { + _logger.LogInformation("Handling notification: {Message}", notification?.Message); - return Task.CompletedTask; - } + return Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/samples/SnsEventFunctionWithParallelism/SnsEventFunctionWithParallelism.csproj b/samples/SnsEventFunctionWithParallelism/SnsEventFunctionWithParallelism.csproj index 032cdb6..346dbd2 100644 --- a/samples/SnsEventFunctionWithParallelism/SnsEventFunctionWithParallelism.csproj +++ b/samples/SnsEventFunctionWithParallelism/SnsEventFunctionWithParallelism.csproj @@ -3,7 +3,7 @@ net6.0 true - Lambda + enable diff --git a/samples/SnsEventFunctionWithParallelism/aws-lambda-tools-defaults.json b/samples/SnsEventFunctionWithParallelism/aws-lambda-tools-defaults.json index b2cffa4..e151d97 100644 --- a/samples/SnsEventFunctionWithParallelism/aws-lambda-tools-defaults.json +++ b/samples/SnsEventFunctionWithParallelism/aws-lambda-tools-defaults.json @@ -11,8 +11,8 @@ "profile":"RG", "region" : "eu-west-1", "configuration" : "Release", - "framework": "netcoreapp2.1", - "function-runtime": "dotnetcore3.1", + "framework": "net6.0", + "function-runtime": "dotnet6", "function-name": "test-lambda-template", "function-memory-size" : 256, "function-timeout" : 30, diff --git a/samples/SqsEventFunction/Function.cs b/samples/SqsEventFunction/Function.cs new file mode 100644 index 0000000..0fc4b0b --- /dev/null +++ b/samples/SqsEventFunction/Function.cs @@ -0,0 +1,61 @@ +using System; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Amazon.Lambda.Core; +using Amazon.Lambda.SQSEvents; +using Kralizek.Lambda; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] + +namespace SqsEventFunction; + +public class Function : EventFunction +{ + protected override void Configure(IConfigurationBuilder builder) + { + builder.AddEnvironmentVariables(); + } + + protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) + { + logging.AddConfiguration(Configuration.GetSection("Logging")); + + logging.AddLambdaLogger(new LambdaLoggerOptions + { + IncludeCategory = true, + IncludeLogLevel = true, + IncludeNewline = true + }); + } + + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) + { + services.UseQueueMessageHandler(); + } +} + +public class TestMessage +{ + [JsonPropertyName("message")] + public string? Message { get; set; } +} + +public class TestMessageHandler : IMessageHandler +{ + private readonly ILogger _logger; + + public TestMessageHandler(ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public Task HandleAsync(TestMessage? message, ILambdaContext context) + { + _logger.LogInformation("Received notification: {Message}", message?.Message); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/samples/SqsEventFunction/SqsEventFunction.csproj b/samples/SqsEventFunction/SqsEventFunction.csproj new file mode 100644 index 0000000..de9ea76 --- /dev/null +++ b/samples/SqsEventFunction/SqsEventFunction.csproj @@ -0,0 +1,19 @@ + + + + net6.0 + true + enable + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/SqsEventFunction/aws-lambda-tools-defaults.json b/samples/SqsEventFunction/aws-lambda-tools-defaults.json new file mode 100644 index 0000000..6148f89 --- /dev/null +++ b/samples/SqsEventFunction/aws-lambda-tools-defaults.json @@ -0,0 +1,21 @@ +{ + "Information": [ + "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", + "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.", + + "dotnet lambda help", + + "All the command line options for the Lambda command can be specified in this file." + ], + + "profile": "", + "region": "", + "configuration": "Release", + "framework": "net6.0", + "function-name": "SqsEventFunction", + "function-role": "", + "function-runtime": "dotnet6", + "function-memory-size": 128, + "function-timeout": 30, + "function-handler": "SqsEventFunction::SqsEventFunction.Function::FunctionHandlerAsync" +} diff --git a/samples/SqsEventFunctionWithParallelism/Function.cs b/samples/SqsEventFunctionWithParallelism/Function.cs new file mode 100644 index 0000000..a3ee342 --- /dev/null +++ b/samples/SqsEventFunctionWithParallelism/Function.cs @@ -0,0 +1,61 @@ +using System; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Amazon.Lambda.Core; +using Amazon.Lambda.SQSEvents; +using Kralizek.Lambda; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] + +namespace SqsEventFunctionWithParallelism; + +public class Function : EventFunction +{ + protected override void Configure(IConfigurationBuilder builder) + { + builder.AddEnvironmentVariables(); + } + + protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) + { + logging.AddConfiguration(Configuration.GetSection("Logging")); + + logging.AddLambdaLogger(new LambdaLoggerOptions + { + IncludeCategory = true, + IncludeLogLevel = true, + IncludeNewline = true + }); + } + + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) + { + services.UseQueueMessageHandler().WithParallelExecution(4); + } +} + +public class TestMessage +{ + [JsonPropertyName("message")] + public string? Message { get; set; } +} + +public class TestMessageHandler : IMessageHandler +{ + private readonly ILogger _logger; + + public TestMessageHandler(ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public Task HandleAsync(TestMessage? message, ILambdaContext context) + { + _logger.LogInformation("Received notification: {Message}", message?.Message); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/samples/SqsEventFunctionWithParallelism/SqsEventFunctionWithParallelism.csproj b/samples/SqsEventFunctionWithParallelism/SqsEventFunctionWithParallelism.csproj new file mode 100644 index 0000000..de9ea76 --- /dev/null +++ b/samples/SqsEventFunctionWithParallelism/SqsEventFunctionWithParallelism.csproj @@ -0,0 +1,19 @@ + + + + net6.0 + true + enable + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/SqsEventFunctionWithParallelism/aws-lambda-tools-defaults.json b/samples/SqsEventFunctionWithParallelism/aws-lambda-tools-defaults.json new file mode 100644 index 0000000..a2411c1 --- /dev/null +++ b/samples/SqsEventFunctionWithParallelism/aws-lambda-tools-defaults.json @@ -0,0 +1,21 @@ +{ + "Information": [ + "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", + "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.", + + "dotnet lambda help", + + "All the command line options for the Lambda command can be specified in this file." + ], + + "profile": "", + "region": "", + "configuration": "Release", + "framework": "net6.0", + "function-name": "SqsEventFunctionWithParallelism", + "function-role": "", + "function-runtime": "dotnet6", + "function-memory-size": 128, + "function-timeout": 30, + "function-handler": "SqsEventFunctionWithParallelism::SqsEventFunctionWithParallelism.Function::FunctionHandlerAsync" +} diff --git a/src/Kralizek.Lambda.Template.Sns/INotificationHandler.cs b/src/Kralizek.Lambda.Template.Sns/INotificationHandler.cs index c525c2a..890d401 100644 --- a/src/Kralizek.Lambda.Template.Sns/INotificationHandler.cs +++ b/src/Kralizek.Lambda.Template.Sns/INotificationHandler.cs @@ -1,11 +1,18 @@ using System.Threading.Tasks; using Amazon.Lambda.Core; -using Amazon.Lambda.SNSEvents; -namespace Kralizek.Lambda +namespace Kralizek.Lambda; + +/// +/// An interface that describes an handler for SNS notifications whose internal type is . +/// +/// The internal type of the SNS notification. +public interface INotificationHandler where TNotification : class { - public interface INotificationHandler where TNotification : class - { - Task HandleAsync(TNotification notification, ILambdaContext context); - } + /// + /// The method used to handle the notification. + /// + /// The notification. + /// A representation of the execution context. + Task HandleAsync(TNotification? notification, ILambdaContext context); } \ No newline at end of file diff --git a/src/Kralizek.Lambda.Template.Sns/INotificationSerializer.cs b/src/Kralizek.Lambda.Template.Sns/INotificationSerializer.cs index cf1a62e..b6a7dff 100644 --- a/src/Kralizek.Lambda.Template.Sns/INotificationSerializer.cs +++ b/src/Kralizek.Lambda.Template.Sns/INotificationSerializer.cs @@ -1,14 +1,29 @@ using System.Text.Json; -namespace Kralizek.Lambda +namespace Kralizek.Lambda; + +/// +/// An interface to describe a serializer of SNS notifications. +/// +public interface INotificationSerializer { - public interface INotificationSerializer - { - public TMessage Deserialize(string input); - } + /// + /// Deserializes a into . + /// + /// The input string to be deserialized. + /// The type of the result. + /// The result. Can be null. + public TMessage? Deserialize(string input); +} - public class DefaultJsonNotificationSerializer : INotificationSerializer - { - public TMessage Deserialize(string input) => JsonSerializer.Deserialize(input); - } +/// +/// The default implementation of using . +/// +public class DefaultJsonNotificationSerializer : INotificationSerializer +{ + /// + /// Deserializes a into using . + /// + /// + public TMessage? Deserialize(string input) => JsonSerializer.Deserialize(input); } \ No newline at end of file diff --git a/src/Kralizek.Lambda.Template.Sns/Kralizek.Lambda.Template.Sns.csproj b/src/Kralizek.Lambda.Template.Sns/Kralizek.Lambda.Template.Sns.csproj index c349ca9..767f2f8 100644 --- a/src/Kralizek.Lambda.Template.Sns/Kralizek.Lambda.Template.Sns.csproj +++ b/src/Kralizek.Lambda.Template.Sns/Kralizek.Lambda.Template.Sns.csproj @@ -5,6 +5,7 @@ Kralizek.Lambda.Template.Sns aws-lambda-csharp;dotnet-core;aws-lambda;aws;lambda;csharp;sns Extension to Kralizek.Lambda.Template to better support AWS Lambda functions that respond to SNS notifications. + enable diff --git a/src/Kralizek.Lambda.Template.Sns/ParallelSnsEventHandler.cs b/src/Kralizek.Lambda.Template.Sns/ParallelSnsEventHandler.cs index e2fd7fb..7d8dbc6 100644 --- a/src/Kralizek.Lambda.Template.Sns/ParallelSnsEventHandler.cs +++ b/src/Kralizek.Lambda.Template.Sns/ParallelSnsEventHandler.cs @@ -1,6 +1,4 @@ using System; -using System.Linq; -using System.Text.Json; using System.Threading.Tasks; using Amazon.Lambda.Core; using Amazon.Lambda.SNSEvents; @@ -8,52 +6,69 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace Kralizek.Lambda +namespace Kralizek.Lambda; + + +/// +/// A set of options to customize the parallel execution of SNS notifications. +/// +public class ParallelSnsExecutionOptions +{ + /// + /// The top limit of concurrent threads processing the incoming notifications. + /// + public int MaxDegreeOfParallelism { get; set; } = Environment.ProcessorCount; +} + +/// +/// An implementation of specialized for that processes all the records in parallel. +/// +/// The internal type of the SNS notification. +public class ParallelSnsEventHandler: IEventHandler where TNotification : class { - public class ParallelSnsExecutionOptions + private readonly ILogger _logger; + private readonly IServiceProvider _serviceProvider; + private readonly ParallelSnsExecutionOptions _options; + + public ParallelSnsEventHandler(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IOptions options) { - public int MaxDegreeOfParallelism { get; set; } = System.Environment.ProcessorCount; + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + _logger = loggerFactory?.CreateLogger("SnsForEachAsyncEventHandler") ?? throw new ArgumentNullException(nameof(loggerFactory)); + _options = options?.Value ?? throw new ArgumentNullException(nameof(options)); } - public class ParallelSnsEventHandler: IEventHandler where TNotification : class + /// + /// Handles the by processing each record in parallel. + /// + /// The incoming event. + /// The execution context. + /// Thrown if there is no registered implementation of . + public async Task HandleAsync(SNSEvent? input, ILambdaContext context) { - private readonly ILogger _logger; - private readonly IServiceProvider _serviceProvider; - private readonly ParallelSnsExecutionOptions _options; - - public ParallelSnsEventHandler(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IOptions options) - { - _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - _logger = loggerFactory?.CreateLogger("SnsForEachAsyncEventHandler") ?? throw new ArgumentNullException(nameof(loggerFactory)); - _options = options?.Value ?? throw new ArgumentNullException(nameof(options)); - } - - public async Task HandleAsync(SNSEvent input, ILambdaContext context) + if (input is { Records.Count: > 0 }) { - if (input.Records.Any()) + await input.Records.ForEachAsync(_options.MaxDegreeOfParallelism, async record => { - await input.Records.ForEachAsync(_options.MaxDegreeOfParallelism, async record => + using var scope = _serviceProvider.CreateScope(); + + var message = record.Sns.Message; + + var serializer = _serviceProvider.GetRequiredService(); + var notification = serializer.Deserialize(message); + + _logger.LogDebug("Message received: {Message}", message); + + var messageHandler = scope.ServiceProvider.GetService>(); + + if (messageHandler == null) { - using (var scope = _serviceProvider.CreateScope()) - { - var message = record.Sns.Message; - - var serializer = _serviceProvider.GetRequiredService(); - var notification = serializer.Deserialize(message); - - _logger.LogDebug("Message received: {Message}", message); - - var messageHandler = scope.ServiceProvider.GetService>(); - if (messageHandler == null) - { - _logger.LogCritical("No {Handler} could be found", $"INotificationHandler<{typeof(TNotification).Name}>"); - throw new InvalidOperationException($"No INotificationHandler<{typeof(TNotification).Name}> could be found."); - } - - await messageHandler.HandleAsync(notification, context); - } - }); - } + _logger.LogCritical("No {Handler} could be found", $"INotificationHandler<{typeof(TNotification).Name}>"); + + throw new InvalidOperationException($"No INotificationHandler<{typeof(TNotification).Name}> could be found."); + } + + await messageHandler.HandleAsync(notification, context); + }); } } -} +} \ No newline at end of file diff --git a/src/Kralizek.Lambda.Template.Sns/ServiceCollectionExtensions.cs b/src/Kralizek.Lambda.Template.Sns/ServiceCollectionExtensions.cs index 9f0f345..2f73e1f 100644 --- a/src/Kralizek.Lambda.Template.Sns/ServiceCollectionExtensions.cs +++ b/src/Kralizek.Lambda.Template.Sns/ServiceCollectionExtensions.cs @@ -3,92 +3,121 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -namespace Kralizek.Lambda +namespace Kralizek.Lambda; + +public static class ServiceCollectionExtensions { - public static class ServiceCollectionExtensions + [Obsolete("Use `services.UseNotificationHandler().UseParallelExecution(maxDegreeOfParallelism);` instead.")] + public static IServiceCollection ConfigureSnsParallelExecution(this IServiceCollection services, int maxDegreeOfParallelism) { - [Obsolete("Use `services.UseNotificationHandler().UseParallelExecution(maxDegreeOfParallelism);` instead.")] - public static IServiceCollection ConfigureSnsParallelExecution(this IServiceCollection services, int maxDegreeOfParallelism) - { - services.Configure(option => option.MaxDegreeOfParallelism = maxDegreeOfParallelism); + services.Configure(option => option.MaxDegreeOfParallelism = maxDegreeOfParallelism); - return services; - } - - public static INotificationHandlerConfigurator WithParallelExecution(this INotificationHandlerConfigurator configurator, int? maxDegreeOfParallelism = null) - where TNotification : class - { - ArgumentNullException.ThrowIfNull(configurator); + return services; + } - if (maxDegreeOfParallelism <= 1) throw new ArgumentOutOfRangeException(nameof(maxDegreeOfParallelism), $"{nameof(maxDegreeOfParallelism)} must be greater than 1"); + /// + /// Customizes the registration of the to process the records in parallel. + /// + /// A configurator used to facilitate the configuration of the . + /// The top limit of concurrent threads processing the incoming notifications. + /// The internal type of the SNS notification. + /// The once it has been configured. + /// Thrown if is less or equal than 1. + public static INotificationHandlerConfigurator WithParallelExecution(this INotificationHandlerConfigurator configurator, int? maxDegreeOfParallelism = null) + where TNotification : class + { + ArgumentNullException.ThrowIfNull(configurator); - configurator.Services.AddTransient, ParallelSnsEventHandler>(); + if (maxDegreeOfParallelism <= 1) throw new ArgumentOutOfRangeException(nameof(maxDegreeOfParallelism), $"{nameof(maxDegreeOfParallelism)} must be greater than 1"); - if (maxDegreeOfParallelism.HasValue) - { - configurator.Services.Configure(options => options.MaxDegreeOfParallelism = maxDegreeOfParallelism.Value); - } + configurator.Services.AddTransient, ParallelSnsEventHandler>(); - return configurator; + if (maxDegreeOfParallelism.HasValue) + { + configurator.Services.Configure(options => options.MaxDegreeOfParallelism = maxDegreeOfParallelism.Value); } - public static IServiceCollection UseCustomNotificationSerializer(this IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Singleton) - where TSerializer : INotificationSerializer - { - ArgumentNullException.ThrowIfNull(services); + return configurator; + } + + /// + /// Registers a custom serializer for notification messages. + /// + /// The collection of service registrations. + /// The lifetime used for the to register. Defaults to . + /// The concrete type of the to be registered. + /// The configured collection of service registrations. + public static IServiceCollection UseCustomNotificationSerializer(this IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Singleton) + where TSerializer : INotificationSerializer + { + ArgumentNullException.ThrowIfNull(services); - services.Add(ServiceDescriptor.Describe(typeof(INotificationSerializer), typeof(TSerializer), lifetime)); + services.Add(ServiceDescriptor.Describe(typeof(INotificationSerializer), typeof(TSerializer), lifetime)); - return services; - } + return services; + } + + [Obsolete("Use `services.UseNotificationHandler().UseParallelExecution();` instead.")] + public static IServiceCollection UseNotificationHandler(this IServiceCollection services, bool enableParallelExecution) + where TNotification : class + where THandler : class, INotificationHandler + { + var configurator = UseNotificationHandler(services); - [Obsolete("Use `services.UseNotificationHandler().UseParallelExecution();` instead.")] - public static IServiceCollection UseNotificationHandler(this IServiceCollection services, bool enableParallelExecution) - where TNotification : class - where THandler : class, INotificationHandler + if (enableParallelExecution) { - var configurator = UseNotificationHandler(services); + configurator.WithParallelExecution(); + } - if (enableParallelExecution) - { - configurator.WithParallelExecution(); - } + return services; + } - return services; - } - - public static INotificationHandlerConfigurator UseNotificationHandler(this IServiceCollection services) - where TNotification : class - where THandler : class, INotificationHandler - { - services.AddOptions(); + /// + /// Registers all the services needed to handle notifications of type . + /// + /// The collection of service registrations. + /// The lifetime used for the to register. Defaults to . + /// The internal type of the SNS notification. + /// The concrete type of the to be registered. + /// The configured collection of service registrations. + public static INotificationHandlerConfigurator UseNotificationHandler(this IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Transient) + where TNotification : class + where THandler : class, INotificationHandler + { + services.AddOptions(); - services.AddTransient, SnsEventHandler>(); + services.AddTransient, SnsEventHandler>(); - services.TryAddSingleton(); + services.TryAddSingleton(); - services.AddTransient, THandler>(); + services.Add(ServiceDescriptor.Describe(typeof(INotificationHandler), typeof(THandler), lifetime)); - var configurator = new NotificationHandlerConfigurator(services); + var configurator = new NotificationHandlerConfigurator(services); - return configurator; - } + return configurator; } - - public interface INotificationHandlerConfigurator - where TNotification : class +} + +/// +/// An interface used to represent a configurator of . +/// +/// The internal type of the SNS notification. +public interface INotificationHandlerConfigurator + where TNotification : class +{ + /// + /// The collection of service registrations. + /// + IServiceCollection Services { get; } +} + +internal sealed class NotificationHandlerConfigurator : INotificationHandlerConfigurator + where TNotification : class +{ + public NotificationHandlerConfigurator(IServiceCollection services) { - IServiceCollection Services { get; } + Services = services ?? throw new ArgumentNullException(nameof(services)); } - - internal sealed class NotificationHandlerConfigurator : INotificationHandlerConfigurator - where TNotification : class - { - public NotificationHandlerConfigurator(IServiceCollection services) - { - Services = services ?? throw new ArgumentNullException(nameof(services)); - } - public IServiceCollection Services { get; } - } + public IServiceCollection Services { get; } } \ No newline at end of file diff --git a/src/Kralizek.Lambda.Template.Sns/SnsEventHandler.cs b/src/Kralizek.Lambda.Template.Sns/SnsEventHandler.cs index 6cee1c9..601b41d 100644 --- a/src/Kralizek.Lambda.Template.Sns/SnsEventHandler.cs +++ b/src/Kralizek.Lambda.Template.Sns/SnsEventHandler.cs @@ -1,50 +1,60 @@ using System; -using System.Collections.Generic; -using System.Text; +using System.Linq; using System.Threading.Tasks; using Amazon.Lambda.Core; using Amazon.Lambda.SNSEvents; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using System.Text.Json; -namespace Kralizek.Lambda +namespace Kralizek.Lambda; + +/// +/// An implementation of specialized for that processes all the records in sequence. +/// +/// The internal type of the SNS notification. +public class SnsEventHandler : IEventHandler where TNotification : class { - public class SnsEventHandler : IEventHandler where TNotification : class - { - private readonly ILogger _logger; - private readonly IServiceProvider _serviceProvider; + private readonly ILogger _logger; + private readonly IServiceProvider _serviceProvider; - public SnsEventHandler(IServiceProvider serviceProvider, ILoggerFactory loggerFactory) - { - _logger = loggerFactory?.CreateLogger("SnsEventHandler") ?? throw new ArgumentNullException(nameof(loggerFactory)); - _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - } + public SnsEventHandler(IServiceProvider serviceProvider, ILoggerFactory loggerFactory) + { + _logger = loggerFactory?.CreateLogger("SnsEventHandler") ?? throw new ArgumentNullException(nameof(loggerFactory)); + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + } - public async Task HandleAsync(SNSEvent input, ILambdaContext context) + /// + /// Handles the by processing each record in sequence. + /// + /// The incoming event. + /// The execution context. + /// Thrown if there is no registered implementation of . + public async Task HandleAsync(SNSEvent? input, ILambdaContext context) + { + if (input is { Records: { } }) { foreach (var record in input.Records) { - using (var scope = _serviceProvider.CreateScope()) - { - var message = record.Sns.Message; + using var scope = _serviceProvider.CreateScope(); + + var message = record.Sns.Message; - var serializer = _serviceProvider.GetRequiredService(); + var serializer = _serviceProvider.GetRequiredService(); - var notification = serializer.Deserialize(message); - - var handler = scope.ServiceProvider.GetService>(); + var notification = serializer.Deserialize(message); - if (handler == null) - { - _logger.LogCritical("No {Handler} could be found", $"INotificationHandler<{typeof(TNotification).Name}>"); - throw new InvalidOperationException($"No INotificationHandler<{typeof(TNotification).Name}> could be found."); - } + var handler = scope.ServiceProvider.GetService>(); - _logger.LogInformation("Invoking notification handler"); - await handler.HandleAsync(notification, context).ConfigureAwait(false); + if (handler == null) + { + _logger.LogCritical("No {Handler} could be found", $"INotificationHandler<{typeof(TNotification).Name}>"); + + throw new InvalidOperationException($"No INotificationHandler<{typeof(TNotification).Name}> could be found."); } + + _logger.LogInformation("Invoking notification handler"); + await handler.HandleAsync(notification, context).ConfigureAwait(false); } } } -} +} \ No newline at end of file diff --git a/src/Kralizek.Lambda.Template.Sqs/IMessageHandler.cs b/src/Kralizek.Lambda.Template.Sqs/IMessageHandler.cs index 65d5660..59f1367 100644 --- a/src/Kralizek.Lambda.Template.Sqs/IMessageHandler.cs +++ b/src/Kralizek.Lambda.Template.Sqs/IMessageHandler.cs @@ -1,10 +1,13 @@ using System.Threading.Tasks; using Amazon.Lambda.Core; -namespace Kralizek.Lambda +namespace Kralizek.Lambda; + +/// +/// An interface that describes an handler for SQS messages whose internal type is . +/// +/// The internal type of the SQS message. +public interface IMessageHandler where TMessage : class { - public interface IMessageHandler where TMessage : class - { - Task HandleAsync(TMessage message, ILambdaContext context); - } -} + Task HandleAsync(TMessage? message, ILambdaContext context); +} \ No newline at end of file diff --git a/src/Kralizek.Lambda.Template.Sqs/IMessageSerializer.cs b/src/Kralizek.Lambda.Template.Sqs/IMessageSerializer.cs index 4822c1e..cf578c0 100644 --- a/src/Kralizek.Lambda.Template.Sqs/IMessageSerializer.cs +++ b/src/Kralizek.Lambda.Template.Sqs/IMessageSerializer.cs @@ -1,14 +1,29 @@ using System.Text.Json; -namespace Kralizek.Lambda +namespace Kralizek.Lambda; + +/// +/// An interface to describe a serializer of SQS messages. +/// +public interface IMessageSerializer { - public interface IMessageSerializer - { - public TMessage Deserialize(string input); - } + /// + /// Deserializes a into . + /// + /// The input string to be deserialized. + /// The internal type of the SQS message. + /// The result. Can be null. + public TMessage? Deserialize(string input); +} - public class DefaultJsonMessageSerializer : IMessageSerializer - { - public TMessage Deserialize(string input) => JsonSerializer.Deserialize(input); - } +/// +/// The default implementation of using . +/// +public class DefaultJsonMessageSerializer : IMessageSerializer +{ + /// + /// Deserializes a into using . + /// + /// + public TMessage? Deserialize(string input) => JsonSerializer.Deserialize(input); } \ No newline at end of file diff --git a/src/Kralizek.Lambda.Template.Sqs/Kralizek.Lambda.Template.Sqs.csproj b/src/Kralizek.Lambda.Template.Sqs/Kralizek.Lambda.Template.Sqs.csproj index add77aa..2b87a7e 100644 --- a/src/Kralizek.Lambda.Template.Sqs/Kralizek.Lambda.Template.Sqs.csproj +++ b/src/Kralizek.Lambda.Template.Sqs/Kralizek.Lambda.Template.Sqs.csproj @@ -5,6 +5,7 @@ Kralizek.Lambda.Template.Sqs aws-lambda-csharp;dotnet-core;aws-lambda;aws;lambda;csharp;sqs Extension to Kralizek.Lambda.Template to better support AWS Lambda functions that respond to SQS messages. + enable diff --git a/src/Kralizek.Lambda.Template.Sqs/ParallelSqsEventHandler.cs b/src/Kralizek.Lambda.Template.Sqs/ParallelSqsEventHandler.cs index 5a27e4c..7524acd 100644 --- a/src/Kralizek.Lambda.Template.Sqs/ParallelSqsEventHandler.cs +++ b/src/Kralizek.Lambda.Template.Sqs/ParallelSqsEventHandler.cs @@ -8,55 +8,68 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace Kralizek.Lambda +namespace Kralizek.Lambda; + +/// +/// A set of options to customize the parallel execution of SQS messages. +/// +public class ParallelSqsExecutionOptions +{ + /// + /// The top limit of concurrent threads processing the incoming notifications. + /// + public int MaxDegreeOfParallelism { get; set; } = Environment.ProcessorCount; +} + +/// +/// An implementation of specialized for that processes all the records in parallel. +/// +/// The internal type of the SQS message. +public class ParallelSqsEventHandler: IEventHandler where TMessage : class { - public class ParallelSqsExecutionOptions + private readonly ILogger _logger; + private readonly IServiceProvider _serviceProvider; + private readonly ParallelSqsExecutionOptions _options; + + public ParallelSqsEventHandler(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IOptions options) { - public int MaxDegreeOfParallelism { get; set; } = System.Environment.ProcessorCount; + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + _logger = loggerFactory?.CreateLogger("SqsForEachAsyncEventHandler") ?? throw new ArgumentNullException(nameof(loggerFactory)); + _options = options?.Value ?? throw new ArgumentNullException(nameof(options)); } - public class ParallelSqsEventHandler: IEventHandler where TMessage : class + /// + /// Handles the by processing each record in parallel. + /// + /// The incoming event. + /// The execution context. + /// Thrown if there is no registered implementation of . + public async Task HandleAsync(SQSEvent? input, ILambdaContext context) { - private readonly ILogger _logger; - private readonly IServiceProvider _serviceProvider; - private readonly ParallelSqsExecutionOptions _options; - - public ParallelSqsEventHandler(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IOptions options) - { - _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - _logger = loggerFactory?.CreateLogger("SqsForEachAsyncEventHandler") ?? - throw new ArgumentNullException(nameof(loggerFactory)); - _options = options?.Value ?? throw new ArgumentNullException(nameof(options)); - } - - public async Task HandleAsync(SQSEvent input, ILambdaContext context) + if (input is { Records.Count: > 0 }) { - if (input.Records.Any()) + await input.Records.ForEachAsync(_options.MaxDegreeOfParallelism, async singleSqsMessage => { - await input.Records.ForEachAsync(_options.MaxDegreeOfParallelism, async singleSqsMessage => + using var scope = _serviceProvider.CreateScope(); + + var sqsMessage = singleSqsMessage.Body; + _logger.LogDebug("Message received: {Message}", sqsMessage); + + var serializer = _serviceProvider.GetRequiredService(); + + var message = serializer.Deserialize(sqsMessage); + + var messageHandler = scope.ServiceProvider.GetService>(); + + if (messageHandler == null) { - using (var scope = _serviceProvider.CreateScope()) - { - var sqsMessage = singleSqsMessage.Body; - _logger.LogDebug("Message received: {Message}", sqsMessage); - - var serializer = _serviceProvider.GetRequiredService(); - var message = serializer != null - ? serializer.Deserialize(sqsMessage) - : JsonSerializer.Deserialize(sqsMessage); - - var messageHandler = scope.ServiceProvider.GetService>(); - - if (messageHandler == null) - { - _logger.LogError("No {Handler} could be found", $"IMessageHandler<{typeof(TMessage).Name}>"); - throw new InvalidOperationException($"No IMessageHandler<{typeof(TMessage).Name}> could be found."); - } - - await messageHandler.HandleAsync(message, context); - } - }); - } + _logger.LogError("No {Handler} could be found", $"IMessageHandler<{typeof(TMessage).Name}>"); + + throw new InvalidOperationException($"No IMessageHandler<{typeof(TMessage).Name}> could be found."); + } + + await messageHandler.HandleAsync(message, context); + }); } } } \ No newline at end of file diff --git a/src/Kralizek.Lambda.Template.Sqs/ServiceCollectionExtensions.cs b/src/Kralizek.Lambda.Template.Sqs/ServiceCollectionExtensions.cs index 14a8112..712182e 100644 --- a/src/Kralizek.Lambda.Template.Sqs/ServiceCollectionExtensions.cs +++ b/src/Kralizek.Lambda.Template.Sqs/ServiceCollectionExtensions.cs @@ -3,92 +3,121 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -namespace Kralizek.Lambda +namespace Kralizek.Lambda; + +public static class ServiceCollectionExtensions { - public static class ServiceCollectionExtensions + [Obsolete("Use `services.UseQueueMessageHandler().UseParallelExecution(maxDegreeOfParallelism);` instead.")] + public static IServiceCollection ConfigureSnsParallelExecution(this IServiceCollection services, int maxDegreeOfParallelism) { - [Obsolete("Use `services.UseQueueMessageHandler().UseParallelExecution(maxDegreeOfParallelism);` instead.")] - public static IServiceCollection ConfigureSnsParallelExecution(this IServiceCollection services, int maxDegreeOfParallelism) - { - services.Configure(option => option.MaxDegreeOfParallelism = maxDegreeOfParallelism); + services.Configure(option => option.MaxDegreeOfParallelism = maxDegreeOfParallelism); - return services; - } + return services; + } - public static IMessageHandlerConfigurator WithParallelExecution(this IMessageHandlerConfigurator configurator, int? maxDegreeOfParallelism = null) - where TMessage : class - { - ArgumentNullException.ThrowIfNull(configurator); - - if (maxDegreeOfParallelism <= 1) throw new ArgumentOutOfRangeException(nameof(maxDegreeOfParallelism), $"{nameof(maxDegreeOfParallelism)} must be greater than 1"); + /// + /// Customizes the registration of the to process the records in parallel. + /// + /// A configurator used to facilitate the configuration of the . + /// The top limit of concurrent threads processing the incoming notifications. + /// The internal type of the SQS messages. + /// The once it has been configured. + /// Thrown if is less or equal than 1. + public static IMessageHandlerConfigurator WithParallelExecution(this IMessageHandlerConfigurator configurator, int? maxDegreeOfParallelism = null) + where TMessage : class + { + ArgumentNullException.ThrowIfNull(configurator); - configurator.Services.AddTransient, ParallelSqsEventHandler>(); + if (maxDegreeOfParallelism <= 1) throw new ArgumentOutOfRangeException(nameof(maxDegreeOfParallelism), $"{nameof(maxDegreeOfParallelism)} must be greater than 1"); - if (maxDegreeOfParallelism.HasValue) - { - configurator.Services.Configure(options => options.MaxDegreeOfParallelism = maxDegreeOfParallelism.Value); - } + configurator.Services.AddTransient, ParallelSqsEventHandler>(); - return configurator; + if (maxDegreeOfParallelism.HasValue) + { + configurator.Services.Configure(options => options.MaxDegreeOfParallelism = maxDegreeOfParallelism.Value); } + + return configurator; + } - public static IServiceCollection UseCustomMessageSerializer(this IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Singleton) - where TSerializer : IMessageSerializer - { - ArgumentNullException.ThrowIfNull(services); + /// + /// Registers a custom serializer for messages. + /// + /// The collection of service registrations. + /// The lifetime used for the to register. Defaults to . + /// The concrete type of the to be registered. + /// The configured collection of service registrations. + public static IServiceCollection UseCustomMessageSerializer(this IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Singleton) + where TSerializer : IMessageSerializer + { + ArgumentNullException.ThrowIfNull(services); - services.Add(ServiceDescriptor.Describe(typeof(IMessageSerializer), typeof(TSerializer), lifetime)); - - return services; - } + services.Add(ServiceDescriptor.Describe(typeof(IMessageSerializer), typeof(TSerializer), lifetime)); - [Obsolete("Use `services.UseQueueMessageHandler();` instead.")] - public static IServiceCollection UseSqsHandler(this IServiceCollection services, bool enableParallelExecution = false) - where TMessage : class - where THandler : class, IMessageHandler - { - var configurator = UseQueueMessageHandler(services); + return services; + } - if (enableParallelExecution) - { - configurator.WithParallelExecution(); - } + [Obsolete("Use `services.UseQueueMessageHandler();` instead.")] + public static IServiceCollection UseSqsHandler(this IServiceCollection services, bool enableParallelExecution = false) + where TMessage : class + where THandler : class, IMessageHandler + { + var configurator = UseQueueMessageHandler(services); - return services; + if (enableParallelExecution) + { + configurator.WithParallelExecution(); } - public static IMessageHandlerConfigurator UseQueueMessageHandler(this IServiceCollection services) - where TMessage : class - where THandler : class, IMessageHandler - { - services.AddOptions(); + return services; + } + + /// + /// Registers all the services needed to handle messages of type . + /// + /// The collection of service registrations. + /// The lifetime used for the to register. Defaults to . + /// The internal type of the SNS notification. + /// The concrete type of the to be registered. + /// The configured collection of service registrations. + public static IMessageHandlerConfigurator UseQueueMessageHandler(this IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Transient) + where TMessage : class + where THandler : class, IMessageHandler + { + services.AddOptions(); - services.AddTransient, SqsEventHandler>(); + services.AddTransient, SqsEventHandler>(); - services.TryAddSingleton(); + services.TryAddSingleton(); - services.AddTransient, THandler>(); + services.Add(ServiceDescriptor.Describe(typeof(IMessageHandler), typeof(THandler), lifetime)); - var configurator = new MessageHandlerConfigurator(services); + var configurator = new MessageHandlerConfigurator(services); - return configurator; - } + return configurator; } +} - public interface IMessageHandlerConfigurator - where TMessage : class - { - IServiceCollection Services { get; } - } +/// +/// An interface used to represent a configurator of . +/// +/// The internal type of the SQS message. +public interface IMessageHandlerConfigurator + where TMessage : class +{ + /// + /// The collection of service registrations. + /// + IServiceCollection Services { get; } +} - internal sealed class MessageHandlerConfigurator : IMessageHandlerConfigurator - where TMessage : class +internal sealed class MessageHandlerConfigurator : IMessageHandlerConfigurator + where TMessage : class +{ + public MessageHandlerConfigurator(IServiceCollection services) { - public MessageHandlerConfigurator(IServiceCollection services) - { - Services = services ?? throw new ArgumentNullException(nameof(services)); - } - - public IServiceCollection Services { get; } + Services = services ?? throw new ArgumentNullException(nameof(services)); } -} + + public IServiceCollection Services { get; } +} \ No newline at end of file diff --git a/src/Kralizek.Lambda.Template.Sqs/SqsEventHandler.cs b/src/Kralizek.Lambda.Template.Sqs/SqsEventHandler.cs index d92e1ee..3f0ce7e 100644 --- a/src/Kralizek.Lambda.Template.Sqs/SqsEventHandler.cs +++ b/src/Kralizek.Lambda.Template.Sqs/SqsEventHandler.cs @@ -4,45 +4,56 @@ using Amazon.Lambda.SQSEvents; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using System.Text.Json; -namespace Kralizek.Lambda +namespace Kralizek.Lambda; + +/// +/// An implementation of specialized for that processes all the records in sequence. +/// +/// The internal type of the SQS message. +public class SqsEventHandler : IEventHandler where TMessage : class { - public class SqsEventHandler : IEventHandler where TMessage : class - { - private readonly ILogger _logger; - private readonly IServiceProvider _serviceProvider; + private readonly ILogger _logger; + private readonly IServiceProvider _serviceProvider; - public SqsEventHandler(IServiceProvider serviceProvider, ILoggerFactory loggerFactory) - { - _logger = loggerFactory?.CreateLogger("SqsEventHandler") ?? throw new ArgumentNullException(nameof(loggerFactory)); - _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - } + public SqsEventHandler(IServiceProvider serviceProvider, ILoggerFactory loggerFactory) + { + _logger = loggerFactory?.CreateLogger("SqsEventHandler") ?? throw new ArgumentNullException(nameof(loggerFactory)); + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + } - public async Task HandleAsync(SQSEvent input, ILambdaContext context) + /// + /// Handles the by processing each record in sequence. + /// + /// The incoming event. + /// The execution context. + /// Thrown if there is no registered implementation of . + public async Task HandleAsync(SQSEvent? input, ILambdaContext context) + { + if (input is { Records: { } }) { foreach (var record in input.Records) { - using (var scope = _serviceProvider.CreateScope()) - { - var sqsMessage = record.Body; + using var scope = _serviceProvider.CreateScope(); + + var sqsMessage = record.Body; - var serializer = _serviceProvider.GetRequiredService(); - - var message = serializer.Deserialize(sqsMessage); + var serializer = _serviceProvider.GetRequiredService(); - var handler = scope.ServiceProvider.GetService>(); + var message = serializer.Deserialize(sqsMessage); - if (handler == null) - { - _logger.LogError("No {Handler} could be found", $"IMessageHandler<{typeof(TMessage).Name}>"); - throw new InvalidOperationException($"No IMessageHandler<{typeof(TMessage).Name}> could be found."); - } + var handler = scope.ServiceProvider.GetService>(); - _logger.LogInformation("Invoking notification handler"); - await handler.HandleAsync(message, context).ConfigureAwait(false); + if (handler == null) + { + _logger.LogError("No {Handler} could be found", $"IMessageHandler<{typeof(TMessage).Name}>"); + + throw new InvalidOperationException($"No IMessageHandler<{typeof(TMessage).Name}> could be found."); } + + _logger.LogInformation("Invoking notification handler"); + await handler.HandleAsync(message, context).ConfigureAwait(false); } } } -} +} \ No newline at end of file diff --git a/src/Kralizek.Lambda.Template/AsyncExtensions.cs b/src/Kralizek.Lambda.Template/AsyncExtensions.cs index 70b5b08..d072ca3 100644 --- a/src/Kralizek.Lambda.Template/AsyncExtensions.cs +++ b/src/Kralizek.Lambda.Template/AsyncExtensions.cs @@ -4,29 +4,28 @@ using System.Linq; using System.Threading.Tasks; -namespace Kralizek.Lambda +namespace Kralizek.Lambda; + +public static class AsyncExtensions { - public static class AsyncExtensions + /// + /// Extensions on collection + /// Lambda style extensions to cater a foreach with concurrency. + /// + /// + /// The collection please make sure the collection can handle the concurrency. If writing back to the objects in the collection. If null, a completed task is returned. + /// Concurrent threads doing the async + /// The work that needs to be done. If null, a completed task is returned. + /// + public static Task ForEachAsync(this IEnumerable source, int maxDegreeOfParallelism, Func body) { - /// - /// Extensions on collection - /// Lambda style extensions to cater a foreach with concurrency. - /// - /// - /// The collection please make sure the collection can handle the concurrency. If writing back to the objects in the collection - /// Concurrent threads doing the async - /// The work that needs to be done. - /// - public static Task ForEachAsync(this IEnumerable source, int maxDegreeOfParallelism, Func body) - { - return Task.WhenAll( - from partition in Partitioner.Create(source).GetPartitions(maxDegreeOfParallelism) - select Task.Run(async delegate - { - using (partition) - while (partition.MoveNext()) - await body(partition.Current); - })); - } + return Task.WhenAll( + from partition in Partitioner.Create(source).GetPartitions(maxDegreeOfParallelism) + select Task.Run(async delegate + { + using (partition) + while (partition.MoveNext()) + await body(partition.Current); + })); } -} +} \ No newline at end of file diff --git a/src/Kralizek.Lambda.Template/EventFunction.cs b/src/Kralizek.Lambda.Template/EventFunction.cs index 4e5b994..84da961 100644 --- a/src/Kralizek.Lambda.Template/EventFunction.cs +++ b/src/Kralizek.Lambda.Template/EventFunction.cs @@ -4,35 +4,58 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Kralizek.Lambda +namespace Kralizek.Lambda; + +/// +/// A base class used for all Event Functions. +/// +/// The type of the incoming request. +public abstract class EventFunction : Function { - public abstract class EventFunction : Function + /// + /// The entrypoint used by the Lambda runtime for executing the function. + /// + /// The incoming request. + /// A representation of the execution context. + /// The exception is thrown if no handler is registered for the incoming input. + public async Task FunctionHandlerAsync(TInput? input, ILambdaContext context) { - public async Task FunctionHandlerAsync(TInput input, ILambdaContext context) - { - using (var scope = ServiceProvider.CreateScope()) - { - var handler = scope.ServiceProvider.GetService>(); + using var scope = ServiceProvider.CreateScope(); - if (handler == null) - { - Logger.LogCritical("No {Handler} could be found", $"IEventHandler<{typeof(TInput).Name}>"); - throw new InvalidOperationException($"No IEventHandler<{typeof(TInput).Name}> could be found."); - } - - Logger.LogInformation("Invoking handler"); - await handler.HandleAsync(input, context).ConfigureAwait(false); - } - } + var handler = scope.ServiceProvider.GetService>(); - protected void RegisterHandler(IServiceCollection services) where THandler : class, IEventHandler + if (handler == null) { - services.AddTransient, THandler>(); + Logger.LogCritical("No {Handler} could be found", $"IEventHandler<{typeof(TInput).Name}>"); + throw new InvalidOperationException($"No IEventHandler<{typeof(TInput).Name}> could be found."); } + + Logger.LogInformation("Invoking handler"); + await handler.HandleAsync(input, context).ConfigureAwait(false); } - public interface IEventHandler + /// + /// Registers the handler for the request ot type . + /// + /// The collections of services. + /// The lifetime of the handler. Defaults to . + /// The type of the handler for requests of type . + protected void RegisterHandler(IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Transient) where THandler : class, IEventHandler { - Task HandleAsync(TInput input, ILambdaContext context); + services.Add(ServiceDescriptor.Describe(typeof(IEventHandler), typeof(THandler), lifetime)); } +} + +/// +/// An interface that describes an handler for events with inputs of type . +/// +/// The type of the incoming request. +public interface IEventHandler +{ + /// + /// The method used to handle the incoming event. + /// + /// The incoming request. + /// A representation of the execution context. + Task HandleAsync(TInput? input, ILambdaContext context); } \ No newline at end of file diff --git a/src/Kralizek.Lambda.Template/Function.cs b/src/Kralizek.Lambda.Template/Function.cs index 5c10b0b..222dc95 100644 --- a/src/Kralizek.Lambda.Template/Function.cs +++ b/src/Kralizek.Lambda.Template/Function.cs @@ -3,49 +3,72 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Kralizek.Lambda +namespace Kralizek.Lambda; + +/// +/// A base class for all functions +/// +public abstract class Function { - public abstract class Function + /// + /// The base constructor for all classes. This constructor is responsible for initializing the function. + /// + protected Function() { - protected Function() - { - var services = new ServiceCollection(); + var services = new ServiceCollection(); - var builder = new ConfigurationBuilder(); + var builder = new ConfigurationBuilder(); - Configure(builder); - - Configuration = builder.Build(); - - var executionEnvironment = new LambdaExecutionEnvironment - { - EnvironmentName = Configuration["Environment"], - IsLambda = Configuration["LAMBDA_RUNTIME_DIR"] != null - }; - - services.AddSingleton(executionEnvironment); + Configure(builder); - services.AddSingleton(Configuration); + Configuration = builder.Build(); - services.AddLogging(logging => ConfigureLogging(logging, executionEnvironment)); - - ConfigureServices(services, executionEnvironment); - - ServiceProvider = services.BuildServiceProvider(); - - Logger = ServiceProvider.GetRequiredService>(); - } + var executionEnvironment = new LambdaExecutionEnvironment + { + EnvironmentName = Configuration["Environment"], + IsLambda = Configuration["LAMBDA_RUNTIME_DIR"] != null + }; - protected virtual void Configure(IConfigurationBuilder builder) { } + services.AddSingleton(executionEnvironment); - protected virtual void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) { } + services.AddSingleton(Configuration); - protected virtual void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) { } + services.AddLogging(logging => ConfigureLogging(logging, executionEnvironment)); - protected IConfigurationRoot Configuration { get; } + ConfigureServices(services, executionEnvironment); - protected IServiceProvider ServiceProvider { get; } + ServiceProvider = services.BuildServiceProvider(); - protected ILogger Logger { get; } + Logger = ServiceProvider.GetRequiredService>(); } -} + + /// + /// Use this method to register your configuration flow. Exactly like in ASP.NET Core. + /// + protected virtual void Configure(IConfigurationBuilder builder) { } + + /// + /// Use this method to register your services. Exactly like in ASP.NET Core. + /// + protected virtual void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) { } + + /// + /// Use this method to configure the logging. Exactly like in ASP.NET Core. + /// + protected virtual void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) { } + + /// + /// The root configuration. + /// + protected IConfigurationRoot Configuration { get; } + + /// + /// The service provider. + /// + protected IServiceProvider ServiceProvider { get; } + + /// + /// The default logger for the function. + /// + protected ILogger Logger { get; } +} \ No newline at end of file diff --git a/src/Kralizek.Lambda.Template/IExecutionEnvironment.cs b/src/Kralizek.Lambda.Template/IExecutionEnvironment.cs index 55276d9..8092604 100644 --- a/src/Kralizek.Lambda.Template/IExecutionEnvironment.cs +++ b/src/Kralizek.Lambda.Template/IExecutionEnvironment.cs @@ -1,31 +1,58 @@ using System; -namespace Kralizek.Lambda +namespace Kralizek.Lambda; + +/// +/// A representation of the current execution environment +/// +public interface IExecutionEnvironment { - public interface IExecutionEnvironment - { - string EnvironmentName { get; } + /// + /// The name of the current environment as defined in the environment variables. + /// + string EnvironmentName { get; } - bool IsLambda { get; } - } + /// + /// Specifies whether the current execution is in the Lambda runtime. + /// + bool IsLambda { get; } +} - public class LambdaExecutionEnvironment : IExecutionEnvironment +internal class LambdaExecutionEnvironment : IExecutionEnvironment +{ + internal const string DevelopmentEnvironmentName = "Development"; + internal const string ProductionEnvironmentName = "Production"; + + public string EnvironmentName { get; init; } = DevelopmentEnvironmentName; + + public bool IsLambda { get; init; } +} + +/// +/// A set of extensions for . +/// +public static class ExecutionEnvironmentExtensions +{ + /// + /// Checks whether the function is being executed in the environment specified by . + /// + /// The current execution environment. + /// The name of the execution environment to check. + public static bool IsEnvironment(this IExecutionEnvironment executionEnvironment, string environmentName) { - public string EnvironmentName { get; set; } - - public bool IsLambda { get; set; } + return string.Equals(executionEnvironment.EnvironmentName, environmentName, StringComparison.OrdinalIgnoreCase); } - public static class ExecutionEnvironmentExtensions - { - public static bool IsEnvironment(this IExecutionEnvironment executionEnvironment, string environmentName) - { - return string.Equals(executionEnvironment.EnvironmentName, environmentName, StringComparison.OrdinalIgnoreCase); - } - - public static bool IsDevelopment(this IExecutionEnvironment executionEnvironment) => executionEnvironment.EnvironmentName == null || IsEnvironment(executionEnvironment, "development"); - - public static bool IsProduction(this IExecutionEnvironment executionEnvironment) => IsEnvironment(executionEnvironment, "production"); - } + /// + /// Checks whether the function is being executed in the Development environment. + /// + /// The current execution environment. + public static bool IsDevelopment(this IExecutionEnvironment executionEnvironment) => IsEnvironment(executionEnvironment, LambdaExecutionEnvironment.DevelopmentEnvironmentName); + + /// + /// Checks whether the function is being executed in the Production environment. + /// + /// The current execution environment. + public static bool IsProduction(this IExecutionEnvironment executionEnvironment) => IsEnvironment(executionEnvironment, LambdaExecutionEnvironment.ProductionEnvironmentName); } \ No newline at end of file diff --git a/src/Kralizek.Lambda.Template/Kralizek.Lambda.Template.csproj b/src/Kralizek.Lambda.Template/Kralizek.Lambda.Template.csproj index e599d75..1d2541d 100644 --- a/src/Kralizek.Lambda.Template/Kralizek.Lambda.Template.csproj +++ b/src/Kralizek.Lambda.Template/Kralizek.Lambda.Template.csproj @@ -5,6 +5,7 @@ Kralizek.Lambda.Template aws-lambda-csharp;dotnet-core;aws-lambda;aws;lambda;csharp A structured template to create AWS Lambda in C#. It supports Logging, Dependency Injection and Configuration like ASP.NET Core projects do. + enable diff --git a/src/Kralizek.Lambda.Template/RequestResponseFunction.cs b/src/Kralizek.Lambda.Template/RequestResponseFunction.cs index 9d9c357..5f73b9c 100644 --- a/src/Kralizek.Lambda.Template/RequestResponseFunction.cs +++ b/src/Kralizek.Lambda.Template/RequestResponseFunction.cs @@ -4,36 +4,62 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Kralizek.Lambda +namespace Kralizek.Lambda; + +/// +/// A base class used for all Request/Response Functions. +/// +/// The type of the incoming request. +/// The type of the outgoing response. +public abstract class RequestResponseFunction : Function { - public abstract class RequestResponseFunction : Function + /// + /// The entrypoint used by the Lambda runtime for executing the function. + /// + /// The incoming request. + /// A representation of the execution context. + /// The exception is thrown if no handler is registered for the incoming input. + public async Task FunctionHandlerAsync(TInput input, ILambdaContext context) { - public async Task FunctionHandlerAsync(TInput input, ILambdaContext context) - { - using (var scope = ServiceProvider.CreateScope()) - { - var handler = scope.ServiceProvider.GetService>(); - - if (handler == null) - { - Logger.LogCritical("No {Handler} could be found", $"IRequestResponseHandler<{typeof(TInput).Name}, {typeof(TOutput).Name}>"); - throw new InvalidOperationException($"No IRequestResponseHandler<{typeof(TInput).Name}, {typeof(TOutput).Name}> could be found."); - } + using var scope = ServiceProvider.CreateScope(); - Logger.LogInformation("Invoking handler"); + var handler = scope.ServiceProvider.GetService>(); - return await handler.HandleAsync(input, context).ConfigureAwait(false); - } + if (handler == null) + { + Logger.LogCritical("No {Handler} could be found", $"IRequestResponseHandler<{typeof(TInput).Name}, {typeof(TOutput).Name}>"); + throw new InvalidOperationException($"No IRequestResponseHandler<{typeof(TInput).Name}, {typeof(TOutput).Name}> could be found."); } - protected void RegisterHandler(IServiceCollection services) where THandler : class, IRequestResponseHandler - { - services.AddTransient, THandler>(); - } + Logger.LogInformation("Invoking handler"); + + return await handler.HandleAsync(input, context).ConfigureAwait(false); } - public interface IRequestResponseHandler - { - Task HandleAsync(TInput input, ILambdaContext context); + /// + /// Registers the handler for the request ot type . + /// + /// The collections of services. + /// The lifetime of the handler. Defaults to . + /// The type of the handler for requests of type . + protected void RegisterHandler(IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Transient) where THandler : class, IRequestResponseHandler + { + services.Add(ServiceDescriptor.Describe(typeof(IRequestResponseHandler), typeof(THandler), lifetime)); } +} + +/// +/// An interface that describes an handler for events with inputs of type . +/// +/// The type of the incoming request. +/// The type of the outgoing response. +public interface IRequestResponseHandler +{ + /// + /// The method used to handle the incoming event. + /// + /// The incoming request. + /// A representation of the execution context. + /// The produced output. + Task HandleAsync(TInput? input, ILambdaContext context); } \ No newline at end of file diff --git a/templates/content/EmptyEventFunction/EmptyEventFunction.csproj b/templates/content/EmptyEventFunction/EmptyEventFunction.csproj index 0d2e0ac..a4f64ea 100644 --- a/templates/content/EmptyEventFunction/EmptyEventFunction.csproj +++ b/templates/content/EmptyEventFunction/EmptyEventFunction.csproj @@ -4,10 +4,13 @@ net6.0 true Lambda + true + true + enable - + diff --git a/templates/content/EmptyEventFunction/Function.cs b/templates/content/EmptyEventFunction/Function.cs index bda2b71..94bf936 100644 --- a/templates/content/EmptyEventFunction/Function.cs +++ b/templates/content/EmptyEventFunction/Function.cs @@ -6,26 +6,25 @@ [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] -namespace EmptyEventFunction +namespace EmptyEventFunction; + +public class Function : EventFunction { - public class Function : EventFunction + protected override void Configure(IConfigurationBuilder builder) { - protected override void Configure(IConfigurationBuilder builder) - { - // Use this method to register your configuration flow. Exactly like in ASP.NET Core - } + // Use this method to register your configuration flow. Exactly like in ASP.NET Core + } - protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) - { - // Use this method to configure the logging - } + protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) + { + // Use this method to configure the logging + } - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) - { - // You need this line to register your handler - RegisterHandler(services); + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) + { + // You need this line to register your handler + RegisterHandler(services); - // Use this method to register your services. Exactly like in ASP.NET Core - } + // Use this method to register your services. Exactly like in ASP.NET Core } -} +} \ No newline at end of file diff --git a/templates/content/EmptyEventFunction/StringEventHandler.cs b/templates/content/EmptyEventFunction/StringEventHandler.cs index 9bb4f2c..9a014c0 100644 --- a/templates/content/EmptyEventFunction/StringEventHandler.cs +++ b/templates/content/EmptyEventFunction/StringEventHandler.cs @@ -4,22 +4,21 @@ using Kralizek.Lambda; using Microsoft.Extensions.Logging; -namespace EmptyEventFunction +namespace EmptyEventFunction; + +public class StringEventHandler : IEventHandler { - public class StringEventHandler : IEventHandler - { - private readonly ILogger _logger; + private readonly ILogger _logger; - public StringEventHandler(ILogger logger) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public StringEventHandler(ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public Task HandleAsync(string input, ILambdaContext context) - { - _logger.LogInformation($"Received: {input}"); + public Task HandleAsync(string? input, ILambdaContext context) + { + _logger.LogInformation("Received: {Input}", input); - return Task.CompletedTask; - } + return Task.CompletedTask; } } \ No newline at end of file diff --git a/templates/content/EmptyEventFunction/aws-lambda-tools-defaults.json b/templates/content/EmptyEventFunction/aws-lambda-tools-defaults.json index 6201fdc..ceaf6c1 100644 --- a/templates/content/EmptyEventFunction/aws-lambda-tools-defaults.json +++ b/templates/content/EmptyEventFunction/aws-lambda-tools-defaults.json @@ -11,10 +11,10 @@ "profile": "DefaultProfile", "region": "DefaultRegion", "configuration": "Release", - "framework": "netcoreapp3.1", + "framework": "net6.0", "function-name": "EmptyEventFunction", "function-role": "DefaultRole", - "function-runtime": "dotnetcore3.1", + "function-runtime": "dotnet6", "function-memory-size": 128, "function-timeout": 30, "function-handler": "EmptyEventFunction::EmptyEventFunction.Function::FunctionHandlerAsync" diff --git a/templates/content/EmptyRequestResponseFunction/EmptyRequestResponseFunction.csproj b/templates/content/EmptyRequestResponseFunction/EmptyRequestResponseFunction.csproj index ce465b2..9ae74f0 100644 --- a/templates/content/EmptyRequestResponseFunction/EmptyRequestResponseFunction.csproj +++ b/templates/content/EmptyRequestResponseFunction/EmptyRequestResponseFunction.csproj @@ -3,11 +3,14 @@ net6.0 true + enable Lambda + true + true - + \ No newline at end of file diff --git a/templates/content/EmptyRequestResponseFunction/Function.cs b/templates/content/EmptyRequestResponseFunction/Function.cs index 16d261e..64aac7f 100644 --- a/templates/content/EmptyRequestResponseFunction/Function.cs +++ b/templates/content/EmptyRequestResponseFunction/Function.cs @@ -6,26 +6,25 @@ [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] -namespace EmptyRequestResponseFunction +namespace EmptyRequestResponseFunction; + +public class Function : RequestResponseFunction { - public class Function : RequestResponseFunction + protected override void Configure(IConfigurationBuilder builder) { - protected override void Configure(IConfigurationBuilder builder) - { - // Use this method to register your configuration flow. Exactly like in ASP.NET Core - } + // Use this method to register your configuration flow. Exactly like in ASP.NET Core + } - protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) - { - // Use this method to install logger providers - } + protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) + { + // Use this method to install logger providers + } - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) - { - // You need this line to register your handler - RegisterHandler(services); + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) + { + // You need this line to register your handler + RegisterHandler(services); - // Use this method to register your services. Exactly like in ASP.NET Core - } + // Use this method to register your services. Exactly like in ASP.NET Core } } diff --git a/templates/content/EmptyRequestResponseFunction/ToUpperStringRequestResponseHandler.cs b/templates/content/EmptyRequestResponseFunction/ToUpperStringRequestResponseHandler.cs index 9ed1d34..dff2533 100644 --- a/templates/content/EmptyRequestResponseFunction/ToUpperStringRequestResponseHandler.cs +++ b/templates/content/EmptyRequestResponseFunction/ToUpperStringRequestResponseHandler.cs @@ -4,24 +4,23 @@ using Kralizek.Lambda; using Microsoft.Extensions.Logging; -namespace EmptyRequestResponseFunction +namespace EmptyRequestResponseFunction; + +public class ToUpperStringRequestResponseHandler : IRequestResponseHandler { - public class ToUpperStringRequestResponseHandler : IRequestResponseHandler - { - private readonly ILogger _logger; + private readonly ILogger _logger; - public ToUpperStringRequestResponseHandler(ILogger logger) + public ToUpperStringRequestResponseHandler(ILogger logger) + { + if (logger == null) { - if (logger == null) - { - throw new ArgumentNullException(nameof(logger)); - } - _logger = logger; + throw new ArgumentNullException(nameof(logger)); } + _logger = logger; + } - public async Task HandleAsync(string input, ILambdaContext context) - { - return input?.ToUpper(); - } + public async Task HandleAsync(string? input, ILambdaContext context) + { + return input?.ToUpper(); } } \ No newline at end of file diff --git a/templates/content/EmptyRequestResponseFunction/aws-lambda-tools-defaults.json b/templates/content/EmptyRequestResponseFunction/aws-lambda-tools-defaults.json index 826a93d..7278b20 100644 --- a/templates/content/EmptyRequestResponseFunction/aws-lambda-tools-defaults.json +++ b/templates/content/EmptyRequestResponseFunction/aws-lambda-tools-defaults.json @@ -11,10 +11,10 @@ "profile": "DefaultProfile", "region": "DefaultRegion", "configuration": "Release", - "framework": "netcoreapp3.1", + "framework": "net6.0", "function-name": "EmptyRequestResponseFunction", "function-role": "DefaultRole", - "function-runtime": "dotnetcore3.1", + "function-runtime": "dotnet6", "function-memory-size": 128, "function-timeout": 30, "function-handler": "EmptyRequestResponseFunction::EmptyRequestResponseFunction.Function::FunctionHandlerAsync" diff --git a/templates/content/RichEventFunction/Function.cs b/templates/content/RichEventFunction/Function.cs index ceca879..f0f2588 100644 --- a/templates/content/RichEventFunction/Function.cs +++ b/templates/content/RichEventFunction/Function.cs @@ -7,45 +7,44 @@ [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] -namespace RichEventFunction +namespace RichEventFunction; + +public class Function : EventFunction { - public class Function : EventFunction + protected override void Configure(IConfigurationBuilder builder) { - protected override void Configure(IConfigurationBuilder builder) - { - // Use this method to register your configuration flow. Exactly like in ASP.NET Core - builder.SetBasePath(Directory.GetCurrentDirectory()); - builder.AddJsonFile("appsettings.json", optional: true); - builder.AddEnvironmentVariables(); - } + // Use this method to register your configuration flow. Exactly like in ASP.NET Core + builder.SetBasePath(Directory.GetCurrentDirectory()); + builder.AddJsonFile("appsettings.json", optional: true); + builder.AddEnvironmentVariables(); + } - protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) - { - // Use this method to configure the logging + protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) + { + // Use this method to configure the logging - logging.AddConfiguration(Configuration.GetSection("Logging")); + logging.AddConfiguration(Configuration.GetSection("Logging")); - /* Pushes the valid log entries into the CloudWatch log group created for this Lambda function */ - logging.AddLambdaLogger(new LambdaLoggerOptions - { - IncludeCategory = true, - IncludeLogLevel = true, - IncludeNewline = true, - Filter = (categoryName, logLevel) => - { - /* Here you can filter which logs should go to CloudWatch. */ - - return logLevel >= LogLevel.Information; - } - }); - } - - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) + /* Pushes the valid log entries into the CloudWatch log group created for this Lambda function */ + logging.AddLambdaLogger(new LambdaLoggerOptions { - // You need this line to register your handler - RegisterHandler(services); + IncludeCategory = true, + IncludeLogLevel = true, + IncludeNewline = true, + Filter = (categoryName, logLevel) => + { + /* Here you can filter which logs should go to CloudWatch. */ + + return logLevel >= LogLevel.Information; + } + }); + } + + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) + { + // You need this line to register your handler + RegisterHandler(services); - // Use this method to register your services. Exactly like in ASP.NET Core - } + // Use this method to register your services. Exactly like in ASP.NET Core } -} +} \ No newline at end of file diff --git a/templates/content/RichEventFunction/RichEventFunction.csproj b/templates/content/RichEventFunction/RichEventFunction.csproj index b77a9f4..3500436 100644 --- a/templates/content/RichEventFunction/RichEventFunction.csproj +++ b/templates/content/RichEventFunction/RichEventFunction.csproj @@ -3,18 +3,18 @@ net6.0 true + enable Lambda + true + true - - - - - - - - + + + + + diff --git a/templates/content/RichEventFunction/StringEventHandler.cs b/templates/content/RichEventFunction/StringEventHandler.cs index f1e40b3..e139255 100644 --- a/templates/content/RichEventFunction/StringEventHandler.cs +++ b/templates/content/RichEventFunction/StringEventHandler.cs @@ -4,26 +4,25 @@ using Kralizek.Lambda; using Microsoft.Extensions.Logging; -namespace RichEventFunction +namespace RichEventFunction; + +public class StringEventHandler : IEventHandler { - public class StringEventHandler : IEventHandler - { - private readonly ILogger _logger; + private readonly ILogger _logger; - public StringEventHandler(ILogger logger) + public StringEventHandler(ILogger logger) + { + if (logger == null) { - if (logger == null) - { - throw new ArgumentNullException(nameof(logger)); - } - _logger = logger; + throw new ArgumentNullException(nameof(logger)); } + _logger = logger; + } - public Task HandleAsync(string input, ILambdaContext context) - { - _logger.LogInformation(new {input}, state => $"Received: {state.input}"); + public Task HandleAsync(string? input, ILambdaContext context) + { + _logger.LogInformation("Received: {Input}", input); - return Task.CompletedTask; - } + return Task.CompletedTask; } } \ No newline at end of file diff --git a/templates/content/RichEventFunction/aws-lambda-tools-defaults.json b/templates/content/RichEventFunction/aws-lambda-tools-defaults.json index a5b4dc4..a4f166a 100644 --- a/templates/content/RichEventFunction/aws-lambda-tools-defaults.json +++ b/templates/content/RichEventFunction/aws-lambda-tools-defaults.json @@ -11,10 +11,10 @@ "profile": "DefaultProfile", "region": "DefaultRegion", "configuration": "Release", - "framework": "netcoreapp3.1", + "framework": "net6.0", "function-name": "RichEventFunction", "function-role": "DefaultRole", - "function-runtime": "dotnetcore3.1", + "function-runtime": "dotnet6", "function-memory-size": 128, "function-timeout": 30, "function-handler": "RichEventFunction::RichEventFunction.Function::FunctionHandlerAsync" diff --git a/templates/content/RichRequestResponseFunction/Function.cs b/templates/content/RichRequestResponseFunction/Function.cs index 87c3914..f70a0ff 100644 --- a/templates/content/RichRequestResponseFunction/Function.cs +++ b/templates/content/RichRequestResponseFunction/Function.cs @@ -7,46 +7,45 @@ [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] -namespace RichRequestResponseFunction +namespace RichRequestResponseFunction; + +public class Function : RequestResponseFunction { - public class Function : RequestResponseFunction + protected override void Configure(IConfigurationBuilder builder) { - protected override void Configure(IConfigurationBuilder builder) - { - // Use this method to register your configuration flow. Exactly like in ASP.NET Core - builder.SetBasePath(Directory.GetCurrentDirectory()); + // Use this method to register your configuration flow. Exactly like in ASP.NET Core + builder.SetBasePath(Directory.GetCurrentDirectory()); - builder.AddJsonFile("appsettings.json", optional: true); - builder.AddEnvironmentVariables(); - } + builder.AddJsonFile("appsettings.json", optional: true); + builder.AddEnvironmentVariables(); + } - protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) - { - // Use this method to install logger providers + protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) + { + // Use this method to install logger providers - logging.AddConfiguration(Configuration.GetSection("Logging")); + logging.AddConfiguration(Configuration.GetSection("Logging")); - /* Pushes the valid log entries into the CloudWatch log group created for this Lambda function */ - logging.AddLambdaLogger(new LambdaLoggerOptions - { - IncludeCategory = true, - IncludeLogLevel = true, - IncludeNewline = true, - Filter = (categoryName, logLevel) => - { - /* Here you can filter which logs should go to CloudWatch. */ - - return logLevel >= LogLevel.Information; - } - }); - } - - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) + /* Pushes the valid log entries into the CloudWatch log group created for this Lambda function */ + logging.AddLambdaLogger(new LambdaLoggerOptions { - // You need this line to register your handler - RegisterHandler(services); + IncludeCategory = true, + IncludeLogLevel = true, + IncludeNewline = true, + Filter = (categoryName, logLevel) => + { + /* Here you can filter which logs should go to CloudWatch. */ + + return logLevel >= LogLevel.Information; + } + }); + } + + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) + { + // You need this line to register your handler + RegisterHandler(services); - // Use this method to register your services. Exactly like in ASP.NET Core - } + // Use this method to register your services. Exactly like in ASP.NET Core } } diff --git a/templates/content/RichRequestResponseFunction/RichRequestResponseFunction.csproj b/templates/content/RichRequestResponseFunction/RichRequestResponseFunction.csproj index 2361aa2..3500436 100644 --- a/templates/content/RichRequestResponseFunction/RichRequestResponseFunction.csproj +++ b/templates/content/RichRequestResponseFunction/RichRequestResponseFunction.csproj @@ -3,18 +3,18 @@ net6.0 true + enable Lambda + true + true - - - - - - - - + + + + + diff --git a/templates/content/RichRequestResponseFunction/ToUpperStringRequestResponseHandler.cs b/templates/content/RichRequestResponseFunction/ToUpperStringRequestResponseHandler.cs index fc5702d..a170197 100644 --- a/templates/content/RichRequestResponseFunction/ToUpperStringRequestResponseHandler.cs +++ b/templates/content/RichRequestResponseFunction/ToUpperStringRequestResponseHandler.cs @@ -4,24 +4,23 @@ using Kralizek.Lambda; using Microsoft.Extensions.Logging; -namespace RichRequestResponseFunction +namespace RichRequestResponseFunction; + +public class ToUpperStringRequestResponseHandler : IRequestResponseHandler { - public class ToUpperStringRequestResponseHandler : IRequestResponseHandler - { - private readonly ILogger _logger; + private readonly ILogger _logger; - public ToUpperStringRequestResponseHandler(ILogger logger) + public ToUpperStringRequestResponseHandler(ILogger logger) + { + if (logger == null) { - if (logger == null) - { - throw new ArgumentNullException(nameof(logger)); - } - _logger = logger; + throw new ArgumentNullException(nameof(logger)); } + _logger = logger; + } - public async Task HandleAsync(string input, ILambdaContext context) - { - return input?.ToUpper(); - } + public async Task HandleAsync(string? input, ILambdaContext context) + { + return input?.ToUpper(); } } \ No newline at end of file diff --git a/templates/content/RichRequestResponseFunction/aws-lambda-tools-defaults.json b/templates/content/RichRequestResponseFunction/aws-lambda-tools-defaults.json index 9e8a2d4..20a284d 100644 --- a/templates/content/RichRequestResponseFunction/aws-lambda-tools-defaults.json +++ b/templates/content/RichRequestResponseFunction/aws-lambda-tools-defaults.json @@ -11,10 +11,10 @@ "profile": "DefaultProfile", "region": "DefaultRegion", "configuration": "Release", - "framework": "netcoreapp3.1", + "framework": "net6.0", "function-name": "RichRequestResponseFunction", "function-role": "DefaultRole", - "function-runtime": "dotnetcore3.1", + "function-runtime": "dotnet6", "function-memory-size": 128, "function-timeout": 30, "function-handler": "RichRequestResponseFunction::RichRequestResponseFunction.Function::FunctionHandlerAsync" diff --git a/templates/content/SnsEventFunction/Function.cs b/templates/content/SnsEventFunction/Function.cs index 32c7365..75a6ccc 100644 --- a/templates/content/SnsEventFunction/Function.cs +++ b/templates/content/SnsEventFunction/Function.cs @@ -7,26 +7,25 @@ [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] -namespace SnsEventFunction +namespace SnsEventFunction; + +public class Function : EventFunction { - public class Function : EventFunction + protected override void Configure(IConfigurationBuilder builder) { - protected override void Configure(IConfigurationBuilder builder) - { - // Use this method to register your configuration flow. Exactly like in ASP.NET Core - } + // Use this method to register your configuration flow. Exactly like in ASP.NET Core + } - protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) - { - // Use this method to install logger providers - } + protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) + { + // Use this method to install logger providers + } - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) - { - // You need this line to register your handler - services.UseNotificationHandler(); + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) + { + // You need this line to register your handler + services.UseNotificationHandler(); - // Use this method to register your services. Exactly like in ASP.NET Core - } + // Use this method to register your services. Exactly like in ASP.NET Core } } diff --git a/templates/content/SnsEventFunction/Notification.cs b/templates/content/SnsEventFunction/Notification.cs index bdabf01..74d0953 100644 --- a/templates/content/SnsEventFunction/Notification.cs +++ b/templates/content/SnsEventFunction/Notification.cs @@ -1,14 +1,13 @@ -using System.Text.Json; +using System.Text.Json.Serialization; -namespace SnsEventFunction +namespace SnsEventFunction; + +/* + This class represents the notification you push into SNS and that is forwarded to this Lambda + Add properties so that the message can be properly deserialized. +*/ +public class Notification { - /* - This class represents the notification you push into SNS and that is forwarded to this Lambda - Add properties so that the message can be properly deserialized. - */ - public class Notification - { - [JsonPropertyName("message")] - public string Message { get; set; } - } + [JsonPropertyName("message")] + public string? Message { get; set; } } \ No newline at end of file diff --git a/templates/content/SnsEventFunction/NotificationHandler.cs b/templates/content/SnsEventFunction/NotificationHandler.cs index d4a2cf0..8f19662 100644 --- a/templates/content/SnsEventFunction/NotificationHandler.cs +++ b/templates/content/SnsEventFunction/NotificationHandler.cs @@ -4,22 +4,21 @@ using Kralizek.Lambda; using Microsoft.Extensions.Logging; -namespace SnsEventFunction +namespace SnsEventFunction; + +public class NotificationHandler : INotificationHandler { - public class NotificationHandler : INotificationHandler - { - private readonly ILogger _logger; + private readonly ILogger _logger; - public NotificationHandler(ILogger logger) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public NotificationHandler(ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public Task HandleAsync(Notification notification, ILambdaContext context) - { - _logger.LogInformation($"Received notification: {notification.Message}"); + public Task HandleAsync(Notification? notification, ILambdaContext context) + { + _logger.LogInformation("Received notification: {Message}", notification?.Message); - return Task.CompletedTask; - } + return Task.CompletedTask; } } \ No newline at end of file diff --git a/templates/content/SnsEventFunction/SnsEventFunction.csproj b/templates/content/SnsEventFunction/SnsEventFunction.csproj index c57e63b..8158007 100644 --- a/templates/content/SnsEventFunction/SnsEventFunction.csproj +++ b/templates/content/SnsEventFunction/SnsEventFunction.csproj @@ -3,12 +3,15 @@ net6.0 true + enable Lambda + true + true - - + + \ No newline at end of file diff --git a/templates/content/SnsEventFunction/aws-lambda-tools-defaults.json b/templates/content/SnsEventFunction/aws-lambda-tools-defaults.json index 9c7f895..9c93e59 100644 --- a/templates/content/SnsEventFunction/aws-lambda-tools-defaults.json +++ b/templates/content/SnsEventFunction/aws-lambda-tools-defaults.json @@ -11,10 +11,10 @@ "profile": "", "region": "", "configuration": "Release", - "framework": "netcoreapp3.1", + "framework": "net6.0", "function-name": "SnsEventFunction", "function-role": "", - "function-runtime": "dotnetcore3.1", + "function-runtime": "dotnet6", "function-memory-size": 128, "function-timeout": 30, "function-handler": "SnsEventFunction::SnsEventFunction.Function::FunctionHandlerAsync" diff --git a/templates/content/SqsEventFunction/Function.cs b/templates/content/SqsEventFunction/Function.cs index c65c2b9..faf46e0 100644 --- a/templates/content/SqsEventFunction/Function.cs +++ b/templates/content/SqsEventFunction/Function.cs @@ -7,26 +7,25 @@ [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] -namespace SqsEventFunction +namespace SqsEventFunction; + +public class Function : EventFunction { - public class Function : EventFunction + protected override void Configure(IConfigurationBuilder builder) { - protected override void Configure(IConfigurationBuilder builder) - { - // Use this method to register your configuration flow. Exactly like in ASP.NET Core - } + // Use this method to register your configuration flow. Exactly like in ASP.NET Core + } - protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) - { - // Use this method to install logger providers - } + protected override void ConfigureLogging(ILoggingBuilder logging, IExecutionEnvironment executionEnvironment) + { + // Use this method to install logger providers + } - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) - { - // You need this line to register your handler - services.UseSqsHandler(); + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) + { + // You need this line to register your handler + services.UseSqsHandler(); - // Use this method to register your services. Exactly like in ASP.NET Core - } + // Use this method to register your services. Exactly like in ASP.NET Core } } diff --git a/templates/content/SqsEventFunction/SqsEventFunction.csproj b/templates/content/SqsEventFunction/SqsEventFunction.csproj index 48cb6e0..2547272 100644 --- a/templates/content/SqsEventFunction/SqsEventFunction.csproj +++ b/templates/content/SqsEventFunction/SqsEventFunction.csproj @@ -3,12 +3,15 @@ net6.0 true + enable Lambda + true + true - - + + \ No newline at end of file diff --git a/templates/content/SqsEventFunction/TestMessage.cs b/templates/content/SqsEventFunction/TestMessage.cs index 19dbdcd..e273cb1 100644 --- a/templates/content/SqsEventFunction/TestMessage.cs +++ b/templates/content/SqsEventFunction/TestMessage.cs @@ -1,14 +1,13 @@ -using System.Text.Json; +using System.Text.Json.Serialization; -namespace SqsEventFunction +namespace SqsEventFunction; + +/* + This class represents the message you push into SQS and that is forwarded to this Lambda + Add properties so that the message can be properly deserialized. +*/ +public class TestMessage { - /* - This class represents the message you push into SQS and that is forwarded to this Lambda - Add properties so that the message can be properly deserialized. - */ - public class TestMessage - { - [JsonPropertyName("message")] - public string Message { get; set; } - } + [JsonPropertyName("message")] + public string? Message { get; set; } } \ No newline at end of file diff --git a/templates/content/SqsEventFunction/TestMessageHandler.cs b/templates/content/SqsEventFunction/TestMessageHandler.cs index 875170d..ec71e99 100644 --- a/templates/content/SqsEventFunction/TestMessageHandler.cs +++ b/templates/content/SqsEventFunction/TestMessageHandler.cs @@ -4,22 +4,21 @@ using Kralizek.Lambda; using Microsoft.Extensions.Logging; -namespace SqsEventFunction +namespace SqsEventFunction; + +public class TestMessageHandler : IMessageHandler { - public class TestMessageHandler : IMessageHandler - { - private readonly ILogger _logger; + private readonly ILogger _logger; - public TestMessageHandler(ILogger logger) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public TestMessageHandler(ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public Task HandleAsync(TestMessage message, ILambdaContext context) - { - _logger.LogInformation($"Received notification: {message.Message}"); + public Task HandleAsync(TestMessage? message, ILambdaContext context) + { + _logger.LogInformation("Received notification: {Message}", message?.Message); - return Task.CompletedTask; - } + return Task.CompletedTask; } } \ No newline at end of file diff --git a/templates/content/SqsEventFunction/aws-lambda-tools-defaults.json b/templates/content/SqsEventFunction/aws-lambda-tools-defaults.json index d2f4ab7..6148f89 100644 --- a/templates/content/SqsEventFunction/aws-lambda-tools-defaults.json +++ b/templates/content/SqsEventFunction/aws-lambda-tools-defaults.json @@ -11,10 +11,10 @@ "profile": "", "region": "", "configuration": "Release", - "framework": "netcoreapp3.1", + "framework": "net6.0", "function-name": "SqsEventFunction", "function-role": "", - "function-runtime": "dotnetcore3.1", + "function-runtime": "dotnet6", "function-memory-size": 128, "function-timeout": 30, "function-handler": "SqsEventFunction::SqsEventFunction.Function::FunctionHandlerAsync" diff --git a/tests/Tests.Lambda.Template/CustomSerializerTests.cs b/tests/Tests.Lambda.Template/CustomSerializerTests.cs index 57a9d2c..e256f4b 100644 --- a/tests/Tests.Lambda.Template/CustomSerializerTests.cs +++ b/tests/Tests.Lambda.Template/CustomSerializerTests.cs @@ -1,116 +1,113 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using Amazon.Lambda.Core; -using Amazon.Lambda.SNSEvents; using Amazon.Lambda.SQSEvents; -using Amazon.Lambda.TestUtilities; using Kralizek.Lambda; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Moq; using System.Text.Json; using NUnit.Framework; -namespace Tests.Lambda +namespace Tests.Lambda; + +[TestFixture] +public class CustomSerializerTests { - [TestFixture] - public class CustomSerializerTests + private const string RawMessage = "hello world"; + private const string Message = "hello world top secret injection from serializer"; + + [SetUp] + public void Initialize() { - [SetUp] - public void Initialize() - { - } + } - private class SomeEvent - { - public string message { get; set; } - } + private class SomeEvent + { + public string message { get; set; } + } - private class CustomSerializer : IMessageSerializer + private class CustomSerializer : IMessageSerializer + { + public T Deserialize(string input) { - public T Deserialize(string input) - { - // do some fancy 'serializing' - // use input parameter instead of the hardcoded value here - return JsonSerializer.Deserialize("{\r\n\"message\": \"hello world topsecret injection from serializer\"\r\n}"); - } + // do some fancy 'serializing' + // use input parameter instead of the hardcoded value here + return JsonSerializer.Deserialize($"{{\r\n\"message\": \"{Message}\"\r\n}}")!; } + } - // Handlers - private class DummyHandler : IMessageHandler + // Handlers + private class DummyHandler : IMessageHandler + { + public Task HandleAsync(SomeEvent evt, ILambdaContext context) { - public Task HandleAsync(SomeEvent evt, ILambdaContext context) - { - Assert.True(evt.message == "hello world topsecret injection from serializer"); - return Task.CompletedTask; - } + Assert.That(evt?.message, Is.EqualTo(Message)); + + return Task.CompletedTask; } + } - private class DummyHandlerNoChanges : IMessageHandler + private class DummyHandlerNoChanges : IMessageHandler + { + public Task HandleAsync(SomeEvent evt, ILambdaContext context) { - public Task HandleAsync(SomeEvent evt, ILambdaContext context) - { - Assert.True(evt.message == "hello world"); - return Task.CompletedTask; - } + Assert.That(evt?.message, Is.EqualTo(RawMessage)); + return Task.CompletedTask; } + } - // Functions - private class DummyFunction : EventFunction + // Functions + private class DummyFunction : EventFunction + { + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) { - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) - { - services.UseQueueMessageHandler(); + services.UseQueueMessageHandler(); - services.AddSingleton(); - } + services.AddSingleton(); } + } - private class DummyFunctionNoChanges : EventFunction + private class DummyFunctionNoChanges : EventFunction + { + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) { - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) - { - services.UseQueueMessageHandler(); - } + services.UseQueueMessageHandler(); } + } - [Test] - public async Task HandleAsync_DeserializesCorrectly() - { - var instance = new DummyFunction(); + [Test] + public async Task HandleAsync_DeserializesCorrectly() + { + var instance = new DummyFunction(); - await instance.FunctionHandlerAsync(new SQSEvent() + await instance.FunctionHandlerAsync(new SQSEvent() + { + Records = new List() { - Records = new List() + new SQSEvent.SQSMessage() { - new SQSEvent.SQSMessage() - { - // or xml.. or json.. or whatever you want - Body = "{\r\n\"message\": \"hello world\"\r\n}" - } + // or xml.. or json.. or whatever you want + Body = $"{{\r\n\"message\": \"{RawMessage}\"\r\n}}" } - }, null); - } + } + }, null!); + } - [Test] - public async Task HandleAsync_DeserializesCorrectly_WithoutChanges() + [Test] + public async Task HandleAsync_DeserializesCorrectly_WithoutChanges() + { + var instance = new DummyFunctionNoChanges(); + await instance.FunctionHandlerAsync(new SQSEvent() { - var instance = new DummyFunctionNoChanges(); - await instance.FunctionHandlerAsync(new SQSEvent() + Records = new List() { - Records = new List() + new SQSEvent.SQSMessage() { - new SQSEvent.SQSMessage() - { - // or xml.. or json.. or whatever you want - Body = "{\r\n\"message\": \"hello world\"\r\n}" - } + // or xml.. or json.. or whatever you want + Body = $"{{\r\n\"message\": \"{RawMessage}\"\r\n}}" } - }, null); - } + } + }, null!); } -} +} \ No newline at end of file diff --git a/tests/Tests.Lambda.Template/EventFunctionDisposalTests.cs b/tests/Tests.Lambda.Template/EventFunctionDisposalTests.cs index 5047642..f7cf559 100644 --- a/tests/Tests.Lambda.Template/EventFunctionDisposalTests.cs +++ b/tests/Tests.Lambda.Template/EventFunctionDisposalTests.cs @@ -6,65 +6,64 @@ using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; -namespace Tests.Lambda +namespace Tests.Lambda; + +public class EventFunctionDisposalTests { - public class EventFunctionDisposalTests + [Test] + public void FunctionHandlerAsync_is_awaited_before_disposal() { - [Test] - public void FunctionHandlerAsync_is_awaited_before_disposal() - { - var dependency = new DisposableDependency(); - var taskCompletionSource = new TaskCompletionSource(); - var sut = new TestEventFunction(dependency, taskCompletionSource); + var dependency = new DisposableDependency(); + var taskCompletionSource = new TaskCompletionSource(); + var sut = new TestEventFunction(dependency, taskCompletionSource); - var result = sut.FunctionHandlerAsync("Hi there", new TestLambdaContext()); + var result = sut.FunctionHandlerAsync("Hi there", new TestLambdaContext()); - Assert.That(dependency.Disposed, Is.False, "Dependency should not be disposed"); - Assert.That(result.IsCompleted, Is.False, "The task should not be completed"); + Assert.That(dependency.Disposed, Is.False, "Dependency should not be disposed"); + Assert.That(result.IsCompleted, Is.False, "The task should not be completed"); - taskCompletionSource.SetResult("done"); + taskCompletionSource.SetResult("done"); - Assert.That(dependency.Disposed, Is.True, "Dependency should be disposed"); - Assert.That(result.IsCompleted, Is.True, "The task should be completed"); - } - - public class TestEventFunction : EventFunction - { - private readonly DisposableDependency _dependency; - private readonly TaskCompletionSource _tcs; + Assert.That(dependency.Disposed, Is.True, "Dependency should be disposed"); + Assert.That(result.IsCompleted, Is.True, "The task should be completed"); + } - public TestEventFunction(DisposableDependency dependency, TaskCompletionSource tcs) - { - _dependency = dependency; - _tcs = tcs; - } + public class TestEventFunction : EventFunction + { + private readonly DisposableDependency _dependency; + private readonly TaskCompletionSource _tcs; - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) - { - services.AddScoped(_ => _dependency); - services.AddTransient>(s => new TestHandler(s.GetRequiredService(), _tcs)); - } - } - - public class DisposableDependency : IDisposable + public TestEventFunction(DisposableDependency dependency, TaskCompletionSource tcs) { - public bool Disposed { get; private set; } - public void Dispose() => Disposed = true; + _dependency = dependency; + _tcs = tcs; } - private class TestHandler : IEventHandler + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) { - // ReSharper disable once NotAccessedField.Local - private readonly DisposableDependency _dependency; - private readonly TaskCompletionSource _tcs; + services.AddScoped(_ => _dependency); + services.AddTransient>(s => new TestHandler(s.GetRequiredService(), _tcs)); + } + } + + public class DisposableDependency : IDisposable + { + public bool Disposed { get; private set; } + public void Dispose() => Disposed = true; + } - public TestHandler(DisposableDependency dependency, TaskCompletionSource tcs) - { - _dependency = dependency; - _tcs = tcs; - } + private class TestHandler : IEventHandler + { + // ReSharper disable once NotAccessedField.Local + private readonly DisposableDependency _dependency; + private readonly TaskCompletionSource _tcs; - public Task HandleAsync(string input, ILambdaContext context) => _tcs.Task; + public TestHandler(DisposableDependency dependency, TaskCompletionSource tcs) + { + _dependency = dependency; + _tcs = tcs; } + + public Task HandleAsync(string input, ILambdaContext context) => _tcs.Task; } } \ No newline at end of file diff --git a/tests/Tests.Lambda.Template/EventFunctionTests.cs b/tests/Tests.Lambda.Template/EventFunctionTests.cs index 17a7680..f1a0843 100644 --- a/tests/Tests.Lambda.Template/EventFunctionTests.cs +++ b/tests/Tests.Lambda.Template/EventFunctionTests.cs @@ -1,71 +1,67 @@ using System; -using System.Threading.Tasks; -using Amazon.Lambda.Core; using Amazon.Lambda.TestUtilities; using Kralizek.Lambda; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Moq; using NUnit.Framework; -namespace Tests.Lambda +namespace Tests.Lambda; + +[TestFixture] +public class EventFunctionTests { - [TestFixture] - public class EventFunctionTests + private TestEventFunction CreateSystemUnderTest() { - private TestEventFunction CreateSystemUnderTest() - { - return new TestEventFunction(); - } + return new TestEventFunction(); + } - [Test] - public void Configure_should_be_invoked_on_type_initialization() - { - var sut = CreateSystemUnderTest(); + [Test] + public void Configure_should_be_invoked_on_type_initialization() + { + var sut = CreateSystemUnderTest(); - Assert.True(sut.IsConfigureInvoked); - } + Assert.True(sut.IsConfigureInvoked); + } - [Test] - public void ConfigureServices_should_be_invoked_on_type_initialization() - { - var sut = CreateSystemUnderTest(); + [Test] + public void ConfigureServices_should_be_invoked_on_type_initialization() + { + var sut = CreateSystemUnderTest(); - Assert.True(sut.IsConfigureServicesInvoked); - } + Assert.True(sut.IsConfigureServicesInvoked); + } - [Test] - public void ConfigureLogging_should_be_invoked_on_type_initialization() - { - var sut = CreateSystemUnderTest(); + [Test] + public void ConfigureLogging_should_be_invoked_on_type_initialization() + { + var sut = CreateSystemUnderTest(); - Assert.True(sut.IsConfigureLoggingInvoked); - } + Assert.True(sut.IsConfigureLoggingInvoked); + } - [Test] - public void FunctionHandlerAsync_throws_if_no_handler_is_registered() - { - var sut = CreateSystemUnderTest(); + [Test] + public void FunctionHandlerAsync_throws_if_no_handler_is_registered() + { + var sut = CreateSystemUnderTest(); - var context = new TestLambdaContext(); + var context = new TestLambdaContext(); - Assert.ThrowsAsync(() => sut.FunctionHandlerAsync("Hello World", context)); - } + Assert.ThrowsAsync(() => sut.FunctionHandlerAsync("Hello World", context)); + } - public class TestEventFunction : EventFunction - { - protected override void Configure(IConfigurationBuilder builder) => IsConfigureInvoked = true; + public class TestEventFunction : EventFunction + { + protected override void Configure(IConfigurationBuilder builder) => IsConfigureInvoked = true; - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) => IsConfigureServicesInvoked = true; + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) => IsConfigureServicesInvoked = true; - protected override void ConfigureLogging(ILoggingBuilder loggerFactory, IExecutionEnvironment executionEnvironment) => IsConfigureLoggingInvoked = true; + protected override void ConfigureLogging(ILoggingBuilder loggerFactory, IExecutionEnvironment executionEnvironment) => IsConfigureLoggingInvoked = true; - public bool IsConfigureInvoked { get; private set; } + public bool IsConfigureInvoked { get; private set; } - public bool IsConfigureServicesInvoked { get; private set; } + public bool IsConfigureServicesInvoked { get; private set; } - public bool IsConfigureLoggingInvoked { get; private set; } - } + public bool IsConfigureLoggingInvoked { get; private set; } } } \ No newline at end of file diff --git a/tests/Tests.Lambda.Template/RequestResponseFunctionDisposalTests.cs b/tests/Tests.Lambda.Template/RequestResponseFunctionDisposalTests.cs index e1179b7..e3d9ee6 100644 --- a/tests/Tests.Lambda.Template/RequestResponseFunctionDisposalTests.cs +++ b/tests/Tests.Lambda.Template/RequestResponseFunctionDisposalTests.cs @@ -6,65 +6,64 @@ using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; -namespace Tests.Lambda +namespace Tests.Lambda; + +public class RequestResponseFunctionDisposalTests { - public class RequestResponseFunctionDisposalTests + [Test] + public void FunctionHandlerAsync_is_awaited_before_disposal() { - [Test] - public void FunctionHandlerAsync_is_awaited_before_disposal() - { - var dependency = new DisposableDependency(); - var taskCompletionSource = new TaskCompletionSource(); - var sut = new TestEventFunction(dependency, taskCompletionSource); + var dependency = new DisposableDependency(); + var taskCompletionSource = new TaskCompletionSource(); + var sut = new TestEventFunction(dependency, taskCompletionSource); - var result = sut.FunctionHandlerAsync("Hi there", new TestLambdaContext()); + var result = sut.FunctionHandlerAsync("Hi there", new TestLambdaContext()); - Assert.That(dependency.Disposed, Is.False, "Dependency should not be disposed"); - Assert.That(result.IsCompleted, Is.False, "The task should not be completed"); + Assert.That(dependency.Disposed, Is.False, "Dependency should not be disposed"); + Assert.That(result.IsCompleted, Is.False, "The task should not be completed"); - taskCompletionSource.SetResult("done"); + taskCompletionSource.SetResult("done"); - Assert.That(dependency.Disposed, Is.True, "Dependency should be disposed"); - Assert.That(result.IsCompleted, Is.True, "The task should be completed"); - } - - public class TestEventFunction : RequestResponseFunction - { - private readonly DisposableDependency _dependency; - private readonly TaskCompletionSource _tcs; + Assert.That(dependency.Disposed, Is.True, "Dependency should be disposed"); + Assert.That(result.IsCompleted, Is.True, "The task should be completed"); + } - public TestEventFunction(DisposableDependency dependency, TaskCompletionSource tcs) - { - _dependency = dependency; - _tcs = tcs; - } + public class TestEventFunction : RequestResponseFunction + { + private readonly DisposableDependency _dependency; + private readonly TaskCompletionSource _tcs; - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) - { - services.AddScoped(_ => _dependency); - services.AddTransient>(s => new TestHandler(s.GetRequiredService(), _tcs)); - } - } - - public class DisposableDependency : IDisposable + public TestEventFunction(DisposableDependency dependency, TaskCompletionSource tcs) { - public bool Disposed { get; private set; } - public void Dispose() => Disposed = true; + _dependency = dependency; + _tcs = tcs; } - private class TestHandler : IRequestResponseHandler + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) { - // ReSharper disable once NotAccessedField.Local - private readonly DisposableDependency _dependency; - private readonly TaskCompletionSource _tcs; + services.AddScoped(_ => _dependency); + services.AddTransient>(s => new TestHandler(s.GetRequiredService(), _tcs)); + } + } + + public class DisposableDependency : IDisposable + { + public bool Disposed { get; private set; } + public void Dispose() => Disposed = true; + } - public TestHandler(DisposableDependency dependency, TaskCompletionSource tcs) - { - _dependency = dependency; - _tcs = tcs; - } + private class TestHandler : IRequestResponseHandler + { + // ReSharper disable once NotAccessedField.Local + private readonly DisposableDependency _dependency; + private readonly TaskCompletionSource _tcs; - public Task HandleAsync(string input, ILambdaContext context) => _tcs.Task; + public TestHandler(DisposableDependency dependency, TaskCompletionSource tcs) + { + _dependency = dependency; + _tcs = tcs; } + + public Task HandleAsync(string input, ILambdaContext context) => _tcs.Task; } } \ No newline at end of file diff --git a/tests/Tests.Lambda.Template/RequestResponseFunctionTests.cs b/tests/Tests.Lambda.Template/RequestResponseFunctionTests.cs index 6939842..d5f6ee2 100644 --- a/tests/Tests.Lambda.Template/RequestResponseFunctionTests.cs +++ b/tests/Tests.Lambda.Template/RequestResponseFunctionTests.cs @@ -1,5 +1,4 @@ using System; -using System.Threading.Tasks; using Amazon.Lambda.TestUtilities; using Kralizek.Lambda; using Microsoft.Extensions.Configuration; @@ -7,63 +6,62 @@ using Microsoft.Extensions.Logging; using NUnit.Framework; -namespace Tests.Lambda +namespace Tests.Lambda; + +[TestFixture] +public class RequestResponseFunctionTests { - [TestFixture] - public class RequestResponseFunctionTests + private RequestResponseFunction CreateSystemUnderTest() { - private RequestResponseFunction CreateSystemUnderTest() - { - return new RequestResponseFunction(); - } + return new RequestResponseFunction(); + } - [Test] - public void Configure_should_be_invoked_on_type_initialization() - { - var sut = CreateSystemUnderTest(); + [Test] + public void Configure_should_be_invoked_on_type_initialization() + { + var sut = CreateSystemUnderTest(); - Assert.True(sut.IsConfigureInvoked); - } + Assert.True(sut.IsConfigureInvoked); + } - [Test] - public void ConfigureServices_should_be_invoked_on_type_initialization() - { - var sut = CreateSystemUnderTest(); + [Test] + public void ConfigureServices_should_be_invoked_on_type_initialization() + { + var sut = CreateSystemUnderTest(); - Assert.True(sut.IsConfigureServicesInvoked); - } + Assert.True(sut.IsConfigureServicesInvoked); + } - [Test] - public void ConfigureLogging_should_be_invoked_on_type_initialization() - { - var sut = CreateSystemUnderTest(); + [Test] + public void ConfigureLogging_should_be_invoked_on_type_initialization() + { + var sut = CreateSystemUnderTest(); - Assert.True(sut.IsConfigureLoggingInvoked); - } + Assert.True(sut.IsConfigureLoggingInvoked); + } - [Test] - public void FunctionHandlerAsync_throws_if_no_handler_is_registered() - { - var sut = CreateSystemUnderTest(); + [Test] + public void FunctionHandlerAsync_throws_if_no_handler_is_registered() + { + var sut = CreateSystemUnderTest(); - var context = new TestLambdaContext(); + var context = new TestLambdaContext(); - Assert.ThrowsAsync(() => sut.FunctionHandlerAsync("Hello World", context)); - } + Assert.ThrowsAsync(() => sut.FunctionHandlerAsync("Hello World", context)); + } - public class RequestResponseFunction : RequestResponseFunction - { - protected override void Configure(IConfigurationBuilder builder) => IsConfigureInvoked = true; + public class RequestResponseFunction : RequestResponseFunction + { + protected override void Configure(IConfigurationBuilder builder) => IsConfigureInvoked = true; - protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) => IsConfigureServicesInvoked = true; + protected override void ConfigureServices(IServiceCollection services, IExecutionEnvironment executionEnvironment) => IsConfigureServicesInvoked = true; - protected override void ConfigureLogging(ILoggingBuilder loggerFactory, IExecutionEnvironment executionEnvironment) => IsConfigureLoggingInvoked = true; + protected override void ConfigureLogging(ILoggingBuilder loggerFactory, IExecutionEnvironment executionEnvironment) => IsConfigureLoggingInvoked = true; - public bool IsConfigureInvoked { get; private set; } + public bool IsConfigureInvoked { get; private set; } - public bool IsConfigureServicesInvoked { get; private set; } + public bool IsConfigureServicesInvoked { get; private set; } - public bool IsConfigureLoggingInvoked { get; private set; } - } + public bool IsConfigureLoggingInvoked { get; private set; } } -} +} \ No newline at end of file diff --git a/tests/Tests.Lambda.Template/Sns/ParallelSnsEventHandlerTests.cs b/tests/Tests.Lambda.Template/Sns/ParallelSnsEventHandlerTests.cs index d23bf30..a6109ef 100644 --- a/tests/Tests.Lambda.Template/Sns/ParallelSnsEventHandlerTests.cs +++ b/tests/Tests.Lambda.Template/Sns/ParallelSnsEventHandlerTests.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using Amazon.Lambda.Core; using Amazon.Lambda.SNSEvents; -using Amazon.Lambda.SQSEvents; using Amazon.Lambda.TestUtilities; using Kralizek.Lambda; using Microsoft.Extensions.DependencyInjection; @@ -13,307 +12,306 @@ using Moq; using NUnit.Framework; -namespace Tests.Lambda.Sns +namespace Tests.Lambda.Sns; + +[TestFixture] +public class ParallelSnsEventHandlerTests { - [TestFixture] - public class ParallelSnsEventHandlerTests + private Mock _mockNotificationSerializer; + private Mock> _mockNotificationHandler; + private Mock _mockServiceScopeFactory; + private Mock _mockServiceProvider; + private Mock _mockLoggerFactory; + private Mock _mockServiceScope; + private ParallelSnsExecutionOptions _parallelExecutionOptions; + + [SetUp] + public void Initialize() { - private Mock _mockNotificationSerializer; - private Mock> _mockNotificationHandler; - private Mock _mockServiceScopeFactory; - private Mock _mockServiceProvider; - private Mock _mockLoggerFactory; - private Mock _mockServiceScope; - private ParallelSnsExecutionOptions _parallelExecutionOptions; - - [SetUp] - public void Initialize() - { - _mockNotificationSerializer = new Mock(); - _mockNotificationSerializer.Setup(p => p.Deserialize(It.IsAny())).Returns(() => new TestNotification()); + _mockNotificationSerializer = new Mock(); + _mockNotificationSerializer.Setup(p => p.Deserialize(It.IsAny())).Returns(() => new TestNotification()); - _mockNotificationHandler = new Mock>(); - _mockNotificationHandler - .Setup(p => p.HandleAsync(It.IsAny(), It.IsAny())) - .Returns(Task.CompletedTask); + _mockNotificationHandler = new Mock>(); + _mockNotificationHandler + .Setup(p => p.HandleAsync(It.IsAny(), It.IsAny())) + .Returns(Task.CompletedTask); - _mockServiceScope = new Mock(); + _mockServiceScope = new Mock(); - _mockServiceScopeFactory = new Mock(); - _mockServiceScopeFactory - .Setup(p => p.CreateScope()) - .Returns(_mockServiceScope.Object); + _mockServiceScopeFactory = new Mock(); + _mockServiceScopeFactory + .Setup(p => p.CreateScope()) + .Returns(_mockServiceScope.Object); - _mockServiceProvider = new Mock(); - _mockServiceProvider - .Setup(p => p.GetService(typeof(INotificationHandler))) - .Returns(_mockNotificationHandler.Object); + _mockServiceProvider = new Mock(); + _mockServiceProvider + .Setup(p => p.GetService(typeof(INotificationHandler))) + .Returns(_mockNotificationHandler.Object); - _mockServiceProvider - .Setup(p => p.GetService(typeof(IServiceScopeFactory))) - .Returns(_mockServiceScopeFactory.Object); + _mockServiceProvider + .Setup(p => p.GetService(typeof(IServiceScopeFactory))) + .Returns(_mockServiceScopeFactory.Object); - _mockServiceProvider - .Setup(p => p.GetService(typeof(INotificationSerializer))) - .Returns(_mockNotificationSerializer.Object); + _mockServiceProvider + .Setup(p => p.GetService(typeof(INotificationSerializer))) + .Returns(_mockNotificationSerializer.Object); - _mockServiceScope - .Setup(p => p.ServiceProvider) - .Returns(_mockServiceProvider.Object); + _mockServiceScope + .Setup(p => p.ServiceProvider) + .Returns(_mockServiceProvider.Object); - _mockLoggerFactory = new Mock(); - _mockLoggerFactory - .Setup(p => p.CreateLogger(It.IsAny())) - .Returns(Mock.Of()); + _mockLoggerFactory = new Mock(); + _mockLoggerFactory + .Setup(p => p.CreateLogger(It.IsAny())) + .Returns(Mock.Of()); - _parallelExecutionOptions = new ParallelSnsExecutionOptions { MaxDegreeOfParallelism = 4 }; + _parallelExecutionOptions = new ParallelSnsExecutionOptions { MaxDegreeOfParallelism = 4 }; - } + } - private ParallelSnsEventHandler CreateSystemUnderTest() - { - return new ParallelSnsEventHandler(_mockServiceProvider.Object, _mockLoggerFactory.Object, Options.Create(_parallelExecutionOptions)); - } + private ParallelSnsEventHandler CreateSystemUnderTest() + { + return new ParallelSnsEventHandler(_mockServiceProvider.Object, _mockLoggerFactory.Object, Options.Create(_parallelExecutionOptions)); + } - [Test] - public async Task HandleAsync_resolves_MessageHandler_for_each_record() + [Test] + public async Task HandleAsync_resolves_MessageHandler_for_each_record() + { + var snsEvent = new SNSEvent { - var snsEvent = new SNSEvent + Records = new List { - Records = new List + new SNSEvent.SNSRecord { - new SNSEvent.SNSRecord + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } + Message = "{}" } } - }; + } + }; - var lambdaContext = new TestLambdaContext(); + var lambdaContext = new TestLambdaContext(); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - await sut.HandleAsync(snsEvent, lambdaContext); + await sut.HandleAsync(snsEvent, lambdaContext); - _mockServiceProvider.Verify(p => p.GetService(typeof(INotificationHandler)), Times.Exactly(snsEvent.Records.Count)); - } + _mockServiceProvider.Verify(p => p.GetService(typeof(INotificationHandler)), Times.Exactly(snsEvent.Records.Count)); + } - [Test] - public async Task HandleAsync_resolves_NotificationHandler_for_each_record() + [Test] + public async Task HandleAsync_resolves_NotificationHandler_for_each_record() + { + var snsEvent = new SNSEvent { - var snsEvent = new SNSEvent + Records = new List { - Records = new List + new SNSEvent.SNSRecord { - new SNSEvent.SNSRecord + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } + Message = "{}" } } - }; + } + }; - var lambdaContext = new TestLambdaContext(); + var lambdaContext = new TestLambdaContext(); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - await sut.HandleAsync(snsEvent, lambdaContext); + await sut.HandleAsync(snsEvent, lambdaContext); - _mockServiceProvider.Verify(p => p.GetService(typeof(INotificationHandler)), Times.Exactly(snsEvent.Records.Count)); - } + _mockServiceProvider.Verify(p => p.GetService(typeof(INotificationHandler)), Times.Exactly(snsEvent.Records.Count)); + } - [Test] - public void HandleAsync_throws_InvalidOperation_if_NotificationHandler_is_not_registered() + [Test] + public void HandleAsync_throws_InvalidOperation_if_NotificationHandler_is_not_registered() + { + var snsEvent = new SNSEvent { - var snsEvent = new SNSEvent + Records = new List { - Records = new List + new SNSEvent.SNSRecord { - new SNSEvent.SNSRecord + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } + Message = "{}" } } - }; + } + }; - var lambdaContext = new TestLambdaContext(); + var lambdaContext = new TestLambdaContext(); - _mockServiceProvider = new Mock(); - _mockServiceProvider.Setup(p => p.GetService(typeof(IServiceScopeFactory))).Returns(_mockServiceScopeFactory.Object); - _mockServiceScope.Setup(p => p.ServiceProvider).Returns(_mockServiceProvider.Object); + _mockServiceProvider = new Mock(); + _mockServiceProvider.Setup(p => p.GetService(typeof(IServiceScopeFactory))).Returns(_mockServiceScopeFactory.Object); + _mockServiceScope.Setup(p => p.ServiceProvider).Returns(_mockServiceProvider.Object); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - Assert.ThrowsAsync(() => sut.HandleAsync(snsEvent, lambdaContext)); - } + Assert.ThrowsAsync(() => sut.HandleAsync(snsEvent, lambdaContext)); + } - [Test] - public async Task MaxDegreeOfParallelism_Should_ProperlyPropagated() + [Test] + public async Task MaxDegreeOfParallelism_Should_ProperlyPropagated() + { + var snsEvent = new SNSEvent { - var snsEvent = new SNSEvent + Records = new List { - Records = new List + new SNSEvent.SNSRecord { - new SNSEvent.SNSRecord + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } + Message = "{}" } } - }; + } + }; - var cq = new ConcurrentQueue(); + var cq = new ConcurrentQueue(); - _parallelExecutionOptions = new ParallelSnsExecutionOptions { MaxDegreeOfParallelism = 2 }; - _mockNotificationHandler.Setup(p => p.HandleAsync(It.IsAny(), It.IsAny())) - .Returns(async () => + _parallelExecutionOptions = new ParallelSnsExecutionOptions { MaxDegreeOfParallelism = 2 }; + _mockNotificationHandler.Setup(p => p.HandleAsync(It.IsAny(), It.IsAny())) + .Returns(async () => + { + var t = Task.Delay(1); + cq.Enqueue(t); + if (cq.Count > 2) { - var t = Task.Delay(1); - cq.Enqueue(t); - if (cq.Count > 2) - { - throw new Exception("not good"); - } - await t; - cq.TryDequeue(out t); - }); + throw new Exception("not good"); + } + await t; + cq.TryDequeue(out t); + }); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - await sut.HandleAsync(snsEvent, new TestLambdaContext()); + await sut.HandleAsync(snsEvent, new TestLambdaContext()); - _mockNotificationHandler.VerifyAll(); - _mockNotificationHandler.Verify( - handler => handler.HandleAsync(It.IsAny(), It.IsAny()), - Times.Exactly(snsEvent.Records.Count)); - } + _mockNotificationHandler.VerifyAll(); + _mockNotificationHandler.Verify( + handler => handler.HandleAsync(It.IsAny(), It.IsAny()), + Times.Exactly(snsEvent.Records.Count)); + } - [Test] - public void MaxDegreeOfParallelism_Should_ProperlyPropagated_And_Limited_To_Set_Max() + [Test] + public void MaxDegreeOfParallelism_Should_ProperlyPropagated_And_Limited_To_Set_Max() + { + var snsEvent = new SNSEvent { - var snsEvent = new SNSEvent + Records = new List { - Records = new List + new SNSEvent.SNSRecord { - new SNSEvent.SNSRecord + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } + Message = "{}" } } - }; + } + }; - var cq = new ConcurrentQueue(); + var cq = new ConcurrentQueue(); - //We are checking if parallelism actually does what it's supposed to do. So we should have more then 2 concurrent processes running - _parallelExecutionOptions = new ParallelSnsExecutionOptions { MaxDegreeOfParallelism = 4 }; - _mockNotificationHandler.Setup(p => p.HandleAsync(It.IsAny(), It.IsAny())) - .Returns(async () => + //We are checking if parallelism actually does what it's supposed to do. So we should have more then 2 concurrent processes running + _parallelExecutionOptions = new ParallelSnsExecutionOptions { MaxDegreeOfParallelism = 4 }; + _mockNotificationHandler.Setup(p => p.HandleAsync(It.IsAny(), It.IsAny())) + .Returns(async () => + { + var t = Task.Delay(1); + cq.Enqueue(t); + Console.WriteLine(cq.Count); + if (cq.Count > 2) { - var t = Task.Delay(1); - cq.Enqueue(t); - Console.WriteLine(cq.Count); - if (cq.Count > 2) - { - throw new Exception("Concurrent Tasks exceeded 2"); - } - await t; - cq.TryDequeue(out t); - }); + throw new Exception("Concurrent Tasks exceeded 2"); + } + await t; + cq.TryDequeue(out t); + }); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - Assert.ThrowsAsync(() => sut.HandleAsync(snsEvent, new TestLambdaContext())); - } + Assert.ThrowsAsync(() => sut.HandleAsync(snsEvent, new TestLambdaContext())); } -} +} \ No newline at end of file diff --git a/tests/Tests.Lambda.Template/Sns/ServiceCollectionExtensionsTests.cs b/tests/Tests.Lambda.Template/Sns/ServiceCollectionExtensionsTests.cs index 98814fb..7bdffa0 100644 --- a/tests/Tests.Lambda.Template/Sns/ServiceCollectionExtensionsTests.cs +++ b/tests/Tests.Lambda.Template/Sns/ServiceCollectionExtensionsTests.cs @@ -3,110 +3,109 @@ using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; -namespace Tests.Lambda.Sns +namespace Tests.Lambda.Sns; + +[TestFixture] +public class ServiceCollectionExtensionsTests { - [TestFixture] - public class ServiceCollectionExtensionsTests + [Test] + public void UseNotificationHandler_registers_default_SnsEventHandler() { - [Test] - public void UseNotificationHandler_registers_default_SnsEventHandler() - { - var services = new ServiceCollection(); + var services = new ServiceCollection(); - services.AddLogging(); + services.AddLogging(); - services.UseNotificationHandler(); + services.UseNotificationHandler(); - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - var handler = serviceProvider.GetRequiredService>(); + var handler = serviceProvider.GetRequiredService>(); - Assert.That(handler, Is.InstanceOf>()); - } + Assert.That(handler, Is.InstanceOf>()); + } - [Test] - public void UseNotificationHandler_registers_ParallelSnsEventHandler_when_parallel_execution_is_enabled_obsolete() - { - var services = new ServiceCollection(); + [Test] + public void UseNotificationHandler_registers_ParallelSnsEventHandler_when_parallel_execution_is_enabled_obsolete() + { + var services = new ServiceCollection(); - services.AddLogging(); + services.AddLogging(); #pragma warning disable CS0618 - services.UseNotificationHandler(enableParallelExecution: true); + services.UseNotificationHandler(enableParallelExecution: true); #pragma warning restore CS0618 - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - var handler = serviceProvider.GetRequiredService>(); + var handler = serviceProvider.GetRequiredService>(); - Assert.That(handler, Is.InstanceOf>()); - } + Assert.That(handler, Is.InstanceOf>()); + } - [Test] - public void UseNotificationHandler_registers_ParallelSnsEventHandler_when_parallel_execution_is_enabled() - { - var services = new ServiceCollection(); + [Test] + public void UseNotificationHandler_registers_ParallelSnsEventHandler_when_parallel_execution_is_enabled() + { + var services = new ServiceCollection(); - services.AddLogging(); + services.AddLogging(); - services.UseNotificationHandler().WithParallelExecution(); + services.UseNotificationHandler().WithParallelExecution(); - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - var handler = serviceProvider.GetRequiredService>(); + var handler = serviceProvider.GetRequiredService>(); - Assert.That(handler, Is.InstanceOf>()); - } + Assert.That(handler, Is.InstanceOf>()); + } - [Test] - public void UseNotificationHandler_registers_INotificationHandler() - { - var services = new ServiceCollection(); + [Test] + public void UseNotificationHandler_registers_INotificationHandler() + { + var services = new ServiceCollection(); - services.AddLogging(); + services.AddLogging(); - services.UseNotificationHandler(); + services.UseNotificationHandler(); - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - serviceProvider.GetRequiredService>(); - } + serviceProvider.GetRequiredService>(); + } - [Test] - public void UseNotificationHandler_registers_INotificationSerializer() - { - var services = new ServiceCollection(); + [Test] + public void UseNotificationHandler_registers_INotificationSerializer() + { + var services = new ServiceCollection(); - services.AddLogging(); + services.AddLogging(); - services.UseNotificationHandler(); + services.UseNotificationHandler(); - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - serviceProvider.GetRequiredService(); - } + serviceProvider.GetRequiredService(); + } - [Test] - public void UseCustomNotificationSerializer_registers_custom_serializer() - { - var services = new ServiceCollection(); + [Test] + public void UseCustomNotificationSerializer_registers_custom_serializer() + { + var services = new ServiceCollection(); - services.AddLogging(); + services.AddLogging(); - services.UseNotificationHandler(); + services.UseNotificationHandler(); - services.UseCustomNotificationSerializer(); + services.UseCustomNotificationSerializer(); - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - var serializer = serviceProvider.GetRequiredService(); + var serializer = serviceProvider.GetRequiredService(); - Assert.That(serializer, Is.InstanceOf()); - } + Assert.That(serializer, Is.InstanceOf()); + } - private class TestSerializer : INotificationSerializer - { - public TMessage Deserialize(string input) => default; - } + private class TestSerializer : INotificationSerializer + { + public TMessage Deserialize(string input) => default; } } \ No newline at end of file diff --git a/tests/Tests.Lambda.Template/Sns/SnsEventHandlerDisposalTests.cs b/tests/Tests.Lambda.Template/Sns/SnsEventHandlerDisposalTests.cs index 6d8dfbf..068d18b 100644 --- a/tests/Tests.Lambda.Template/Sns/SnsEventHandlerDisposalTests.cs +++ b/tests/Tests.Lambda.Template/Sns/SnsEventHandlerDisposalTests.cs @@ -11,136 +11,135 @@ using Microsoft.Extensions.Options; using NUnit.Framework; -namespace Tests.Lambda.Sns +namespace Tests.Lambda.Sns; + +public class SnsEventHandlerDisposalTests { - public class SnsEventHandlerDisposalTests + [Test] + public async Task EventHandler_Should_Use_Scoped_Object_In_ForEach_Loop() { - [Test] - public async Task EventHandler_Should_Use_Scoped_Object_In_ForEach_Loop() + var snsEvent = new SNSEvent { - var snsEvent = new SNSEvent + Records = new List { - Records = new List + new SNSEvent.SNSRecord { - new SNSEvent.SNSRecord + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } + Message = "{}" } } - }; + } + }; - var dependency = new DisposableDependency(); + var dependency = new DisposableDependency(); - var services = new ServiceCollection(); + var services = new ServiceCollection(); - services.AddScoped(_ => dependency); + services.AddScoped(_ => dependency); - var tcs = new TaskCompletionSource(); - services.AddTransient, SqsEventHandler>(); + var tcs = new TaskCompletionSource(); + services.AddTransient, SqsEventHandler>(); - services.AddTransient, - TestNotificationScopedHandler>(provider => - new TestNotificationScopedHandler(provider.GetRequiredService(), tcs)); + services.AddTransient, + TestNotificationScopedHandler>(provider => + new TestNotificationScopedHandler(provider.GetRequiredService(), tcs)); - services.AddSingleton(); + services.AddSingleton(); - var sp = services.BuildServiceProvider(); - var snsEventHandler = new SnsEventHandler(sp, NullLoggerFactory.Instance); + var sp = services.BuildServiceProvider(); + var snsEventHandler = new SnsEventHandler(sp, NullLoggerFactory.Instance); - var task = snsEventHandler.HandleAsync(snsEvent, new TestLambdaContext()); + var task = snsEventHandler.HandleAsync(snsEvent, new TestLambdaContext()); - Assert.That(dependency.Disposed, Is.False, "Dependency should not be disposed"); - Assert.That(task.IsCompleted, Is.False, "The task should not be completed"); + Assert.That(dependency.Disposed, Is.False, "Dependency should not be disposed"); + Assert.That(task.IsCompleted, Is.False, "The task should not be completed"); - tcs.SetResult(new TestNotification()); + tcs.SetResult(new TestNotification()); - await task; + await task; - Assert.That(dependency.Disposed, Is.True, "Dependency should be disposed"); - Assert.That(task.IsCompleted, Is.True, "The task should be completed"); - } + Assert.That(dependency.Disposed, Is.True, "Dependency should be disposed"); + Assert.That(task.IsCompleted, Is.True, "The task should be completed"); + } - [Test] - public async Task EventHandler_Should_Use_Scoped_Object_In_ForEachAsync_Loop() + [Test] + public async Task EventHandler_Should_Use_Scoped_Object_In_ForEachAsync_Loop() + { + var snsEvent = new SNSEvent { - var snsEvent = new SNSEvent + Records = new List { - Records = new List + new SNSEvent.SNSRecord { - new SNSEvent.SNSRecord + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } + Message = "{}" } } - }; + } + }; - var dependency = new DisposableDependency(); + var dependency = new DisposableDependency(); - var services = new ServiceCollection(); + var services = new ServiceCollection(); - services.AddScoped(_ => dependency); + services.AddScoped(_ => dependency); - var tcs = new TaskCompletionSource(); - services.AddTransient, ParallelSnsEventHandler>(); + var tcs = new TaskCompletionSource(); + services.AddTransient, ParallelSnsEventHandler>(); - services.AddTransient, - TestNotificationScopedHandler>(provider => new TestNotificationScopedHandler(provider.GetRequiredService(), tcs)); + services.AddTransient, + TestNotificationScopedHandler>(provider => new TestNotificationScopedHandler(provider.GetRequiredService(), tcs)); - services.AddSingleton(); - - var sp = services.BuildServiceProvider(); + services.AddSingleton(); - var sqsEventHandler = new ParallelSnsEventHandler(sp, new NullLoggerFactory(), Options.Create(new ParallelSnsExecutionOptions { MaxDegreeOfParallelism = 4 })); + var sp = services.BuildServiceProvider(); - var task = sqsEventHandler.HandleAsync(snsEvent, new TestLambdaContext()); + var sqsEventHandler = new ParallelSnsEventHandler(sp, new NullLoggerFactory(), Options.Create(new ParallelSnsExecutionOptions { MaxDegreeOfParallelism = 4 })); - Assert.That(dependency.Disposed, Is.False, "Dependency should not be disposed"); - Assert.That(task.IsCompleted, Is.False, "The task should not be completed"); + var task = sqsEventHandler.HandleAsync(snsEvent, new TestLambdaContext()); - tcs.SetResult(new TestNotification()); - await task; - Assert.That(dependency.Disposed, Is.True, "Dependency should be disposed"); - Assert.That(task.IsCompleted, Is.True, "The task should be completed"); - } + Assert.That(dependency.Disposed, Is.False, "Dependency should not be disposed"); + Assert.That(task.IsCompleted, Is.False, "The task should not be completed"); - private class DisposableDependency : IDisposable - { - public bool Disposed { get; private set; } - public void Dispose() => Disposed = true; - } + tcs.SetResult(new TestNotification()); + await task; + Assert.That(dependency.Disposed, Is.True, "Dependency should be disposed"); + Assert.That(task.IsCompleted, Is.True, "The task should be completed"); + } - private class TestNotificationScopedHandler: INotificationHandler - { - private readonly DisposableDependency _dependency; - private readonly TaskCompletionSource _tcs; + private class DisposableDependency : IDisposable + { + public bool Disposed { get; private set; } + public void Dispose() => Disposed = true; + } - public TestNotificationScopedHandler(DisposableDependency dependency, TaskCompletionSource tcs) - { - _dependency = dependency; - _tcs = tcs; - } + private class TestNotificationScopedHandler: INotificationHandler + { + private readonly DisposableDependency _dependency; + private readonly TaskCompletionSource _tcs; - public Task HandleAsync(TestNotification message, ILambdaContext context) => _tcs.Task; + public TestNotificationScopedHandler(DisposableDependency dependency, TaskCompletionSource tcs) + { + _dependency = dependency; + _tcs = tcs; } + + public Task HandleAsync(TestNotification message, ILambdaContext context) => _tcs.Task; } -} +} \ No newline at end of file diff --git a/tests/Tests.Lambda.Template/Sns/SnsEventHandlerTests.cs b/tests/Tests.Lambda.Template/Sns/SnsEventHandlerTests.cs index 0c4bd66..7c1adcb 100644 --- a/tests/Tests.Lambda.Template/Sns/SnsEventHandlerTests.cs +++ b/tests/Tests.Lambda.Template/Sns/SnsEventHandlerTests.cs @@ -11,189 +11,188 @@ using NUnit.Framework; -namespace Tests.Lambda.Sns +namespace Tests.Lambda.Sns; + +[TestFixture] +public class SnsEventHandlerTests { - [TestFixture] - public class SnsEventHandlerTests - { - private Mock _mockNotificationSerializer; - private Mock> _mockNotificationHandler; - private Mock _mockServiceScopeFactory; - private Mock _mockServiceProvider; - private Mock _mockLoggerFactory; - private Mock _mockServiceScope; + private Mock _mockNotificationSerializer; + private Mock> _mockNotificationHandler; + private Mock _mockServiceScopeFactory; + private Mock _mockServiceProvider; + private Mock _mockLoggerFactory; + private Mock _mockServiceScope; - [SetUp] - public void Initialize() - { - _mockNotificationSerializer = new Mock(); - _mockNotificationSerializer.Setup(p => p.Deserialize(It.IsAny())).Returns(() => new TestNotification()); + [SetUp] + public void Initialize() + { + _mockNotificationSerializer = new Mock(); + _mockNotificationSerializer.Setup(p => p.Deserialize(It.IsAny())).Returns(() => new TestNotification()); - _mockNotificationHandler = new Mock>(); - _mockNotificationHandler.Setup(p => p.HandleAsync(It.IsAny(), It.IsAny())) - .Returns(Task.CompletedTask); + _mockNotificationHandler = new Mock>(); + _mockNotificationHandler.Setup(p => p.HandleAsync(It.IsAny(), It.IsAny())) + .Returns(Task.CompletedTask); - _mockServiceScope = new Mock(); + _mockServiceScope = new Mock(); - _mockServiceScopeFactory = new Mock(); - _mockServiceScopeFactory.Setup(p => p.CreateScope()).Returns(_mockServiceScope.Object); + _mockServiceScopeFactory = new Mock(); + _mockServiceScopeFactory.Setup(p => p.CreateScope()).Returns(_mockServiceScope.Object); - _mockServiceProvider = new Mock(); - _mockServiceProvider.Setup(p => p.GetService(typeof(INotificationHandler))) - .Returns(_mockNotificationHandler.Object); - _mockServiceProvider.Setup(p => p.GetService(typeof(IServiceScopeFactory))) - .Returns(_mockServiceScopeFactory.Object); + _mockServiceProvider = new Mock(); + _mockServiceProvider.Setup(p => p.GetService(typeof(INotificationHandler))) + .Returns(_mockNotificationHandler.Object); + _mockServiceProvider.Setup(p => p.GetService(typeof(IServiceScopeFactory))) + .Returns(_mockServiceScopeFactory.Object); - _mockServiceProvider - .Setup(p => p.GetService(typeof(INotificationSerializer))) - .Returns(_mockNotificationSerializer.Object); + _mockServiceProvider + .Setup(p => p.GetService(typeof(INotificationSerializer))) + .Returns(_mockNotificationSerializer.Object); - _mockServiceScope.Setup(p => p.ServiceProvider).Returns(_mockServiceProvider.Object); + _mockServiceScope.Setup(p => p.ServiceProvider).Returns(_mockServiceProvider.Object); - _mockLoggerFactory = new Mock(); - _mockLoggerFactory.Setup(p => p.CreateLogger(It.IsAny())) - .Returns(Mock.Of()); - } + _mockLoggerFactory = new Mock(); + _mockLoggerFactory.Setup(p => p.CreateLogger(It.IsAny())) + .Returns(Mock.Of()); + } - private SnsEventHandler CreateSystemUnderTest() - { - return new SnsEventHandler(_mockServiceProvider.Object, _mockLoggerFactory.Object); - } + private SnsEventHandler CreateSystemUnderTest() + { + return new SnsEventHandler(_mockServiceProvider.Object, _mockLoggerFactory.Object); + } - [Test] - public async Task HandleAsync_resolves_NotificationHandler_for_each_record() + [Test] + public async Task HandleAsync_resolves_NotificationHandler_for_each_record() + { + var snsEvent = new SNSEvent { - var snsEvent = new SNSEvent + Records = new List { - Records = new List + new SNSEvent.SNSRecord { - new SNSEvent.SNSRecord + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } + Message = "{}" } } - }; + } + }; - var lambdaContext = new TestLambdaContext(); + var lambdaContext = new TestLambdaContext(); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - await sut.HandleAsync(snsEvent, lambdaContext); + await sut.HandleAsync(snsEvent, lambdaContext); - _mockServiceProvider.Verify(p => p.GetService(typeof(INotificationHandler)), Times.Exactly(snsEvent.Records.Count)); - } + _mockServiceProvider.Verify(p => p.GetService(typeof(INotificationHandler)), Times.Exactly(snsEvent.Records.Count)); + } - [Test] - public async Task HandleAsync_creates_a_scope_for_each_record() + [Test] + public async Task HandleAsync_creates_a_scope_for_each_record() + { + var snsEvent = new SNSEvent { - var snsEvent = new SNSEvent + Records = new List { - Records = new List + new SNSEvent.SNSRecord { - new SNSEvent.SNSRecord + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } + Message = "{}" } } - }; + } + }; - var lambdaContext = new TestLambdaContext(); + var lambdaContext = new TestLambdaContext(); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - await sut.HandleAsync(snsEvent, lambdaContext); + await sut.HandleAsync(snsEvent, lambdaContext); - _mockServiceScopeFactory.Verify(p => p.CreateScope(), Times.Exactly(snsEvent.Records.Count)); - } + _mockServiceScopeFactory.Verify(p => p.CreateScope(), Times.Exactly(snsEvent.Records.Count)); + } - [Test] - public async Task HandleAsync_executes_NotificationHandler_for_each_record() + [Test] + public async Task HandleAsync_executes_NotificationHandler_for_each_record() + { + var snsEvent = new SNSEvent { - var snsEvent = new SNSEvent + Records = new List { - Records = new List + new SNSEvent.SNSRecord { - new SNSEvent.SNSRecord + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } + Message = "{}" } } - }; + } + }; - var lambdaContext = new TestLambdaContext(); + var lambdaContext = new TestLambdaContext(); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - await sut.HandleAsync(snsEvent, lambdaContext); + await sut.HandleAsync(snsEvent, lambdaContext); - _mockNotificationHandler.Verify(p => p.HandleAsync(It.IsAny(), lambdaContext), Times.Exactly(snsEvent.Records.Count)); - } + _mockNotificationHandler.Verify(p => p.HandleAsync(It.IsAny(), lambdaContext), Times.Exactly(snsEvent.Records.Count)); + } - [Test] - public void HandleAsync_throws_InvalidOperation_if_NotificationHandler_is_not_registered() + [Test] + public void HandleAsync_throws_InvalidOperation_if_NotificationHandler_is_not_registered() + { + var snsEvent = new SNSEvent { - var snsEvent = new SNSEvent + Records = new List { - Records = new List + new SNSEvent.SNSRecord { - new SNSEvent.SNSRecord + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } - }, - new SNSEvent.SNSRecord + Message = "{}" + } + }, + new SNSEvent.SNSRecord + { + Sns = new SNSEvent.SNSMessage { - Sns = new SNSEvent.SNSMessage - { - Message = "{}" - } + Message = "{}" } } - }; + } + }; - var lambdaContext = new TestLambdaContext(); + var lambdaContext = new TestLambdaContext(); - _mockServiceProvider = new Mock(); - _mockServiceProvider.Setup(p => p.GetService(typeof(IServiceScopeFactory))).Returns(_mockServiceScopeFactory.Object); - _mockServiceScope.Setup(p => p.ServiceProvider).Returns(_mockServiceProvider.Object); + _mockServiceProvider = new Mock(); + _mockServiceProvider.Setup(p => p.GetService(typeof(IServiceScopeFactory))).Returns(_mockServiceScopeFactory.Object); + _mockServiceScope.Setup(p => p.ServiceProvider).Returns(_mockServiceProvider.Object); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - Assert.ThrowsAsync(() => sut.HandleAsync(snsEvent, lambdaContext)); - } + Assert.ThrowsAsync(() => sut.HandleAsync(snsEvent, lambdaContext)); } -} +} \ No newline at end of file diff --git a/tests/Tests.Lambda.Template/Sns/TestNotification.cs b/tests/Tests.Lambda.Template/Sns/TestNotification.cs index 288f33e..5f7636c 100644 --- a/tests/Tests.Lambda.Template/Sns/TestNotification.cs +++ b/tests/Tests.Lambda.Template/Sns/TestNotification.cs @@ -2,15 +2,14 @@ using Amazon.Lambda.Core; using Kralizek.Lambda; -namespace Tests.Lambda.Sns +namespace Tests.Lambda.Sns; + +public class TestNotification { - public class TestNotification - { - } +} - public class TestNotificationHandler : INotificationHandler - { - public Task HandleAsync(TestNotification notification, ILambdaContext context) => Task.CompletedTask; - } +public class TestNotificationHandler : INotificationHandler +{ + public Task HandleAsync(TestNotification notification, ILambdaContext context) => Task.CompletedTask; } \ No newline at end of file diff --git a/tests/Tests.Lambda.Template/Sqs/ServiceCollectionExtensionsTests.cs b/tests/Tests.Lambda.Template/Sqs/ServiceCollectionExtensionsTests.cs index 1dfb955..d831526 100644 --- a/tests/Tests.Lambda.Template/Sqs/ServiceCollectionExtensionsTests.cs +++ b/tests/Tests.Lambda.Template/Sqs/ServiceCollectionExtensionsTests.cs @@ -1,134 +1,138 @@ -using System.Collections.Generic; -using System.Text; -using Amazon.Lambda.SQSEvents; +using Amazon.Lambda.SQSEvents; using Kralizek.Lambda; using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; -using Tests.Lambda.Sns; -namespace Tests.Lambda.Sqs +namespace Tests.Lambda.Sqs; + +[TestFixture] +public class ServiceCollectionExtensionsTests { - [TestFixture] - public class ServiceCollectionExtensionsTests + [Test] + public void UseSqsHandler_registers_default_SqsEventHandler() { - [Test] - public void UseSqsHandler_registers_default_SqsEventHandler() - { - var services = new ServiceCollection(); + var services = new ServiceCollection(); - services.AddLogging(); + services.AddLogging(); - services.UseSqsHandler(); +#pragma warning disable CS0618 + services.UseSqsHandler(); +#pragma warning restore CS0618 - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - var handler = serviceProvider.GetRequiredService>(); + var handler = serviceProvider.GetRequiredService>(); - Assert.That(handler, Is.InstanceOf>()); - } + Assert.That(handler, Is.InstanceOf>()); + } - [Test] - public void UseSqsHandler_registers_ParallelSqsEventHandler_when_parallel_execution_is_enabled() - { - var services = new ServiceCollection(); + [Test] + public void UseSqsHandler_registers_ParallelSqsEventHandler_when_parallel_execution_is_enabled() + { + var services = new ServiceCollection(); - services.AddLogging(); + services.AddLogging(); - services.UseSqsHandler(enableParallelExecution: true); +#pragma warning disable CS0618 + services.UseSqsHandler(enableParallelExecution: true); +#pragma warning restore CS0618 - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - var handler = serviceProvider.GetRequiredService>(); + var handler = serviceProvider.GetRequiredService>(); - Assert.That(handler, Is.InstanceOf>()); - } + Assert.That(handler, Is.InstanceOf>()); + } - [Test] - public void UseSqsHandler_registers_IMessageHandler() - { - var services = new ServiceCollection(); + [Test] + public void UseSqsHandler_registers_IMessageHandler() + { + var services = new ServiceCollection(); - services.AddLogging(); + services.AddLogging(); - services.UseSqsHandler(); +#pragma warning disable CS0618 + services.UseSqsHandler(); +#pragma warning restore CS0618 - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - serviceProvider.GetRequiredService>(); - } + serviceProvider.GetRequiredService>(); + } - [Test] - public void UseSqsHandler_registers_IMessageSerializer() - { - var services = new ServiceCollection(); + [Test] + public void UseSqsHandler_registers_IMessageSerializer() + { + var services = new ServiceCollection(); - services.AddLogging(); + services.AddLogging(); - services.UseSqsHandler(); +#pragma warning disable CS0618 + services.UseSqsHandler(); +#pragma warning restore CS0618 - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - serviceProvider.GetRequiredService(); - } + serviceProvider.GetRequiredService(); + } - [Test] - public void UseQueueMessageHandler_registers_default_SqsEventHandler() - { - var services = new ServiceCollection(); + [Test] + public void UseQueueMessageHandler_registers_default_SqsEventHandler() + { + var services = new ServiceCollection(); - services.AddLogging(); + services.AddLogging(); - services.UseQueueMessageHandler(); + services.UseQueueMessageHandler(); - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - var handler = serviceProvider.GetRequiredService>(); + var handler = serviceProvider.GetRequiredService>(); - Assert.That(handler, Is.InstanceOf>()); - } + Assert.That(handler, Is.InstanceOf>()); + } - [Test] - public void UseQueueMessageHandler_registers_ParallelSqsEventHandler_when_parallel_execution_is_enabled() - { - var services = new ServiceCollection(); + [Test] + public void UseQueueMessageHandler_registers_ParallelSqsEventHandler_when_parallel_execution_is_enabled() + { + var services = new ServiceCollection(); - services.AddLogging(); + services.AddLogging(); - services.UseQueueMessageHandler().WithParallelExecution(); + services.UseQueueMessageHandler().WithParallelExecution(); - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - var handler = serviceProvider.GetRequiredService>(); + var handler = serviceProvider.GetRequiredService>(); - Assert.That(handler, Is.InstanceOf>()); - } + Assert.That(handler, Is.InstanceOf>()); + } - [Test] - public void UseQueueMessageHandler_registers_IMessageHandler() - { - var services = new ServiceCollection(); + [Test] + public void UseQueueMessageHandler_registers_IMessageHandler() + { + var services = new ServiceCollection(); - services.AddLogging(); + services.AddLogging(); - services.UseQueueMessageHandler(); + services.UseQueueMessageHandler(); - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - serviceProvider.GetRequiredService>(); - } + serviceProvider.GetRequiredService>(); + } - [Test] - public void UseQueueMessageHandler_registers_IMessageSerializer() - { - var services = new ServiceCollection(); + [Test] + public void UseQueueMessageHandler_registers_IMessageSerializer() + { + var services = new ServiceCollection(); - services.AddLogging(); + services.AddLogging(); - services.UseQueueMessageHandler(); + services.UseQueueMessageHandler(); - var serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); - serviceProvider.GetRequiredService(); - } + serviceProvider.GetRequiredService(); } -} +} \ No newline at end of file diff --git a/tests/Tests.Lambda.Template/Sqs/SqsEventHandlerDisposalTests.cs b/tests/Tests.Lambda.Template/Sqs/SqsEventHandlerDisposalTests.cs index 8951153..2cd8ddc 100644 --- a/tests/Tests.Lambda.Template/Sqs/SqsEventHandlerDisposalTests.cs +++ b/tests/Tests.Lambda.Template/Sqs/SqsEventHandlerDisposalTests.cs @@ -10,124 +10,123 @@ using Microsoft.Extensions.Options; using NUnit.Framework; -namespace Tests.Lambda.Sqs +namespace Tests.Lambda.Sqs; + +public class SqsEventHandlerDisposalTests { - public class SqsEventHandlerDisposalTests + [Test] + public async Task EventHandler_Should_Use_Scoped_Object_In_ForEach_Loop() { - [Test] - public async Task EventHandler_Should_Use_Scoped_Object_In_ForEach_Loop() + var sqsEvent = new SQSEvent { - var sqsEvent = new SQSEvent + Records = new List { - Records = new List + new SQSEvent.SQSMessage + { + Body = "{}" + }, + new SQSEvent.SQSMessage { - new SQSEvent.SQSMessage - { - Body = "{}" - }, - new SQSEvent.SQSMessage - { - Body = "{}" - }, - } - }; + Body = "{}" + }, + } + }; - var dependency = new DisposableDependency(); + var dependency = new DisposableDependency(); - var services = new ServiceCollection(); + var services = new ServiceCollection(); - services.AddScoped(_ => dependency); + services.AddScoped(_ => dependency); - var tcs = new TaskCompletionSource(); - services.AddTransient, SqsEventHandler>(); + var tcs = new TaskCompletionSource(); + services.AddTransient, SqsEventHandler>(); - services.AddTransient, - TestMessageScopedHandler>(provider => - new TestMessageScopedHandler(provider.GetRequiredService(), tcs)); + services.AddTransient, + TestMessageScopedHandler>(provider => + new TestMessageScopedHandler(provider.GetRequiredService(), tcs)); - services.AddSingleton(); + services.AddSingleton(); - var sp = services.BuildServiceProvider(); - var sqsEventHandler = new SqsEventHandler(sp, new NullLoggerFactory()); + var sp = services.BuildServiceProvider(); + var sqsEventHandler = new SqsEventHandler(sp, new NullLoggerFactory()); - var task = sqsEventHandler.HandleAsync(sqsEvent, new TestLambdaContext()); + var task = sqsEventHandler.HandleAsync(sqsEvent, new TestLambdaContext()); - Assert.That(dependency.Disposed, Is.False, "Dependency should not be disposed"); - Assert.That(task.IsCompleted, Is.False, "The task should not be completed"); + Assert.That(dependency.Disposed, Is.False, "Dependency should not be disposed"); + Assert.That(task.IsCompleted, Is.False, "The task should not be completed"); - tcs.SetResult(new TestMessage()); - await task; - Assert.That(dependency.Disposed, Is.True, "Dependency should be disposed"); - Assert.That(task.IsCompleted, Is.True, "The task should be completed"); + tcs.SetResult(new TestMessage()); + await task; + Assert.That(dependency.Disposed, Is.True, "Dependency should be disposed"); + Assert.That(task.IsCompleted, Is.True, "The task should be completed"); - } + } - [Test] - public async Task EventHandler_Should_Use_Scoped_Object_In_ForEachAsync_Loop() + [Test] + public async Task EventHandler_Should_Use_Scoped_Object_In_ForEachAsync_Loop() + { + var sqsEvent = new SQSEvent { - var sqsEvent = new SQSEvent + Records = new List { - Records = new List + new SQSEvent.SQSMessage { - new SQSEvent.SQSMessage - { - Body = "{}" - }, - new SQSEvent.SQSMessage - { - Body = "{}" - }, - } - }; + Body = "{}" + }, + new SQSEvent.SQSMessage + { + Body = "{}" + }, + } + }; - var dependency = new DisposableDependency(); + var dependency = new DisposableDependency(); - var services = new ServiceCollection(); + var services = new ServiceCollection(); - services.AddScoped(_ => dependency); + services.AddScoped(_ => dependency); - var tcs = new TaskCompletionSource(); - services.AddTransient, ParallelSqsEventHandler>(); + var tcs = new TaskCompletionSource(); + services.AddTransient, ParallelSqsEventHandler>(); - services.AddTransient, - TestMessageScopedHandler>(provider => - new TestMessageScopedHandler(provider.GetRequiredService(), tcs)); + services.AddTransient, + TestMessageScopedHandler>(provider => + new TestMessageScopedHandler(provider.GetRequiredService(), tcs)); - services.AddSingleton(); + services.AddSingleton(); - var sp = services.BuildServiceProvider(); - var sqsEventHandler = new ParallelSqsEventHandler(sp, new NullLoggerFactory(), Options.Create(new ParallelSqsExecutionOptions{MaxDegreeOfParallelism = 4})); + var sp = services.BuildServiceProvider(); + var sqsEventHandler = new ParallelSqsEventHandler(sp, new NullLoggerFactory(), Options.Create(new ParallelSqsExecutionOptions{MaxDegreeOfParallelism = 4})); - var task = sqsEventHandler.HandleAsync(sqsEvent, new TestLambdaContext()); + var task = sqsEventHandler.HandleAsync(sqsEvent, new TestLambdaContext()); - Assert.That(dependency.Disposed, Is.False, "Dependency should not be disposed"); - Assert.That(task.IsCompleted, Is.False, "The task should not be completed"); + Assert.That(dependency.Disposed, Is.False, "Dependency should not be disposed"); + Assert.That(task.IsCompleted, Is.False, "The task should not be completed"); - tcs.SetResult(new TestMessage()); - await task; - Assert.That(dependency.Disposed, Is.True, "Dependency should be disposed"); - Assert.That(task.IsCompleted, Is.True, "The task should be completed"); - } - - private class DisposableDependency : IDisposable - { - public bool Disposed { get; private set; } - public void Dispose() => Disposed = true; - } + tcs.SetResult(new TestMessage()); + await task; + Assert.That(dependency.Disposed, Is.True, "Dependency should be disposed"); + Assert.That(task.IsCompleted, Is.True, "The task should be completed"); + } - private class TestMessageScopedHandler : IMessageHandler - { - private readonly DisposableDependency _dependency; - private readonly TaskCompletionSource _tcs; + private class DisposableDependency : IDisposable + { + public bool Disposed { get; private set; } + public void Dispose() => Disposed = true; + } - public TestMessageScopedHandler(DisposableDependency dependency, TaskCompletionSource tcs) - { - _dependency = dependency; - _tcs = tcs; - } + private class TestMessageScopedHandler : IMessageHandler + { + private readonly DisposableDependency _dependency; + private readonly TaskCompletionSource _tcs; - public Task HandleAsync(TestMessage message, ILambdaContext context) => _tcs.Task; + public TestMessageScopedHandler(DisposableDependency dependency, TaskCompletionSource tcs) + { + _dependency = dependency; + _tcs = tcs; } + + public Task HandleAsync(TestMessage message, ILambdaContext context) => _tcs.Task; } -} +} \ No newline at end of file diff --git a/tests/Tests.Lambda.Template/Sqs/SqsEventHandlerTests.cs b/tests/Tests.Lambda.Template/Sqs/SqsEventHandlerTests.cs index 06d0ca5..f30b8ee 100644 --- a/tests/Tests.Lambda.Template/Sqs/SqsEventHandlerTests.cs +++ b/tests/Tests.Lambda.Template/Sqs/SqsEventHandlerTests.cs @@ -10,167 +10,167 @@ using Moq; using NUnit.Framework; -namespace Tests.Lambda.Sqs { - [TestFixture] - public class SqsEventHandlerTests - { - private Mock mockMessageSerializer; - private Mock> mockMessageHandler; - private Mock mockServiceScopeFactory; - private Mock mockServiceProvider; - private Mock mockLoggerFactory; - private Mock mockServiceScope; +namespace Tests.Lambda.Sqs; +[TestFixture] +public class SqsEventHandlerTests +{ + private Mock mockMessageSerializer; + private Mock> mockMessageHandler; + private Mock mockServiceScopeFactory; + private Mock mockServiceProvider; + private Mock mockLoggerFactory; + private Mock mockServiceScope; - [SetUp] - public void Initialize() - { - mockMessageSerializer = new Mock(); - mockMessageSerializer - .Setup(p => p.Deserialize(It.IsAny())) - .Returns(() => new TestMessage()); + [SetUp] + public void Initialize() + { + mockMessageSerializer = new Mock(); + + mockMessageSerializer + .Setup(p => p.Deserialize(It.IsAny())) + .Returns(() => new TestMessage()); - mockMessageHandler = new Mock>(); - mockMessageHandler.Setup(p => p.HandleAsync(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); + mockMessageHandler = new Mock>(); + mockMessageHandler.Setup(p => p.HandleAsync(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); - mockServiceScope = new Mock(); + mockServiceScope = new Mock(); - mockServiceScopeFactory = new Mock(); + mockServiceScopeFactory = new Mock(); - mockServiceScopeFactory.Setup(p => p.CreateScope()).Returns(mockServiceScope.Object); + mockServiceScopeFactory.Setup(p => p.CreateScope()).Returns(mockServiceScope.Object); - mockServiceProvider = new Mock(); - mockServiceProvider.Setup(p => p.GetService(typeof(IMessageHandler))) - .Returns(mockMessageHandler.Object); - mockServiceProvider.Setup(p => p.GetService(typeof(IServiceScopeFactory))) - .Returns(mockServiceScopeFactory.Object); + mockServiceProvider = new Mock(); + mockServiceProvider.Setup(p => p.GetService(typeof(IMessageHandler))) + .Returns(mockMessageHandler.Object); + mockServiceProvider.Setup(p => p.GetService(typeof(IServiceScopeFactory))) + .Returns(mockServiceScopeFactory.Object); - mockServiceProvider - .Setup(p => p.GetService(typeof(IMessageSerializer))) - .Returns(mockMessageSerializer.Object); + mockServiceProvider + .Setup(p => p.GetService(typeof(IMessageSerializer))) + .Returns(mockMessageSerializer.Object); - mockServiceScope.Setup(p => p.ServiceProvider).Returns(mockServiceProvider.Object); + mockServiceScope.Setup(p => p.ServiceProvider).Returns(mockServiceProvider.Object); - mockLoggerFactory = new Mock(); - mockLoggerFactory.Setup(p => p.CreateLogger(It.IsAny())) - .Returns(Mock.Of()); - } + mockLoggerFactory = new Mock(); + mockLoggerFactory.Setup(p => p.CreateLogger(It.IsAny())) + .Returns(Mock.Of()); + } - private SqsEventHandler CreateSystemUnderTest() - { - return new SqsEventHandler(mockServiceProvider.Object, mockLoggerFactory.Object); - } + private SqsEventHandler CreateSystemUnderTest() + { + return new SqsEventHandler(mockServiceProvider.Object, mockLoggerFactory.Object); + } - [Test] - public async Task HandleAsync_resolves_MessageHandler_for_each_record() + [Test] + public async Task HandleAsync_resolves_MessageHandler_for_each_record() + { + var sqsEvent = new SQSEvent { - var sqsEvent = new SQSEvent + Records = new List { - Records = new List + new SQSEvent.SQSMessage { - new SQSEvent.SQSMessage - { - Body = "{}" - }, - new SQSEvent.SQSMessage - { - Body = "{}" - }, - } - }; + Body = "{}" + }, + new SQSEvent.SQSMessage + { + Body = "{}" + }, + } + }; - var lambdaContext = new TestLambdaContext(); + var lambdaContext = new TestLambdaContext(); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - await sut.HandleAsync(sqsEvent, lambdaContext); + await sut.HandleAsync(sqsEvent, lambdaContext); - mockServiceProvider.Verify(p => p.GetService(typeof(IMessageHandler)), Times.Exactly(sqsEvent.Records.Count)); - } + mockServiceProvider.Verify(p => p.GetService(typeof(IMessageHandler)), Times.Exactly(sqsEvent.Records.Count)); + } - [Test] - public async Task HandleAsync_creates_a_scope_for_each_record() + [Test] + public async Task HandleAsync_creates_a_scope_for_each_record() + { + var sqsEvent = new SQSEvent { - var sqsEvent = new SQSEvent + Records = new List { - Records = new List + new SQSEvent.SQSMessage + { + Body = "{}" + }, + new SQSEvent.SQSMessage { - new SQSEvent.SQSMessage - { - Body = "{}" - }, - new SQSEvent.SQSMessage - { - Body = "{}" - }, - } - }; + Body = "{}" + }, + } + }; - var lambdaContext = new TestLambdaContext(); + var lambdaContext = new TestLambdaContext(); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - await sut.HandleAsync(sqsEvent, lambdaContext); + await sut.HandleAsync(sqsEvent, lambdaContext); - mockServiceScopeFactory.Verify(p => p.CreateScope(), Times.Exactly(sqsEvent.Records.Count)); - } + mockServiceScopeFactory.Verify(p => p.CreateScope(), Times.Exactly(sqsEvent.Records.Count)); + } - [Test] - public async Task HandleAsync_executes_NotificationHandler_for_each_record() + [Test] + public async Task HandleAsync_executes_NotificationHandler_for_each_record() + { + var sqsEvent = new SQSEvent { - var sqsEvent = new SQSEvent + Records = new List { - Records = new List + new SQSEvent.SQSMessage + { + Body = "{}" + }, + new SQSEvent.SQSMessage { - new SQSEvent.SQSMessage - { - Body = "{}" - }, - new SQSEvent.SQSMessage - { - Body = "{}" - }, - } - }; + Body = "{}" + }, + } + }; - var lambdaContext = new TestLambdaContext(); + var lambdaContext = new TestLambdaContext(); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - await sut.HandleAsync(sqsEvent, lambdaContext); + await sut.HandleAsync(sqsEvent, lambdaContext); - mockMessageHandler.Verify(p => p.HandleAsync(It.IsAny(), lambdaContext), Times.Exactly(sqsEvent.Records.Count)); - } + mockMessageHandler.Verify(p => p.HandleAsync(It.IsAny(), lambdaContext), Times.Exactly(sqsEvent.Records.Count)); + } - [Test] - public void HandleAsync_throws_InvalidOperation_if_NotificationHandler_is_not_registered() + [Test] + public void HandleAsync_throws_InvalidOperation_if_NotificationHandler_is_not_registered() + { + var sqsEvent = new SQSEvent { - var sqsEvent = new SQSEvent + Records = new List { - Records = new List + new SQSEvent.SQSMessage + { + Body = "{}" + }, + new SQSEvent.SQSMessage { - new SQSEvent.SQSMessage - { - Body = "{}" - }, - new SQSEvent.SQSMessage - { - Body = "{}" - }, - } - }; - - var lambdaContext = new TestLambdaContext(); - - mockServiceProvider = new Mock(); - mockServiceProvider.Setup(p => p.GetService(typeof(IServiceScopeFactory))).Returns(mockServiceScopeFactory.Object); + Body = "{}" + }, + } + }; + + var lambdaContext = new TestLambdaContext(); + + mockServiceProvider = new Mock(); + mockServiceProvider.Setup(p => p.GetService(typeof(IServiceScopeFactory))).Returns(mockServiceScopeFactory.Object); - mockServiceScope.Setup(p => p.ServiceProvider).Returns(mockServiceProvider.Object); + mockServiceScope.Setup(p => p.ServiceProvider).Returns(mockServiceProvider.Object); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - Assert.ThrowsAsync(() => sut.HandleAsync(sqsEvent, lambdaContext)); - } + Assert.ThrowsAsync(() => sut.HandleAsync(sqsEvent, lambdaContext)); } } \ No newline at end of file diff --git a/tests/Tests.Lambda.Template/Sqs/SqsForEachAsyncEventHandlerTests.cs b/tests/Tests.Lambda.Template/Sqs/SqsForEachAsyncEventHandlerTests.cs index 3a7f7bf..8e3b708 100644 --- a/tests/Tests.Lambda.Template/Sqs/SqsForEachAsyncEventHandlerTests.cs +++ b/tests/Tests.Lambda.Template/Sqs/SqsForEachAsyncEventHandlerTests.cs @@ -12,248 +12,247 @@ using Moq; using NUnit.Framework; -namespace Tests.Lambda.Sqs +namespace Tests.Lambda.Sqs; + +[TestFixture] +public class ParallelSqsEventHandlerTests { - [TestFixture] - public class ParallelSqsEventHandlerTests + private Mock _mockMessageSerializer; + private Mock> _mockMessageHandler; + private Mock _mockServiceScopeFactory; + private Mock _mockServiceProvider; + private Mock _mockLoggerFactory; + private Mock _mockServiceScope; + private ParallelSqsExecutionOptions _parallelExecutionOptions; + + [SetUp] + public void Initialize() { - private Mock _mockMessageSerializer; - private Mock> _mockMessageHandler; - private Mock _mockServiceScopeFactory; - private Mock _mockServiceProvider; - private Mock _mockLoggerFactory; - private Mock _mockServiceScope; - private ParallelSqsExecutionOptions _parallelExecutionOptions; - - [SetUp] - public void Initialize() - { - _mockMessageSerializer = new Mock(); + _mockMessageSerializer = new Mock(); - _mockMessageSerializer - .Setup(p => p.Deserialize(It.IsAny())) - .Returns(() => new TestMessage()); + _mockMessageSerializer + .Setup(p => p.Deserialize(It.IsAny())) + .Returns(() => new TestMessage()); - _mockMessageHandler = new Mock>(); - _mockMessageHandler.Setup(p => p.HandleAsync(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); + _mockMessageHandler = new Mock>(); + _mockMessageHandler.Setup(p => p.HandleAsync(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); - _mockServiceScope = new Mock(); + _mockServiceScope = new Mock(); - _mockServiceScopeFactory = new Mock(); + _mockServiceScopeFactory = new Mock(); - _mockServiceScopeFactory.Setup(p => p.CreateScope()).Returns(_mockServiceScope.Object); + _mockServiceScopeFactory.Setup(p => p.CreateScope()).Returns(_mockServiceScope.Object); - _mockServiceProvider = new Mock(); - _mockServiceProvider.Setup(p => p.GetService(typeof(IMessageHandler))) - .Returns(_mockMessageHandler.Object); - _mockServiceProvider.Setup(p => p.GetService(typeof(IServiceScopeFactory))) - .Returns(_mockServiceScopeFactory.Object); + _mockServiceProvider = new Mock(); + _mockServiceProvider.Setup(p => p.GetService(typeof(IMessageHandler))) + .Returns(_mockMessageHandler.Object); + _mockServiceProvider.Setup(p => p.GetService(typeof(IServiceScopeFactory))) + .Returns(_mockServiceScopeFactory.Object); - _mockServiceProvider - .Setup(p => p.GetService(typeof(IMessageSerializer))) - .Returns(_mockMessageSerializer.Object); + _mockServiceProvider + .Setup(p => p.GetService(typeof(IMessageSerializer))) + .Returns(_mockMessageSerializer.Object); - _mockServiceScope.Setup(p => p.ServiceProvider).Returns(_mockServiceProvider.Object); + _mockServiceScope.Setup(p => p.ServiceProvider).Returns(_mockServiceProvider.Object); - _mockLoggerFactory = new Mock(); - _mockLoggerFactory.Setup(p => p.CreateLogger(It.IsAny())) - .Returns(Mock.Of()); + _mockLoggerFactory = new Mock(); + _mockLoggerFactory.Setup(p => p.CreateLogger(It.IsAny())) + .Returns(Mock.Of()); - _parallelExecutionOptions = new ParallelSqsExecutionOptions { MaxDegreeOfParallelism = 4 }; - } + _parallelExecutionOptions = new ParallelSqsExecutionOptions { MaxDegreeOfParallelism = 4 }; + } - private ParallelSqsEventHandler CreateSystemUnderTest() - { - return new ParallelSqsEventHandler(_mockServiceProvider.Object, _mockLoggerFactory.Object, Options.Create(_parallelExecutionOptions)); - } + private ParallelSqsEventHandler CreateSystemUnderTest() + { + return new ParallelSqsEventHandler(_mockServiceProvider.Object, _mockLoggerFactory.Object, Options.Create(_parallelExecutionOptions)); + } - [Test] - public async Task HandleAsync_resolves_MessageHandler_for_each_record() + [Test] + public async Task HandleAsync_resolves_MessageHandler_for_each_record() + { + var sqsEvent = new SQSEvent { - var sqsEvent = new SQSEvent + Records = new List { - Records = new List + new SQSEvent.SQSMessage { - new SQSEvent.SQSMessage - { - Body = "{}" - }, - new SQSEvent.SQSMessage - { - Body = "{}" - }, - new SQSEvent.SQSMessage - { - Body = "{}" - }, - new SQSEvent.SQSMessage - { - Body = "{}" - }, - } - }; + Body = "{}" + }, + new SQSEvent.SQSMessage + { + Body = "{}" + }, + new SQSEvent.SQSMessage + { + Body = "{}" + }, + new SQSEvent.SQSMessage + { + Body = "{}" + }, + } + }; - var lambdaContext = new TestLambdaContext(); + var lambdaContext = new TestLambdaContext(); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - await sut.HandleAsync(sqsEvent, lambdaContext); + await sut.HandleAsync(sqsEvent, lambdaContext); - _mockServiceProvider.Verify(p => p.GetService(typeof(IMessageHandler)), Times.Exactly(sqsEvent.Records.Count)); - } + _mockServiceProvider.Verify(p => p.GetService(typeof(IMessageHandler)), Times.Exactly(sqsEvent.Records.Count)); + } - [Test] - public async Task HandleAsync_creates_a_scope_for_each_record() + [Test] + public async Task HandleAsync_creates_a_scope_for_each_record() + { + var sqsEvent = new SQSEvent { - var sqsEvent = new SQSEvent + Records = new List { - Records = new List + new SQSEvent.SQSMessage { - new SQSEvent.SQSMessage - { - Body = "{}" - }, - new SQSEvent.SQSMessage - { - Body = "{}" - }, - } - }; + Body = "{}" + }, + new SQSEvent.SQSMessage + { + Body = "{}" + }, + } + }; - var lambdaContext = new TestLambdaContext(); + var lambdaContext = new TestLambdaContext(); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - await sut.HandleAsync(sqsEvent, lambdaContext); + await sut.HandleAsync(sqsEvent, lambdaContext); - _mockServiceScopeFactory.Verify(p => p.CreateScope(), Times.Exactly(sqsEvent.Records.Count)); - } + _mockServiceScopeFactory.Verify(p => p.CreateScope(), Times.Exactly(sqsEvent.Records.Count)); + } - [Test] - public void HandleAsync_throws_InvalidOperation_if_NotificationHandler_is_not_registered() + [Test] + public void HandleAsync_throws_InvalidOperation_if_NotificationHandler_is_not_registered() + { + var sqsEvent = new SQSEvent { - var sqsEvent = new SQSEvent + Records = new List { - Records = new List + new SQSEvent.SQSMessage { - new SQSEvent.SQSMessage - { - Body = "{}" - }, - new SQSEvent.SQSMessage - { - Body = "{}" - }, - } - }; + Body = "{}" + }, + new SQSEvent.SQSMessage + { + Body = "{}" + }, + } + }; - var lambdaContext = new TestLambdaContext(); + var lambdaContext = new TestLambdaContext(); - _mockServiceProvider = new Mock(); - _mockServiceProvider.Setup(p => p.GetService(typeof(IServiceScopeFactory))).Returns(_mockServiceScopeFactory.Object); + _mockServiceProvider = new Mock(); + _mockServiceProvider.Setup(p => p.GetService(typeof(IServiceScopeFactory))).Returns(_mockServiceScopeFactory.Object); - _mockServiceScope.Setup(p => p.ServiceProvider).Returns(_mockServiceProvider.Object); + _mockServiceScope.Setup(p => p.ServiceProvider).Returns(_mockServiceProvider.Object); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - Assert.ThrowsAsync(() => sut.HandleAsync(sqsEvent, lambdaContext)); - } + Assert.ThrowsAsync(() => sut.HandleAsync(sqsEvent, lambdaContext)); + } - [Test] - public async Task MaxDegreeOfParallelism_Should_ProperlyPropagated() + [Test] + public async Task MaxDegreeOfParallelism_Should_ProperlyPropagated() + { + var sqsEvent = new SQSEvent { - var sqsEvent = new SQSEvent + Records = new List { - Records = new List + new SQSEvent.SQSMessage { - new SQSEvent.SQSMessage - { - Body = "{}" - }, - new SQSEvent.SQSMessage - { - Body = "{}" - }, - new SQSEvent.SQSMessage - { - Body = "{}" - }, - } - }; + Body = "{}" + }, + new SQSEvent.SQSMessage + { + Body = "{}" + }, + new SQSEvent.SQSMessage + { + Body = "{}" + }, + } + }; - var cq = new ConcurrentQueue(); + var cq = new ConcurrentQueue(); - _parallelExecutionOptions = new ParallelSqsExecutionOptions {MaxDegreeOfParallelism = 2}; - _mockMessageHandler.Setup(p => p.HandleAsync(It.IsAny(), It.IsAny())) - .Returns(async ()=> + _parallelExecutionOptions = new ParallelSqsExecutionOptions {MaxDegreeOfParallelism = 2}; + _mockMessageHandler.Setup(p => p.HandleAsync(It.IsAny(), It.IsAny())) + .Returns(async ()=> + { + var t = Task.Delay(1); + cq.Enqueue(t); + if (cq.Count > 2) { - var t = Task.Delay(1); - cq.Enqueue(t); - if (cq.Count > 2) - { - throw new Exception("not good"); - } - await t; - cq.TryDequeue(out t); - }); - - var sut = CreateSystemUnderTest(); - - await sut.HandleAsync(sqsEvent, new TestLambdaContext()); - - _mockMessageHandler.VerifyAll(); - _mockMessageHandler.Verify( - handler => handler.HandleAsync(It.IsAny(), It.IsAny()), - Times.Exactly(sqsEvent.Records.Count)); - } - - [Test] - public void MaxDegreeOfParallelism_Should_ProperlyPropagated_And_Limited_To_Set_Max() + throw new Exception("not good"); + } + await t; + cq.TryDequeue(out t); + }); + + var sut = CreateSystemUnderTest(); + + await sut.HandleAsync(sqsEvent, new TestLambdaContext()); + + _mockMessageHandler.VerifyAll(); + _mockMessageHandler.Verify( + handler => handler.HandleAsync(It.IsAny(), It.IsAny()), + Times.Exactly(sqsEvent.Records.Count)); + } + + [Test] + public void MaxDegreeOfParallelism_Should_ProperlyPropagated_And_Limited_To_Set_Max() + { + var sqsEvent = new SQSEvent { - var sqsEvent = new SQSEvent + Records = new List { - Records = new List + new SQSEvent.SQSMessage { - new SQSEvent.SQSMessage - { - Body = "{}" - }, - new SQSEvent.SQSMessage - { - Body = "{}" - }, - new SQSEvent.SQSMessage - { - Body = "{}" - }, - new SQSEvent.SQSMessage - { - Body = "{}" - }, - } - }; + Body = "{}" + }, + new SQSEvent.SQSMessage + { + Body = "{}" + }, + new SQSEvent.SQSMessage + { + Body = "{}" + }, + new SQSEvent.SQSMessage + { + Body = "{}" + }, + } + }; - var cq = new ConcurrentQueue(); + var cq = new ConcurrentQueue(); - //We are checking if parallelism actually does what it's supposed to do. So we should have more then 2 concurrent processes running - _parallelExecutionOptions = new ParallelSqsExecutionOptions { MaxDegreeOfParallelism = 4 }; - _mockMessageHandler.Setup(p => p.HandleAsync(It.IsAny(), It.IsAny())) - .Returns(async () => + //We are checking if parallelism actually does what it's supposed to do. So we should have more then 2 concurrent processes running + _parallelExecutionOptions = new ParallelSqsExecutionOptions { MaxDegreeOfParallelism = 4 }; + _mockMessageHandler.Setup(p => p.HandleAsync(It.IsAny(), It.IsAny())) + .Returns(async () => + { + var t = Task.Delay(1); + cq.Enqueue(t); + Console.WriteLine(cq.Count); + if (cq.Count > 2) { - var t = Task.Delay(1); - cq.Enqueue(t); - Console.WriteLine(cq.Count); - if (cq.Count > 2) - { - throw new Exception("Concurrent Tasks exceeded 2"); - } - await t; - cq.TryDequeue(out t); - }); + throw new Exception("Concurrent Tasks exceeded 2"); + } + await t; + cq.TryDequeue(out t); + }); - var sut = CreateSystemUnderTest(); + var sut = CreateSystemUnderTest(); - Assert.ThrowsAsync(() => sut.HandleAsync(sqsEvent, new TestLambdaContext())); - } + Assert.ThrowsAsync(() => sut.HandleAsync(sqsEvent, new TestLambdaContext())); } -} +} \ No newline at end of file diff --git a/tests/Tests.Lambda.Template/Sqs/TestNotification.cs b/tests/Tests.Lambda.Template/Sqs/TestNotification.cs index b9fc249..d25b629 100644 --- a/tests/Tests.Lambda.Template/Sqs/TestNotification.cs +++ b/tests/Tests.Lambda.Template/Sqs/TestNotification.cs @@ -2,15 +2,14 @@ using Amazon.Lambda.Core; using Kralizek.Lambda; -namespace Tests.Lambda.Sqs +namespace Tests.Lambda.Sqs; + +public class TestMessage { - public class TestMessage - { - } +} - public class TestMessageHandler : IMessageHandler - { - public Task HandleAsync(TestMessage message, ILambdaContext context) => Task.CompletedTask; - } +public class TestMessageHandler : IMessageHandler +{ + public Task HandleAsync(TestMessage message, ILambdaContext context) => Task.CompletedTask; } \ No newline at end of file