diff --git a/MapperAI.sln.DotSettings.user b/MapperAI.sln.DotSettings.user index ee45d31..c4c3244 100644 --- a/MapperAI.sln.DotSettings.user +++ b/MapperAI.sln.DotSettings.user @@ -1,6 +1,6 @@  ForceIncluded - <SessionState ContinuousTestingMode="0" IsActive="True" Name="Test_Should_Create_4_Files_With_CSharp_Extension" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <SessionState ContinuousTestingMode="0" Name="Test_Should_Create_4_Files_With_CSharp_Extension" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <TestAncestor> <TestId>xUnit::8B3E109D-96CA-4B6D-B379-6AF70646DC25::net8.0::MapperAI.Test.FileMapperTests.Test_Should_Create_4_Files_With_CSharp_Extension</TestId> <TestId>xUnit::8B3E109D-96CA-4B6D-B379-6AF70646DC25::net8.0::MapperAI.Test.ClassMapperTests.Test1</TestId> @@ -8,7 +8,7 @@ <TestId>xUnit::8B3E109D-96CA-4B6D-B379-6AF70646DC25::net8.0::MapperAI.Test.FileMapperTests.Test_Should_Create_4_Files_With_Go_Extension</TestId> </TestAncestor> </SessionState> - <SessionState ContinuousTestingMode="0" Name="Test1" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="Test1" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <TestAncestor> <TestId>xUnit::8B3E109D-96CA-4B6D-B379-6AF70646DC25::net8.0::MapperAI.Test.ClassMapperTests.Test1</TestId> <TestId>xUnit::8B3E109D-96CA-4B6D-B379-6AF70646DC25::net8.0::MapperAI.Test.FileMapperTests.Test1</TestId> @@ -16,5 +16,6 @@ <TestId>xUnit::8B3E109D-96CA-4B6D-B379-6AF70646DC25::net8.0::MapperAI.Test.PdfMapperTests.Test1</TestId> <TestId>xUnit::8B3E109D-96CA-4B6D-B379-6AF70646DC25::net8.0::MapperAI.Test.FileMapperTests.Test_Should_Create_4_Files_With_Go_Extension</TestId> <TestId>xUnit::8B3E109D-96CA-4B6D-B379-6AF70646DC25::net8.0::MapperAI.Test.FileMapperTests.Test_Should_Create_4_Files_With_CSharp_Extension</TestId> + <TestId>xUnit::8B3E109D-96CA-4B6D-B379-6AF70646DC25::net8.0::MapperAI.Test.DI.DependencyInjectionTests.AddMapperAI_ShouldRegisterServices</TestId> </TestAncestor> </SessionState> \ No newline at end of file diff --git a/src/MapperAI.Core/Clients/GeminiMapperClient.cs b/src/MapperAI.Core/Clients/GeminiMapperClient.cs index c45e97a..e813ad3 100644 --- a/src/MapperAI.Core/Clients/GeminiMapperClient.cs +++ b/src/MapperAI.Core/Clients/GeminiMapperClient.cs @@ -10,8 +10,8 @@ public class GeminiMapperClient : MapperClientBase, IMapperClient { private const string EndpointBase = "https://generativelanguage.googleapis.com/v1beta"; - public GeminiMapperClient(MapperClientConfiguration mapperClientConfiguration, IMapperSerializer serializer) : base( - mapperClientConfiguration, serializer) + + public GeminiMapperClient(MapperClientConfiguration mapperClientConfiguration, IMapperSerializer serializer, HttpClient httpClient) : base(mapperClientConfiguration, serializer, httpClient) { } diff --git a/src/MapperAI.Core/Clients/MapperClientBase.cs b/src/MapperAI.Core/Clients/MapperClientBase.cs index 091ede7..1237e46 100644 --- a/src/MapperAI.Core/Clients/MapperClientBase.cs +++ b/src/MapperAI.Core/Clients/MapperClientBase.cs @@ -12,11 +12,11 @@ public abstract class MapperClientBase private readonly HttpClient _httpClient; private readonly IMapperSerializer _serializer; - protected MapperClientBase(MapperClientConfiguration mapperClientConfiguration, IMapperSerializer serializer) + protected MapperClientBase(MapperClientConfiguration mapperClientConfiguration, IMapperSerializer serializer, HttpClient httpClient) { MapperClientConfiguration = mapperClientConfiguration; - _httpClient = new HttpClient(); _serializer = serializer; + _httpClient = httpClient; } protected async Task GetAsync(string endpoint, object body, CancellationToken cancellationToken = default) @@ -31,7 +31,7 @@ protected async Task GetAsync(string endpoint, object body, Cancel throw new MapperRequestStatusException($"Request failed with status: {response.StatusCode}"); } - string result = await response.Content.ReadAsStringAsync(); + string result = await response.Content.ReadAsStringAsync(cancellationToken); return JsonDocument.Parse(result); } catch (Exception ex) diff --git a/src/MapperAI.Core/Clients/MapperClientFactory.cs b/src/MapperAI.Core/Clients/MapperClientFactory.cs index 18b5522..eb0995c 100644 --- a/src/MapperAI.Core/Clients/MapperClientFactory.cs +++ b/src/MapperAI.Core/Clients/MapperClientFactory.cs @@ -5,24 +5,17 @@ namespace MapperAI.Core.Clients; -public class MapperClientFactory : IMapperClientFactory +public class MapperClientFactory(IMapperSerializer serializer, HttpClient httpClient) : IMapperClientFactory { - private readonly IMapperSerializer _serializer; - - public MapperClientFactory(IMapperSerializer serializer) - { - _serializer = serializer; - } - public IMapperClient CreateClient(MapperClientConfiguration configuration) { switch (configuration.Type) { case ModelType.Ollama: - return new OllamaMapperClient(configuration, _serializer); + return new OllamaMapperClient(configuration, serializer, httpClient); default: - return new GeminiMapperClient(configuration, _serializer); + return new GeminiMapperClient(configuration, serializer, httpClient); } } } \ No newline at end of file diff --git a/src/MapperAI.Core/Clients/Models/MapperClientConfiguration.cs b/src/MapperAI.Core/Clients/Models/MapperClientConfiguration.cs index a81bab9..fea4023 100644 --- a/src/MapperAI.Core/Clients/Models/MapperClientConfiguration.cs +++ b/src/MapperAI.Core/Clients/Models/MapperClientConfiguration.cs @@ -1,23 +1,18 @@ using MapperAI.Core.Enums; +using MapperAI.Core.Extensions.Enums; namespace MapperAI.Core.Clients.Models; public class MapperClientConfiguration { - public string Model { get; set; } public string? ApiKey { get; set; } public ModelType Type { get; set; } - - public MapperClientConfiguration(string model, string? apiKey, ModelType type) + public string Model => Type.GetEnumDescriptionValue(); + + public MapperClientConfiguration( string? apiKey, ModelType type) { - Model = model; ApiKey = apiKey; Type = type; } - public MapperClientConfiguration(string model, ModelType type) - { - Model = model; - Type = type; - } } \ No newline at end of file diff --git a/src/MapperAI.Core/Clients/OllamaMapperClient.cs b/src/MapperAI.Core/Clients/OllamaMapperClient.cs index 0be8492..d17cccd 100644 --- a/src/MapperAI.Core/Clients/OllamaMapperClient.cs +++ b/src/MapperAI.Core/Clients/OllamaMapperClient.cs @@ -11,11 +11,11 @@ public class OllamaMapperClient : MapperClientBase ,IMapperClient { private const string Endpoint = "http://localhost:11434/api/generate"; - public OllamaMapperClient(MapperClientConfiguration mapperClientConfiguration, IMapperSerializer serializer) : base(mapperClientConfiguration, serializer) + + public OllamaMapperClient(MapperClientConfiguration mapperClientConfiguration, IMapperSerializer serializer, HttpClient httpClient) : base(mapperClientConfiguration, serializer, httpClient) { } - public async Task SendAsync(string prompt, CancellationToken cancellationToken) { dynamic payload = CreatePayload(prompt); diff --git a/src/MapperAI.Core/DI/DependencyInjection.cs b/src/MapperAI.Core/DI/DependencyInjection.cs new file mode 100644 index 0000000..0db4233 --- /dev/null +++ b/src/MapperAI.Core/DI/DependencyInjection.cs @@ -0,0 +1,36 @@ +using MapperAI.Core.Clients; +using MapperAI.Core.Clients.Interfaces; +using MapperAI.Core.Clients.Models; +using MapperAI.Core.Enums; +using MapperAI.Core.Mappers; +using MapperAI.Core.Mappers.Interfaces; +using MapperAI.Core.Serializers; +using MapperAI.Core.Serializers.Interfaces; +using Microsoft.Extensions.DependencyInjection; + +namespace MapperAI.Core.DI; + +public static class DependencyInjection +{ + public static IServiceCollection AddMapperAI(this IServiceCollection services, string apiKey, ModelType modelType) + { + services.AddMapperClientConfiguration(apiKey, modelType); + services.AddHttpClient(); + services.AddSingleton(); + services.AddSingleton(); + services.AddScoped(); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + return services; + } + + private static void AddMapperClientConfiguration(this IServiceCollection services, string apiKey, ModelType modelType) + { + MapperClientConfiguration configuration = new(apiKey, modelType); + services.AddSingleton(s => configuration); + + } +} \ No newline at end of file diff --git a/src/MapperAI.Core/Enums/ModelType.cs b/src/MapperAI.Core/Enums/ModelType.cs index 7d82494..d2f4eab 100644 --- a/src/MapperAI.Core/Enums/ModelType.cs +++ b/src/MapperAI.Core/Enums/ModelType.cs @@ -1,9 +1,11 @@ -namespace MapperAI.Core.Enums; +using System.ComponentModel; + +namespace MapperAI.Core.Enums; public enum ModelType { - - Gemini, + [Description("gemini-2.0-flash")] + GeminiFlash2_0, ChatGpt, Ollama } \ No newline at end of file diff --git a/src/MapperAI.Core/Extensions/Enums/EnumExtension.cs b/src/MapperAI.Core/Extensions/Enums/EnumExtension.cs new file mode 100644 index 0000000..88339b1 --- /dev/null +++ b/src/MapperAI.Core/Extensions/Enums/EnumExtension.cs @@ -0,0 +1,14 @@ +using System.ComponentModel; +using System.Reflection; + +namespace MapperAI.Core.Extensions.Enums; + +public static class EnumExtension +{ + public static string GetEnumDescriptionValue(this Enum data) + { + FieldInfo? field = data.GetType().GetField(data.ToString()); + var attribute = field?.GetCustomAttribute(); + return attribute?.Description ?? throw new InvalidEnumArgumentException(); + } +} \ No newline at end of file diff --git a/src/MapperAI.Core/Initializers/DependencyInitializer.cs b/src/MapperAI.Core/Extensions/Initializers/InitializerExtension.cs similarity index 89% rename from src/MapperAI.Core/Initializers/DependencyInitializer.cs rename to src/MapperAI.Core/Extensions/Initializers/InitializerExtension.cs index 8f7154e..bd5d09e 100644 --- a/src/MapperAI.Core/Initializers/DependencyInitializer.cs +++ b/src/MapperAI.Core/Extensions/Initializers/InitializerExtension.cs @@ -1,11 +1,10 @@ -using System.Reflection; +using System.Reflection; -namespace MapperAI.Core.Initializers; +namespace MapperAI.Core.Extensions.Initializers; -public class DependencyInitializer +public static class InitializerExtension { - - public static void Initialize(object obj) + public static void Initialize(this object obj) { var properties = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); var genericProperties = properties.Where(x => x.PropertyType.IsGenericType); @@ -17,6 +16,7 @@ public static void Initialize(object obj) } } } + private static void InitializeDependencyProperties(Type? itemType, PropertyInfo property, object obj) { if (itemType == null) @@ -47,5 +47,4 @@ private static void InitializeDependencyProperties(Type? itemType, PropertyInfo property.SetValue(obj, listInstance); } - } \ No newline at end of file diff --git a/src/MapperAI.Core/MapperAI.Core.csproj b/src/MapperAI.Core/MapperAI.Core.csproj index 51bba51..63affeb 100644 --- a/src/MapperAI.Core/MapperAI.Core.csproj +++ b/src/MapperAI.Core/MapperAI.Core.csproj @@ -15,6 +15,7 @@ + diff --git a/src/MapperAI.Core/Mappers/Models/FileMapperConfiguration.cs b/src/MapperAI.Core/Mappers/Models/FileMapperConfiguration.cs index 1e1c8cc..8059abf 100644 --- a/src/MapperAI.Core/Mappers/Models/FileMapperConfiguration.cs +++ b/src/MapperAI.Core/Mappers/Models/FileMapperConfiguration.cs @@ -6,7 +6,6 @@ public class FileMapperConfiguration public string InputFolder { get; set; } public string? NameSpace { get; set; } public string Extension { get; set; } = "C#"; - public string? LanguageVersion { get; set; } public string? FileName { get; set; } public bool IsUniqueClass => FileName != null; diff --git a/src/MapperAI.Core/Mappers/PdfMapper.cs b/src/MapperAI.Core/Mappers/PdfMapper.cs index c961a8f..97227b2 100644 --- a/src/MapperAI.Core/Mappers/PdfMapper.cs +++ b/src/MapperAI.Core/Mappers/PdfMapper.cs @@ -2,7 +2,7 @@ using iText.Kernel.Pdf.Canvas.Parser; using MapperAI.Core.Clients.Interfaces; using MapperAI.Core.Clients.Models; -using MapperAI.Core.Initializers; +using MapperAI.Core.Extensions.Initializers; using MapperAI.Core.Mappers.Interfaces; using MapperAI.Core.Serializers.Interfaces; @@ -27,7 +27,7 @@ public PdfMapper(IMapperSerializer serializer, IMapperClientFactory mapperClient IMapperClient iai = _mapperClientFactory.CreateClient(_clientConfiguration); string pdfContent = ExtractPdfContent(pdfPath); T destinyObject = new T(); - DependencyInitializer.Initialize(destinyObject); + destinyObject.Initialize(); string prompt = CreatePrompt(pdfContent, _serializer.Serialize(destinyObject)); MapperClientResponse result = await iai.SendAsync(prompt, cancellationToken); return _serializer.Deserialize(result.Value); diff --git a/test/MapperAI.Test/BaseTests.cs b/test/MapperAI.Test/BaseTests.cs index 6e264ca..3b67c06 100644 --- a/test/MapperAI.Test/BaseTests.cs +++ b/test/MapperAI.Test/BaseTests.cs @@ -13,6 +13,6 @@ public abstract class BaseTests public BaseTests() { Serializer = new MapperSerializer(); - Factory = new MapperClientFactory(Serializer); + Factory = new MapperClientFactory(Serializer, new HttpClient()); } } \ No newline at end of file diff --git a/test/MapperAI.Test/ClassMapperTests.cs b/test/MapperAI.Test/ClassMapperTests.cs index 7d70ccb..9938c63 100644 --- a/test/MapperAI.Test/ClassMapperTests.cs +++ b/test/MapperAI.Test/ClassMapperTests.cs @@ -8,12 +8,12 @@ namespace MapperAI.Test; public class ClassMapperTests : BaseTests { private readonly IClassMapper _classMapper; - private readonly MapperClientConfiguration _clientConfiguration; public ClassMapperTests() { - _clientConfiguration = new MapperClientConfiguration("gemini-2.0-flash", Environment.GetEnvironmentVariable("GEMINI_KEY"),ModelType.Gemini); - _classMapper = new ClassMapper(Serializer, Factory, _clientConfiguration); + var clientConfiguration = new MapperClientConfiguration(Environment.GetEnvironmentVariable("GEMINI_KEY"),ModelType.GeminiFlash2_0); + _classMapper = new ClassMapper(Serializer, Factory, clientConfiguration); + } diff --git a/test/MapperAI.Test/DI/DependencyInjectionTests.cs b/test/MapperAI.Test/DI/DependencyInjectionTests.cs new file mode 100644 index 0000000..450e0a6 --- /dev/null +++ b/test/MapperAI.Test/DI/DependencyInjectionTests.cs @@ -0,0 +1,28 @@ +using MapperAI.Core.Clients.Interfaces; +using MapperAI.Core.DI; +using MapperAI.Core.Enums; +using MapperAI.Core.Mappers.Interfaces; +using MapperAI.Core.Serializers.Interfaces; +using Microsoft.Extensions.DependencyInjection; + +namespace MapperAI.Test.DI; + +public class DependencyInjectionTests +{ + [Fact] + public void AddMapperAI_ShouldRegisterServices() + { + var services = new ServiceCollection(); + + // Act + services.AddMapperAI("fake-key", ModelType.GeminiFlash2_0); + var provider = services.BuildServiceProvider(); + + // Assert + Assert.NotNull(provider.GetService()); + Assert.NotNull(provider.GetService()); + Assert.NotNull(provider.GetService()); + Assert.NotNull(provider.GetService()); + Assert.NotNull(provider.GetService()); + } +} \ No newline at end of file diff --git a/test/MapperAI.Test/FileMapperTests.cs b/test/MapperAI.Test/FileMapperTests.cs index bc79dd4..dc99bbc 100644 --- a/test/MapperAI.Test/FileMapperTests.cs +++ b/test/MapperAI.Test/FileMapperTests.cs @@ -9,8 +9,6 @@ namespace MapperAI.Test; public class FileMapperTests : BaseTests { - - private readonly MapperClientConfiguration _clientConfiguration; private readonly IFileMapper _mapper; private string InputFolder => Path.Combine(FoldersHelpers.GetProjectDefaultPath(), "Class"); @@ -18,8 +16,8 @@ public class FileMapperTests : BaseTests public FileMapperTests() { - _clientConfiguration = new MapperClientConfiguration("gemini-2.0-flash", Environment.GetEnvironmentVariable("GEMINI_KEY"),ModelType.Gemini); - _mapper = new FileMapper(Factory, Serializer, _clientConfiguration); + var clientConfiguration = new MapperClientConfiguration( Environment.GetEnvironmentVariable("GEMINI_KEY"),ModelType.GeminiFlash2_0); + _mapper = new FileMapper(Factory, Serializer, clientConfiguration); } @@ -45,11 +43,11 @@ public async Task Test_Should_Create_4_Files_With_Go_Extension() public async Task Test_Should_Create_4_Files_With_CSharp_Extension() { - string extesionToMap = "js"; + string extesionToMap = "php"; FileMapperConfiguration configuration = new FileMapperConfiguration(InputFolder, OutputFolder) { NameSpace = "MapperAI.Test.MappedClasses", - Extension = extesionToMap + Extension = extesionToMap, }; await _mapper.MapAsync(configuration); var files = Directory.GetFiles(OutputFolder); diff --git a/test/MapperAI.Test/PdfMapperTests.cs b/test/MapperAI.Test/PdfMapperTests.cs index cad5e75..4c0e7a1 100644 --- a/test/MapperAI.Test/PdfMapperTests.cs +++ b/test/MapperAI.Test/PdfMapperTests.cs @@ -8,12 +8,11 @@ namespace MapperAI.Test; public class PdfMapperTests : BaseTests { private readonly IPDFMapper _pdfMapper; - private readonly MapperClientConfiguration _clientConfiguration; public PdfMapperTests() { - _clientConfiguration = new MapperClientConfiguration("gemini-2.0-flash", Environment.GetEnvironmentVariable("GEMINI_KEY"),ModelType.Gemini); - _pdfMapper = new PdfMapper(Serializer, Factory, _clientConfiguration); + var clientConfiguration = new MapperClientConfiguration( Environment.GetEnvironmentVariable("GEMINI_KEY"),ModelType.GeminiFlash2_0); + _pdfMapper = new PdfMapper(Serializer, Factory, clientConfiguration); } [Fact]