Add project files.

This commit is contained in:
Bangara Raju Kottedi 2026-01-19 15:48:56 +05:30
parent b41d842114
commit 703c08022b
17 changed files with 575 additions and 0 deletions

View File

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SampleApplication\SampleApplication.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,101 @@
using SampleApplication.Entities;
namespace SampleApplication.Tests
{
public class ShapeProcessTest
{
[Fact]
public void Rectangle_Calculation_IsCorrect()
{
var processor = new SampleApplication.ShapeProcessor();
var rectangle = new ShapeInput
{
Id = 1,
Type = ShapeType.Rectangle,
Dimensions = new[] { 10.0, 5.0 }
};
processor.AddShapes(new[] { rectangle });
var (area, perimeter) = processor.GetResult(rectangle.Id);
Assert.Equal(50.0, area, 2);
Assert.Equal(30.0, perimeter, 2);
}
[Fact]
public void Circle_Calculation_IsCorrect()
{
var processor = new SampleApplication.ShapeProcessor();
var circle = new ShapeInput
{
Id = 1,
Type = ShapeType.Circle,
Dimensions = new[] { 7.0 }
};
processor.AddShapes(new[] { circle });
var (area, perimeter) = processor.GetResult(circle.Id);
Assert.Equal(153.94, area, 2);
Assert.Equal(43.98, perimeter, 2);
}
[Fact]
public void Triangle_Calculation_IsCorrect()
{
var processor = new SampleApplication.ShapeProcessor();
var triangle = new ShapeInput
{
Id = 1,
Type = ShapeType.Triangle,
Dimensions = new[] { 3.0, 4.0 }
};
processor.AddShapes(new[] { triangle });
var (area, perimeter) = processor.GetResult(triangle.Id);
Assert.Equal(6.00, area, 2);
Assert.Equal(12.00, perimeter, 2);
}
[Fact]
public void Invalid_Dimensions_Throws()
{
var processor = new SampleApplication.ShapeProcessor();
Assert.Throws<ArgumentException>(() =>
processor.AddShapes(new[]
{
new ShapeInput
{
Id = 1,
Type = ShapeType.Rectangle,
Dimensions = new[] { -1.0, 2.0 }
}
})
);
}
[Fact]
public void Statistics_AreCorrect()
{
var processor = new SampleApplication.ShapeProcessor();
var shapes = new[]
{
new ShapeInput { Id = 1, Type = ShapeType.Rectangle, Dimensions = new[] { 2.0, 3.0 } },
new ShapeInput { Id = 2, Type = ShapeType.Circle, Dimensions = new[] { 3.0 } }
};
processor.AddShapes(shapes);
var (totalShapes, totalArea, maxPerimeterId) = processor.GetStatistics();
Assert.Equal(2, totalShapes);
Assert.Equal(34.27,totalArea, 2);
Assert.Equal(2, maxPerimeterId);
}
}
}

4
SampleApplication.slnx Normal file
View File

@ -0,0 +1,4 @@
<Solution>
<Project Path="SampleApplication.Tests/SampleApplication.Tests.csproj" />
<Project Path="SampleApplication/SampleApplication.csproj" />
</Solution>

View File

@ -0,0 +1,77 @@
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SampleApplication.Entities;
namespace SampleApplication.Controllers
{
[Route("api/shapes")]
public class ShapesController : ControllerBase
{
private readonly SampleDbContext _db;
private readonly ShapeProcessor _calculator;
public ShapesController(
SampleDbContext db,
ShapeProcessor calculator)
{
_db = db;
_calculator = calculator;
}
[HttpPost]
public IActionResult AddShapes(List<ShapeInput> shapes)
{
// TODO:
// - Validate
// - Ignore duplicate IDs
// - Save via EF Core
this._calculator.AddShapes(shapes);
this._db.Shapes.AddRange(shapes);
return Ok(shapes);
}
[HttpGet("{id}")]
public async Task<IActionResult> Get(int id)
{
// TODO:
var shape = await this._db.Shapes.Where(s => s.Id == id).FirstOrDefaultAsync();
return Ok(shape);
}
[HttpGet("stats")]
public IActionResult Stats()
{
// TODO:
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,13 @@
using Microsoft.OpenApi;
namespace SampleApplication.Entities
{
public class ShapeInput
{
public int Id { get; set; }
public ShapeType Type { get; set; }
public double[] Dimensions { get; set; }
}
}

View File

@ -0,0 +1,49 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using SampleApplication;
#nullable disable
namespace SampleApplication.Migrations
{
[DbContext(typeof(SampleDbContext))]
[Migration("20260119092501_InitialCreate")]
partial class InitialCreate
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("SampleApplication.Entities.ShapeInput", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.PrimitiveCollection<string>("Dimensions")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("Shapes");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace SampleApplication.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Shapes",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Type = table.Column<int>(type: "int", nullable: false),
Dimensions = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Shapes", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Shapes");
}
}
}

View File

@ -0,0 +1,46 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using SampleApplication;
#nullable disable
namespace SampleApplication.Migrations
{
[DbContext(typeof(SampleDbContext))]
partial class SampleDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("SampleApplication.Entities.ShapeInput", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.PrimitiveCollection<string>("Dimensions")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("Shapes");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,32 @@
using Microsoft.EntityFrameworkCore;
using SampleApplication;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddDbContext<SampleDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddSingleton<ShapeProcessor>();
builder.Services.AddControllers();
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

View File

@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5078",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7203;http://localhost:5078",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.2" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,6 @@
@SampleApplication_HostAddress = http://localhost:5078
GET {{SampleApplication_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@ -0,0 +1,13 @@
using Microsoft.EntityFrameworkCore;
namespace SampleApplication
{
public class SampleDbContext : DbContext
{
public SampleDbContext(DbContextOptions<SampleDbContext> options) : base(options)
{
}
public DbSet<Entities.ShapeInput> Shapes { get; set; }
}
}

View File

@ -0,0 +1,102 @@
using SampleApplication.Entities;
using System.Net.Http.Headers;
using System.Reflection.Metadata.Ecma335;
namespace SampleApplication
{
public class ShapeProcessor
{
public List<ShapeInput> shapes = new List<ShapeInput>();
public void AddShapes(IEnumerable<ShapeInput> shapes)
{
// TODO:
// - Ignore duplicate Ids
// - Validate dimensions
// - Store shapes in memory
foreach(var shape in shapes)
{
if (!ValidateDimensions(shape))
{
throw new ArgumentException("Invalid shape dimensions");
}
this.shapes.Add(shape);
}
}
public (double Area, double Perimeter) GetResult(int id)
{
// TODO:
// - Throw KeyNotFoundException if not found
// - Compute area & perimeter
if(!shapes.Any(s => s.Id == id))
{
throw new KeyNotFoundException("Shape not found");
}
var shape = shapes.First(s => s.Id == id);
return this.CalculateAreaPerimeter(shape);
}
public (int TotalShapes, double TotalArea, int MaxPerimeterId) GetStatistics()
{
// TODO:
var totalShapes = shapes.Count;
double totalArea = shapes.Sum(s => this.CalculateAreaPerimeter(s).area);
var maxPerimeterShape = shapes.OrderByDescending(s => this.CalculateAreaPerimeter(s).perimeter).FirstOrDefault();
return (totalShapes, totalArea, maxPerimeterShape != null ? maxPerimeterShape.Id : 0);
}
private bool ValidateDimensions(ShapeInput shape)
{
return shape.Type switch
{
ShapeType.Circle => shape.Dimensions.Length == 1 && shape.Dimensions[0] > 0,
ShapeType.Rectangle => shape.Dimensions.Length == 2 && shape.Dimensions.All(d => d > 0),
ShapeType.Triangle => shape.Dimensions.Length == 2,
_ => false,
};
}
private (double area, double perimeter) CalculateAreaPerimeter(ShapeInput shape)
{
var perimeter = shape.Type switch
{
ShapeType.Circle =>
2 * Math.PI * shape.Dimensions[0],
ShapeType.Rectangle =>
2 * (shape.Dimensions[0] + shape.Dimensions[1]),
ShapeType.Triangle => this.CalculateTrianglePerimeter(shape),
_ => throw new NotSupportedException("Shape type not supported"),
};
var area = shape.Type switch
{
ShapeType.Circle =>
Math.PI * shape.Dimensions[0] * shape.Dimensions[0],
ShapeType.Rectangle =>
shape.Dimensions[0] * shape.Dimensions[1],
ShapeType.Triangle =>
0.5 * shape.Dimensions[0] * shape.Dimensions[1],
_ => throw new NotSupportedException("Shape type not supported"),
};
return (area, perimeter);
}
private double CalculateTrianglePerimeter(ShapeInput shape)
{
double @base = shape.Dimensions[0];
double height = shape.Dimensions[1];
double hypotenuse = Math.Sqrt(@base * @base + height * height);
return @base + height + hypotenuse;
}
}
}

View File

@ -0,0 +1,9 @@
namespace SampleApplication
{
public enum ShapeType
{
Rectangle,
Circle,
Triangle
}
}

View File

@ -0,0 +1,11 @@
{
"ConnectionStrings": {
"DefaultConnection": "Server=KBR\\SQLSERVER2022;Database=SampleAppDb;User Id=sa;Password=pass@1234;TrustServerCertificate=True"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}