@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
1,551 lines (1,287 loc) • 102 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.aspnetXUnitTemplate = void 0;
exports.aspnetXUnitTemplate = {
id: 'aspnet-xunit',
name: 'aspnet-xunit',
displayName: 'ASP.NET Core with xUnit Testing',
description: 'Enterprise .NET API with comprehensive xUnit testing suite',
language: 'csharp',
framework: 'aspnet-xunit',
version: '1.0.0',
tags: ['aspnet', 'xunit', 'testing', 'moq', 'fluentassertions'],
port: 5000,
dependencies: {},
features: ['authentication', 'database', 'validation', 'logging', 'testing'],
files: {
// Main project file
'{{serviceName}}.csproj': `<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.0" />
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.0" />
</ItemGroup>
</Project>`,
// Test project file
'{{serviceName}}.Tests.csproj': `<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.6.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Moq" Version="4.20.69" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.0" />
<PackageReference Include="AutoFixture" Version="4.18.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.18.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.18.0" />
<PackageReference Include="Testcontainers.MsSql" Version="3.6.0" />
<PackageReference Include="Bogus" Version="34.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../{{serviceName}}.csproj" />
</ItemGroup>
</Project>`,
// Program.cs
'Program.cs': `using {{serviceName}}.Data;
using {{serviceName}}.Services;
using {{serviceName}}.Models;
using {{serviceName}}.DTOs;
using {{serviceName}}.Profiles;
using {{serviceName}}.Validators;
using Microsoft.EntityFrameworkCore;
using AutoMapper;
using FluentValidation;
using Serilog;
using Microsoft.OpenApi.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Configure Serilog
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
builder.Host.UseSerilog();
// Add services to the container
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
// Configure Entity Framework
builder.Services.AddDbContext<ApplicationDbContext>(options =>
{
if (builder.Environment.IsEnvironment("Testing"))
{
options.UseInMemoryDatabase("TestDb");
}
else
{
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
}
});
// Configure AutoMapper
builder.Services.AddAutoMapper(typeof(UserProfile), typeof(ProductProfile), typeof(OrderProfile));
// Configure FluentValidation
builder.Services.AddValidatorsFromAssemblyContaining<CreateUserDtoValidator>();
// Configure Swagger/OpenAPI
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "{{serviceName}} API",
Version = "v1",
Description = "Enterprise API with comprehensive xUnit testing",
Contact = new OpenApiContact
{
Name = "{{serviceName}} Team",
Email = "team@{{serviceName}}.com"
}
});
// Include XML comments
var xmlFile = $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
if (File.Exists(xmlPath))
{
c.IncludeXmlComments(xmlPath);
}
// Configure JWT authentication in Swagger
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header,
},
new List<string>()
}
});
});
// Configure JWT Authentication
var jwtSettings = builder.Configuration.GetSection("JwtSettings");
var secretKey = jwtSettings["SecretKey"];
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtSettings["Issuer"],
ValidAudience = jwtSettings["Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey!))
};
});
// Register application services
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.AddScoped<IAuthService, AuthService>();
var app = builder.Build();
// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
// Ensure database is created (skip in testing environment)
if (!app.Environment.IsEnvironment("Testing"))
{
using var scope = app.Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
context.Database.EnsureCreated();
}
app.Run();
// Make the implicit Program class public for testing
public partial class Program { }`,
// Models/User.cs
'Models/User.cs': `using System.ComponentModel.DataAnnotations;
namespace {{serviceName}}.Models;
public class User
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; } = string.Empty;
[Required]
[EmailAddress]
[StringLength(255)]
public string Email { get; set; } = string.Empty;
[Required]
public string PasswordHash { get; set; } = string.Empty;
[StringLength(20)]
public string? PhoneNumber { get; set; }
public DateTime DateOfBirth { get; set; }
[StringLength(500)]
public string? Address { get; set; }
[StringLength(100)]
public string? City { get; set; }
[StringLength(100)]
public string? Country { get; set; }
public bool IsActive { get; set; } = true;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? UpdatedAt { get; set; }
// Navigation properties
public virtual ICollection<Order> Orders { get; set; } = new List<Order>();
}`,
// Models/Product.cs
'Models/Product.cs': `using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace {{serviceName}}.Models;
public class Product
{
public int Id { get; set; }
[Required]
[StringLength(200)]
public string Name { get; set; } = string.Empty;
[StringLength(1000)]
public string? Description { get; set; }
[Required]
[Column(TypeName = "decimal(18,2)")]
public decimal Price { get; set; }
[StringLength(100)]
public string? Category { get; set; }
[StringLength(50)]
public string? Brand { get; set; }
public int StockQuantity { get; set; }
[StringLength(500)]
public string? ImageUrl { get; set; }
public bool IsActive { get; set; } = true;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? UpdatedAt { get; set; }
// Navigation properties
public virtual ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>();
}`,
// Models/Order.cs
'Models/Order.cs': `using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace {{serviceName}}.Models;
public class Order
{
public int Id { get; set; }
[Required]
public int UserId { get; set; }
[Required]
[StringLength(50)]
public string OrderNumber { get; set; } = string.Empty;
[Required]
[Column(TypeName = "decimal(18,2)")]
public decimal TotalAmount { get; set; }
[Required]
[StringLength(50)]
public string Status { get; set; } = "Pending";
[StringLength(500)]
public string? ShippingAddress { get; set; }
[StringLength(500)]
public string? Notes { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? UpdatedAt { get; set; }
// Navigation properties
public virtual User User { get; set; } = null!;
public virtual ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>();
}`,
// Models/OrderItem.cs
'Models/OrderItem.cs': `using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace {{serviceName}}.Models;
public class OrderItem
{
public int Id { get; set; }
[Required]
public int OrderId { get; set; }
[Required]
public int ProductId { get; set; }
[Required]
public int Quantity { get; set; }
[Required]
[Column(TypeName = "decimal(18,2)")]
public decimal UnitPrice { get; set; }
[Required]
[Column(TypeName = "decimal(18,2)")]
public decimal TotalPrice { get; set; }
// Navigation properties
public virtual Order Order { get; set; } = null!;
public virtual Product Product { get; set; } = null!;
}`,
// DTOs/UserDtos.cs
'DTOs/UserDtos.cs': `using System.ComponentModel.DataAnnotations;
namespace {{serviceName}}.DTOs;
public class UserDto
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string? PhoneNumber { get; set; }
public DateTime DateOfBirth { get; set; }
public string? Address { get; set; }
public string? City { get; set; }
public string? Country { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
}
public class CreateUserDto
{
[Required]
[StringLength(100)]
public string Name { get; set; } = string.Empty;
[Required]
[EmailAddress]
[StringLength(255)]
public string Email { get; set; } = string.Empty;
[Required]
[StringLength(100, MinimumLength = 6)]
public string Password { get; set; } = string.Empty;
[StringLength(20)]
public string? PhoneNumber { get; set; }
public DateTime DateOfBirth { get; set; }
[StringLength(500)]
public string? Address { get; set; }
[StringLength(100)]
public string? City { get; set; }
[StringLength(100)]
public string? Country { get; set; }
}
public class UpdateUserDto
{
[StringLength(100)]
public string? Name { get; set; }
[EmailAddress]
[StringLength(255)]
public string? Email { get; set; }
[StringLength(20)]
public string? PhoneNumber { get; set; }
public DateTime? DateOfBirth { get; set; }
[StringLength(500)]
public string? Address { get; set; }
[StringLength(100)]
public string? City { get; set; }
[StringLength(100)]
public string? Country { get; set; }
public bool? IsActive { get; set; }
}
public class OrderDto
{
public int Id { get; set; }
public int UserId { get; set; }
public string OrderNumber { get; set; } = string.Empty;
public decimal TotalAmount { get; set; }
public string Status { get; set; } = string.Empty;
public string? ShippingAddress { get; set; }
public string? Notes { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
public UserDto? User { get; set; }
public List<OrderItemDto> OrderItems { get; set; } = new();
}
public class OrderItemDto
{
public int Id { get; set; }
public int OrderId { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public decimal TotalPrice { get; set; }
public ProductDto? Product { get; set; }
}`,
// DTOs/ProductDtos.cs
'DTOs/ProductDtos.cs': `using System.ComponentModel.DataAnnotations;
namespace {{serviceName}}.DTOs;
public class ProductDto
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string? Description { get; set; }
public decimal Price { get; set; }
public string? Category { get; set; }
public string? Brand { get; set; }
public int StockQuantity { get; set; }
public string? ImageUrl { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
}
public class CreateProductDto
{
[Required]
[StringLength(200)]
public string Name { get; set; } = string.Empty;
[StringLength(1000)]
public string? Description { get; set; }
[Required]
[Range(0.01, double.MaxValue)]
public decimal Price { get; set; }
[StringLength(100)]
public string? Category { get; set; }
[StringLength(50)]
public string? Brand { get; set; }
[Required]
[Range(0, int.MaxValue)]
public int StockQuantity { get; set; }
[StringLength(500)]
public string? ImageUrl { get; set; }
}
public class UpdateProductDto
{
[StringLength(200)]
public string? Name { get; set; }
[StringLength(1000)]
public string? Description { get; set; }
[Range(0.01, double.MaxValue)]
public decimal? Price { get; set; }
[StringLength(100)]
public string? Category { get; set; }
[StringLength(50)]
public string? Brand { get; set; }
[Range(0, int.MaxValue)]
public int? StockQuantity { get; set; }
[StringLength(500)]
public string? ImageUrl { get; set; }
public bool? IsActive { get; set; }
}`,
// Profiles/UserProfile.cs
'Profiles/UserProfile.cs': `using AutoMapper;
using {{serviceName}}.Models;
using {{serviceName}}.DTOs;
namespace {{serviceName}}.Profiles;
public class UserProfile : Profile
{
public UserProfile()
{
CreateMap<User, UserDto>();
CreateMap<CreateUserDto, User>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.PasswordHash, opt => opt.Ignore())
.ForMember(dest => dest.IsActive, opt => opt.MapFrom(src => true))
.ForMember(dest => dest.CreatedAt, opt => opt.MapFrom(src => DateTime.UtcNow))
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.Orders, opt => opt.Ignore());
CreateMap<UpdateUserDto, User>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.PasswordHash, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.MapFrom(src => DateTime.UtcNow))
.ForMember(dest => dest.Orders, opt => opt.Ignore())
.ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null));
}
}`,
// Profiles/ProductProfile.cs
'Profiles/ProductProfile.cs': `using AutoMapper;
using {{serviceName}}.Models;
using {{serviceName}}.DTOs;
namespace {{serviceName}}.Profiles;
public class ProductProfile : Profile
{
public ProductProfile()
{
CreateMap<Product, ProductDto>();
CreateMap<CreateProductDto, Product>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.IsActive, opt => opt.MapFrom(src => true))
.ForMember(dest => dest.CreatedAt, opt => opt.MapFrom(src => DateTime.UtcNow))
.ForMember(dest => dest.UpdatedAt, opt => opt.Ignore())
.ForMember(dest => dest.OrderItems, opt => opt.Ignore());
CreateMap<UpdateProductDto, Product>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore())
.ForMember(dest => dest.UpdatedAt, opt => opt.MapFrom(src => DateTime.UtcNow))
.ForMember(dest => dest.OrderItems, opt => opt.Ignore())
.ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null));
}
}`,
// Profiles/OrderProfile.cs
'Profiles/OrderProfile.cs': `using AutoMapper;
using {{serviceName}}.Models;
using {{serviceName}}.DTOs;
namespace {{serviceName}}.Profiles;
public class OrderProfile : Profile
{
public OrderProfile()
{
CreateMap<Order, OrderDto>();
CreateMap<OrderItem, OrderItemDto>();
}
}`,
// Validators/CreateUserDtoValidator.cs
'Validators/CreateUserDtoValidator.cs': `using FluentValidation;
using {{serviceName}}.DTOs;
namespace {{serviceName}}.Validators;
public class CreateUserDtoValidator : AbstractValidator<CreateUserDto>
{
public CreateUserDtoValidator()
{
RuleFor(x => x.Name)
.NotEmpty()
.Length(2, 100);
RuleFor(x => x.Email)
.NotEmpty()
.EmailAddress()
.MaximumLength(255);
RuleFor(x => x.Password)
.NotEmpty()
.MinimumLength(6)
.MaximumLength(100)
.Matches(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)")
.WithMessage("Password must contain at least one lowercase letter, one uppercase letter, and one digit");
RuleFor(x => x.PhoneNumber)
.MaximumLength(20)
.When(x => !string.IsNullOrEmpty(x.PhoneNumber));
RuleFor(x => x.DateOfBirth)
.LessThan(DateTime.Now.AddYears(-13))
.WithMessage("User must be at least 13 years old");
}
}`,
// Data/ApplicationDbContext.cs
'Data/ApplicationDbContext.cs': `using Microsoft.EntityFrameworkCore;
using {{serviceName}}.Models;
namespace {{serviceName}}.Data;
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<User> Users { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<OrderItem> OrderItems { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// User configuration
modelBuilder.Entity<User>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.Email).IsUnique();
entity.Property(e => e.Name).IsRequired().HasMaxLength(100);
entity.Property(e => e.Email).IsRequired().HasMaxLength(255);
entity.Property(e => e.PasswordHash).IsRequired();
});
// Product configuration
modelBuilder.Entity<Product>(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.Name).IsRequired().HasMaxLength(200);
entity.Property(e => e.Price).HasColumnType("decimal(18,2)");
});
// Order configuration
modelBuilder.Entity<Order>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.OrderNumber).IsUnique();
entity.Property(e => e.OrderNumber).IsRequired().HasMaxLength(50);
entity.Property(e => e.TotalAmount).HasColumnType("decimal(18,2)");
entity.Property(e => e.Status).IsRequired().HasMaxLength(50);
entity.HasOne(e => e.User)
.WithMany(u => u.Orders)
.HasForeignKey(e => e.UserId)
.OnDelete(DeleteBehavior.Restrict);
});
// OrderItem configuration
modelBuilder.Entity<OrderItem>(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.UnitPrice).HasColumnType("decimal(18,2)");
entity.Property(e => e.TotalPrice).HasColumnType("decimal(18,2)");
entity.HasOne(e => e.Order)
.WithMany(o => o.OrderItems)
.HasForeignKey(e => e.OrderId)
.OnDelete(DeleteBehavior.Cascade);
entity.HasOne(e => e.Product)
.WithMany(p => p.OrderItems)
.HasForeignKey(e => e.ProductId)
.OnDelete(DeleteBehavior.Restrict);
});
}
}`,
// Services/IUserService.cs
'Services/IUserService.cs': `using {{serviceName}}.DTOs;
namespace {{serviceName}}.Services;
public interface IUserService
{
Task<UserDto?> GetByIdAsync(int id);
Task<UserDto?> GetByEmailAsync(string email);
Task<IEnumerable<UserDto>> GetAllAsync();
Task<UserDto> CreateAsync(CreateUserDto createUserDto);
Task<UserDto?> UpdateAsync(int id, UpdateUserDto updateUserDto);
Task<bool> DeleteAsync(int id);
Task<bool> ExistsAsync(int id);
Task<bool> EmailExistsAsync(string email);
}`,
// Services/UserService.cs
'Services/UserService.cs': `using Microsoft.EntityFrameworkCore;
using AutoMapper;
using {{serviceName}}.Data;
using {{serviceName}}.Models;
using {{serviceName}}.DTOs;
using BCrypt.Net;
namespace {{serviceName}}.Services;
public class UserService : IUserService
{
private readonly ApplicationDbContext _context;
private readonly IMapper _mapper;
private readonly ILogger<UserService> _logger;
public UserService(ApplicationDbContext context, IMapper mapper, ILogger<UserService> logger)
{
_context = context;
_mapper = mapper;
_logger = logger;
}
public async Task<UserDto?> GetByIdAsync(int id)
{
_logger.LogInformation("Getting user by ID: {UserId}", id);
var user = await _context.Users
.AsNoTracking()
.FirstOrDefaultAsync(u => u.Id == id);
return user != null ? _mapper.Map<UserDto>(user) : null;
}
public async Task<UserDto?> GetByEmailAsync(string email)
{
_logger.LogInformation("Getting user by email: {Email}", email);
var user = await _context.Users
.AsNoTracking()
.FirstOrDefaultAsync(u => u.Email == email);
return user != null ? _mapper.Map<UserDto>(user) : null;
}
public async Task<IEnumerable<UserDto>> GetAllAsync()
{
_logger.LogInformation("Getting all users");
var users = await _context.Users
.AsNoTracking()
.OrderBy(u => u.Name)
.ToListAsync();
return _mapper.Map<IEnumerable<UserDto>>(users);
}
public async Task<UserDto> CreateAsync(CreateUserDto createUserDto)
{
_logger.LogInformation("Creating new user with email: {Email}", createUserDto.Email);
if (await EmailExistsAsync(createUserDto.Email))
{
throw new InvalidOperationException($"User with email {createUserDto.Email} already exists");
}
var user = _mapper.Map<User>(createUserDto);
user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(createUserDto.Password);
_context.Users.Add(user);
await _context.SaveChangesAsync();
_logger.LogInformation("User created successfully with ID: {UserId}", user.Id);
return _mapper.Map<UserDto>(user);
}
public async Task<UserDto?> UpdateAsync(int id, UpdateUserDto updateUserDto)
{
_logger.LogInformation("Updating user with ID: {UserId}", id);
var user = await _context.Users.FindAsync(id);
if (user == null)
{
_logger.LogWarning("User not found with ID: {UserId}", id);
return null;
}
if (!string.IsNullOrEmpty(updateUserDto.Email) &&
updateUserDto.Email != user.Email &&
await EmailExistsAsync(updateUserDto.Email))
{
throw new InvalidOperationException($"User with email {updateUserDto.Email} already exists");
}
_mapper.Map(updateUserDto, user);
user.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
_logger.LogInformation("User updated successfully with ID: {UserId}", id);
return _mapper.Map<UserDto>(user);
}
public async Task<bool> DeleteAsync(int id)
{
_logger.LogInformation("Deleting user with ID: {UserId}", id);
var user = await _context.Users.FindAsync(id);
if (user == null)
{
_logger.LogWarning("User not found with ID: {UserId}", id);
return false;
}
_context.Users.Remove(user);
await _context.SaveChangesAsync();
_logger.LogInformation("User deleted successfully with ID: {UserId}", id);
return true;
}
public async Task<bool> ExistsAsync(int id)
{
return await _context.Users.AnyAsync(u => u.Id == id);
}
public async Task<bool> EmailExistsAsync(string email)
{
return await _context.Users.AnyAsync(u => u.Email == email);
}
}`,
// Services/IProductService.cs
'Services/IProductService.cs': `using {{serviceName}}.DTOs;
namespace {{serviceName}}.Services;
public interface IProductService
{
Task<ProductDto?> GetByIdAsync(int id);
Task<IEnumerable<ProductDto>> GetAllAsync();
Task<IEnumerable<ProductDto>> GetByCategoryAsync(string category);
Task<ProductDto> CreateAsync(CreateProductDto createProductDto);
Task<ProductDto?> UpdateAsync(int id, UpdateProductDto updateProductDto);
Task<bool> DeleteAsync(int id);
Task<bool> ExistsAsync(int id);
}`,
// Services/ProductService.cs
'Services/ProductService.cs': `using Microsoft.EntityFrameworkCore;
using AutoMapper;
using {{serviceName}}.Data;
using {{serviceName}}.Models;
using {{serviceName}}.DTOs;
namespace {{serviceName}}.Services;
public class ProductService : IProductService
{
private readonly ApplicationDbContext _context;
private readonly IMapper _mapper;
private readonly ILogger<ProductService> _logger;
public ProductService(ApplicationDbContext context, IMapper mapper, ILogger<ProductService> logger)
{
_context = context;
_mapper = mapper;
_logger = logger;
}
public async Task<ProductDto?> GetByIdAsync(int id)
{
var product = await _context.Products
.AsNoTracking()
.FirstOrDefaultAsync(p => p.Id == id);
return product != null ? _mapper.Map<ProductDto>(product) : null;
}
public async Task<IEnumerable<ProductDto>> GetAllAsync()
{
var products = await _context.Products
.AsNoTracking()
.OrderBy(p => p.Name)
.ToListAsync();
return _mapper.Map<IEnumerable<ProductDto>>(products);
}
public async Task<IEnumerable<ProductDto>> GetByCategoryAsync(string category)
{
var products = await _context.Products
.AsNoTracking()
.Where(p => p.Category == category)
.OrderBy(p => p.Name)
.ToListAsync();
return _mapper.Map<IEnumerable<ProductDto>>(products);
}
public async Task<ProductDto> CreateAsync(CreateProductDto createProductDto)
{
var product = _mapper.Map<Product>(createProductDto);
_context.Products.Add(product);
await _context.SaveChangesAsync();
return _mapper.Map<ProductDto>(product);
}
public async Task<ProductDto?> UpdateAsync(int id, UpdateProductDto updateProductDto)
{
var product = await _context.Products.FindAsync(id);
if (product == null)
{
return null;
}
_mapper.Map(updateProductDto, product);
product.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
return _mapper.Map<ProductDto>(product);
}
public async Task<bool> DeleteAsync(int id)
{
var product = await _context.Products.FindAsync(id);
if (product == null)
{
return false;
}
_context.Products.Remove(product);
await _context.SaveChangesAsync();
return true;
}
public async Task<bool> ExistsAsync(int id)
{
return await _context.Products.AnyAsync(p => p.Id == id);
}
}`,
// Services/IOrderService.cs (stub)
'Services/IOrderService.cs': `namespace {{serviceName}}.Services;
public interface IOrderService
{
// Add order service methods here
}`,
// Services/OrderService.cs (stub)
'Services/OrderService.cs': `namespace {{serviceName}}.Services;
public class OrderService : IOrderService
{
// Add order service implementation here
}`,
// Services/IAuthService.cs (stub)
'Services/IAuthService.cs': `namespace {{serviceName}}.Services;
public interface IAuthService
{
Task<string> AuthenticateAsync(string email, string password);
}`,
// Services/AuthService.cs (stub)
'Services/AuthService.cs': `namespace {{serviceName}}.Services;
public class AuthService : IAuthService
{
public Task<string> AuthenticateAsync(string email, string password)
{
// Add authentication logic here
throw new NotImplementedException();
}
}`,
// Controllers/UsersController.cs
'Controllers/UsersController.cs': `using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using {{serviceName}}.Services;
using {{serviceName}}.DTOs;
namespace {{serviceName}}.Controllers;
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IUserService _userService;
private readonly ILogger<UsersController> _logger;
public UsersController(IUserService userService, ILogger<UsersController> logger)
{
_userService = userService;
_logger = logger;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<UserDto>>> GetUsers()
{
try
{
var users = await _userService.GetAllAsync();
return Ok(users);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while getting users");
return StatusCode(500, "An error occurred while processing your request");
}
}
[HttpGet("{id}")]
public async Task<ActionResult<UserDto>> GetUser(int id)
{
try
{
var user = await _userService.GetByIdAsync(id);
if (user == null)
{
return NotFound($"User with ID {id} not found");
}
return Ok(user);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while getting user {UserId}", id);
return StatusCode(500, "An error occurred while processing your request");
}
}
[HttpPost]
public async Task<ActionResult<UserDto>> CreateUser([FromBody] CreateUserDto createUserDto)
{
try
{
var user = await _userService.CreateAsync(createUserDto);
return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
}
catch (InvalidOperationException ex)
{
return BadRequest(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while creating user");
return StatusCode(500, "An error occurred while processing your request");
}
}
[HttpPut("{id}")]
public async Task<ActionResult<UserDto>> UpdateUser(int id, [FromBody] UpdateUserDto updateUserDto)
{
try
{
var user = await _userService.UpdateAsync(id, updateUserDto);
if (user == null)
{
return NotFound($"User with ID {id} not found");
}
return Ok(user);
}
catch (InvalidOperationException ex)
{
return BadRequest(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while updating user {UserId}", id);
return StatusCode(500, "An error occurred while processing your request");
}
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteUser(int id)
{
try
{
var result = await _userService.DeleteAsync(id);
if (!result)
{
return NotFound($"User with ID {id} not found");
}
return NoContent();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while deleting user {UserId}", id);
return StatusCode(500, "An error occurred while processing your request");
}
}
}`,
// Controllers/ProductsController.cs
'Controllers/ProductsController.cs': `using Microsoft.AspNetCore.Mvc;
using {{serviceName}}.Services;
using {{serviceName}}.DTOs;
namespace {{serviceName}}.Controllers;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductService _productService;
private readonly ILogger<ProductsController> _logger;
public ProductsController(IProductService productService, ILogger<ProductsController> logger)
{
_productService = productService;
_logger = logger;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<ProductDto>>> GetProducts()
{
try
{
var products = await _productService.GetAllAsync();
return Ok(products);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while getting products");
return StatusCode(500, "An error occurred while processing your request");
}
}
[HttpGet("category/{category}")]
public async Task<ActionResult<IEnumerable<ProductDto>>> GetProductsByCategory(string category)
{
try
{
var products = await _productService.GetByCategoryAsync(category);
return Ok(products);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while getting products by category {Category}", category);
return StatusCode(500, "An error occurred while processing your request");
}
}
[HttpGet("{id}")]
public async Task<ActionResult<ProductDto>> GetProduct(int id)
{
try
{
var product = await _productService.GetByIdAsync(id);
if (product == null)
{
return NotFound($"Product with ID {id} not found");
}
return Ok(product);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while getting product {ProductId}", id);
return StatusCode(500, "An error occurred while processing your request");
}
}
[HttpPost]
public async Task<ActionResult<ProductDto>> CreateProduct([FromBody] CreateProductDto createProductDto)
{
try
{
var product = await _productService.CreateAsync(createProductDto);
return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, product);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while creating product");
return StatusCode(500, "An error occurred while processing your request");
}
}
[HttpPut("{id}")]
public async Task<ActionResult<ProductDto>> UpdateProduct(int id, [FromBody] UpdateProductDto updateProductDto)
{
try
{
var product = await _productService.UpdateAsync(id, updateProductDto);
if (product == null)
{
return NotFound($"Product with ID {id} not found");
}
return Ok(product);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while updating product {ProductId}", id);
return StatusCode(500, "An error occurred while processing your request");
}
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteProduct(int id)
{
try
{
var result = await _productService.DeleteAsync(id);
if (!result)
{
return NotFound($"Product with ID {id} not found");
}
return NoContent();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while deleting product {ProductId}", id);
return StatusCode(500, "An error occurred while processing your request");
}
}
}`,
// Tests/TestFixtures/DatabaseFixture.cs
'Tests/TestFixtures/DatabaseFixture.cs': `using Microsoft.EntityFrameworkCore;
using {{serviceName}}.Data;
namespace {{serviceName}}.Tests.TestFixtures;
public class DatabaseFixture : IDisposable
{
public ApplicationDbContext Context { get; private set; }
public DatabaseFixture()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase($"TestDb_{Guid.NewGuid()}")
.Options;
Context = new ApplicationDbContext(options);
Context.Database.EnsureCreated();
}
public void Dispose()
{
Context.Dispose();
}
}`,
// Tests/TestFixtures/TestDataGenerator.cs
'Tests/TestFixtures/TestDataGenerator.cs': `using Bogus;
using {{serviceName}}.Models;
using {{serviceName}}.DTOs;
namespace {{serviceName}}.Tests.TestFixtures;
public static class TestDataGenerator
{
public static Faker<User> UserFaker => new Faker<User>()
.RuleFor(u => u.Id, f => f.Random.Int(1, 1000))
.RuleFor(u => u.Name, f => f.Person.FullName)
.RuleFor(u => u.Email, f => f.Person.Email)
.RuleFor(u => u.PasswordHash, f => BCrypt.Net.BCrypt.HashPassword("TestPassword123"))
.RuleFor(u => u.PhoneNumber, f => f.Phone.PhoneNumber())
.RuleFor(u => u.DateOfBirth, f => f.Person.DateOfBirth.AddYears(-f.Random.Int(18, 65)))
.RuleFor(u => u.Address, f => f.Address.FullAddress())
.RuleFor(u => u.City, f => f.Address.City())
.RuleFor(u => u.Country, f => f.Address.Country())
.RuleFor(u => u.IsActive, f => f.Random.Bool(0.9f))
.RuleFor(u => u.CreatedAt, f => f.Date.Past())
.RuleFor(u => u.UpdatedAt, f => f.Date.Recent());
public static Faker<CreateUserDto> CreateUserDtoFaker => new Faker<CreateUserDto>()
.RuleFor(u => u.Name, f => f.Person.FullName)
.RuleFor(u => u.Email, f => f.Person.Email)
.RuleFor(u => u.Password, f => "TestPassword123")
.RuleFor(u => u.PhoneNumber, f => f.Phone.PhoneNumber())
.RuleFor(u => u.DateOfBirth, f => f.Person.DateOfBirth.AddYears(-f.Random.Int(18, 65)))
.RuleFor(u => u.Address, f => f.Address.FullAddress())
.RuleFor(u => u.City, f => f.Address.City())
.RuleFor(u => u.Country, f => f.Address.Country());
public static Faker<UpdateUserDto> UpdateUserDtoFaker => new Faker<UpdateUserDto>()
.RuleFor(u => u.Name, f => f.Person.FullName)
.RuleFor(u => u.Email, f => f.Person.Email)
.RuleFor(u => u.PhoneNumber, f => f.Phone.PhoneNumber())
.RuleFor(u => u.DateOfBirth, f => f.Person.DateOfBirth.AddYears(-f.Random.Int(18, 65)))
.RuleFor(u => u.Address, f => f.Address.FullAddress())
.RuleFor(u => u.City, f => f.Address.City())
.RuleFor(u => u.Country, f => f.Address.Country())
.RuleFor(u => u.IsActive, f => f.Random.Bool(0.9f));
public static Faker<Product> ProductFaker => new Faker<Product>()
.RuleFor(p => p.Id, f => f.Random.Int(1, 1000))
.RuleFor(p => p.Name, f => f.Commerce.ProductName())
.RuleFor(p => p.Description, f => f.Commerce.ProductDescription())
.RuleFor(p => p.Price, f => f.Random.Decimal(1, 1000))
.RuleFor(p => p.Category, f => f.Commerce.Categories(1)[0])
.RuleFor(p => p.Brand, f => f.Company.CompanyName())
.RuleFor(p => p.StockQuantity, f => f.Random.Int(0, 100))
.RuleFor(p => p.ImageUrl, f => f.Image.PicsumUrl())
.RuleFor(p => p.IsActive, f => f.Random.Bool(0.9f))
.RuleFor(p => p.CreatedAt, f => f.Date.Past())
.RuleFor(p => p.UpdatedAt, f => f.Date.Recent());
public static Faker<CreateProductDto> CreateProductDtoFaker => new Faker<CreateProductDto>()
.RuleFor(p => p.Name, f => f.Commerce.ProductName())
.RuleFor(p => p.Description, f => f.Commerce.ProductDescription())
.RuleFor(p => p.Price, f => f.Random.Decimal(1, 1000))
.RuleFor(p => p.Category, f => f.Commerce.Categories(1)[0])
.RuleFor(p => p.Brand, f => f.Company.CompanyName())
.RuleFor(p => p.StockQuantity, f => f.Random.Int(0, 100))
.RuleFor(p => p.ImageUrl, f => f.Image.PicsumUrl());
public static Faker<UpdateProductDto> UpdateProductDtoFaker => new Faker<UpdateProductDto>()
.RuleFor(p => p.Name, f => f.Commerce.ProductName())
.RuleFor(p => p.Description, f => f.Commerce.ProductDescription())
.RuleFor(p => p.Price, f => f.Random.Decimal(1, 1000))
.RuleFor(p => p.Category, f => f.Commerce.Categories(1)[0])
.RuleFor(p => p.Brand, f => f.Company.CompanyName())
.RuleFor(p => p.StockQuantity, f => f.Random.Int(0, 100))
.RuleFor(p => p.ImageUrl, f => f.Image.PicsumUrl())
.RuleFor(p => p.IsActive, f => f.Random.Bool(0.9f));
}`,
// Tests/Services/UserServiceTests.cs
'Tests/Services/UserServiceTests.cs': `using Xunit;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Moq;
using AutoMapper;
using {{serviceName}}.Services;
using {{serviceName}}.Data;
using {{serviceName}}.Models;
using {{serviceName}}.DTOs;
using {{serviceName}}.Profiles;
using {{serviceName}}.Tests.TestFixtures;
namespace {{serviceName}}.Tests.Services;
public class UserServiceTests : IClassFixture<DatabaseFixture>
{
private readonly DatabaseFixture _fixture;
private readonly Mock<ILogger<UserService>> _loggerMock;
private readonly IMapper _mapper;
private readonly UserService _userService;
public UserServiceTests(DatabaseFixture fixture)
{
_fixture = fixture;
_loggerMock = new Mock<ILogger<UserService>>();
var config = new MapperConfiguration(cfg => cfg.AddProfile<UserProfile>());
_mapper = config.CreateMapper();
_userService = new UserService(_fixture.Context, _mapper, _loggerMock.Object);
}
[Fact]
public async Task GetByIdAsync_WithValidId_ShouldReturnUserDto()
{
// Arrange
var user = TestDataGenerator.UserFaker.Generate();
_fixture.Context.Users.Add(user);
await _fixture.Context.SaveChangesAsync();
// Act
var result = await _userService.GetByIdAsync(user.Id);
// Assert
result.Should().NotBeNull();
result!.Id.Should().Be(user.Id);
result.Name.Should().Be(user.Name);
result.Email.Should().Be(user.Email);
}
[Fact]
public async Task GetByIdAsync_WithInvalidId_ShouldReturnNull()
{
// Arrange
var invalidId = 99999;
// Act
var result = await _userService.GetByIdAsync(invalidId);
// Assert
result.Should().BeNull();
}
[Fact]
public async Task GetByEmailAsync_WithValidEmail_ShouldReturnUserDto()
{
// Arrange
var user = TestDataGenerator.UserFaker.Generate();
_fixture.Context.Users.Add(user);
await _fixture.Context.SaveChangesAsync();
// Act
var result = await _userService.GetByEmailAsync(user.Email);
// Assert
result.Should().NotBeNull();
result!.Email.Should().Be(user.Email);
result.Name.Should().Be(user.Name);
}
[Fact]
public async Task GetByEmailAsync_WithInvalidEmail_ShouldReturnNull()
{
// Arrange
var invalidEmail = "nonexistent@example.com";
// Act
var result = await _userService.GetByEmailAsync(invalidEmail);
// Assert
result.Should().BeNull();
}
[Fact]
public async Task GetAllAsync_ShouldReturnAllUsers()
{
// Arrange
var users = TestDataGenerator.UserFaker.Generate(3);
_fixture.Context.Users.AddRange(users);
await _fixture.Context.SaveChangesAsync();
// Act
var result = await _userService.GetAllAsync();
// Assert
result.Should().NotBeEmpty();
result.Should().HaveCountGreaterOrEqualTo(3);
}
[Fact]
public async Task CreateAsync_WithValidData_ShouldCreateUser()
{
// Arrange
var createUserDto = TestDataGenerator.CreateUserDtoFaker.Generate();
// Act
var result = await _userService.CreateAsync(createUserDto);
// Assert
result.Should().NotBeNull();
result.Name.Should().Be(createUserDto.Name);
result.Email.Should().Be(createUserDto.Email);
result.Id.Should().BeGreaterThan(0);
// Verify user exists in database
var userInDb = await _fixture.Context.Users.FindAsync(result.Id);
userInDb.Should().NotBeNull();
userInDb!.PasswordHash.Should().NotBeEmpty();
BCrypt.Net.BCrypt.Verify(createUserDto.Password, userInDb.PasswordHash).Should().BeTrue();
}
[Fact]
public async Task CreateAsync_WithExistingEmail_ShouldThrowException()
{
// Arrange
var existingUser = TestDataGenerator.UserFaker.Generate();
_fixture.Context.Users.Add(existingUser);
await _fixture.Context.SaveChangesAsync();
var createUserDto = TestDataGenerator.CreateUserDtoFaker.Generate();
createUserDto.Email = existingUser.Email;
// Act & Assert
await _userService.Invoking(s => s.CreateAsync(createUserDto))
.Should().ThrowAsync<InvalidOperationException>()
.WithMessage($"User with email {existingUser.Email} already exists");
}
[Fact]
public async Task UpdateAsync_WithValidData_ShouldUpdateUser()
{
// Arrange
var user = TestDataGenerator.UserFaker.Generate();
_fixture.Context.Users.Add(user);
await _fixture.Context.SaveChangesAsync();
var updateUserDto = TestDataGenerator.UpdateUserDtoFaker.Generate();
// Act
var result = await _userService.Update