diff --git a/src/Nox.Generator/Domain/ModelGenerator/EntitiesGenerator.cs b/src/Nox.Generator/Domain/ModelGenerator/EntitiesGenerator.cs index 9b25f29693..e223da7688 100644 --- a/src/Nox.Generator/Domain/ModelGenerator/EntitiesGenerator.cs +++ b/src/Nox.Generator/Domain/ModelGenerator/EntitiesGenerator.cs @@ -27,6 +27,7 @@ private static void GenerateEntity(SourceProductionContext context, string solut var code = new CodeBuilder($"{entity.Name}.g.cs", context); code.AppendLine($"using Nox.Types;"); + code.AppendLine($"using System;"); code.AppendLine($"using System.Collections.Generic;"); code.AppendLine(); code.AppendLine($"namespace {solutionNameSpace}.Domain;"); diff --git a/src/Nox.Generator/Infrastructure/Persistence/DbContextGenerator/DbContextGenerator.cs b/src/Nox.Generator/Infrastructure/Persistence/DbContextGenerator/DbContextGenerator.cs index ac12826106..4ab0836c19 100644 --- a/src/Nox.Generator/Infrastructure/Persistence/DbContextGenerator/DbContextGenerator.cs +++ b/src/Nox.Generator/Infrastructure/Persistence/DbContextGenerator/DbContextGenerator.cs @@ -24,7 +24,7 @@ public static void Generate(SourceProductionContext context, string solutionName code.AppendLine(@"using Microsoft.EntityFrameworkCore;"); code.AppendLine(@"using Nox.Solution;"); code.AppendLine(@"using Nox.Types.EntityFramework.vNext;"); - code.AppendLine(@"using SampleWebApp.Domain;"); + code.AppendLine($@"using {solutionNameSpace}.Domain;"); code.AppendLine(); code.AppendLine($"namespace {solutionNameSpace}.Infrastructure.Persistence;"); code.AppendLine(); @@ -83,8 +83,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) } } - base.OnModelCreating(modelBuilder); } + + base.OnModelCreating(modelBuilder); } } "); diff --git a/src/SampleWebApp/Examples/SampleWebAppDbContextExample.cs b/src/SampleWebApp/Examples/SampleWebAppDbContextExample.cs index 83eb2c9f20..664a93388f 100644 --- a/src/SampleWebApp/Examples/SampleWebAppDbContextExample.cs +++ b/src/SampleWebApp/Examples/SampleWebAppDbContextExample.cs @@ -49,8 +49,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) _databaseConfigurator.ConfigureEntity(modelBuilder.Entity(type), entity); } } - - base.OnModelCreating(modelBuilder); } + + base.OnModelCreating(modelBuilder); } } \ No newline at end of file diff --git a/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/Country.g.cs b/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/Country.g.cs index 6805e60b72..7fde1e2354 100644 --- a/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/Country.g.cs +++ b/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/Country.g.cs @@ -3,6 +3,7 @@ #nullable enable using Nox.Types; +using System; using System.Collections.Generic; namespace SampleWebApp.Domain; diff --git a/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/CountryLocalNames.g.cs b/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/CountryLocalNames.g.cs index 4a0eb3b9e7..79c7b172ac 100644 --- a/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/CountryLocalNames.g.cs +++ b/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/CountryLocalNames.g.cs @@ -3,6 +3,7 @@ #nullable enable using Nox.Types; +using System; using System.Collections.Generic; namespace SampleWebApp.Domain; diff --git a/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/Currency.g.cs b/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/Currency.g.cs index 296af5eefa..b8cd178735 100644 --- a/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/Currency.g.cs +++ b/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/Currency.g.cs @@ -3,6 +3,7 @@ #nullable enable using Nox.Types; +using System; using System.Collections.Generic; namespace SampleWebApp.Domain; diff --git a/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/SampleWebAppDbContext.g.cs b/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/SampleWebAppDbContext.g.cs index 8cca3953cd..25db3cec68 100644 --- a/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/SampleWebAppDbContext.g.cs +++ b/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/SampleWebAppDbContext.g.cs @@ -52,8 +52,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) } } - base.OnModelCreating(modelBuilder); } + + base.OnModelCreating(modelBuilder); } } diff --git a/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/Store.g.cs b/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/Store.g.cs index 462ee71e47..fd77ec669a 100644 --- a/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/Store.g.cs +++ b/src/SampleWebApp/Generated/Nox.Generator/Nox.Generator.NoxCodeGenerator/Store.g.cs @@ -3,6 +3,7 @@ #nullable enable using Nox.Types; +using System; using System.Collections.Generic; namespace SampleWebApp.Domain; diff --git a/tests/Nox.Generator.Tests/Database/Design/test.solution.nox.yaml b/tests/Nox.Generator.Tests/Database/Design/test.solution.nox.yaml new file mode 100644 index 0000000000..406937e071 --- /dev/null +++ b/tests/Nox.Generator.Tests/Database/Design/test.solution.nox.yaml @@ -0,0 +1,237 @@ +# +# workplace.solution.nox.yaml +# +# yaml-language-server: $schema=https://noxorg.dev/schemas/solution.json +# + +name: TestDatabaseWebApp + +description: Sample Nox solution yaml configuration + +variables: + DATABASE_PROVIDER: slqServer + DATABASE_SERVER: localhost + DATABASE_USER: sa + DATABASE_PASSWORD: Developer*123 + DATABASE_PORT: "5432" + +environments: + + - name: dev + description: Used for development and testing + + - name: test + description: Test environment + + - name: uat + description: For them end users to check it works + + - name: prod + description: Production environment used for, well - the real thing! + isProduction: true + +versionControl: + provider: azureDevops + host: https://dev.azure.com/iwgplc + folders: + sourceCode: /src + containers: /docker + +team: + + - name: Andre Sharpe + userName: andre.sharpe@iwgplc.com + roles: [architect, owner, administrator, developer, manager] + + - name: Jan Schutte + userName: jan.schutte@iwgplc.com + roles: [architect, administrator, developer, devOpsEngineer] + + - name: Anton Du Plessis + userName: anton.duplessis@iwgplc.com + roles: [projectManager] + + - name: Morne Van Zyl + userName: morne.vanzyl@iwgplc.com + roles: [technicalWriter] + + - name: Dmytro Dorodnykh + userName: dmytro.dorodnykh@iwgplc.com + roles: [developer] + + - name: Oleksandr Vlasenko + userName: oleksandr.vlasenko@regus.com + roles: [architect, developer] + +domain: + + entities: + + - name: TestEntity + description: Entity created for testing database + + userInterface: + icon: world + + persistence: + isVersioned: true + tableName: TestEntity + schema: dbo + create: + isEnabled: true + raiseEvents: true + read: + isEnabled: true + update: + isEnabled: true + raiseEvents: true + delete: + isEnabled: true + raiseEvents: true + useSoftDelete: true + + keys: + + - name: Id + type: text + textTypeOptions: + isUnicode: false + minLength: 2 + maxLength: 2 + + attributes: + + - name: TextTestField + type: text + textTypeOptions: + minLength: 4 + maxLength: 63 + isRequired: true + + - name: NumberTestField + type: number + numberTypeOptions: + minValue: 4 + maxValue: 894 + isRequired: true + + - name: MoneyTestField + type: money + + #- name: CountryCode2TestField + # type: countryCode2 + + #- name: GeoCoordTestField + # type: latLong + + #- name: AreaTestField + # type: area + + #- name: BooleanTestField + # type: boolean + + #- name: CountryCode3TestField + # type: countryCode3 + + #- name: CountryNumberTestField + # type: countryNumber + + #- name: CurrencyCodeTestField + # type: currencyCode + + #- name: CurrencyNumberTestField + # type: currencyNumber + + #- name: DateTimeTestField + # type: dateTime + + #- name: DateTimeRangeTestField + # type: dateTimeRange + + #- name: DistanceTestField + # type: distance + + #- name: EmailTestField + # type: email + + #- name: EncryptedTextTestField + # type: encryptedText + + #- name: GuidTestField + # type: guid + + #- name: HashedTextTestField + # type: hashedText + + #- name: InternetDomainTestField + # type: internetDomain + + #- name: IpAddressTestField + # type: ipAddress + + #- name: LengthTestField + # type: length + + #- name: MacAddressTestField + # type: macAddress + + #- name: MonthTestField + # type: month + + #- name: NuidTestField + # type: nuid + + #- name: PasswordTestField + # type: password + + #- name: PercentageTestField + # type: percentage + + #- name: PhoneNumberTestField + # type: phoneNumber + + #- name: StreetAddressTestField + # type: streetAddress + + #- name: TempratureTestField + # type: temperature + + #- name: UriTestField + # type: uri + + #- name: VolumeTestField + # type: volume + + #- name: WeightTestField + # type: weight + + #- name: YearTestField + # type: year + + #- name: CultureCodeTestField + # type: cultureCode + + #- name: LanguageCodeTestField + # type: languageCode + +infrastructure: + + persistence: + databaseServer: + name: SampleCurrencyDb + + # Sql Server + serverUri: sqlserver.iwgplc.com + provider: sqlServer + port: 1433 + user: sqluser + password: sqlpassword + + messaging: + integrationEventServer: + name: IntegrationBus + provider: rabbitMq + serverUri: rabbitmq://localhost + port: 5672 + user: guest + password: guest diff --git a/tests/Nox.Generator.Tests/Database/Models/AuditableEntityBase.cs b/tests/Nox.Generator.Tests/Database/Models/AuditableEntityBase.cs new file mode 100644 index 0000000000..f76e75eed7 --- /dev/null +++ b/tests/Nox.Generator.Tests/Database/Models/AuditableEntityBase.cs @@ -0,0 +1,40 @@ +// Generated + +#nullable enable + +using System; + +namespace TestDatabaseWebApp.Domain; + +public partial class AuditableEntityBase +{ + /// + /// The date and time when this entity was first created (in Coordinated Universal Time). + /// + public DateTime CreatedAtUtc { get; set; } + + /// + /// The user that created the entity. + /// + public string? CreatedBy { get; set; } + + /// + /// The date and time when this entity was last updated (in Coordinated Universal Time). + /// + public DateTime? UpdatedAtUtc { get; set; } + + /// + /// The user that last updated the entity. + /// + public string? UpdatedBy { get; set; } + + /// + /// The date and time when this entity was deleted (in Coordinated Universal Time). + /// + public DateTime? DeletedAtUtc { get; set; } + + /// + /// The user that deleted the entity. + /// + public string? DeletedBy { get; set; } +} diff --git a/tests/Nox.Generator.Tests/Database/Models/TestDatabaseWebAppDbContext.g.cs b/tests/Nox.Generator.Tests/Database/Models/TestDatabaseWebAppDbContext.g.cs new file mode 100644 index 0000000000..0dc5cd59d3 --- /dev/null +++ b/tests/Nox.Generator.Tests/Database/Models/TestDatabaseWebAppDbContext.g.cs @@ -0,0 +1,56 @@ +// Generated + +#nullable enable + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Nox.Solution; +using Nox.Types.EntityFramework.vNext; +using System; +using TestDatabaseWebApp.Domain; + +namespace TestDatabaseWebApp.Infrastructure.Persistence; + +public partial class TestDatabaseWebAppDbContext : DbContext +{ + private NoxSolution _noxSolution { get; set; } + private INoxDatabaseConfigurator _databaseConfigurator { get; set; } + + public TestDatabaseWebAppDbContext( + DbContextOptions options, + NoxSolution noxSolution, + INoxDatabaseConfigurator databaseConfigurator + ) : base(options) + { + _noxSolution = noxSolution; + _databaseConfigurator = databaseConfigurator; + } + + public DbSet TestEntities { get; set; } = null!; + + + public static void RegisterDbContext(IServiceCollection services) + { + services.AddDbContext(); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + if (_noxSolution.Domain != null) + { + foreach (var entity in _noxSolution.Domain.Entities) + { + var type = Type.GetType("TestDatabaseWebApp.Domain." + entity.Name); + + if (type != null) + { + _databaseConfigurator.ConfigureEntity(modelBuilder.Entity(type), entity); + } + } + + } + + base.OnModelCreating(modelBuilder); + } +} + diff --git a/tests/Nox.Generator.Tests/Database/Models/TestEntity.cs b/tests/Nox.Generator.Tests/Database/Models/TestEntity.cs new file mode 100644 index 0000000000..b08f576244 --- /dev/null +++ b/tests/Nox.Generator.Tests/Database/Models/TestEntity.cs @@ -0,0 +1,36 @@ +// Generated + +#nullable enable + +using Nox.Types; +using System; +using System.Collections.Generic; + +namespace TestDatabaseWebApp.Domain; + +/// +/// Entity created for testing database. +/// +public partial class TestEntity : AuditableEntityBase +{ + + /// + /// (Optional) + /// + public Text Id { get; set; } = null!; + + /// + /// (Required) + /// + public Text TextTestField { get; set; } = null!; + + /// + /// (Required) + /// + public Number NumberTestField { get; set; } = null!; + + /// + /// (Optional) + /// + public Money? MoneyTestField { get; set; } = null!; +} diff --git a/tests/Nox.Generator.Tests/Database/SqliteIntegrationTests.cs b/tests/Nox.Generator.Tests/Database/SqliteIntegrationTests.cs new file mode 100644 index 0000000000..7fcd9dc9c6 --- /dev/null +++ b/tests/Nox.Generator.Tests/Database/SqliteIntegrationTests.cs @@ -0,0 +1,74 @@ +using FluentAssertions; +using Nox.Types; +using NoxSourceGeneratorTests.DatabaseTests; +using System.Linq; +using TestDatabaseWebApp.Domain; +using Xunit; + +namespace Nox.Generator.Test.DatabaseTests; + +public class SqliteIntegrationTests : SqliteTestBase +{ + [Fact] + public void GeneratedEntity_CanSaveAndReadFields_AllTypes() + { + // TODO: + // array + // color + // colour + // autoNumber + // collection + // entity + // file + // formula + // image + // imagePng + // imageJpg + // imageSvg + // object + // user + // cultureCode + // languageCode + // yaml + // uri + // url + // date + // dateTimeDuration + // dateTimeSchedule + // html + // json + // time + // translatedText + // markdown + // jwtToken + + // TODO: commented types + + var text = "TestTextValue"; + var number = 123; + var money = 10; + var currencyCode = CurrencyCode.UAH; + + var newItem = new TestEntity() + { + Id = Text.From(text), + TextTestField = Text.From(text), + NumberTestField = Number.From(number), + MoneyTestField = Money.From(money, currencyCode), + }; + DbContext.TestEntities.Add(newItem); + DbContext.SaveChanges(); + + // Force the recreation of DBContext and ensure we have fresh data from database + RecreateDbContext(); + + var testEntity = DbContext.TestEntities.First(); + + // TODO: make it work without .Value + testEntity.Id.Value.Should().Be(text); + testEntity.TextTestField.Value.Should().Be(text); + testEntity.NumberTestField.Value.Should().Be(number); + testEntity.MoneyTestField.Value.Amount.Should().Be(money); + testEntity.MoneyTestField.Value.CurrencyCode.Should().Be(currencyCode); + } +} \ No newline at end of file diff --git a/tests/Nox.Generator.Tests/Database/SqliteTestBase.cs b/tests/Nox.Generator.Tests/Database/SqliteTestBase.cs new file mode 100644 index 0000000000..7a2998941d --- /dev/null +++ b/tests/Nox.Generator.Tests/Database/SqliteTestBase.cs @@ -0,0 +1,55 @@ +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Nox.Solution; +using Nox.Types.EntityFramework.Sqlite; +using System; +using TestDatabaseWebApp.Infrastructure.Persistence; + +namespace NoxSourceGeneratorTests.DatabaseTests; + +public abstract class SqliteTestBase : IDisposable +{ + //private const string _inMemoryConnectionStringTemplate = "DataSource=:memory:"; + private const string _inMemoryConnectionStringTemplate = @"DataSource=test_database_{0}.db"; + private static string _inMemoryConnectionString = string.Empty; + private const string _testSolutionFile = @"./Database/Design/test.solution.nox.yaml"; + private readonly SqliteConnection _connection; + + protected TestDatabaseWebAppDbContext DbContext; + + protected SqliteTestBase() + { + _inMemoryConnectionString = string.Format(_inMemoryConnectionStringTemplate, DateTime.UtcNow.Ticks); + _connection = new SqliteConnection(_inMemoryConnectionString); + _connection.Open(); + DbContext = CreateDbContext(_connection); + } + + private static TestDatabaseWebAppDbContext CreateDbContext(SqliteConnection connection) + { + var databaseConfigurator = new SqliteDatabaseConfigurator(); + var solution = new NoxSolutionBuilder() + .UseYamlFile(_testSolutionFile) + .Build(); + var options = new DbContextOptionsBuilder() + .UseSqlite(connection) + .Options; + var dbContext = new TestDatabaseWebAppDbContext(options, solution, databaseConfigurator); + dbContext.Database.EnsureCreated(); + return dbContext; + } + + internal void RecreateDbContext() + { + var previousDbContext = DbContext; + DbContext = CreateDbContext(_connection); + previousDbContext.Dispose(); + } + + public void Dispose() + { + GC.SuppressFinalize(this); + DbContext?.Dispose(); + _connection.Dispose(); + } +} \ No newline at end of file diff --git a/tests/Nox.Generator.Tests/Nox.Generator.Tests.csproj b/tests/Nox.Generator.Tests/Nox.Generator.Tests.csproj index 6f5f7daf9d..5522aa9b93 100644 --- a/tests/Nox.Generator.Tests/Nox.Generator.Tests.csproj +++ b/tests/Nox.Generator.Tests/Nox.Generator.Tests.csproj @@ -2,7 +2,7 @@ net7.0 - false + false @@ -13,6 +13,9 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + + + @@ -30,7 +33,10 @@ + + + @@ -38,6 +44,7 @@ + @@ -74,6 +81,9 @@ + + Always + Always