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;
}
}
}