using System; using System.IO; using System.Linq; using Amazon.Lambda.Annotations.SourceGenerator.FileIO; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace Amazon.Lambda.Annotations.SourceGenerator { /// /// This class contains utility methods to determine the .NET project's root directory and resolve the AWS serverless template file path. /// public class CloudFormationTemplateHandler { private const string DEFAULT_CONFIG_FILE_NAME = "aws-lambda-tools-defaults.json"; private const string DEFAULT_SERVERLESS_TEMPLATE_NAME = "serverless.template"; private readonly IFileManager _fileManager; private readonly IDirectoryManager _directoryManager; public CloudFormationTemplateHandler(IFileManager fileManager, IDirectoryManager directoryManager) { _fileManager = fileManager; _directoryManager = directoryManager; } /// /// This method takes any file path in the customer's .NET project and resolves the path to the .csproj file. /// /// /// This is the first .csproj file we find searching upward from the source file, where there /// is only a single .csproj in the current directory. /// /// The .NET project path public string DetermineProjectPath(string sourceFilePath) { if (!_fileManager.Exists(sourceFilePath)) return string.Empty; var directoryPath = _directoryManager.GetDirectoryName(sourceFilePath); while (!string.IsNullOrEmpty(directoryPath)) { var csprojFilesInDirectory = _directoryManager.GetFiles(directoryPath, "*.csproj"); if (csprojFilesInDirectory.Length == 1) { return csprojFilesInDirectory[0]; } directoryPath = _directoryManager.GetDirectoryName(directoryPath); } return string.Empty; } /// /// Determines the path to the AWS serverless template file. /// If the file does not exist then an empty file is created before returning the path. /// public string FindTemplate(string projectRootDirectory) { var templateAbsolutePath = DetermineTemplatePath(projectRootDirectory); if (!_fileManager.Exists(templateAbsolutePath)) _fileManager.Create(templateAbsolutePath).Close(); return templateAbsolutePath; } /// /// Checks if the AWS serverless template file exists. /// public bool DoesTemplateExist(string projectRootDirectory) { var templateAbsolutePath = DetermineTemplatePath(projectRootDirectory); return _fileManager.Exists(templateAbsolutePath); } /// /// Determines the file format of the AWS serverless template. /// If the template does not exist or if the template is empty, then by default is returned. /// public CloudFormationTemplateFormat DetermineTemplateFormat(string templatePath) { if (!_fileManager.Exists(templatePath)) { return CloudFormationTemplateFormat.Json; } var content = _fileManager.ReadAllText(templatePath); content = content.Trim(); if (string.IsNullOrEmpty(content)) { return CloudFormationTemplateFormat.Json; } return content[0] == '{' ? CloudFormationTemplateFormat.Json : CloudFormationTemplateFormat.Yaml; } /// /// This is a helper method to determine the path to the AWS serverless template file. /// It will first look for inside the project root directory and will try to resolve the template file path from the `template` property. /// If does not exist or if the 'template' property is not found, then default to projectRootDirectory/ /// private string DetermineTemplatePath(string projectRootDirectory) { if (!_directoryManager.Exists(projectRootDirectory)) throw new DirectoryNotFoundException("Failed to find the project root directory"); var templateAbsolutePath = string.Empty; var defaultConfigFile = _directoryManager.GetFiles(projectRootDirectory, DEFAULT_CONFIG_FILE_NAME, SearchOption.AllDirectories) .FirstOrDefault(); if (_fileManager.Exists(defaultConfigFile)) // the templateAbsolutePath will be empty if the template property is not found in the default config file templateAbsolutePath = GetTemplatePathFromDefaultConfigFile(defaultConfigFile); // if the default config file does not exist or if the template property is not found in the default config file // set the template path inside the project root directory. if (string.IsNullOrEmpty(templateAbsolutePath)) templateAbsolutePath = Path.Combine(projectRootDirectory, DEFAULT_SERVERLESS_TEMPLATE_NAME); return templateAbsolutePath; } /// /// This method parses the default config file and tries to resolve the serverless template path from the 'template' property. /// private string GetTemplatePathFromDefaultConfigFile(string defaultConfigFile) { JToken rootToken; try { rootToken = JObject.Parse(_fileManager.ReadAllText(defaultConfigFile)); } catch (Exception) { return string.Empty; } var templateRelativePath = rootToken["template"]? .ToObject()? .Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); if (string.IsNullOrEmpty(templateRelativePath)) return string.Empty; var templateAbsolutePath = Path.Combine(_directoryManager.GetDirectoryName(defaultConfigFile), templateRelativePath); return templateAbsolutePath; } } }