From b7e4fe90be322ad3779c38a20cb58970c138997c Mon Sep 17 00:00:00 2001 From: cihataydin Date: Fri, 6 Jun 2025 21:17:45 +0300 Subject: [PATCH 1/4] feat(39): implements sample management API with CRUD operations and EF Core migrations --- README.md | 24 ++++ .../postgres-cluster/pgpool/pgpool.conf | 4 +- src/Api/Controllers/SampleController.cs | 117 ++++++++++++++++++ src/Api/Controllers/V2/SampleController.cs | 36 ------ .../{Mappings => MappingProfiles}/.gitkeep | 0 .../MappingProfiles/SampleMappingProfile.cs | 25 ++++ src/Api/Microservice.csproj | 2 + src/Api/Program.cs | 11 +- src/Api/appsettings.Development.json | 5 +- src/Domain/Domain.csproj | 3 + src/Domain/Interfaces/ISampleService.cs | 13 ++ .../Sample/CreateSampleRequestModel.cs | 6 + .../Request/Sample/GetSamplesRequestModel.cs | 6 + .../Sample/UpdateSampleRequestModel.cs | 8 ++ .../Sample/CreateSampleResponseModel.cs | 9 ++ .../Response/Sample/GetSampleResponseModel.cs | 12 ++ .../Sample/GetSamplesResponseModel.cs | 6 + .../Sample/UpdateSampleResponseModel.cs | 12 ++ src/Domain/Services/SampleService.cs | 69 +++++++++++ src/Infra/Data/DataContext.cs | 2 +- src/Infra/Data/DataContextFactory.cs | 28 +++++ src/Infra/Infra.csproj | 10 +- ...0250606135347_InitialMigration.Designer.cs | 51 ++++++++ .../20250606135347_InitialMigration.cs | 36 ++++++ .../Migrations/DataContextModelSnapshot.cs | 48 +++++++ 25 files changed, 496 insertions(+), 47 deletions(-) create mode 100644 src/Api/Controllers/SampleController.cs delete mode 100644 src/Api/Controllers/V2/SampleController.cs rename src/Api/{Mappings => MappingProfiles}/.gitkeep (100%) create mode 100644 src/Api/MappingProfiles/SampleMappingProfile.cs create mode 100644 src/Domain/Interfaces/ISampleService.cs create mode 100644 src/Domain/Models/Request/Sample/CreateSampleRequestModel.cs create mode 100644 src/Domain/Models/Request/Sample/GetSamplesRequestModel.cs create mode 100644 src/Domain/Models/Request/Sample/UpdateSampleRequestModel.cs create mode 100644 src/Domain/Models/Response/Sample/CreateSampleResponseModel.cs create mode 100644 src/Domain/Models/Response/Sample/GetSampleResponseModel.cs create mode 100644 src/Domain/Models/Response/Sample/GetSamplesResponseModel.cs create mode 100644 src/Domain/Models/Response/Sample/UpdateSampleResponseModel.cs create mode 100644 src/Domain/Services/SampleService.cs create mode 100644 src/Infra/Data/DataContextFactory.cs create mode 100644 src/Infra/Migrations/20250606135347_InitialMigration.Designer.cs create mode 100644 src/Infra/Migrations/20250606135347_InitialMigration.cs create mode 100644 src/Infra/Migrations/DataContextModelSnapshot.cs diff --git a/README.md b/README.md index 7a51ce0..a753ecd 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,30 @@ docker-compose --env-file .env.docker up --build -d ## Monitoring Tools Enable real‑time metrics, logs, and health checks for the application by following the step‑by‑step setup guide. [Click for Guide](dockerfiles/monitoring/GUIDE.md) +## Migration + +1. **Install the EF Core CLI tool (dotnet-ef)** + ```bash + dotnet tool install --global dotnet-ef + +This command adds the EF Core command-line tools to your system globally. + +2. **Verify the installation** + ```bash + dotnet ef --version +If you see a version number, the tool has been installed successfully. + +3. **Create a new migration** + ```bash + dotnet ef migrations add InitialMigration --context DataContext --project src/Infra +- --context DataContext: Specifies which DbContext to use when generating the migration. +- --project src/Infra: Specifies the project folder where the migration files will be created. + +4. **Apply the migration to the database** + ```bash + dotnet ef database update --context DataContext --project src/Infra +This command applies the latest migration to the database. + ## Deployment Service may be in sleep mode on the first request. You may need to wait for a few seconds. [Live Demo Url](https://api-0oqs.onrender.com/scalar) diff --git a/dockerfiles/postgres-cluster/pgpool/pgpool.conf b/dockerfiles/postgres-cluster/pgpool/pgpool.conf index 436bb72..a684c01 100644 --- a/dockerfiles/postgres-cluster/pgpool/pgpool.conf +++ b/dockerfiles/postgres-cluster/pgpool/pgpool.conf @@ -511,7 +511,7 @@ sr_check_user = 'admin' # Streaming replication check user # This is necessary even if you disable streaming # replication delay check by sr_check_period = 0 -sr_check_password = 'AESIdSY+WwYAncDcAca4aPcdg==' +sr_check_password = 'AESptsT8MwnzDZFxT53C6qdEg==' # Password for streaming replication check user # Leaving it empty will make Pgpool-II to first look for the # Password in pool_passwd file before using the empty password @@ -564,7 +564,7 @@ health_check_timeout = '10' # 0 means no timeout health_check_user = 'admin' # Health check user -health_check_password = 'AESIdSY+WwYAncDcAca4aPcdg==' +health_check_password = 'AESptsT8MwnzDZFxT53C6qdEg==' # Password for health check user # Leaving it empty will make Pgpool-II to first look for the # Password in pool_passwd file before using the empty password diff --git a/src/Api/Controllers/SampleController.cs b/src/Api/Controllers/SampleController.cs new file mode 100644 index 0000000..5f7665d --- /dev/null +++ b/src/Api/Controllers/SampleController.cs @@ -0,0 +1,117 @@ +namespace Api.Controllers +{ + using Asp.Versioning; + using Domain.Interfaces; + using Domain.Models.Request.Sample; + using Microsoft.AspNetCore.Mvc; + + /// + /// Represents the sample controller. + /// + [ApiController] + [Route("sample")] + [Tags("sample")] + [ApiVersion("1.0")] + public class SampleController : ControllerBase + { + private readonly ISampleService sampleService; + + /// + /// Initializes a new instance of the class. + /// + /// The sample service. + public SampleController(ISampleService sampleService) + { + this.sampleService = sampleService ?? throw new ArgumentNullException(nameof(sampleService)); + } + + /// + /// Gets a samples. + /// + /// The sample response model. + [HttpGet] + public async Task Get() + { + var result = await this.sampleService.GetSamplesAsync(new GetSamplesRequestModel(), CancellationToken.None); + return this.Ok(result); + } + + /// + /// Gets a sample by ID. + /// + /// The sample ID. + /// The sample response model. + [HttpGet("{id:guid}")] + public async Task Get(Guid id) + { + var result = await this.sampleService.GetSampleAsync(id, CancellationToken.None); + if (result == null) + { + return this.NotFound(); + } + + return this.Ok(result); + } + + /// + /// Creates a new sample. + /// + /// The create sample request model. + /// The created sample response model. + [HttpPost] + public async Task Create([FromBody] CreateSampleRequestModel request) + { + if (request == null) + { + return this.BadRequest("Request cannot be null."); + } + + var result = await this.sampleService.CreateSampleAsync(request, CancellationToken.None); + return this.CreatedAtAction(nameof(this.Get), new { id = result.Id }, result); + } + + /// + /// Updates an existing sample. + /// + /// The update sample request model. + /// The updated sample response model. + [HttpPut] + public async Task Update([FromBody] UpdateSampleRequestModel request) + { + if (request == null || request.Id == Guid.Empty) + { + return this.BadRequest("Request cannot be null and ID must be provided."); + } + + var result = await this.sampleService.UpdateSampleAsync(request, CancellationToken.None); + if (result == null) + { + return this.NotFound(); + } + + return this.Ok(result); + } + + /// + /// Deletes a sample by ID. + /// + /// The sample ID. + /// A boolean indicating success or failure. + [HttpDelete("{id:guid}")] + public async Task Delete(Guid id) + { + if (id == Guid.Empty) + { + return this.BadRequest("ID must be provided."); + } + + var result = await this.sampleService.DeleteSampleAsync(id, CancellationToken.None); + if (!result) + { + return this.NotFound(); + } + + return this.NoContent(); + } + } +} \ No newline at end of file diff --git a/src/Api/Controllers/V2/SampleController.cs b/src/Api/Controllers/V2/SampleController.cs deleted file mode 100644 index 649a4f0..0000000 --- a/src/Api/Controllers/V2/SampleController.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Api.Controllers.V2 -{ - using Asp.Versioning; - using Microsoft.AspNetCore.Mvc; - - /// - /// Represents the sample controller. - /// - [ApiController] - [Route("sample")] - [Tags("sample")] - [ApiVersion("2.0")] - public class SampleController : ControllerBase - { - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching", - }; - - /// - /// Gets the weather forecast. - /// - /// The weather forecast. - [HttpGet] - public IEnumerable Get() - { - var forecast = Enumerable.Range(1, 5).Select(index => - new WeatherForecast( - DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - Random.Shared.Next(-20, 55), - Summaries[Random.Shared.Next(Summaries.Length)])) - .ToArray(); - return forecast; - } - } -} diff --git a/src/Api/Mappings/.gitkeep b/src/Api/MappingProfiles/.gitkeep similarity index 100% rename from src/Api/Mappings/.gitkeep rename to src/Api/MappingProfiles/.gitkeep diff --git a/src/Api/MappingProfiles/SampleMappingProfile.cs b/src/Api/MappingProfiles/SampleMappingProfile.cs new file mode 100644 index 0000000..13cd6fa --- /dev/null +++ b/src/Api/MappingProfiles/SampleMappingProfile.cs @@ -0,0 +1,25 @@ +namespace Api.MappingProfiles +{ + using AutoMapper; + using Domain.Entities; + using Domain.Models.Request.Sample; + using Domain.Models.Response.Sample; + + /// + /// AutoMapper profile for mapping between domain entities and response models. + /// + public class SampleProfile : Profile + { + /// + /// Initializes a new instance of the class. + /// + public SampleProfile() + { + this.CreateMap(); + this.CreateMap(); + this.CreateMap(); + this.CreateMap(); + this.CreateMap(); + } + } +} diff --git a/src/Api/Microservice.csproj b/src/Api/Microservice.csproj index c46a509..8ee4870 100644 --- a/src/Api/Microservice.csproj +++ b/src/Api/Microservice.csproj @@ -18,6 +18,8 @@ $(NoWarn);SA1516 + App-Development-Secrets + diff --git a/src/Api/Program.cs b/src/Api/Program.cs index b80aa88..5e57787 100644 --- a/src/Api/Program.cs +++ b/src/Api/Program.cs @@ -1,9 +1,11 @@ #pragma warning disable SA1200 // Using directive should appear within a namespace declaration using Api.Configurations; +using Api.MappingProfiles; using Api.Middlewares; using Asp.Versioning; using Asp.Versioning.Builder; using Domain.Interfaces; +using Domain.Services; using Infra.Data; using Infra.Repositories; using Microsoft.EntityFrameworkCore; @@ -18,8 +20,11 @@ builder.Services.AddControllers(); builder.Services - .AddDbContext(opts => opts.UseInMemoryDatabase("DemoDb")) - .AddScoped(typeof(IRepository<>), typeof(Repository<>)); + .AddDbContext(opts => opts.UseNpgsql(builder.Configuration.GetConnectionString("PgpoolDb"))) + .AddScoped(typeof(IRepository<>), typeof(Repository<>)) + .AddScoped(typeof(ISampleService), typeof(SampleService)); + +builder.Services.AddAutoMapper(typeof(SampleProfile).Assembly); builder.Services.AddApiVersioning(options => { @@ -82,7 +87,7 @@ .WithName("GetWeatherForecast") .WithOpenApi() .WithApiVersionSet(new ApiVersionSet(new ApiVersionSetBuilder(string.Empty), "weatherForecast")) -.HasApiVersion(new ApiVersion(1, 0)); +.HasApiVersion(new ApiVersion(2, 0)); app.MapControllers(); app.MapMetrics(); diff --git a/src/Api/appsettings.Development.json b/src/Api/appsettings.Development.json index 7a50c09..2f2906d 100644 --- a/src/Api/appsettings.Development.json +++ b/src/Api/appsettings.Development.json @@ -7,5 +7,8 @@ "Microsoft.AspNetCore": "Warning" } }, - "UseJsonFormat": false + "UseJsonFormat": false, + "ConnectionStrings": { + "PgpoolDb": "" + } } \ No newline at end of file diff --git a/src/Domain/Domain.csproj b/src/Domain/Domain.csproj index 99f72e9..7a0c42b 100644 --- a/src/Domain/Domain.csproj +++ b/src/Domain/Domain.csproj @@ -5,4 +5,7 @@ enable enable + + + diff --git a/src/Domain/Interfaces/ISampleService.cs b/src/Domain/Interfaces/ISampleService.cs new file mode 100644 index 0000000..5a731dd --- /dev/null +++ b/src/Domain/Interfaces/ISampleService.cs @@ -0,0 +1,13 @@ +using Domain.Models.Request.Sample; +using Domain.Models.Response.Sample; + +namespace Domain.Interfaces; + +public interface ISampleService +{ + Task CreateSampleAsync(CreateSampleRequestModel request, CancellationToken cancellationToken = default); + Task GetSamplesAsync(GetSamplesRequestModel request, CancellationToken cancellationToken = default); + Task UpdateSampleAsync(UpdateSampleRequestModel request, CancellationToken cancellationToken = default); + Task GetSampleAsync(Guid id, CancellationToken cancellationToken = default); + Task DeleteSampleAsync(Guid id, CancellationToken cancellationToken = default); +} diff --git a/src/Domain/Models/Request/Sample/CreateSampleRequestModel.cs b/src/Domain/Models/Request/Sample/CreateSampleRequestModel.cs new file mode 100644 index 0000000..321e968 --- /dev/null +++ b/src/Domain/Models/Request/Sample/CreateSampleRequestModel.cs @@ -0,0 +1,6 @@ +namespace Domain.Models.Request.Sample; + +public class CreateSampleRequestModel +{ + public string? Name { get; set; } +} diff --git a/src/Domain/Models/Request/Sample/GetSamplesRequestModel.cs b/src/Domain/Models/Request/Sample/GetSamplesRequestModel.cs new file mode 100644 index 0000000..b8d9965 --- /dev/null +++ b/src/Domain/Models/Request/Sample/GetSamplesRequestModel.cs @@ -0,0 +1,6 @@ +namespace Domain.Models.Request.Sample; + +public class GetSamplesRequestModel +{ + public string? Name { get; set; } +} diff --git a/src/Domain/Models/Request/Sample/UpdateSampleRequestModel.cs b/src/Domain/Models/Request/Sample/UpdateSampleRequestModel.cs new file mode 100644 index 0000000..3fd763d --- /dev/null +++ b/src/Domain/Models/Request/Sample/UpdateSampleRequestModel.cs @@ -0,0 +1,8 @@ +namespace Domain.Models.Request.Sample; + +public class UpdateSampleRequestModel +{ + public Guid Id { get; set; } + + public string? Name { get; set; } +} diff --git a/src/Domain/Models/Response/Sample/CreateSampleResponseModel.cs b/src/Domain/Models/Response/Sample/CreateSampleResponseModel.cs new file mode 100644 index 0000000..2ee9c2c --- /dev/null +++ b/src/Domain/Models/Response/Sample/CreateSampleResponseModel.cs @@ -0,0 +1,9 @@ +namespace Domain.Models.Response.Sample; + +public class CreateSampleResponseModel +{ + public Guid Id { get; set; } + public string? Name { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } +} diff --git a/src/Domain/Models/Response/Sample/GetSampleResponseModel.cs b/src/Domain/Models/Response/Sample/GetSampleResponseModel.cs new file mode 100644 index 0000000..e7afff6 --- /dev/null +++ b/src/Domain/Models/Response/Sample/GetSampleResponseModel.cs @@ -0,0 +1,12 @@ +namespace Domain.Models.Response.Sample; + +public class GetSampleResponseModel +{ + public Guid? Id { get; set; } + + public string? Name { get; set; } + + public DateTime? UpdatedAt { get; set; } + + public DateTime? CreatedAt { get; set; } +} \ No newline at end of file diff --git a/src/Domain/Models/Response/Sample/GetSamplesResponseModel.cs b/src/Domain/Models/Response/Sample/GetSamplesResponseModel.cs new file mode 100644 index 0000000..47e747a --- /dev/null +++ b/src/Domain/Models/Response/Sample/GetSamplesResponseModel.cs @@ -0,0 +1,6 @@ +namespace Domain.Models.Response.Sample; + +public class GetSamplesResponseModel +{ + public IList Samples { get; set; } = new List(); +} diff --git a/src/Domain/Models/Response/Sample/UpdateSampleResponseModel.cs b/src/Domain/Models/Response/Sample/UpdateSampleResponseModel.cs new file mode 100644 index 0000000..febcfa2 --- /dev/null +++ b/src/Domain/Models/Response/Sample/UpdateSampleResponseModel.cs @@ -0,0 +1,12 @@ +namespace Domain.Models.Response.Sample; + +public class UpdateSampleResponseModel +{ + public Guid Id { get; set; } + + public string? Name { get; set; } + + public DateTime? UpdatedAt { get; set; } + + public DateTime? CreatedAt { get; set; } +} diff --git a/src/Domain/Services/SampleService.cs b/src/Domain/Services/SampleService.cs new file mode 100644 index 0000000..ca07584 --- /dev/null +++ b/src/Domain/Services/SampleService.cs @@ -0,0 +1,69 @@ +using System.Linq.Expressions; +using System.Threading.Tasks; +using AutoMapper; +using Domain.Entities; +using Domain.Interfaces; +using Domain.Models.Request.Sample; +using Domain.Models.Response.Sample; + +namespace Domain.Services; + +public class SampleService : ISampleService +{ + private readonly IRepository _repository; + private readonly IMapper _mapper; + + public SampleService(IRepository repository, + IMapper mapper) + { + _repository = repository; + _mapper = mapper; + } + + public async Task GetSampleAsync(Guid id, CancellationToken cancellationToken = default) + { + var entity = await _repository.GetByIdAsync(id, cancellationToken); + return _mapper.Map(entity); + } + + public async Task GetSamplesAsync(GetSamplesRequestModel request, CancellationToken cancellationToken = default) + { + Expression> predicate = e => true; + if (!string.IsNullOrEmpty(request.Name)) + { + predicate = e => e.Name == request.Name; + + } + var samples = await _repository.ListAsync(predicate, true, cancellationToken); + var response = new GetSamplesResponseModel + { + Samples = _mapper.Map>(samples) + }; + return response; + } + + public async Task CreateSampleAsync(CreateSampleRequestModel request, CancellationToken cancellationToken = default) + { + var entity = _mapper.Map(request); + var createdEntity = await _repository.AddAsync(entity, cancellationToken); + await _repository.SaveChangesAsync(cancellationToken); + return _mapper.Map(createdEntity); + } + + public async Task UpdateSampleAsync(UpdateSampleRequestModel request, CancellationToken cancellationToken = default) + { + var entity = _mapper.Map(request); + var updatedEntity = _repository.Update(entity); + await _repository.SaveChangesAsync(cancellationToken); + return _mapper.Map(updatedEntity); + } + + public async Task DeleteSampleAsync(Guid id, CancellationToken cancellationToken = default) + { + var entity = await _repository.GetByIdAsync(id, cancellationToken); + if (entity == null) return false; + + _repository.Remove(entity); + return true && await _repository.SaveChangesAsync(cancellationToken) > 0; + } +} \ No newline at end of file diff --git a/src/Infra/Data/DataContext.cs b/src/Infra/Data/DataContext.cs index eced47b..3499dcb 100644 --- a/src/Infra/Data/DataContext.cs +++ b/src/Infra/Data/DataContext.cs @@ -6,5 +6,5 @@ public class DataContext : DbContext { public DataContext(DbContextOptions opts) : base(opts) { } - public virtual DbSet Products => Set(); + public virtual DbSet Samples => Set(); } \ No newline at end of file diff --git a/src/Infra/Data/DataContextFactory.cs b/src/Infra/Data/DataContextFactory.cs new file mode 100644 index 0000000..30b7d91 --- /dev/null +++ b/src/Infra/Data/DataContextFactory.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; + +namespace Infra.Data +{ + public class DataContextFactory : IDesignTimeDbContextFactory + { + public DataContext CreateDbContext(string[] args) + { + var infraRoot = Directory.GetCurrentDirectory(); + var apiRoot = Path.GetFullPath(Path.Combine(infraRoot, "..", "Api")); + + var configuration = new ConfigurationBuilder() + .SetBasePath(apiRoot) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true) + .Build(); + + var connectionString = configuration.GetConnectionString("PgpoolDb"); + + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseNpgsql(connectionString); + + return new DataContext(optionsBuilder.Options); + } + } +} diff --git a/src/Infra/Infra.csproj b/src/Infra/Infra.csproj index 9806bce..c0ce890 100644 --- a/src/Infra/Infra.csproj +++ b/src/Infra/Infra.csproj @@ -8,10 +8,12 @@ - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - + + + + + + \ No newline at end of file diff --git a/src/Infra/Migrations/20250606135347_InitialMigration.Designer.cs b/src/Infra/Migrations/20250606135347_InitialMigration.Designer.cs new file mode 100644 index 0000000..d50af63 --- /dev/null +++ b/src/Infra/Migrations/20250606135347_InitialMigration.Designer.cs @@ -0,0 +1,51 @@ +// +using System; +using Infra.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infra.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20250606135347_InitialMigration")] + partial class InitialMigration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.SampleEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Samples"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Infra/Migrations/20250606135347_InitialMigration.cs b/src/Infra/Migrations/20250606135347_InitialMigration.cs new file mode 100644 index 0000000..26fec34 --- /dev/null +++ b/src/Infra/Migrations/20250606135347_InitialMigration.cs @@ -0,0 +1,36 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infra.Migrations +{ + /// + public partial class InitialMigration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Samples", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Samples", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Samples"); + } + } +} diff --git a/src/Infra/Migrations/DataContextModelSnapshot.cs b/src/Infra/Migrations/DataContextModelSnapshot.cs new file mode 100644 index 0000000..a29c4a2 --- /dev/null +++ b/src/Infra/Migrations/DataContextModelSnapshot.cs @@ -0,0 +1,48 @@ +// +using System; +using Infra.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infra.Migrations +{ + [DbContext(typeof(DataContext))] + partial class DataContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.SampleEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Samples"); + }); +#pragma warning restore 612, 618 + } + } +} From 268b9e4098b2c95f500bc87e7c2425a5dd5898f4 Mon Sep 17 00:00:00 2001 From: cihataydin Date: Fri, 6 Jun 2025 21:52:56 +0300 Subject: [PATCH 2/4] chore(39): adds pgpool.conf to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index da18949..5521d2a 100644 --- a/.gitignore +++ b/.gitignore @@ -398,3 +398,5 @@ FodyWeavers.xsd *.sln.iml .DS_Store + +pgpool.conf \ No newline at end of file From d88f5fccbeaef4ae47d92d5d5aa68da3305f8805 Mon Sep 17 00:00:00 2001 From: cihataydin Date: Fri, 6 Jun 2025 21:53:31 +0300 Subject: [PATCH 3/4] chore(39): add .gitkeep to pgpool directory --- dockerfiles/postgres-cluster/pgpool/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 dockerfiles/postgres-cluster/pgpool/.gitkeep diff --git a/dockerfiles/postgres-cluster/pgpool/.gitkeep b/dockerfiles/postgres-cluster/pgpool/.gitkeep new file mode 100644 index 0000000..e69de29 From 52970dcd983765c081dc7ffd5cf21bcd5818ca37 Mon Sep 17 00:00:00 2001 From: cihataydin Date: Fri, 6 Jun 2025 21:56:54 +0300 Subject: [PATCH 4/4] chore(39): update streaming replication and health check passwords in pgpool.conf --- .gitignore | 2 -- dockerfiles/postgres-cluster/pgpool/pgpool.conf | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 5521d2a..da18949 100644 --- a/.gitignore +++ b/.gitignore @@ -398,5 +398,3 @@ FodyWeavers.xsd *.sln.iml .DS_Store - -pgpool.conf \ No newline at end of file diff --git a/dockerfiles/postgres-cluster/pgpool/pgpool.conf b/dockerfiles/postgres-cluster/pgpool/pgpool.conf index a684c01..a49fde5 100644 --- a/dockerfiles/postgres-cluster/pgpool/pgpool.conf +++ b/dockerfiles/postgres-cluster/pgpool/pgpool.conf @@ -511,7 +511,7 @@ sr_check_user = 'admin' # Streaming replication check user # This is necessary even if you disable streaming # replication delay check by sr_check_period = 0 -sr_check_password = 'AESptsT8MwnzDZFxT53C6qdEg==' +sr_check_password = 'AESQwC+qXIlBxY3DhcDQ5HExQ==' # Password for streaming replication check user # Leaving it empty will make Pgpool-II to first look for the # Password in pool_passwd file before using the empty password @@ -564,7 +564,7 @@ health_check_timeout = '10' # 0 means no timeout health_check_user = 'admin' # Health check user -health_check_password = 'AESptsT8MwnzDZFxT53C6qdEg==' +health_check_password = 'AESQwC+qXIlBxY3DhcDQ5HExQ==' # Password for health check user # Leaving it empty will make Pgpool-II to first look for the # Password in pool_passwd file before using the empty password