Skip to content

Commit

Permalink
feat: Add SQLServer database
Browse files Browse the repository at this point in the history
  • Loading branch information
bilby91 committed Nov 26, 2024
1 parent 6c79064 commit b1cd941
Show file tree
Hide file tree
Showing 18 changed files with 207 additions and 82 deletions.
13 changes: 13 additions & 0 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "9.0.0",
"commands": [
"dotnet-ef"
],
"rollForward": false
}
}
}
14 changes: 10 additions & 4 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
{
"name": "crunchloop-dotnet-interview",

// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/dotnet:0-7.0",
// Update the 'dockerComposeFile' list if you have more compose files or use different names.
// The .devcontainer/docker-compose.yml file contains any overrides you need/want to make.
"dockerComposeFile": ["docker-compose.yml"],

// Set the workspace folder
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
// The 'service' property is the name of the service for the container that VS Code should
// use. Update this value and .devcontainer/docker-compose.yml to the real service name.
"service": "app",

// The optional 'workspaceFolder' property is the path VS Code should open by default when
// connected. This is typically a file mount in .devcontainer/docker-compose.yml
"workspaceFolder": "/app",

// Features to add to the dev container. More info: https://containers.dev/features.
"features": {},
Expand Down
15 changes: 15 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
version: '3.8'

services:
app:
image: mcr.microsoft.com/devcontainers/dotnet:8.0
command: sleep infinity
depends_on:
- sqlserver
volumes:
- ..:/app
sqlserver:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
- ACCEPT_EULA=Y
- MSSQL_SA_PASSWORD=Password123
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v3
with:
dotnet-version: "7.0.100"
dotnet-version: "8.0"
- name: Install dependencies
run: dotnet restore
- name: Build
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

[![Open in Coder](https://dev.crunchloop.io/open-in-coder.svg)](https://dev.crunchloop.io/templates/fly-containers/workspace?param.Git%[email protected]:crunchloop/dotnet-interview.git)

This is a simple Todo List API built in .NET 7. This project is currently being used for .NET full-stack candidates.
This is a simple Todo List API built in .NET 8. This project is currently being used for .NET full-stack candidates.

## Database

The project comes with a devcontainer that provisions a SQL Server database. If you are not going to use the devcontainer, make sure to provision a SQL Server database and
update the connection string.

## Build

Expand Down
29 changes: 6 additions & 23 deletions TodoApi.Tests/Controllers/TodoListsControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ private DbContextOptions<TodoContext> DatabaseContextOptions()

private void PopulateDatabaseContext(TodoContext context)
{
context.TodoList.Add(new Models.TodoList { Id = 1, Name = "Task 1" });
context.TodoList.Add(new Models.TodoList { Id = 2, Name = "Task 2" });
context.TodoList.Add(new TodoList { Id = 1, Name = "Task 1" });
context.TodoList.Add(new TodoList { Id = 2, Name = "Task 2" });
context.SaveChanges();
}

Expand Down Expand Up @@ -60,22 +60,6 @@ public async Task GetTodoList_WhenCalled_ReturnsTodoListById()
}
}

[Fact]
public async Task PutTodoList_WhenTodoListIdDoesntMatch_ReturnsBadRequest()
{
using (var context = new TodoContext(DatabaseContextOptions()))
{
PopulateDatabaseContext(context);

var controller = new TodoListsController(context);

var todoList = await context.TodoList.Where(x => x.Id == 2).FirstAsync();
var result = await controller.PutTodoList(1, todoList);

Assert.IsType<BadRequestResult>(result);
}
}

[Fact]
public async Task PutTodoList_WhenTodoListDoesntExist_ReturnsBadRequest()
{
Expand All @@ -85,7 +69,7 @@ public async Task PutTodoList_WhenTodoListDoesntExist_ReturnsBadRequest()

var controller = new TodoListsController(context);

var result = await controller.PutTodoList(3, new TodoList { Id = 3});
var result = await controller.PutTodoList(3, new Dtos.UpdateTodoList { Name = "Task 3" });

Assert.IsType<NotFoundResult>(result);
}
Expand All @@ -101,9 +85,9 @@ public async Task PutTodoList_WhenCalled_UpdatesTheTodoList()
var controller = new TodoListsController(context);

var todoList = await context.TodoList.Where(x => x.Id == 2).FirstAsync();
var result = await controller.PutTodoList(todoList.Id, todoList);
var result = await controller.PutTodoList(todoList.Id, new Dtos.UpdateTodoList { Name = "Changed Task 2" });

Assert.IsType<NoContentResult>(result);
Assert.IsType<OkObjectResult>(result);
}
}

Expand All @@ -116,8 +100,7 @@ public async Task PostTodoList_WhenCalled_CreatesTodoList()

var controller = new TodoListsController(context);

var todoList = new TodoList { Name = "Task 3" };
var result = await controller.PostTodoList(todoList);
var result = await controller.PostTodoList(new Dtos.CreateTodoList { Name = "Task 3" });

Assert.IsType<CreatedAtActionResult>(result.Result);
Assert.Equal(
Expand Down
2 changes: 1 addition & 1 deletion TodoApi.Tests/TodoApi.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

Expand Down
52 changes: 12 additions & 40 deletions TodoApi/Controllers/TodoListsController.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Dtos;
using TodoApi.Models;

namespace TodoApi.Controllers
Expand All @@ -19,23 +20,13 @@ public TodoListsController(TodoContext context)
[HttpGet]
public async Task<ActionResult<IList<TodoList>>> GetTodoLists()
{
if (_context.TodoList == null)
{
return NotFound();
}

return Ok(await _context.TodoList.ToListAsync());
}

// GET: api/todolists/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoList>> GetTodoList(long id)
{
if (_context.TodoList == null)
{
return NotFound();
}

var todoList = await _context.TodoList.FindAsync(id);

if (todoList == null)
Expand All @@ -49,43 +40,28 @@ public async Task<ActionResult<TodoList>> GetTodoList(long id)
// PUT: api/todolists/5
// To protect from over-posting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPut("{id}")]
public async Task<ActionResult> PutTodoList(long id, TodoList todoList)
public async Task<ActionResult> PutTodoList(long id, UpdateTodoList payload)
{
if (id != todoList.Id)
{
return BadRequest();
}

_context.Entry(todoList).State = EntityState.Modified;
var todoList = await _context.TodoList.FindAsync(id);

try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
if (todoList == null)
{
if (!TodoListExists(id))
{
return NotFound();
}
else
{
throw;
}
return NotFound();
}

return NoContent();
todoList.Name = payload.Name;
await _context.SaveChangesAsync();

return Ok(todoList);
}

// POST: api/todolists
// To protect from over-posting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPost]
public async Task<ActionResult<TodoList>> PostTodoList(TodoList todoList)
public async Task<ActionResult<TodoList>> PostTodoList(CreateTodoList payload)
{
if (_context.TodoList == null)
{
return Problem("Entity set 'TodoContext.TodoList' is null.");
}
var todoList = new TodoList { Name = payload.Name };

_context.TodoList.Add(todoList);
await _context.SaveChangesAsync();

Expand All @@ -96,10 +72,6 @@ public async Task<ActionResult<TodoList>> PostTodoList(TodoList todoList)
[HttpDelete("{id}")]
public async Task<ActionResult> DeleteTodoList(long id)
{
if (_context.TodoList == null)
{
return NotFound();
}
var todoList = await _context.TodoList.FindAsync(id);
if (todoList == null)
{
Expand Down
6 changes: 6 additions & 0 deletions TodoApi/Dtos/CreateTodoList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace TodoApi.Dtos;

public class CreateTodoList
{
public required string Name { get; set; }
}
6 changes: 6 additions & 0 deletions TodoApi/Dtos/UpdateTodoList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace TodoApi.Dtos;

public class UpdateTodoList
{
public required string Name { get; set; }
}
45 changes: 45 additions & 0 deletions TodoApi/Migrations/20241126134649_CreateTodoLists.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions TodoApi/Migrations/20241126134649_CreateTodoLists.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace TodoApi.Migrations
{
/// <inheritdoc />
public partial class CreateTodoLists : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "TodoList",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_TodoList", x => x.Id);
});
}

/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "TodoList");
}
}
}
42 changes: 42 additions & 0 deletions TodoApi/Migrations/TodoContextModelSnapshot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

#nullable disable

namespace TodoApi.Migrations
{
[DbContext(typeof(TodoContext))]
partial class TodoContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128);

SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);

modelBuilder.Entity("TodoApi.Models.TodoList", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("TodoList");
});
#pragma warning restore 612, 618
}
}
}
2 changes: 1 addition & 1 deletion TodoApi/Models/TodoList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ namespace TodoApi.Models;
public class TodoList
{
public long Id { get; set; }
public string? Name { get; set; }
public required string Name { get; set; }
}
Loading

0 comments on commit b1cd941

Please sign in to comment.