using System.Text.RegularExpressions;
using Core;
using Core.Dto.Yaml;
using Core.Exceptions;
using Core.Helpers;
using Core.Yaml;
using Generator.Mappers;
using Newtonsoft.Json;
using YamlDotNet.Serialization;
namespace Generator.DataSource.Yaml;
public class OpenApiYamlExtractor
{
///
/// Retrieves an openapi-formatted yaml at a specific location
///
/// file location
///
public OpenApiYaml ExtractNode(string path)
{
var text = PathHelper.TextFrom(path);
var deserializer = new DeserializerBuilder()
.IgnoreUnmatchedProperties()
.Build();
var dto = deserializer.Deserialize(text);
return dto.ToModel(path);
}
///
/// Loads all configs that can be found in the config file located at the given path
///
/// Given path
///
/// When config file couldn't has been deserialize
/// When the given path does not exists
public Dictionary LoadConfigs(string configPath)
{
try
{
var json = PathHelper.TextFrom(configPath);
var configDto = JsonConvert.DeserializeObject(json);
return (configDto ?? throw new InvalidOperationException()).Map();
}
catch (FileNotFoundException)
{
throw new ConfigException($"No config file found at {configPath}. Fix it or i'll never generate anything for you");
}
}
///
/// Extract all scoped references of a specification file. In other words, this will extracts all references
/// between the given spec file and the closer reference-related schemas, including paths and params files
///
/// Given file path
///
public ISet ExtractScopedRefs(string filePath) => ExtractRefs(filePath, false, new HashSet());
///
/// Extract all possibles references from a given specification file
///
/// Given file path
///
public ISet ExtractAllRefs(string filePath) => ExtractRefs(filePath, true, new HashSet());
private ISet ExtractRefs(string filePath, bool deepSearch, ISet refs)
{
const string pattern = """(\$ref):\s{1}['|"](.+)['|"]""";
var text = PathHelper.TextFrom(filePath);
var matches = Regex.Matches(text, pattern);
foreach (Match m in matches)
{
var rawReference = m.Groups[2].Value;
var absolutePath = AbsolutePathFromRef(filePath, rawReference);
//If path references itself or circular dependency is detected
if (absolutePath == string.Empty || refs.Contains(absolutePath)) continue;
if (!deepSearch && IsSchema(absolutePath))
{
refs.Add(absolutePath);
continue;
}
var subRefs = ExtractRefs(absolutePath, deepSearch, refs);
refs.UnionWith(subRefs);
refs.Add(absolutePath);
}
return refs;
}
private string AbsolutePathFromRef(string relative, string reference)
{
var result = PathHelper.GetFolder(relative) + reference;
var r = result.Split("#")[0];
return r[^1].Equals('/') ? string.Empty : PathHelper.AbsolutePath(r);
}
private bool IsSchema(string path)
{
var l = new Location(path);
return l.LastElement().Contains("schemas");
}
}