Import from internal git

This commit is contained in:
2025-10-11 13:08:09 +02:00
commit 97aaa715dc
175 changed files with 7014 additions and 0 deletions

365
.gitignore vendored Normal file
View File

@@ -0,0 +1,365 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
#JetBrains config folder
.idea/

View File

@@ -0,0 +1,12 @@
using NUnit.Framework;
namespace ApiGenerator.tests;
public class ConfigManagerTests
{
[Test]
public void Test()
{
}
}

74
CLI/appsettings.json Normal file
View File

@@ -0,0 +1,74 @@
{
"Environment": {
"invite" : "powershell.exe",
"localRoot": "C:/Users/Laurent/Desktop/ag2/ApiGenerator/architecture/api/apis/example",
"dockerRoot" : "/api/"
},
"DefaultArguments": {
"specIdentifier" : "example",
"generatorVersion" : "7.3.0"
},
"General": {
"apiFolder" : "apis",
"outputFolder": "generated"
},
"Dotnet" : {
"ServerFolder": "dotnet-api",
"CommonFolder": "dotnet-models",
"ClientFolder": "dotnet-client"
},
"Javascript" : {
"Folder": "javascript-client"
},
"Java": {
"Folder": "java"
},
"OpenApi" : {
"OutputFolder": "openapi",
"GeneratorConfigFile" : "openapi-generator-config.yaml",
"GeneratorIgnoreFile" : ".openapi-generator-ignore",
"specExtension": ".yaml",
"schemasIdentifier": "-schemas",
"configExtension": ".json",
"configIdentifier": "-config"
},
"Templates" : {
"folder": "templates",
"plantUml": "plantUml.mustache",
"dotnetServerGeneratorConfig": "dotnet-server-generator-config.mustache",
"dotnetClientGeneratorConfig": "dotnet-client-generator-config.mustache",
"javaGeneratorConfig": "java-generator-config.mustache",
"serverIgnore": "dotnet-server-ignore.mustache",
"clientIgnore": "dotnet-client-ignore.mustache"
},
"DockerImages": {
"javascriptImage":"temporaly_missing_please_fill_javascripImage_field_in_appsettings.json",
"openapiGeneratorImageName":"openapitools/openapi-generator-cli",
"openapiGeneratorVersion":"7.3.0",
"openapiGeneratorImage":"openapitools/openapi-generator-cli:v7.3.0",
"dotnetSdkImage":"mcr.microsoft.com/dotnet/sdk:8.0",
"mavenImage":"maven:3.9.5"
},
"Publish": {
"npmRegistry":"https://npm-registry-sample.url",
"nugetRegistry":"https://nuget-registry-sample.url",
"mavenRegistry":"https://maven-registry-sample.url"
},
"Credentials": {
"token": "TOKEN_SAMPLE",
"email":"EMAIL_SAMPLE",
"username": "USERNAME_SAMPLE",
"password": "PASSWORD_SAMPLE",
"always-auth": "true"
}
}

View File

@@ -0,0 +1,13 @@
namespace Core.Actions;
public class DotnetPublish
{
public required Location LocalRoot;
public required Location DockerRoot;
public required string Image;
public required string Registry;
public required Location PackageFolder;
public required string AuthorizationToken;
public required string PackageVersion { get; set; }
public required string PackageFile { get; set; }
}

View File

@@ -0,0 +1,19 @@
namespace Core.Actions;
public class JavaPublish
{
public required Location LocalRoot;
public required Location DockerRoot;
public required string Image;
public required Location TemplateFolder;
public required Location OutputFolder;
public required string Group;
public required string Artifact;
public required string Version;
public required string Registry;
public required string Username;
public required string Password;
}

View File

@@ -0,0 +1,16 @@
namespace Core.Actions;
public class JavascriptPublish
{
public required Location LocalRoot;
public required Location DockerRoot;
public required string Image;
public required string Registry;
public required string PackageName;
public required string Version;
public required Location SpecFile;
public required Location FrontFolder;
}

View File

@@ -0,0 +1,7 @@
namespace Core.Actions;
public class PackageDeletion
{
public required string Username;
public required string Password;
}

View File

@@ -0,0 +1,8 @@
namespace Core.Actions;
public class PlantUmlExport
{
public required Location LocalRoot;
public required Location Template;
public required Location Output;
}

View File

@@ -0,0 +1,29 @@
using Core.Dto;
using Core.SpecConfig;
namespace Core;
public class ArgumentsExtractor
{
private ArgumentsDto _args;
public GenerationType? GenType =>
_args.ClientOnly ? GenerationType.Client :
_args.ModelsOnly ? GenerationType.Common :
_args.ApiOnly ? GenerationType.Server :
null;
public PublishType PublishType =>
_args.SafePublish ? PublishType.Safe :
_args.ForcePublish ? PublishType.Force :
PublishType.No;
public string Spec => _args.SpecName;
public bool ExportPuml => _args.ExportPuml;
public ArgumentsExtractor(ArgumentsDto args)
{
_args = args;
}
}

16
Core/Core.csproj Normal file
View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Services.Common" Version="1.0.0" />
<PackageReference Include="YamlDotNet" Version="15.1.2" />
</ItemGroup>
</Project>

31
Core/Dto/ArgumentsDto.cs Normal file
View File

@@ -0,0 +1,31 @@
using CommandLine;
namespace Core.Dto;
public class ArgumentsDto
{
[Option('g', "gui", Required = false)]
public bool Gui { get; set; }
[Option('n', "name", Required = false)]
public string SpecName { get; set; } = null!;
[Option('a', "api-only", Required = false, Default = false)]
public bool ApiOnly { get; set; }
[Option('m', "models-only", Required = false, Default = false)]
public bool ModelsOnly { get; set; }
[Option('c', "client-only", Required = false, Default = false)]
public bool ClientOnly { get; set; }
[Option('s', "safe-publish", Required = false, Default = false)]
public bool SafePublish { get; set; }
[Option('f', "force-publish", Required = false, Default = false)]
public bool ForcePublish { get; set; }
[Option('e', "export-puml", Required = false, Default = false)]
public bool ExportPuml { get; set; }
}

View File

@@ -0,0 +1,25 @@
using Newtonsoft.Json;
namespace Core.Dto;
public class PackageDataDto
{
[JsonProperty("id")]
public string Id;
[JsonProperty("repository")]
public string Repository;
[JsonProperty("format")]
public string Format;
[JsonProperty("group")]
public string Group;
[JsonProperty("name")]
public string Name;
[JsonProperty("version")]
public string Version;
}

View File

@@ -0,0 +1,14 @@
namespace Core.Dto.Settings;
/// <summary>
/// This class gathers all credentials-related configurations
/// </summary>
public class CredentialsConfigDto
{
public string Token { get; set; } = "";
public string Email { get; set; } = "";
public string Username { get; set; } = "";
public string Password { get; set; } = "";
public string AlwaysAuth { get; set; } = "";
}

View File

@@ -0,0 +1,11 @@
namespace Core.Dto.Settings;
/// <summary>
/// This class gathers all default-related configurations
/// </summary>
public class DefaultArgumentsConfigDto
{
public string SpecIdentifier { get; set; } = "";
public string GeneratorVersion { get; set; } = "";
}

View File

@@ -0,0 +1,13 @@
namespace Core.Dto.Settings;
/// <summary>
/// This class gathers docker images
/// </summary>
public class DockerImagesConfigDto
{
public string JavascriptImage { get; set; } = "";
public string OpenApiGeneratorImage { get; set; } = "";
public string DotnetSdkImage { get; set; } = "";
public string MavenImage { get; set; } = "";
}

View File

@@ -0,0 +1,8 @@
namespace Core.Dto.Settings;
public class DotnetConfigDto
{
public string ServerFolder { init; get; } = "";
public string CommonFolder { init; get; } = "";
public string ClientFolder { init; get; } = "";
}

View File

@@ -0,0 +1,12 @@
namespace Core.Dto.Settings;
/// <summary>
/// This class gathers environment-related configuration
/// </summary>
public class EnvironmentConfigDto
{
public string Invite { get; set; } = "";
public string LocalRoot { get; set; } = "";
public string DockerRoot { get; set; } = "";
}

View File

@@ -0,0 +1,7 @@
namespace Core.Dto.Settings;
public class GeneralConfigDto
{
public string ApiFolder { init; get; } = "";
public string OutputFolder { init; get; } = "";
}

View File

@@ -0,0 +1,6 @@
namespace Core.Dto.Settings;
public class JavaConfigDto
{
public string Folder { init; get; } = "";
}

View File

@@ -0,0 +1,8 @@
namespace Core.Dto.Settings;
public class JavascriptConfigDto
{
public string Folder { init; get; } = "";
}

View File

@@ -0,0 +1,15 @@
namespace Core.Dto.Settings;
public class OpenApiConfigDto
{
public string OutputFolder { init; get; } = "";
public string GeneratorConfigFile { init; get; } = "";
public string GeneratorIgnoreFile { init; get; } = "";
public string SpecExtension {init; get; } = "";
public string SchemasIdentifier {init; get; } = "";
public string ConfigExtension {init; get; } = "";
public string ConfigIdentifier {init; get; } = "";
}

View File

@@ -0,0 +1,11 @@
namespace Core.Dto.Settings;
/// <summary>
/// This class gathers publish-related configurations
/// </summary>
public class PublishConfigDto
{
public string NpmRegistry { get; set; } = "";
public string NugetRegistry { get; set; } = "";
public string MavenRegistry { get; set; } = "";
}

View File

@@ -0,0 +1,12 @@
namespace Core.Dto.Settings;
public class TemplatesConfigDto
{
public string Folder { init; get; } = "";
public string PlantUml { init; get; } = "";
public string DotnetServerGeneratorConfig { init; get; } = "";
public string DotnetClientGeneratorConfig { init; get; } = "";
public string JavaGeneratorConfig { init; get; } = "";
public string ServerIgnore { init; get; } = "";
public string ClientIgnore { init; get; } = "";
}

View File

@@ -0,0 +1,19 @@
using YamlDotNet.Serialization;
namespace Core.Dto.Yaml;
public class OpenApiYamlDto
{
[YamlMember(Alias = "openapi")]
public string? Openapi { get; set; }
[YamlMember(Alias = "info")]
public IDictionary<string, object>? Info { get; set; }
[YamlMember(Alias = "tags")]
public IList<object>? Tags { get; set; }
[YamlMember(Alias = "components")]
public IDictionary<string, IDictionary<string, object>>? Components { get; set; }
}

View File

@@ -0,0 +1,26 @@
using Newtonsoft.Json;
namespace Core.Dto.Yaml;
[JsonObject]
public class PackageConfigDto
{
[JsonProperty("name")]
public string Name { get; set; } = "";
[JsonProperty("type")]
public string Type { get; set; } = "";
[JsonProperty("dotnetPackage")]
public string DotnetPackage { get; set; } = "";
[JsonProperty("javascriptPackage")]
public string JavascriptPackage { get; set; } = "";
[JsonProperty("keepModels")]
public bool KeepModels { get; set; }
[JsonProperty("priority")]
public int Priority { get; set; }
}

View File

@@ -0,0 +1,19 @@
using Newtonsoft.Json;
namespace Core.Dto.Yaml;
public class SpecConfigDto
{
[JsonProperty("packageTypes")]
public Dictionary<string, List<string>> PackageTypes { get; set; } = [];
[JsonProperty("javaGroup")]
public string JavaGroup { get; set; } = "";
[JsonProperty("modelSuffix")]
public string ModelSuffix { get; set; } = "";
[JsonProperty("packages")]
public List<PackageConfigDto> Packages { get; set; } = [];
}

View File

@@ -0,0 +1,3 @@
namespace Core.Events;
public record DisplayEventArgs(string? Content);

View File

@@ -0,0 +1,17 @@
namespace Core.Events;
public class DisplayEmitter
{
public event EventHandler<DisplayEventArgs>? OnSay;
public event EventHandler<DisplayEventArgs>? OnWarn;
public void Say(object? sender, string message)
{
OnSay?.Invoke(this, new DisplayEventArgs(message));
}
public void Warn(object? sender, string message)
{
OnWarn?.Invoke(this, new DisplayEventArgs(message));
}
}

9
Core/Events/IEmitter.cs Normal file
View File

@@ -0,0 +1,9 @@
namespace Core.Events;
/*public interface IEmitter<T>
{
public event EventHandler<T>? OnSay;
public event EventHandler<T>? OnWarn;
public void Say(object? sender, T args);
public void Warn(object? sender, T args);
}*/

View File

@@ -0,0 +1,3 @@
namespace Core.Exceptions;
public class ConfigException(string message) : Exception(message);

View File

@@ -0,0 +1,3 @@
namespace Core.Exceptions;
public class FillerException(string message) : Exception(message);

View File

@@ -0,0 +1,3 @@
namespace Core.Exceptions;
public class CommandExecutionException(string message) : Exception(message);

View File

@@ -0,0 +1,3 @@
namespace Core.Exceptions;
public class PackageVersionException(string message) : Exception(message);

View File

@@ -0,0 +1,3 @@
namespace Core.Exceptions;
public class WeirdException(string message) : Exception(message);

View File

@@ -0,0 +1,15 @@
namespace Core.Helpers;
public static class EnumHelper
{
/// <summary>
/// Retrieved maximum possible value in an enum. In other words, return the number of values contained in the enum
/// </summary>
/// <typeparam name="T">Enum</typeparam>
/// <returns>Number of possible values</returns>
public static int GetMaxValue<T>() where T : Enum
{
var i = Enum.GetValues(typeof(T)).Length;
return i;
}
}

View File

@@ -0,0 +1,85 @@
using System.Text;
namespace Core.Helpers;
/// <summary>
/// Provides some path-related actions
/// </summary>
public static class PathHelper
{
/// <summary>
/// Verifies the existence of a path
/// </summary>
/// <param name="path">Path to check</param>
/// <exception cref="FileNotFoundException">If the given path does not exist</exception>
public static void CheckPathValidity(string path)
{
if (!File.Exists(path)) throw new FileNotFoundException($"Could not found file at {path}");
}
/// <summary>
/// Create a file and all its parents directories if the file does not exists
/// </summary>
/// <param name="path"></param>
public static void CreateFileIfNotExists(string path)
{
if (!File.Exists(path)) Directory.CreateDirectory(Path.GetDirectoryName(path)!);
}
/// <summary>
/// Extracts raw text from a file references by given path
/// </summary>
/// <param name="path">Location of file</param>
/// <returns>Raw text</returns>
/// <exception cref="FileNotFoundException">If the given path does not exist</exception>
public static string TextFrom(string path)
{
CheckPathValidity(path);
return File.ReadAllText(path);
}
/// <summary>
/// Get the folder containing the last item of a given path
///
/// </summary>
/// <param name="path">The given path</param>
/// <returns>The folder containing the last element of the given path</returns>
public static string GetFolder(string path)
{
path = path.Replace("\\", "/");
var s = path.Split("/").ToList();
s.RemoveAt(s.Count-1);
var r = string.Join("/", s) + "/";
return r;
}
public static string GetName(string path)
{
return path.Split("/")[0];
}
/// <summary>
/// Concatenates a string representing the path of a folder and a string representing a file name
/// The strings come from default arguments.
/// </summary>
/// <returns>Concatenated path</returns>
public static string FullPath(params string[] pathItems)
{
StringBuilder builder = new StringBuilder();
foreach (var pi in pathItems)
{
builder.Append(pi);
}
return AbsolutePath(builder.ToString());
}
/// <summary>
/// Returns the absolute path based on a relative path
/// </summary>
/// <param name="relative">relative path</param>
/// <returns>Absolute path as string</returns>
public static string AbsolutePath(string relative)
{
return Path.GetFullPath(relative);
}
}

View File

@@ -0,0 +1,19 @@
namespace Core.Helpers;
public static class TypeHelper
{
/// <summary>
/// Safely cast a less derived object to a more derived object
/// </summary>
/// <param name="obj">Object to cast</param>
/// <typeparam name="TBase">Less derived type</typeparam>
/// <typeparam name="TTarget">More derived type</typeparam>
/// <returns></returns>
/// <exception cref="InvalidOperationException">When data parameter is not a derived of TBase</exception>
public static TTarget SafeCast<TBase, TTarget>(TBase obj)
{
if (obj is not TTarget result)
throw new InvalidOperationException($"Cannot cast {typeof(TBase)} to {typeof(TTarget)}");
return result;
}
}

20
Core/ISpecFile.cs Normal file
View File

@@ -0,0 +1,20 @@
using Core.SpecConfig;
namespace Core;
public interface ISpecFile
{
public Location Location { get; }
public string Name { get; }
public string Folder { get; }
public ISet<string> ScopedRefs { get; set; }
public SpecType SpecType { get; }
public string NugetPackage { get; }
public string MavenGroup { get; }
public string NpmPackage { get; }
public string Version { get; }
public int Priority { get; }
public Queue<ISpecFile>GetTasksBy(Language language, GenerationType type);
}

View File

@@ -0,0 +1,8 @@
using Core.Events;
namespace Core.Interfaces;
public interface IController
{
public DisplayEmitter Emitter { get; set; }
}

View File

@@ -0,0 +1,7 @@
namespace Core.Interfaces;
public interface IDataSourceLoader
{
public void LoadAppsettings();
}

View File

@@ -0,0 +1,6 @@
namespace Core.Interfaces;
public interface IExporter
{
public void Export();
}

View File

@@ -0,0 +1,14 @@
using Core.Settings;
namespace Core.Interfaces;
public interface IGeneratorDirector<T> where T : ISpecFile
{
public Task DotnetServer(T file, DotnetConfig config);
public Task DotnetClient(T file, DotnetConfig config);
public Task Java(T file, JavaConfig config);
public Task Javascript(T file, JavascriptConfig config);
}

View File

@@ -0,0 +1,12 @@
using Core.Dto;
using Core.SpecConfig;
namespace Core.Interfaces;
public interface IRepositoryRequest
{
public Task<List<PackageDataDto>> GetVersions(Language language, string packageName);
public Task<bool> DeleteVersion(string id);
}

14
Core/Interfaces/IView.cs Normal file
View File

@@ -0,0 +1,14 @@
namespace Core.Interfaces;
public interface IView
{
void Exception(string message);
void Warning(string message);
void Info(string message);
void Success(string message);
void Progress(int percentage);
}

84
Core/Location.cs Normal file
View File

@@ -0,0 +1,84 @@
using System.Text;
namespace Core;
public class Location
{
public string Path { get; private set; }
public Location(string path)
{
Path = path;
}
public Location(string[] pathItems)
{
Path = new StringBuilder().AppendJoin("/", pathItems).ToString();
Clean();
}
public Location(Location[] pathItems)
{
Path = new StringBuilder().AppendJoin("/", pathItems.Select(pi => pi.ToString())).ToString();
Clean();
}
public string LastElement()
{
return Path.Split(['/', '\\'])[^1];
}
public bool HasExtension(string extension)
{
return LastElement().Split('.')[^1].Contains(extension);
}
public bool IsInSameFolder(Location loc)
{
loc.Clean();
var folder = GetFolder();
var locFolder = loc.GetFolder();
return folder.Equals(locFolder);
}
public string GetFolder() => Path.Split(['/', '\\'])[^2];
public string GetFileName() => LastElement().Split('.')[0];
public Location ConcatenateWith(string[] relatives)
{
var data = new StringBuilder().AppendJoin("/", relatives).ToString();
var path = new Location(Add(data));
path.Clean();
return path;
}
public Location ConcatenateWith(string relative)
{
var path = new Location(Add(relative));
path.Clean();
return path;
}
public Location ConcatenateWith(Location relative)
{
return ConcatenateWith(relative.ToString());
}
private string Add(string value)
{
var builder = new StringBuilder();
var newPath = builder.AppendJoin("/", [Path, value]);
return newPath.ToString();
}
private void Clean()
{
Path = Path
.Replace(@"\", "/")
.Replace("/./", "/")
.Replace("//", "/");
}
public override string ToString() => Path;
}

25
Core/PackageData.cs Normal file
View File

@@ -0,0 +1,25 @@
using System.Text;
namespace Core;
public class PackageData
{
public string Id;
public string Name;
public string Version;
public PackageData()
{
Id = "";
Name = "";
Version = "";
}
public string GetVersions()
{
return new StringBuilder().AppendJoin(", ", Version).ToString();
}
}

View File

@@ -0,0 +1,47 @@
using System.Collections;
using Core.SpecConfig;
namespace Core.Process;
public class GenerationProcess : IEnumerable
{
public ISpecFile BaseFile { get; private set; }
private List<ProcessTask> _tasks;
private PublishType _publishType;
private GenerationType? _generationType;
public GenerationProcess(ISpecFile spec, PublishType pubType, GenerationType? genType)
{
BaseFile = spec;
_tasks = RetrieveTasks();
_publishType = pubType;
_generationType = genType;
}
private List<ProcessTask> RetrieveTasks()
{
var res = new List<ProcessTask>();
foreach (Language language in Enum.GetValues(typeof(Language)))
{
foreach (GenerationType type in Enum.GetValues(typeof(GenerationType)))
{
var tasks = BaseFile.GetTasksBy(language, type);
if (tasks.Count == 0) continue;
res.Add(new ProcessTask(language, type, _publishType, tasks));
}
}
return _generationType == null ? res
: res.Where(p => p.GenerationType == _generationType).ToList();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public ProcessEnumerator GetEnumerator()
{
_tasks = RetrieveTasks();
return new ProcessEnumerator(_tasks);
}
}

76
Core/ProcessEnumerator.cs Normal file
View File

@@ -0,0 +1,76 @@
using System.Collections;
using Core.Helpers;
using Core.SpecConfig;
namespace Core;
public class ProcessEnumerator : IEnumerator
{
private int _languagePosition;
private int _typePosition;
private readonly List<ProcessTask> _enumerable;
object IEnumerator.Current => Current;
public ProcessTask Current
{
get
{
return _enumerable.First(pt => (int)pt.GenerationType == _typePosition
&& (int)pt.Language == _languagePosition);
}
}
public ProcessEnumerator(List<ProcessTask> current)
{
_languagePosition = -1;
_typePosition = -1;
_enumerable = current;
}
public bool MoveNext()
{
if (_typePosition == -1 && _languagePosition == -1)
{
_typePosition += 1;
_languagePosition += 1;
} else if (PassedTypeBoundaries())
{
_typePosition = 0;
_languagePosition += 1;
}
else
{
_typePosition += 1;
}
if (!HasElement() && !PassedMaxPosition())
{
MoveNext();
}
return !PassedMaxPosition();
}
public void Reset()
{
_languagePosition = -1;
_typePosition = -1;
}
private bool PassedLanguageBoundaries() => _languagePosition >= EnumHelper.GetMaxValue<Language>() - 1;
private bool PassedTypeBoundaries() => _typePosition >= EnumHelper.GetMaxValue<GenerationType>() - 1;
private bool PassedMaxPosition() => PassedLanguageBoundaries() && PassedTypeBoundaries();
private bool HasElement()
{
var e= _enumerable.FirstOrDefault(pt => (int)pt.GenerationType == _typePosition
&& (int)pt.Language == _languagePosition);
return e != null;
}
}

View File

@@ -0,0 +1,12 @@
namespace Core.Settings;
/// <summary>
/// This class gathers environment-related configuration
/// </summary>
public class BaseConfig
{
public required string Invite { init; get; } = "";
public required Location LocalRoot { init; get; }
public required Location DockerRoot { init; get; }
public required Location TemplateFolder { init; get; }
}

View File

@@ -0,0 +1,13 @@
namespace Core.Settings;
/// <summary>
/// This class gathers all credentials-related configurations
/// </summary>
public class CredentialsConfig
{
public string Token { init; get; } = "";
public string Email { init; get; } = "";
public string Username { init; get; } = "";
public string Password { init; get; } = "";
public string AlwaysAuth { init; get; } = "";
}

View File

@@ -0,0 +1,10 @@
namespace Core.Settings;
/// <summary>
/// This class gathers all default-related configurations
/// </summary>
public class DefaultArgumentsConfig
{
public string SpecIdentifier { get; set; } = "";
}

View File

@@ -0,0 +1,15 @@
namespace Core.Settings;
/// <summary>
/// This class gathers docker images
/// </summary>
public class DockerImagesConfig
{
public string JavascriptImage { init; get; } = "";
public string OpenApiGeneratorImageName { init; get; } = "";
public string OpenApiGeneratorVersion { init; get; } = "";
public string OpenApiGeneratorImage { init; get; } = "";
public string DotnetSdkImage { init; get; } = "";
public string JavaImage { init; get; } = "";
}

View File

@@ -0,0 +1,42 @@
using Core.SpecConfig;
namespace Core.Settings;
public class DotnetConfig : BaseConfig
{
public GenerationType Type { get; set; } = GenerationType.Server;
public required string GenerationImage { init; get; }
public required string BuildImage { init; get; }
public required string PackageFile { init; get; }
public required string PackageFolder { init; get; }
public required Location SpecFile { init; get; }
public required Location OpenApi { init; get; }
public required Location ServerFolder { init; get; }
public required Location CommonFolder { init; get; }
public required Location ClientFolder { init; get; }
public required Location ConfigFile { init; get; }
public required Location IgnoreFile { init; get; }
public required Location ServerConfigTemplate { init; get; }
public required Location ClientConfigTemplate { init; get; }
public required Location ServerIgnoreTemplate { init; get; }
public required Location ClientIgnoreTemplate { init; get; }
public required string Registry;
public required string AuthorizationToken;
public Location OutputFolder()
=> Type switch
{
GenerationType.Server => ServerFolder,
GenerationType.Common => CommonFolder,
GenerationType.Client => ClientFolder,
_ => throw new ArgumentOutOfRangeException(nameof(Type), Type, null)
};
public Location PackageFolderPath() => OutputFolder().ConcatenateWith([PackageFolder, PackageFile]);
public Location OpenApiFolder() => new ([OutputFolder(), OpenApi]);
public Location OpenApiSpecFile() => new Location([OutputFolder(), OpenApi, OpenApi]).ConcatenateWith("openapi.yaml");
public Location OpenApiConfigFile() => new Location([OutputFolder()]).ConcatenateWith("openapi-generator-config.yaml");
public Location ConfigFilePath() => new ([OutputFolder(), ConfigFile]);
public Location IgnoreFilePath() => new ([OutputFolder(), IgnoreFile]);
}

View File

@@ -0,0 +1,8 @@
namespace Core.Settings;
public class GeneralConfig
{
public required Location ApiFolder { init; get; }
public required Location GenerationFolder { init; get; }
}

View File

@@ -0,0 +1,27 @@
namespace Core.Settings;
public class JavaConfig : BaseConfig
{
public required string GenerationImage { init; get; }
public required string BuildImage { init; get; }
public required string Registry { init; get; }
public required string Artifact { init; get; }
public required Location ServerFolder { init; get; }
public required Location ConfigOutput { init; get; }
public required Location ConfigFile { init; get; }
public required Location OpenApi { init; get; }
public required Location SpecFile { get; set; }
public required Location ServerConfigTemplate { init; get; }
public required string Version { get; set; }
public required string Username { get; set; }
public required string Password { get; set; }
public Location OutputFolder() => ServerFolder;
public Location ConfigFilePath() => new ([OutputFolder(), ConfigFile]);
public Location OpenApiFolder() => new ([OutputFolder(), OpenApi]);
//TODO : Hard coded string ???
public Location OpenApiSpecFile() => new Location([OutputFolder(), OpenApi, OpenApi]).ConcatenateWith("openapi.yaml");
}

View File

@@ -0,0 +1,21 @@
namespace Core.Settings;
public class JavascriptConfig : BaseConfig
{
public required string GenerationImage { init; get; }
public required string BuildImage { get; set; }
public required Location ClientFolder { init; get; }
public required string PackageName { init; get; }
public required string OpenApiVersion { init; get; }
public required string Registry { init; get; }
public required Location SpecFile { init; get; }
public required Location OpenApi { init; get; }
public Location OutputFolder() => ClientFolder;
public Location OpenApiFolder() => new ([OutputFolder(), OpenApi]);
public Location OpenApiSpecFile() => new Location([OutputFolder(), OpenApi, OpenApi]).ConcatenateWith("openapi.yaml");
}

View File

@@ -0,0 +1,21 @@
namespace Core.Settings;
public class OpenApiConfig
{
public required Location Folder { get; set; }
public required Location GeneratorConfigFile { get; set; }
public required Location GeneratorIgnoreFile { get; set; }
public required Location SpecFile { get; set; }
public required Location SpecConfig { get; set; }
public required string SpecExtension { get; set; }
public required string SchemasIdentifier { get; set; }
public required string ConfigIdentifier { get; set; }
public void AddRoot(Location root)
{
SpecConfig = root.ConcatenateWith(SpecConfig);
SpecFile = root.ConcatenateWith(SpecFile);
}
}

View File

@@ -0,0 +1,11 @@
namespace Core.Settings;
/// <summary>
/// This class gathers publish-related configurations
/// </summary>
public class PublishConfig
{
public string NpmRegistry { init; get; } = "";
public string NugetRegistry { init; get; } = "";
public string MavenRegistry { init; get; } = "";
}

View File

@@ -0,0 +1,12 @@
namespace Core.Settings;
public class TemplatesConfig
{
public required Location Folder { init; get; }
public required Location PlantUml { init; get; }
public required Location DotnetServerGeneratorConfig { init; get; }
public required Location DotnetClientGeneratorConfig { init; get; }
public required Location JavaGeneratorConfig { init; get; }
public required Location ServerIgnore { init; get; }
public required Location ClientIgnore { init; get; }
}

View File

@@ -0,0 +1,8 @@
namespace Core.SpecConfig;
public enum GenerationType
{
Common = 0,
Server = 1,
Client = 2,
}

View File

@@ -0,0 +1,9 @@
namespace Core.SpecConfig;
public enum Language
{
Dotnet = 0,
Java = 1,
Javascript = 2
}

View File

@@ -0,0 +1,28 @@
namespace Core.SpecConfig;
public class PackageTypes
{
private readonly Dictionary<Language, List<GenerationType>> _types;
public PackageTypes()
{
_types = new Dictionary<Language, List<GenerationType>>();
}
public HashSet<Language> GetLanguages() => _types.Keys.ToHashSet();
public List<GenerationType> GetBy(Language language) => _types[language];
public void Add(Language language, List<GenerationType> types) => _types.Add(language, types);
public bool TryAdd(Language language, GenerationType types)
{
if (!_types.TryGetValue(language, out List<GenerationType>? value)) return false;
value.Add(types);
return true;
}
public bool Has(Language language, GenerationType type)
=> _types.ContainsKey(language) && _types[language].Contains(type);
}

View File

@@ -0,0 +1,17 @@
namespace Core.SpecConfig;
public class ProcessTask
{
public readonly Language Language;
public readonly GenerationType GenerationType;
public readonly PublishType PublishType;
public readonly Queue<ISpecFile> Tasks;
public ProcessTask(Language l, GenerationType g, PublishType p, Queue<ISpecFile> tasks)
{
Language = l;
GenerationType = g;
Tasks = tasks;
PublishType = p;
}
}

View File

@@ -0,0 +1,8 @@
namespace Core.SpecConfig;
public enum PublishType
{
No = 0,
Safe = 1,
Force = 2,
}

View File

@@ -0,0 +1,7 @@
namespace Core.SpecConfig;
public enum SpecType
{
Model = 0,
Api = 1,
}

View File

@@ -0,0 +1,7 @@
namespace Core.Templates;
public interface ITemplateFactory
{
public ITemplate GetTemplate(string templatePath, IDictionary<string, object> data);
}

View File

@@ -0,0 +1,9 @@
using System.Collections;
namespace Core.Templates;
public interface ITemplate
{
public IEnumerable GetData();
public string GetText();
}

View File

@@ -0,0 +1,23 @@
using System.Collections;
namespace Core.Templates;
public class MustacheTemplate : ITemplate
{
private readonly string _text;
private readonly TemplateData<string, object> _data;
public MustacheTemplate(string text)
{
_text = text;
_data = new TemplateData<string, object>();
}
public void AddProperties(IDictionary<string, object> data)
{
_data.AddAll(data);
}
public IEnumerable GetData() => _data.GetData();
public string GetText() => _text;
}

View File

@@ -0,0 +1,19 @@
using Core.Helpers;
namespace Core.Templates;
public class MustacheTemplateFactory : ITemplateFactory
{
public MustacheTemplateFactory()
{
}
public ITemplate GetTemplate(string templatePath, IDictionary<string, object> data)
{
var text = PathHelper.TextFrom(templatePath);
var template = new MustacheTemplate(text);
template.AddProperties(data);
return template;
}
}

View File

@@ -0,0 +1,29 @@
using Services.Common.ObjUtils;
namespace Core.Templates;
public class TemplateData<TK, TV> where TK : notnull
{
private IDictionary<TK, TV> _data;
public TemplateData()
{
_data = new Dictionary<TK, TV>();
}
public void Add(TK key, TV value)
{
_data.Add(key, value);
}
public void AddAll(IDictionary<TK, TV> properties)
{
foreach (var item in properties)
{
_data.Add(item);
}
}
public IDictionary<TK, TV> GetData() => _data.Clone();
}

75
Core/Yaml/OpenApiYaml.cs Normal file
View File

@@ -0,0 +1,75 @@
using Core.SpecConfig;
using MoreLinq;
namespace Core.Yaml;
public class OpenApiYaml : ISpecFile
{
public Location Location { get; }
public string Name => Location.GetFileName();
public string Folder => Location.GetFolder();
public string? Openapi { get; set; }
public IDictionary<string, object>? Info { get; set; }
public IList<object>? Tags { get; set; }
public YamlConfig Config;
public ISet<string> Models;
public ISet<string> IgnoredModels;
public ISet<OpenApiYaml> ReferencedSchemas;
public ISet<string> ScopedRefs { get; set; }
public ISet<string> OutScopedRefs { get; set; }
public IEnumerable<string> Refs => ScopedRefs.Union(OutScopedRefs);
public SpecType SpecType => Config.Type;
public string NugetPackage => Config.NugetPackage;
public string MavenGroup => Config.JavaGroup;
public string NpmPackage => Config.NpmPackage;
public string Version => (string)Info["version"];
public int Priority => Config.Priority;
public OpenApiYaml(string loc)
{
Location = new Location(loc);
Info = new Dictionary<string, object>();
Tags = new List<object>();
Config = new YamlConfig();
IgnoredModels = new HashSet<string>();
ScopedRefs = new HashSet<string>();
OutScopedRefs = new HashSet<string>();
ReferencedSchemas = new HashSet<OpenApiYaml>();
Models = new HashSet<string>();
}
public Queue<ISpecFile> GetTasksBy(Language language, GenerationType type)
{
var tasks = new Queue<ISpecFile>();
var toGenerate = new List<ISpecFile>();
var packageTypes = Config.PackageTypes;
var specs = GetSpecsInSameFolder().ToList();
if (packageTypes.Has(language, type))
toGenerate.AddRange(type switch
{
GenerationType.Server => specs.Where(s => s.SpecType == SpecType.Api),
GenerationType.Client => specs.Where(s => s.SpecType == SpecType.Api),
GenerationType.Common => specs.Where(s => s.SpecType == SpecType.Model),
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
});
toGenerate.Sort((s1, s2) => s2.Priority - s1.Priority);
toGenerate.ForEach(s => tasks.Enqueue(s));
return tasks;
}
private IEnumerable<ISpecFile> GetSpecsInSameFolder()
{
var result = new List<ISpecFile>{ this };
ReferencedSchemas.ForEach(oay =>
{
if (oay.Location.IsInSameFolder(this.Location)) result.Add(oay);
});
return result;
}
}

16
Core/Yaml/YamlConfig.cs Normal file
View File

@@ -0,0 +1,16 @@
using Core.SpecConfig;
namespace Core.Yaml;
public class YamlConfig
{
public PackageTypes PackageTypes;
public int Priority { get; set; }
public SpecType Type { get; set; }
public string NugetPackage { get; set; } = "";
public string NpmPackage { get; set; } = "";
public string JavaGroup { get; set; } = "";
public string ModelSuffix { get; set; } = "";
public bool KeepModels { get; set; } = false;
}

View File

@@ -0,0 +1,28 @@
using Core;
using Core.Events;
using Core.Interfaces;
using Generator.Services;
namespace Generator.Controllers;
public class AnalyzeController : IController
{
private readonly AnalyzeService _analyzeService;
public DisplayEmitter Emitter { get; set; }
public AnalyzeController(DisplayEmitter emitter, AnalyzeService service)
{
_analyzeService = service;
Emitter = emitter;
}
public string GetSpecText(string folder, string spec) => _analyzeService.GetSpecText(folder, spec);
public bool CanBeGenerated(string folder, string spec)
{
return _analyzeService.CanBeGenerated(folder, spec);
}
public IEnumerable<Location> ListSpecs() => _analyzeService.ListSpecs();
}

View File

@@ -0,0 +1,31 @@
using Core;
using Core.Events;
using Core.Interfaces;
using Generator.Services;
using Generator.views;
namespace Generator.Controllers;
public class ExportController : IController
{
private readonly ExportService _exportService;
public DisplayEmitter Emitter { get; set; }
public ExportController(DisplayEmitter emitter, ExportService service)
{
_exportService = service;
Emitter = emitter;
}
public void PlantUml(ISpecFile file)
{
try
{
_exportService.ExportAsPuml(file);
} catch (Exception e)
{
Emitter.Warn(this, $"{e.Message} \n Cause : {e.Source} \n Full stacktrace : \n {e.StackTrace}");
}
}
}

View File

@@ -0,0 +1,30 @@
using Core.Events;
using Core.Interfaces;
using Core.Process;
using Generator.Services;
namespace Generator.Controllers;
public class GenerationController : IController
{
private readonly GenerationService _genService;
public DisplayEmitter Emitter { get; set; }
public GenerationController(DisplayEmitter emitter, GenerationService service)
{
_genService = service;
Emitter = emitter;
}
public async Task Launch(GenerationProcess process)
{
try
{
foreach (var task in process) await _genService.Launch(task);
}
catch (Exception e)
{
Emitter.Warn(this, $"{e.Message} \n Cause : {e.Source} \n Full stacktrace : \n {e.StackTrace}");
}
}
}

View File

@@ -0,0 +1,37 @@
using Core;
using Core.Events;
using Core.Interfaces;
using Core.Process;
using Core.SpecConfig;
using Generator.Services;
using Generator.views;
namespace Generator.Controllers;
public class PreGenerationController : IController
{
private readonly PreGenerationService _preGenService;
private readonly ConsoleView _view;
public DisplayEmitter Emitter { get; set; }
public PreGenerationController(DisplayEmitter emitter, PreGenerationService preGenService)
{
_view = new ConsoleView();
_preGenService = preGenService;
Emitter = emitter;
}
public GenerationProcess ComputeGeneration(string specPath, GenerationType? generationType, PublishType publishType)
{
try
{
return _preGenService.ComputeGeneration(specPath, generationType, publishType);
}
catch (Exception e)
{
Emitter.Warn(this, e.Message);
throw;
}
}
}

View File

@@ -0,0 +1,33 @@
using Core.Events;
using Core.Interfaces;
using Core.Process;
using Generator.Services;
using Generator.views;
namespace Generator.Controllers;
public class PublicationController : IController
{
private readonly PublicationService _pubService;
public DisplayEmitter Emitter { get; set; }
public PublicationController(DisplayEmitter emitter, PublicationService pubService)
{
_pubService = pubService;
Emitter = emitter;
}
public async Task Publish(GenerationProcess process)
{
try
{
await _pubService.Publish(process);
}
catch (Exception e)
{
Emitter.Warn(this, $"{e.Message} \n Cause : {e.Source} \n Full stacktrace : \n {e.StackTrace}");
}
}
}

View File

@@ -0,0 +1,17 @@
using Generator.DataSource.Settings;
namespace Generator.Daos;
public abstract class AbstractDao
{
protected readonly ConfigManager ConfManager;
protected AbstractDao(ConfigManager confManager, string spec)
{
ConfManager = confManager;
}
protected AbstractDao(ConfigManager confManager) : this(confManager, confManager.GetDefArgs().SpecIdentifier)
{ }
}

View File

@@ -0,0 +1,19 @@
using Core.Actions;
using Generator.DataSource.Settings;
using Generator.Mappers;
namespace Generator.Daos;
public class CredentialsDao : AbstractDao
{
public string Username => ConfManager.Credentials.Username;
public string Password => ConfManager.Credentials.Password;
public CredentialsDao(ConfigManager confManager)
: base(confManager, confManager.GetDefArgs().SpecIdentifier)
{
}
public PackageDeletion GetPackageDeletion() => ConfManager.Credentials.ToPackageDeletion();
}

View File

@@ -0,0 +1,40 @@
using Core;
using Core.Actions;
using Core.Settings;
using Core.SpecConfig;
using Generator.DataSource.Settings;
using Generator.Mappers;
namespace Generator.Daos;
public class DotnetDao : AbstractDao
{
public DotnetDao(ConfigManager confManager)
: base(confManager, confManager.GetDefArgs().SpecIdentifier)
{
}
public DotnetConfig GetDotnetGenerate(GenerationType type, ISpecFile file)
{
var config = ConfManager.Dotnet.Map(ConfManager, file);
config.Type = type;
return config ;
}
public DotnetPublish GetDotnetPublish(GenerationType type, ISpecFile file)
{
var d = GetDotnetGenerate(type, file);
return new DotnetPublish
{
LocalRoot = d.LocalRoot,
DockerRoot = d.DockerRoot,
Image = d.BuildImage,
Registry = d.Registry,
PackageFolder = d.PackageFolderPath(),
PackageVersion = file.Version,
PackageFile = d.PackageFile,
AuthorizationToken = d.AuthorizationToken,
};
}
}

View File

@@ -0,0 +1,14 @@
using Core;
using Generator.DataSource.Settings;
namespace Generator.Daos;
public class EnvironmentDao : AbstractDao
{
public Location ApiFolder() => new ([ConfManager.GetBase().LocalRoot, ConfManager.GetGeneral().ApiFolder]);
public string Invite => ConfManager.GetBase().Invite;
public EnvironmentDao(ConfigManager confManager) : base(confManager)
{ }
}

37
Generator/Daos/JavaDao.cs Normal file
View File

@@ -0,0 +1,37 @@
using Core;
using Core.Actions;
using Core.Settings;
using Core.SpecConfig;
using Generator.DataSource.Settings;
using Generator.Mappers;
namespace Generator.Daos;
public class JavaDao : AbstractDao
{
public JavaDao(ConfigManager confManager) : base(confManager)
{ }
public JavaConfig GetJavaGenerate(ISpecFile file) => ConfManager.Java.Map(ConfManager, file);
public JavaPublish GetJavaPublish(GenerationType type, ISpecFile file)
{
var config = GetJavaGenerate(file);
return new JavaPublish
{
LocalRoot = config.LocalRoot,
DockerRoot = config.DockerRoot,
Image = config.BuildImage,
Registry = config.Registry,
TemplateFolder = config.TemplateFolder,
OutputFolder = config.OutputFolder(),
Group = file.MavenGroup,
Artifact = config.Artifact,
Version = config.Version,
Username = config.Username,
Password = config.Password,
};
}
}

View File

@@ -0,0 +1,35 @@
using Core;
using Core.Actions;
using Core.Settings;
using Core.SpecConfig;
using Generator.DataSource.Settings;
using Generator.Mappers;
namespace Generator.Daos;
public class JavascriptDao : AbstractDao
{
public JavascriptDao(ConfigManager confManager) : base(confManager, confManager.GetDefArgs().SpecIdentifier)
{ }
public JavascriptConfig GetJavascript(ISpecFile file) => ConfManager.Javascript.Map(ConfManager, file);
public JavascriptPublish GetJavascriptPublish(GenerationType type, ISpecFile file)
{
var j = GetJavascript(file);
return new JavascriptPublish
{
LocalRoot = j.LocalRoot,
DockerRoot = j.DockerRoot,
Image = j.BuildImage,
Registry = j.Registry,
PackageName = j.PackageName,
Version = file.Version,
SpecFile = j.OpenApiSpecFile(),
FrontFolder = j.OutputFolder(),
};
}
}

View File

@@ -0,0 +1,23 @@
using Core;
using Core.Settings;
using Generator.DataSource.Settings;
using Generator.Mappers;
namespace Generator.Daos;
public class OpenApiDao : AbstractDao
{
public OpenApiDao(ConfigManager confManager) : base(confManager)
{ }
public Location ConfigOf(string spec) => GetOpenApi(true, spec).SpecConfig;
public OpenApiConfig GetOpenApi(bool isLocal, string file)
{
var items = file.Split("/");
var o = ConfManager.OpenApi.Map(ConfManager, items[0], items[1]);
o.AddRoot(ConfManager.GetRoot(isLocal));
return o;
}
}

View File

@@ -0,0 +1,23 @@
using Core;
using Core.Actions;
using Generator.DataSource.Settings;
using Generator.Mappers;
namespace Generator.Daos;
public class TemplateDao : AbstractDao
{
public TemplateDao(ConfigManager confManager) : base(confManager)
{ }
public PlantUmlExport PlantUml(ISpecFile file)
{
var config = ConfManager.Templates.Map();
return new PlantUmlExport
{
LocalRoot = ConfManager.GetBase().LocalRoot,
Template = config.PlantUml,
Output = ConfManager.GetGeneral().ApiFolder.ConcatenateWith([file.Folder, $"{file.Name}.puml"])
};
}
}

View File

@@ -0,0 +1,46 @@
using Core.Dto.Settings;
using Core.Interfaces;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Generator.DataSource;
/// <summary>
/// Provides some methods to load json objects from a json data source
/// </summary>
public class JsonLoader : IDataSourceLoader
{
private readonly IServiceCollection _services;
private readonly IConfiguration _configuration;
public JsonLoader(IServiceCollection services, IConfiguration configuration)
{
_services = services;
_configuration = configuration;
}
/// <summary>
/// Load data that can be found in appsettings.json
/// </summary>
public void LoadAppsettings()
{
_services.AddOptions<EnvironmentConfigDto>().Bind(_configuration.GetSection("Environment"));
_services.AddOptions<DefaultArgumentsConfigDto>().Bind(_configuration.GetSection("DefaultArguments"));
_services.AddOptions<GeneralConfigDto>().Bind(_configuration.GetSection("General"));
_services.AddOptions<PublishConfigDto>().Bind(_configuration.GetSection("Publish"));
_services.AddOptions<CredentialsConfigDto>().Bind(_configuration.GetSection("Credentials"));
_services.AddOptions<DockerImagesConfigDto>().Bind(_configuration.GetSection("DockerImages"));
_services.AddOptions<JavaConfigDto>().Bind(_configuration.GetSection("Java"));
_services.AddOptions<DotnetConfigDto>().Bind(_configuration.GetSection("Dotnet"));
_services.AddOptions<JavascriptConfigDto>().Bind(_configuration.GetSection("Javascript"));
_services.AddOptions<OpenApiConfigDto>().Bind(_configuration.GetSection("OpenApi"));
_services.AddOptions<TemplatesConfigDto>().Bind(_configuration.GetSection("Templates"));
}
}

View File

@@ -0,0 +1,64 @@
using Core;
using Core.Dto;
using Core.Dto.Settings;
using Core.Settings;
using Generator.Mappers;
using Microsoft.Extensions.Options;
namespace Generator.DataSource.Settings;
/// <summary>
/// This class is intended to encapsulate configurations objects
/// </summary>
public class ConfigManager
{
private readonly ArgumentsDto _args;
private readonly EnvironmentConfigDto _env;
private readonly DefaultArgumentsConfigDto _defArgs;
private readonly GeneralConfigDto _general;
public readonly DockerImagesConfigDto DockerImages;
public readonly TemplatesConfigDto Templates;
public readonly PublishConfigDto Publish;
public readonly CredentialsConfigDto Credentials;
public readonly DotnetConfigDto Dotnet;
public readonly JavaConfigDto Java;
public readonly JavascriptConfigDto Javascript;
public readonly OpenApiConfigDto OpenApi;
public ConfigManager(
ArgumentsDto args,
IOptions<EnvironmentConfigDto> env,
IOptions<DefaultArgumentsConfigDto> defArgs,
IOptions<GeneralConfigDto> general,
IOptions<PublishConfigDto> publish,
IOptions<CredentialsConfigDto> creds,
IOptions<DockerImagesConfigDto> images,
IOptions<DotnetConfigDto> dotnet,
IOptions<JavaConfigDto> java,
IOptions<JavascriptConfigDto> javascript,
IOptions<OpenApiConfigDto> openapi,
IOptions<TemplatesConfigDto> templates)
{
_args = args;
_env = env.Value;
Publish = publish.Value;
Credentials = creds.Value;
_defArgs = defArgs.Value;
_general = general.Value;
DockerImages = images.Value;
Templates = templates.Value;
Dotnet = dotnet.Value;
Java = java.Value;
Javascript = javascript.Value;
OpenApi = openapi.Value;
}
public BaseConfig GetBase() => _env.Map();
public DefaultArgumentsConfig GetDefArgs() => _defArgs.Map(_args);
public GeneralConfig GetGeneral() => _general.Map();
public Location GetRoot(bool isLocal) => isLocal ? GetBase().LocalRoot : GetBase().DockerRoot;
}

View File

@@ -0,0 +1,109 @@
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
{
/// <summary>
/// Retrieves an openapi-formatted yaml at a specific location
/// </summary>
/// <param name="path">file location</param>
/// <returns></returns>
public OpenApiYaml ExtractNode(string path)
{
var text = PathHelper.TextFrom(path);
var deserializer = new DeserializerBuilder()
.IgnoreUnmatchedProperties()
.Build();
var dto = deserializer.Deserialize<OpenApiYamlDto>(text);
return dto.ToModel(path);
}
/// <summary>
/// Loads all configs that can be found in the config file located at the given path
/// </summary>
/// <param name="configPath">Given path</param>
/// <returns></returns>
/// <exception cref="InvalidOperationException">When config file couldn't has been deserialize</exception>
/// <exception cref="ConfigException">When the given path does not exists</exception>
public Dictionary<string, YamlConfig> LoadConfigs(string configPath)
{
try
{
var json = PathHelper.TextFrom(configPath);
var configDto = JsonConvert.DeserializeObject<SpecConfigDto>(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");
}
}
/// <summary>
/// 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
/// </summary>
/// <param name="filePath">Given file path</param>
/// <returns></returns>
public ISet<string> ExtractScopedRefs(string filePath) => ExtractRefs(filePath, false, new HashSet<string>());
/// <summary>
/// Extract all possibles references from a given specification file
/// </summary>
/// <param name="filePath">Given file path</param>
/// <returns></returns>
public ISet<string> ExtractAllRefs(string filePath) => ExtractRefs(filePath, true, new HashSet<string>());
private ISet<string> ExtractRefs(string filePath, bool deepSearch, ISet<string> 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");
}
}

View File

@@ -0,0 +1,164 @@
using System.Text;
using Core;
using Core.Helpers;
using Core.Yaml;
using MoreLinq;
namespace Generator.DataSource.Yaml;
public class YamlBuilder
{
private OpenApiYaml _file;
private bool _isSubNode;
private readonly string _configIdentifier;
private readonly Dictionary<string, YamlConfig> _configs;
private readonly OpenApiYamlExtractor _extractor;
public YamlBuilder(string filePath, string configIdentifier)
{
_isSubNode = false;
_configIdentifier = configIdentifier;
_file = new OpenApiYaml(filePath);
_configs = new Dictionary<string, YamlConfig>();
_extractor = new OpenApiYamlExtractor();
}
/// <summary>
/// Adds all raw references to the object to build
/// </summary>
public void AddReferences()
{
var scopedRefs = _extractor.ExtractScopedRefs(_file.Location.ToString());
var allRefs = _extractor.ExtractAllRefs(_file.Location.ToString());
_file.ScopedRefs = scopedRefs;
_file.OutScopedRefs = allRefs;
_file.OutScopedRefs.ExceptWith(scopedRefs);
}
/// <summary>
/// Attributes the dedicated config file to the object to build
/// </summary>
public void SelectConfig()
{
if (_configs.ContainsKey(_file.Location.GetFileName()))
{
_file.Config = _configs[_file.Location.GetFileName()];
}
else
{
throw new Exception($"{_file.Location} cannot be generated. You either made a typo " +
$"or forgot to register file in {GetConfig(_file.Location)}");
}
}
/// <summary>
/// Adds all built schemas to the object to build
/// </summary>
public void AddSchema()
{
using var enumerator = _file.ScopedRefs.GetEnumerator();
while (enumerator.MoveNext())
{
var loc = new Location(enumerator.Current);
var fileName = loc.GetFileName();
if(!_configs.ContainsKey(fileName)) continue;
var schema = _isSubNode ?
_extractor.ExtractNode(loc.ToString())
: CreateSubNode(loc.ToString());
schema.Config = _configs[fileName];
_file.ReferencedSchemas.Add(schema);
}
}
/// <summary>
/// Adds the models that will be ignored to the object to build
/// </summary>
public void AddIgnoredModels()
{
foreach (var reference in _file.Refs)
{
var loc = new Location(reference);
var schema = _extractor.ExtractNode(loc.ToString());
_file.IgnoredModels.UnionWith(schema.Models);
}
}
/// <summary>
/// Simply output the object to build
/// </summary>
/// <returns></returns>
public OpenApiYaml Create() => _file;
/// <summary>
/// Loads the object to build
/// </summary>
public void LoadSpec()
{
_file = _extractor.ExtractNode(_file.Location.ToString());
_file.Openapi = _file.Openapi;
if (_file.Info != null) _file.Info = new Dictionary<string, object>(_file.Info);
_file.Tags = _file.Tags == null ? new List<object>() : [.._file.Tags];
}
/// <summary>
/// Load all the found config files into the builder
/// </summary>
public void LoadConfigs()
{
LoadConfig(_file.Location);
foreach (var reference in _file.Refs) LoadConfig(new Location(reference));
}
private void LoadConfig(Location path)
{
var configPath = GetConfig(path)?.ToString();
if (configPath == null || _configs.ContainsKey(path.GetFileName())) return;
_extractor.LoadConfigs(configPath)
.ForEach(c => _configs.TryAdd(c.Key, c.Value));
}
private void SetForSubNode() => _isSubNode = true;
private OpenApiYaml CreateSubNode(string path)
{
var yamlBuilder = new YamlBuilder(path, _configIdentifier);
yamlBuilder.SetForSubNode();
yamlBuilder.LoadSpec();
yamlBuilder.AddReferences();
yamlBuilder.LoadConfigs();
yamlBuilder.AddSchema();
yamlBuilder.AddIgnoredModels();
return yamlBuilder.Create();
}
private Location? GetConfig(Location loc)
{
var spec = loc.GetFolder();
var pathItems = loc.Path.Split(['/', '\\']);
pathItems[^1] = spec + _configIdentifier;
var builder = new StringBuilder();
builder.AppendJoin("/", pathItems);
var configPath = builder.ToString();
try
{
PathHelper.CheckPathValidity(configPath);
}
catch (FileNotFoundException)
{
return null;
}
return new Location(configPath);
}
}

View File

@@ -0,0 +1,36 @@
using Core;
using Core.Yaml;
using Generator.Daos;
namespace Generator.DataSource.Yaml;
public class YamlDirector
{
private readonly OpenApiDao _openApiDao;
public YamlDirector(OpenApiDao openApiDao)
{
_openApiDao = openApiDao;
}
/// <summary>
/// Build a yaml object lmao what do you want me to say ? All the information's are in the method signature
/// </summary>
/// <param name="specPath"></param>
/// <returns></returns>
public OpenApiYaml BuildYaml(string specPath)
{
var openApiConfig = _openApiDao.GetOpenApi(true, specPath);
var yamlBuilder = new YamlBuilder(openApiConfig.SpecFile.ToString(), openApiConfig.ConfigIdentifier);
yamlBuilder.LoadSpec();
yamlBuilder.AddReferences();
yamlBuilder.LoadConfigs();
yamlBuilder.SelectConfig();
yamlBuilder.AddSchema();
yamlBuilder.AddIgnoredModels();
return yamlBuilder.Create();
}
}

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Generator</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Microsoft.OpenApi" Version="1.6.13" />
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.6.13" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="mustache-sharp" Version="1.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NUnit" Version="4.1.0" />
<PackageReference Include="Services.Common" Version="1.0.0" />
<PackageReference Include="YamlDotNet" Version="15.1.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,89 @@
using Core;
using Core.Dto;
using Core.Events;
using Generator.Controllers;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace Generator;
public class GeneratorHostedService : IHostedService
{
private readonly ArgumentsDto _args;
private readonly IHostApplicationLifetime _applicationLifetime;
private readonly ILogger<GeneratorHostedService> _logger;
private readonly PreGenerationController _preGenController;
private readonly GenerationController _genController;
private readonly ExportController _exportController;
public GeneratorHostedService(
ArgumentsDto args,
IHostApplicationLifetime applicationLifetime,
ILogger<GeneratorHostedService> logger,
PreGenerationController preGenController,
GenerationController genController,
ExportController exportController)
{
_args = args;
_applicationLifetime = applicationLifetime;
_logger = logger;
_preGenController = preGenController;
_genController = genController;
_exportController = exportController;
_genController.Emitter.OnSay += Display;
_genController.Emitter.OnWarn += Warn;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_applicationLifetime.ApplicationStarted.Register(() =>
{
Task.Run(async() =>
{
var startTime = DateTime.Now;
try
{
var extractor = new ArgumentsExtractor(_args);
var genProcess = _preGenController.ComputeGeneration(extractor.Spec, extractor.GenType, extractor.PublishType);
if (extractor.ExportPuml) _exportController.PlantUml(genProcess.BaseFile);
await _genController.Launch(genProcess);
}
catch (Exception e)
{
_logger.Log(LogLevel.Critical, e, $"A critical exception was thrown. {e.Message}");
}
finally
{
var endTime = DateTime.Now;
Console.WriteLine($"Process started at : {startTime}");
Console.WriteLine($"Process ended at : {endTime}");
Console.WriteLine($"Duration : {(endTime - startTime).Duration():mm':'ss}");
_applicationLifetime.StopApplication();
}
}, cancellationToken);
});
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
private void Display(object? sender, DisplayEventArgs args)
{
Console.WriteLine(args.Content);
}
private void Warn(object? sender, DisplayEventArgs args)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(args.Content);
Console.ResetColor();
}
}

View File

@@ -0,0 +1,91 @@
using System.Diagnostics;
using Core.Events;
using Core.Exceptions;
namespace Generator.Infrastructure;
public class CommandExecutor
{
private readonly Queue<string> _commands;
private string _invite;
private DisplayEmitter _emitter;
public CommandExecutor(string invite, DisplayEmitter emitter)
{
_commands = new Queue<string>();
_invite = invite;
_emitter = emitter;
}
/// <summary>
/// Runs a single asynchronous command
/// </summary>
/// <param name="command">Command to run</param>
/// <exception cref="CommandExecutionException">When the command hasn't been terminated successfully</exception>
public async Task RunAsync(string command)
{
await Task.Run(() =>
{
using var process = new Process();
process.StartInfo = new ProcessStartInfo
{
FileName = _invite,
Arguments = $"{command}",
RedirectStandardOutput = true,
RedirectStandardError = true,
WindowStyle = ProcessWindowStyle.Normal
};
process.Start();
ReadOutput(process);
ReadError(process);
process.WaitForExit();
if(process.ExitCode == 1)
throw new CommandExecutionException($"The following task has failed : \n '{command}'");
});
}
private void ReadError(Process process)
{
Task.Run(async () =>
{
using var output = process.StandardError;
while (!output.EndOfStream)
{
var line = await output.ReadLineAsync();
_emitter.Warn(this, line);
}
});
}
private void ReadOutput(Process process)
{
Task.Run(async () =>
{
using var output = process.StandardOutput;
while (!output.EndOfStream)
{
var line = await output.ReadLineAsync();
_emitter.Say(this, line);
}
});
}
/// <summary>
/// Registers a command to execute later
/// </summary>
/// <param name="command"></param>
public void Register(string command) => _commands.Enqueue(command);
/// <summary>
/// Runs all previously registered command
/// </summary>
public async Task RunRegistered()
{
while (_commands.TryDequeue(out var c))
{
await RunAsync(c);
}
}
}

View File

@@ -0,0 +1,60 @@
using Core;
using Core.Actions;
using Core.Helpers;
using Core.Templates;
using Core.Yaml;
using Generator.Infrastructure.TemplateFiller;
namespace Generator.Infrastructure.Export;
/// <summary>
/// Provides multiple methods to specification references
/// </summary>
public class Exporter
{
private readonly ITemplateFactory _templateFactory;
public Exporter(ITemplateFactory factory)
{
_templateFactory = factory;
}
/// <summary>
/// Export file references as plant uml diagram
/// </summary>
/// <param name="export">plantuml data needed for exportation</param>
/// <param name="file">File to export</param>
public void PlantUml(PlantUmlExport export, ISpecFile file)
{
var yaml = TypeHelper.SafeCast<ISpecFile, OpenApiYaml>(file);
var input = export.LocalRoot.ConcatenateWith(export.Template);
var output = export.LocalRoot.ConcatenateWith(export.Output);
var references = new List<(string Package, string Reference)>();
var schemas = yaml.ReferencedSchemas.ToList();
for (var i = -1; i < schemas.Count; i++)
{
var baseFile = i == -1 ? yaml : schemas[i];
if(!baseFile.Location.IsInSameFolder(yaml.Location)) continue;
foreach (var reference in baseFile.ReferencedSchemas)
{
references.Add((baseFile.NugetPackage, reference.NugetPackage));
}
}
var data = new Dictionary<string, object>()
{
{"specName", file.Folder},
{"packages", references.Select(t => t.Package).ToHashSet()},
{"references", references}
};
var template = _templateFactory.GetTemplate(input.ToString(), data);
var f = new MustacheFiller(template);
f.Fill();
f.Write(output.ToString());
}
}

View File

@@ -0,0 +1,64 @@
using Core;
using Core.Events;
using Core.Helpers;
using Core.Templates;
using Generator.Infrastructure.TemplateFiller;
using Microsoft.OpenApi.Exceptions;
namespace Generator.Infrastructure.OpenApi.Builders;
/// <summary>
/// Provides base behavior for generation purpose
/// </summary>
/// <typeparam name="TFile">Derived type from ISpecFile</typeparam>
public abstract class AbstractBuilder<TFile> where TFile : ISpecFile
{
private readonly ITemplateFactory _templateFactory;
protected readonly CommandExecutor Executor;
protected TFile? SpecFile;
public AbstractBuilder(string invite, DisplayEmitter emitter)
{
Executor = new CommandExecutor(invite, emitter);
_templateFactory = new MustacheTemplateFactory();
SpecFile = default;
}
/// <summary>
/// Registers file used for generation
/// </summary>
/// <param name="specFile"></param>
public void Load(TFile specFile) => SpecFile = TypeHelper.SafeCast<ISpecFile, TFile>(specFile);
/// <summary>
/// Launches generation
/// </summary>
public async Task ExecuteAllAsync() => await Executor.RunRegistered();
/// <summary>
/// Registers generation process
/// </summary>
public abstract void Generate();
/// <summary>
/// Registers build process
/// </summary>
public abstract void Build();
/// <summary>
/// Check if the spec file has been loaded
/// </summary>
/// <exception cref="OpenApiException"></exception>
protected void CheckForNullSpec()
{
if (SpecFile == null) throw new OpenApiException("Spec file hasn't been loaded yet.");
}
protected void MakeTemplate(string inputPath, string outputPath, IDictionary<string, object> data)
{
var template = _templateFactory.GetTemplate(inputPath, data);
var f = new MustacheFiller(template);
f.Fill();
f.Write(outputPath);
}
}

View File

@@ -0,0 +1,118 @@
using System.Text;
using Core;
using Core.Events;
using Core.Helpers;
using Core.Settings;
using Core.Yaml;
using YamlDotNet.Core;
namespace Generator.Infrastructure.OpenApi.Builders;
public class OpenApiDotnetClientBuilder : AbstractBuilder<OpenApiYaml>
{
private readonly DotnetConfig _config;
public OpenApiDotnetClientBuilder(DotnetConfig config, DisplayEmitter emitter)
: base(config.Invite, emitter)
{
_config = config;
}
///<inheritdoc/>
public override void Generate()
{
CheckForNullSpec();
CreateIgnore();
GenerateOpenApi();
var inputFile = new Location([_config.DockerRoot, _config.OpenApiConfigFile()]);
var command = $"docker run --rm -it -v {_config.LocalRoot}:{_config.DockerRoot} {_config.GenerationImage} batch --clean {inputFile}";
Executor.Register(command);
}
///<inheritdoc/>
public override void Build()
{
CheckForNullSpec();
var templateFolder = new Location([_config.DockerRoot, _config.TemplateFolder]);
var packageFolder = new Location([_config.DockerRoot, _config.PackageFolderPath()]);
var stringBuilder = new StringBuilder();
stringBuilder
.Append($"docker run --rm -it -v {_config.LocalRoot}:{_config.DockerRoot} {_config.BuildImage} /bin/sh -c '")
.Append($"cp {templateFolder}/NuGet.config {packageFolder} && ")
.Append($"cd {packageFolder} && ")
.Append($"dotnet pack -c Release -o out -p:PackageVersion={SpecFile.Version}'");
var command = stringBuilder.ToString();
Executor.Register(command);
}
/// <summary>
/// Creates config file used by openapi generator
/// </summary>
public void CreateGeneratorConfig()
{
CheckForNullSpec();
var inputPath = new Location([_config.LocalRoot, _config.ClientConfigTemplate]);
var outputPath = new Location([_config.LocalRoot, _config.ConfigFilePath()]);
PathHelper.CreateFileIfNotExists(outputPath.ToString());
var data = new Dictionary<string, object>
{
{"specPath", new Location([_config.DockerRoot, _config.OpenApiSpecFile()]).ToString()},
{"templateFolder", new Location([_config.DockerRoot, _config.TemplateFolder]).ToString()},
{"generatorVersion", "7.3.0"},
{"outputFolder", new Location([_config.DockerRoot, _config.OutputFolder()]).ToString()},
{"removeModelPackage", false},
{"modelSuffix", SpecFile.Config.ModelSuffix},
{"packageName", SpecFile.Config.NugetPackage},
{"packageVersion", SpecFile.Info["version"]},
{"refs", SpecFile.ReferencedSchemas},
{"modelNameSpace", SpecFile.ReferencedSchemas},
};
MakeTemplate(inputPath.ToString(), outputPath.ToString(), data);
}
/// <summary>
/// Registers the generation of the openapi spec file
/// </summary>
private void GenerateOpenApi()
{
CheckForNullSpec();
var specFile = new Location([_config.DockerRoot, _config.SpecFile]);
var output = new Location([_config.DockerRoot, _config.OpenApiFolder()]);
var command = $"docker run --rm -it -v {_config.LocalRoot}:{_config.DockerRoot} {_config.GenerationImage} " +
$"generate -g openapi-yaml -i {specFile} -o {output}";
Executor.Register(command);
}
/// <summary>
/// Generates the ignore file used by openapi generator
/// </summary>
private void CreateIgnore()
{
CheckForNullSpec();
var inputPath = new Location([_config.LocalRoot, _config.ClientIgnoreTemplate]).ToString();
var outputPath = new Location([_config.LocalRoot, _config.IgnoreFilePath()]).ToString();
PathHelper.CreateFileIfNotExists(outputPath);
var data = new Dictionary<string, object>
{
{"modelSuffix", SpecFile!.Config.ModelSuffix},
{"keepModels", SpecFile.Config.KeepModels},
};
MakeTemplate(inputPath, outputPath, data);
}
}

View File

@@ -0,0 +1,121 @@
using System.Text;
using Core;
using Core.Events;
using Core.Helpers;
using Core.Settings;
using Core.SpecConfig;
using Core.Yaml;
namespace Generator.Infrastructure.OpenApi.Builders;
public class OpenApiDotnetServerBuilder : AbstractBuilder<OpenApiYaml>
{
private readonly DotnetConfig _config;
public OpenApiDotnetServerBuilder(DotnetConfig config, DisplayEmitter emitter)
: base(config.Invite, emitter)
{
_config = config;
}
///<inheritdoc/>
public override void Generate()
{
CheckForNullSpec();
CreateIgnore();
GenerateOpenApi();
var inputFile = new Location([_config.DockerRoot, _config.OpenApiConfigFile()]);
var command = $"docker run --rm -it -v {_config.LocalRoot}:{_config.DockerRoot} " +
$"{_config.GenerationImage} batch --clean {inputFile}";
Executor.Register(command);
}
///<inheritdoc/>
public override void Build()
{
CheckForNullSpec();
var templateFolder = new Location([_config.DockerRoot, _config.TemplateFolder]);
var packageFolder = new Location([_config.DockerRoot, _config.PackageFolderPath()]);
var stringBuilder = new StringBuilder();
stringBuilder
.Append($"docker run --rm -it -v {_config.LocalRoot}:{_config.DockerRoot} {_config.BuildImage} /bin/sh -c '")
.Append($"cp {templateFolder}/NuGet.config {packageFolder} && ")
.Append($"cd {packageFolder} && ")
.Append($"dotnet pack -c Release -o out -p:PackageVersion={SpecFile.Version}'");
var command = stringBuilder.ToString();
Executor.Register(command);
}
/// <summary>
/// Creates config file used by openapi generator
/// </summary>
public void CreateGeneratorConfig()
{
CheckForNullSpec();
var inputPath = new Location([_config.LocalRoot, _config.ServerConfigTemplate]);
var outputPath = new Location([_config.LocalRoot, _config.ConfigFilePath()]);
PathHelper.CreateFileIfNotExists(outputPath.ToString());
var templateData = new Dictionary<string, object>
{
{"specPath", new Location([_config.DockerRoot, _config.OpenApiSpecFile()]).ToString()},
{"templateFolder", new Location([_config.DockerRoot, _config.TemplateFolder]).ToString()},
{"generatorVersion", "7.3.0"},
{"outputFolder", new Location([_config.DockerRoot, _config.OutputFolder()]).ToString()},
{"modelSuffix", SpecFile.Config.ModelSuffix},
{"aspnetCoreVersion", "3.1"},
{"packageName", SpecFile.Config.NugetPackage},
{"packageVersion", SpecFile.Info!["version"]},
{"refs", SpecFile.ReferencedSchemas},
{"modelNameSpace", SpecFile.ReferencedSchemas},
};
MakeTemplate(inputPath.ToString(), outputPath.ToString(), templateData);
}
/// <summary>
/// Registers the generation of the openapi spec file
/// </summary>
private void GenerateOpenApi()
{
CheckForNullSpec();
var specFile = new Location([_config.DockerRoot, _config.SpecFile]);
var output = new Location([_config.DockerRoot, _config.OpenApiFolder()]);
var command = $"docker run --rm -it -v {_config.LocalRoot}:{_config.DockerRoot} {_config.GenerationImage} " +
$"generate -g openapi-yaml -i {specFile} -o {output}";
Executor.Register(command);
}
/// <summary>
/// Generates the ignore file used by openapi generator
/// </summary>
private void CreateIgnore()
{
CheckForNullSpec();
var inputPath = new Location([_config.LocalRoot, _config.ServerIgnoreTemplate]).ToString();
var outputPath = new Location([_config.LocalRoot, _config.IgnoreFilePath()]).ToString();
PathHelper.CreateFileIfNotExists(outputPath);
var data = new Dictionary<string, object>
{
{"modelSuffix", SpecFile!.Config.ModelSuffix},
{"keepModels", SpecFile.Config.KeepModels},
{"ignoredModels", SpecFile.IgnoredModels},
{"isCommon", SpecFile.SpecType == SpecType.Model}
};
MakeTemplate(inputPath, outputPath, data);
}
}

View File

@@ -0,0 +1,98 @@
using System.Text;
using Core;
using Core.Events;
using Core.Helpers;
using Core.Settings;
using Core.Yaml;
namespace Generator.Infrastructure.OpenApi.Builders;
public class OpenApiJavaBuilder : AbstractBuilder<OpenApiYaml>
{
private readonly JavaConfig _config;
public OpenApiJavaBuilder(JavaConfig config, DisplayEmitter emitter)
: base(config.Invite, emitter)
{
_config = config;
}
///<inheritdoc/>
public override void Generate()
{
CheckForNullSpec();
GenerateOpenApi();
var configFile = new Location([_config.DockerRoot, _config.ConfigFilePath()]);
var command = $"docker run --rm -it -v {_config.LocalRoot}:{_config.DockerRoot} {_config.GenerationImage} " +
$"batch --clean {configFile}";
Executor.Register(command);
}
///<inheritdoc/>
public override void Build()
{
CheckForNullSpec();
var templateFolder = new Location([_config.DockerRoot, _config.TemplateFolder]);
var outputFolder = new Location([_config.DockerRoot, _config.OutputFolder()]);
var stringBuilder = new StringBuilder();
stringBuilder.Append($"docker run --rm -it -v {_config.LocalRoot}:{_config.DockerRoot} {_config.BuildImage} /bin/sh -c '");
stringBuilder.Append($"cp {templateFolder}/settings.xml /root/.m2 && ");
stringBuilder.Append($"cd {outputFolder} && ");
stringBuilder.Append("mvn package'");
var command = stringBuilder.ToString();
Executor.Register(command);
}
/// <summary>
/// Creates config file used by openapi generator
/// </summary>
public void CreateGeneratorConfig()
{
CheckForNullSpec();
var inputPath = new Location([_config.LocalRoot, _config.ServerConfigTemplate]);
var outputPath = new Location([_config.LocalRoot, _config.ConfigFilePath()]);
PathHelper.CreateFileIfNotExists(outputPath.ToString());
var artifact = $"{SpecFile.Name}-api";
var data = new Dictionary<string, object>
{
{"specPath", new Location([_config.DockerRoot, _config.OpenApiSpecFile()]).ToString()},
{"templateFolder", new Location([_config.DockerRoot, _config.TemplateFolder]).ToString()},
{"generatorVersion", "7.3.0"},
{"outputFolder", new Location([_config.DockerRoot, _config.OutputFolder()]).ToString()},
{"groupId", SpecFile.Config.JavaGroup},
{"artifactId", artifact},
{"artifactVersion", SpecFile.Info["version"]},
};
MakeTemplate(inputPath.ToString(), outputPath.ToString(), data);
}
/// <summary>
/// Registers the generation of the openapi spec file
/// </summary>
private void GenerateOpenApi()
{
CheckForNullSpec();
var specFile = new Location([_config.DockerRoot, _config.SpecFile]);
var output = new Location([_config.DockerRoot, _config.OpenApiFolder()]);
PathHelper.CreateFileIfNotExists(output.ToString());
var command = $"docker run --rm -it -v {_config.LocalRoot}:{_config.DockerRoot} {_config.GenerationImage} " +
$"generate -g openapi-yaml -i {specFile} -o {output}";
Executor.Register(command);
}
}

View File

@@ -0,0 +1,63 @@
using System.Text;
using Core;
using Core.Events;
using Core.Settings;
using Core.Yaml;
namespace Generator.Infrastructure.OpenApi.Builders;
public class OpenApiJavascriptBuilder : AbstractBuilder<OpenApiYaml>
{
private readonly JavascriptConfig _config;
public OpenApiJavascriptBuilder(JavascriptConfig config, DisplayEmitter emitter)
: base(config.Invite, emitter)
{
_config = config;
}
///<inheritdoc/>
public override void Generate()
{
CheckForNullSpec();
GenerateOpenApi();
var outputFolder = new Location([_config.DockerRoot, _config.ClientFolder]);
var specFile = new Location([_config.DockerRoot, _config.OpenApiSpecFile()]).ToString();
var stringBuilder = new StringBuilder();
stringBuilder
.Append($"docker run --rm -it -v {_config.LocalRoot}:{_config.DockerRoot} {_config.BuildImage} /bin/sh -c '")
.Append($"npm run prepare-workspace -- --apiName={_config.PackageName} --apiVersion={_config.OpenApiVersion} ")
.Append($"--apiFile={specFile} --registry={_config.Registry} && ")
.Append("npm run package && ")
.Append($"mkdir -p {outputFolder} && ")
.Append($"cp -r dist/* {outputFolder}'");
var command = stringBuilder.ToString();
Executor.Register(command);
}
///<inheritdoc/>
public override void Build()
=> throw new InvalidOperationException("Why the heck do you want to compile javascript code ?");
/// <summary>
/// Registers the generation of the openapi spec file
/// </summary>
private void GenerateOpenApi()
{
CheckForNullSpec();
var specFile = new Location([_config.DockerRoot, _config.SpecFile]);
var output = new Location([_config.DockerRoot, _config.OpenApiFolder()]);
var command = $"docker run --rm -it -v {_config.LocalRoot}:{_config.DockerRoot} {_config.GenerationImage} " +
$"generate -g openapi-yaml -i {specFile} -o {output}";
Executor.Register(command);
}
}

View File

@@ -0,0 +1,76 @@
using Core.Events;
using Core.Interfaces;
using Core.Settings;
using Core.Yaml;
using Generator.Infrastructure.OpenApi.Builders;
namespace Generator.Infrastructure.OpenApi;
public class OpenApiDirector : IGeneratorDirector<OpenApiYaml>
{
private DisplayEmitter _emitter;
public OpenApiDirector(DisplayEmitter emitter)
{
_emitter = emitter;
}
/// <summary>
/// Launches dotnet server generation
/// </summary>
/// <param name="file"></param>
/// <param name="config"></param>
public async Task DotnetServer(OpenApiYaml file, DotnetConfig config)
{
var dotnetServerBuilder = new OpenApiDotnetServerBuilder(config, _emitter);
dotnetServerBuilder.Load(file);
dotnetServerBuilder.CreateGeneratorConfig();
dotnetServerBuilder.Generate();
dotnetServerBuilder.Build();
await dotnetServerBuilder.ExecuteAllAsync();
}
/// <summary>
/// Launches dotnet client generation
/// </summary>
/// <param name="file"></param>
/// <param name="config"></param>
public async Task DotnetClient(OpenApiYaml file, DotnetConfig config)
{
var dotnetClientBuilder = new OpenApiDotnetClientBuilder(config, _emitter);
dotnetClientBuilder.Load(file);
dotnetClientBuilder.CreateGeneratorConfig();
dotnetClientBuilder.Generate();
dotnetClientBuilder.Build();
await dotnetClientBuilder.ExecuteAllAsync();
}
/// <summary>
/// Launches java generation
/// </summary>
/// <param name="file"></param>
/// <param name="config"></param>
public async Task Java(OpenApiYaml file, JavaConfig config)
{
var javaBuilder = new OpenApiJavaBuilder(config, _emitter);
javaBuilder.Load(file);
javaBuilder.CreateGeneratorConfig();
javaBuilder.Generate();
javaBuilder.Build();
await javaBuilder.ExecuteAllAsync();
}
/// <summary>
/// Launches javascript generation
/// </summary>
/// <param name="file"></param>
/// <param name="config"></param>
public async Task Javascript(OpenApiYaml file, JavascriptConfig config)
{
_emitter.Warn(this, "Javascript generation is temporally disabled");
/*var jsBuilder = new OpenApiJavascriptBuilder(config, _emitter);
jsBuilder.Load(file);
jsBuilder.Generate();
await jsBuilder.ExecuteAllAsync();*/
}
}

Some files were not shown because too many files have changed in this diff Show More