public void RenameFunction() { // ARRANGE var mockFileManager = GetMockFileManager(string.Empty); var lambdaFunctionModel = GetLambdaFunctionModel("MyAssembly::MyNamespace.MyType::Handler", "OldName", 45, 512, "@Basic", null); var cloudFormationJsonWriter = new CloudFormationJsonWriter(mockFileManager, _mockDirectoryManager, _jsonWriter, _diagnosticReporter); var report = GetAnnotationReport(new List <ILambdaFunctionSerializable> { lambdaFunctionModel }); // ACT cloudFormationJsonWriter.ApplyReport(report); // ASSERT var rootToken = JObject.Parse(mockFileManager.ReadAllText(ServerlessTemplateFilePath)); Assert.NotNull(rootToken["Resources"]["OldName"]); Assert.Equal("MyAssembly::MyNamespace.MyType::Handler", rootToken["Resources"]["OldName"]["Properties"]["Handler"]); // ACT - CHANGE NAME lambdaFunctionModel.Name = "NewName"; cloudFormationJsonWriter.ApplyReport(report); // ASSERT rootToken = JObject.Parse(mockFileManager.ReadAllText(ServerlessTemplateFilePath)); Assert.Null(rootToken["Resources"]["OldName"]); Assert.NotNull(rootToken["Resources"]["NewName"]); Assert.Equal("MyAssembly::MyNamespace.MyType::Handler", rootToken["Resources"]["NewName"]["Properties"]["Handler"]); }
public void SwitchFromPolicyToRole() { // ARRANGE var mockFileManager = GetMockFileManager(string.Empty); var cloudFormationJsonWriter = new CloudFormationJsonWriter(mockFileManager, _mockDirectoryManager, _jsonWriter, _diagnosticReporter); //ACT - USE POLICY var lambdaFunctionModel = GetLambdaFunctionModel("MyAssembly::MyNamespace.MyType::Handler", "TestMethod", 45, 512, null, "Policy1, Policy2, Policy3"); var report = GetAnnotationReport(new List <ILambdaFunctionSerializable> { lambdaFunctionModel }); cloudFormationJsonWriter.ApplyReport(report); // ASSERT var rootToken = JObject.Parse(mockFileManager.ReadAllText(ServerlessTemplateFilePath)); var policyArray = rootToken["Resources"]["TestMethod"]["Properties"]["Policies"]; Assert.Equal(new List <string> { "Policy1", "Policy2", "Policy3" }, policyArray.ToObject <List <string> >()); Assert.Null(rootToken["Resources"]["TestMethod"]["Properties"]["Role"]); // ACT - SWITCH TO ROLE lambdaFunctionModel = GetLambdaFunctionModel("MyAssembly::MyNamespace.MyType::Handler", "TestMethod", 45, 512, "Basic", null); report = GetAnnotationReport(new List <ILambdaFunctionSerializable> { lambdaFunctionModel }); cloudFormationJsonWriter.ApplyReport(report); // ASSERT rootToken = JObject.Parse(mockFileManager.ReadAllText(ServerlessTemplateFilePath)); Assert.Equal("Basic", rootToken["Resources"]["TestMethod"]["Properties"]["Role"]); Assert.Null(rootToken["Resources"]["TestMethod"]["Properties"]["Policies"]); // ACT - SWITCH BACK TO POLICY lambdaFunctionModel = GetLambdaFunctionModel("MyAssembly::MyNamespace.MyType::Handler", "TestMethod", 45, 512, null, "Policy1, Policy2, Policy3"); report = GetAnnotationReport(new List <ILambdaFunctionSerializable> { lambdaFunctionModel }); cloudFormationJsonWriter.ApplyReport(report); // ASSERT rootToken = JObject.Parse(mockFileManager.ReadAllText(ServerlessTemplateFilePath)); policyArray = rootToken["Resources"]["TestMethod"]["Properties"]["Policies"]; Assert.Equal(new List <string> { "Policy1", "Policy2", "Policy3" }, policyArray.ToObject <List <string> >()); Assert.Null(rootToken["Resources"]["TestMethod"]["Properties"]["Role"]); }
public void RemoveOrphanedLambdaFunctions() { // ARRANGE var originalContent = @"{ 'AWSTemplateFormatVersion': '2010-09-09', 'Transform': 'AWS::Serverless-2016-10-31', 'Resources': { 'ObsoleteMethod': { 'Type': 'AWS::Serverless::Function', 'Metadata': { 'Tool': 'Amazon.Lambda.Annotations' }, 'Properties': { 'Runtime': 'dotnetcore3.1', 'CodeUri': '', 'MemorySize': 128, 'Timeout': 100, 'Policies': [ 'AWSLambdaBasicExecutionRole' ], 'Handler': 'MyAssembly::MyNamespace.MyType::Handler' } }, 'MethodNotCreatedFromAnnotationsPackage': { 'Type': 'AWS::Serverless::Function', 'Properties': { 'Runtime': 'dotnetcore3.1', 'CodeUri': '', 'MemorySize': 128, 'Timeout': 100, 'Policies': [ 'AWSLambdaBasicExecutionRole' ], 'Handler': 'MyAssembly::MyNamespace.MyType::Handler' } } } }"; var mockFileManager = GetMockFileManager(originalContent); var lambdaFunctionModel = GetLambdaFunctionModel("MyAssembly::MyNamespace.MyType::Handler", "NewMethod", 45, 512, null, null); var cloudFormationJsonWriter = new CloudFormationJsonWriter(mockFileManager, _mockDirectoryManager, _jsonWriter, _diagnosticReporter); var report = GetAnnotationReport(new List <ILambdaFunctionSerializable> { lambdaFunctionModel }); // ACT cloudFormationJsonWriter.ApplyReport(report); // ASSERT var rootToken = JObject.Parse(mockFileManager.ReadAllText(ServerlessTemplateFilePath)); Assert.Null(rootToken["Resources"]["ObsoleteMethod"]); Assert.NotNull(rootToken["Resources"]["NewMethod"]); Assert.NotNull(rootToken["Resources"]["MethodNotCreatedFromAnnotationsPackage"]); }
public void DoNotModifyFunctionWithoutRequiredMetadata() { // ARRANGE var originalContent = @"{ 'AWSTemplateFormatVersion': '2010-09-09', 'Transform': 'AWS::Serverless-2016-10-31', 'Resources': { 'MethodNotCreatedFromAnnotationsPackage': { 'Type': 'AWS::Serverless::Function', 'Properties': { 'Runtime': 'dotnetcore3.1', 'CodeUri': '', 'MemorySize': 128, 'Timeout': 100, 'Policies': [ 'AWSLambdaBasicExecutionRole' ], 'Handler': 'MyAssembly::MyNamespace.MyType::Handler' } } } }"; var mockFileManager = GetMockFileManager(originalContent); var lambdaFunctionModel = GetLambdaFunctionModel("MyAssembly::MyNamespace.MyType::Handler", "MethodNotCreatedFromAnnotationsPackage", 45, 512, null, "Policy1, Policy2, Policy3"); var cloudFormationJsonWriter = new CloudFormationJsonWriter(mockFileManager, _mockDirectoryManager, _jsonWriter, _diagnosticReporter); var report = GetAnnotationReport(new() { lambdaFunctionModel }); // ACT cloudFormationJsonWriter.ApplyReport(report); // ASSERT var rootToken = JObject.Parse(mockFileManager.ReadAllText(ServerlessTemplateFilePath)); Assert.NotNull(rootToken["Resources"]["MethodNotCreatedFromAnnotationsPackage"]); Assert.Equal(128, rootToken["Resources"]["MethodNotCreatedFromAnnotationsPackage"]["Properties"]["MemorySize"]); // unchanged Assert.Equal(100, rootToken["Resources"]["MethodNotCreatedFromAnnotationsPackage"]["Properties"]["Timeout"]); // unchanged var policies = rootToken["Resources"]["MethodNotCreatedFromAnnotationsPackage"]["Properties"]["Policies"] as JArray; Assert.NotNull(policies); Assert.Single(policies); Assert.Equal("AWSLambdaBasicExecutionRole", policies[0]); // unchanged }
public void UseRefForPolicies() { // ARRANGE var mockFileManager = GetMockFileManager(string.Empty); var lambdaFunctionModel = GetLambdaFunctionModel("MyAssembly::MyNamespace.MyType::Handler", "TestMethod", 45, 512, null, "Policy1, @Policy2, Policy3"); var cloudFormationJsonWriter = new CloudFormationJsonWriter(mockFileManager, _mockDirectoryManager, _jsonWriter, _diagnosticReporter); var report = GetAnnotationReport(new List <ILambdaFunctionSerializable> { lambdaFunctionModel }); // ACT cloudFormationJsonWriter.ApplyReport(report); // ASSERT var rootToken = JObject.Parse(mockFileManager.ReadAllText(ServerlessTemplateFilePath)); var policies = rootToken["Resources"]["TestMethod"]["Properties"]["Policies"] as JArray; Assert.Equal(3, policies.Count); Assert.Equal("Policy1", policies[0]); Assert.Equal("Policy2", policies[1]["Ref"]); Assert.Equal("Policy3", policies[2]); }
public void AddSingletonFunctionToEmptyTemplate() { // ARRANGE var mockFileManager = GetMockFileManager(string.Empty); var lambdaFunctionModel = GetLambdaFunctionModel("MyAssembly::MyNamespace.MyType::Handler", "TestMethod", 45, 512, null, null); var cloudFormationJsonWriter = new CloudFormationJsonWriter(mockFileManager, _mockDirectoryManager, _jsonWriter, _diagnosticReporter); var report = GetAnnotationReport(new List <ILambdaFunctionSerializable>() { lambdaFunctionModel }); // ACT cloudFormationJsonWriter.ApplyReport(report); // ASSERT var rootToken = JObject.Parse(mockFileManager.ReadAllText(ServerlessTemplateFilePath)); var functionToken = rootToken["Resources"]["TestMethod"]; var propertiesToken = functionToken["Properties"]; Assert.Equal("2010-09-09", rootToken["AWSTemplateFormatVersion"]); Assert.Equal("AWS::Serverless-2016-10-31", rootToken["Transform"]); Assert.Equal("AWS::Serverless::Function", functionToken["Type"]); Assert.Equal("Amazon.Lambda.Annotations", functionToken["Metadata"]["Tool"]); Assert.Equal("MyAssembly::MyNamespace.MyType::Handler", propertiesToken["Handler"]); Assert.Equal(512, propertiesToken["MemorySize"]); Assert.Equal(45, propertiesToken["Timeout"]); Assert.Equal(new List <string> { "AWSLambdaBasicExecutionRole" }, propertiesToken["Policies"].ToObject <List <string> >()); Assert.Equal("Zip", propertiesToken["PackageType"]); Assert.Equal(".", propertiesToken["CodeUri"]); Assert.Null(propertiesToken["ImageUri"]); Assert.Null(propertiesToken["ImageConfig"]); }
public void Execute(GeneratorExecutionContext context) { var diagnosticReporter = new DiagnosticReporter(context); try { // retrieve the populated receiver if (!(context.SyntaxContextReceiver is SyntaxReceiver receiver)) { return; } // If no project directory was detected then skip the generator. // This is most likely to happen when the project is empty and doesn't have any classes in it yet. if (string.IsNullOrEmpty(receiver.ProjectDirectory)) { return; } var semanticModelProvider = new SemanticModelProvider(context); if (receiver.StartupClasses.Count > 1) { foreach (var startup in receiver.StartupClasses) { // If there are more than one startup class, report them as errors diagnosticReporter.Report(Diagnostic.Create(DiagnosticDescriptors.MultipleStartupNotAllowed, Location.Create(startup.SyntaxTree, startup.Span), startup.SyntaxTree.FilePath)); } } var configureMethodModel = semanticModelProvider.GetConfigureMethodModel(receiver.StartupClasses.FirstOrDefault()); var annotationReport = new AnnotationReport(); var templateFinder = new CloudFormationTemplateFinder(_fileManager, _directoryManager); foreach (var lambdaMethod in receiver.LambdaMethods) { var lambdaMethodModel = semanticModelProvider.GetMethodSemanticModel(lambdaMethod); // Check for necessary references if (lambdaMethodModel.HasAttribute(context, TypeFullNames.RestApiAttribute) || lambdaMethodModel.HasAttribute(context, TypeFullNames.HttpApiAttribute)) { // Check for arbitrary type from "Amazon.Lambda.APIGatewayEvents" if (context.Compilation.ReferencedAssemblyNames.FirstOrDefault(x => x.Name == "Amazon.Lambda.APIGatewayEvents") == null) { diagnosticReporter.Report(Diagnostic.Create(DiagnosticDescriptors.MissingDependencies, lambdaMethod.GetLocation(), "Amazon.Lambda.APIGatewayEvents")); } } var model = LambdaFunctionModelBuilder.Build(lambdaMethodModel, configureMethodModel, context); // If there are more than one event, report them as errors if (model.LambdaMethod.Events.Count > 1) { foreach (var attribute in lambdaMethodModel.GetAttributes().Where(attribute => TypeFullNames.Events.Contains(attribute.AttributeClass.ToDisplayString()))) { diagnosticReporter.Report(Diagnostic.Create(DiagnosticDescriptors.MultipleEventsNotSupported, Location.Create(attribute.ApplicationSyntaxReference.SyntaxTree, attribute.ApplicationSyntaxReference.Span), DiagnosticSeverity.Error)); } // Skip multi-event lambda method from processing and check remaining lambda methods for diagnostics continue; } var template = new LambdaFunctionTemplate(model); var sourceText = template.TransformText().ToEnvironmentLineEndings(); context.AddSource($"{model.GeneratedMethod.ContainingType.Name}.g.cs", SourceText.From(sourceText, Encoding.UTF8, SourceHashAlgorithm.Sha256)); // report every generated file to build output diagnosticReporter.Report(Diagnostic.Create(DiagnosticDescriptors.CodeGeneration, Location.None, $"{model.GeneratedMethod.ContainingType.Name}.g.cs", sourceText)); annotationReport.LambdaFunctions.Add(model); } // Run the CloudFormation sync if any LambdaMethods exists. Also run if no LambdaMethods exists but there is a // CloudFormation template in case orphaned functions in the template need to be removed. // Both checks are required because if there is no template but there are LambdaMethods the CF template the template will be created. if (receiver.LambdaMethods.Any() || templateFinder.DoesCloudFormationTemplateExist(receiver.ProjectDirectory)) { annotationReport.CloudFormationTemplatePath = templateFinder.FindCloudFormationTemplate(receiver.ProjectDirectory); annotationReport.ProjectRootDirectory = receiver.ProjectDirectory; var cloudFormationJsonWriter = new CloudFormationJsonWriter(_fileManager, _directoryManager, _jsonWriter, diagnosticReporter); cloudFormationJsonWriter.ApplyReport(annotationReport); } } catch (Exception e) { // this is a generator failure, report this as error diagnosticReporter.Report(Diagnostic.Create(DiagnosticDescriptors.UnhandledException, Location.None, e.PrettyPrint())); #if DEBUG throw; #endif } }
public void PackageTypePropertyTest() { // ARRANGE - Set PackageType to Zip var mockFileManager = GetMockFileManager(string.Empty); var lambdaFunctionModel = GetLambdaFunctionModel("MyAssembly::MyNamespace.MyType::Handler", "TestMethod", 45, 512, null, null); lambdaFunctionModel.PackageType = LambdaPackageType.Zip; var cloudFormationJsonWriter = new CloudFormationJsonWriter(mockFileManager, _mockDirectoryManager, _jsonWriter, _diagnosticReporter); var report = GetAnnotationReport(new List <ILambdaFunctionSerializable>() { lambdaFunctionModel }); // ACT cloudFormationJsonWriter.ApplyReport(report); // ASSERT var rootToken = JObject.Parse(mockFileManager.ReadAllText(ServerlessTemplateFilePath)); var propertiesToken = rootToken["Resources"]["TestMethod"]["Properties"]; Assert.Equal("Zip", propertiesToken["PackageType"]); Assert.Equal(".", propertiesToken["CodeUri"]); Assert.Equal("MyAssembly::MyNamespace.MyType::Handler", propertiesToken["Handler"]); // ARRANGE - Change PackageType to Image lambdaFunctionModel.PackageType = LambdaPackageType.Image; report = GetAnnotationReport(new List <ILambdaFunctionSerializable>() { lambdaFunctionModel }); // ACT cloudFormationJsonWriter.ApplyReport(report); // ASSERT rootToken = JObject.Parse(mockFileManager.ReadAllText(ServerlessTemplateFilePath)); propertiesToken = rootToken["Resources"]["TestMethod"]["Properties"]; Assert.Equal("Image", propertiesToken["PackageType"]); Assert.Equal(".", propertiesToken["ImageUri"]); Assert.Equal(new JArray("MyAssembly::MyNamespace.MyType::Handler"), propertiesToken["ImageConfig"]["Command"]); Assert.Null(propertiesToken["CodeUri"]); Assert.Null(propertiesToken["Handler"]); // ARRANGE - Change PackageType back to Zip lambdaFunctionModel.PackageType = LambdaPackageType.Zip; report = GetAnnotationReport(new List <ILambdaFunctionSerializable>() { lambdaFunctionModel }); // ACT cloudFormationJsonWriter.ApplyReport(report); // ASSERT rootToken = JObject.Parse(mockFileManager.ReadAllText(ServerlessTemplateFilePath)); propertiesToken = rootToken["Resources"]["TestMethod"]["Properties"]; Assert.Equal("Zip", propertiesToken["PackageType"]); Assert.Equal(".", propertiesToken["CodeUri"]); Assert.Equal("MyAssembly::MyNamespace.MyType::Handler", propertiesToken["Handler"]); Assert.Null(propertiesToken["ImageUri"]); Assert.Null(propertiesToken["ImageConfig"]); }
public void EventAttributesTest() { // ARRANGE - USE A HTTP GET METHOD var mockFileManager = GetMockFileManager(string.Empty); var lambdaFunctionModel = GetLambdaFunctionModel("MyAssembly::MyNamespace.MyType::Handler", "TestMethod", 45, 512, null, null); var httpAttributeModel = new AttributeModel <HttpApiAttribute>() { Data = new HttpApiAttribute(LambdaHttpMethod.Get, "/Calculator/Add") { Version = HttpApiVersion.V1 } }; lambdaFunctionModel.Attributes = new List <AttributeModel>() { httpAttributeModel }; var cloudFormationJsonWriter = new CloudFormationJsonWriter(mockFileManager, _mockDirectoryManager, _jsonWriter, _diagnosticReporter); var report = GetAnnotationReport(new List <ILambdaFunctionSerializable>() { lambdaFunctionModel }); // ACT cloudFormationJsonWriter.ApplyReport(report); // ASSERT var rootToken = JObject.Parse(mockFileManager.ReadAllText(ServerlessTemplateFilePath)); var getToken = rootToken["Resources"]["TestMethod"]["Properties"]["Events"]["RootGet"]; Assert.NotNull(getToken); Assert.Equal("HttpApi", getToken["Type"]); Assert.Equal("/Calculator/Add", getToken["Properties"]["Path"]); Assert.Equal("GET", getToken["Properties"]["Method"]); Assert.Equal("1.0", getToken["Properties"]["PayloadFormatVersion"]); // ARRANGE - CHANGE TO A HTTP POST METHOD httpAttributeModel = new AttributeModel <HttpApiAttribute>() { Data = new HttpApiAttribute(LambdaHttpMethod.Post, "/Calculator/Add") }; lambdaFunctionModel.Attributes = new List <AttributeModel>() { httpAttributeModel }; // ACT cloudFormationJsonWriter.ApplyReport(report); // ASSERT rootToken = JObject.Parse(mockFileManager.ReadAllText(ServerlessTemplateFilePath)); getToken = rootToken["Resources"]["TestMethod"]["Properties"]["Events"]["RootGet"]; var postToken = rootToken["Resources"]["TestMethod"]["Properties"]["Events"]["RootPost"]; Assert.Null(getToken); // Verify that the HTTP GET method entry is deleted Assert.NotNull(postToken); Assert.Equal("HttpApi", postToken["Type"]); Assert.Equal("/Calculator/Add", postToken["Properties"]["Path"]); Assert.Equal("POST", postToken["Properties"]["Method"]); Assert.Equal("2.0", postToken["Properties"]["PayloadFormatVersion"]); }