Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -337,4 +337,7 @@ ASALocalRun/
.localhistory/

# BeatPulse healthcheck temp database
healthchecksdb
healthchecksdb

#Logs
Log.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.Extensions.DependencyInjection;
using TodoApi.Controllers.Configuration.AutoMapper.AutoMapperProfiles;

namespace TodoApi.Controllers.Configuration.AutoMapper
{
public static class AutoMapperConfiguration
{
public static void Configure(IServiceCollection services)
{
services.AddAutoMapper(typeof(TodoItemApiDataProfile));
services.AddAutoMapper(typeof(TodoItemServiceDataProfile));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using AutoMapper;
using ApiData = TodoApiDto.Shared.Api.Data;
using ServiceData = TodoApiDto.Services.Data;

namespace TodoApi.Controllers.Configuration.AutoMapper.AutoMapperProfiles
{
public class TodoItemApiDataProfile : Profile
{
public TodoItemApiDataProfile()
{
CreateMap<ApiData.Requests.TodoItemCreateRequest, ServiceData.TodoItemCreateModel>();
CreateMap<ApiData.Requests.TodoItemUpdateRequest, ServiceData.TodoItemUpdateModel>();
CreateMap<ServiceData.TodoItem, ApiData.TodoItemViewModel>()
.ForMember(todoItem => todoItem.Id, opt => opt.MapFrom(todoItem => todoItem.Id.ObjectId));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using AutoMapper;
using TodoApiDto.StrongId;
using DbData = TodoApiDto.Repositories.Data;
using ServiceData = TodoApiDto.Services.Data;

namespace TodoApi.Controllers.Configuration.AutoMapper.AutoMapperProfiles
{
public class TodoItemServiceDataProfile : Profile
{
public TodoItemServiceDataProfile()
{
CreateMap<ServiceData.TodoItemCreateModel, DbData.TodoItemCreateModel>();

CreateMap<ServiceData.TodoItemUpdateModel, DbData.TodoItemUpdateModel>()
.ForMember(todoItem => todoItem.Id, opt => opt.MapFrom(todoItem => todoItem.Id.ObjectId));

CreateMap<DbData.TodoItem, ServiceData.TodoItem>()
.ForMember(todoItem => todoItem.Id, opt => opt.MapFrom(todoItem => new TodoId(todoItem.Id)));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Builder;
using TodoApi.Middleware;

namespace TodoApi.Controllers.Configuration.Middleware
{
public static class MiddlewareConfiguration
{
public static void Configure(IApplicationBuilder app)
{
app.UseMiddleware<ExceptionHandlerMiddleware>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.Extensions.DependencyInjection;
using TodoApiDto.Repositories;
using TodoApiDto.Repositories.Interfaces;

namespace TodoApi.Controllers.Configuration
{
public static class RepositoriesConfiguration
{
public static void Configure(IServiceCollection services)
{
services.AddTransient<ITodoRepository, TodoRepository>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.Extensions.DependencyInjection;
using TodoApiDto.Services;
using TodoApiDto.Services.Interfaces;

namespace TodoApi.Controllers.Configuration
{
public class ServicesConfiguration
{
public static void Configure(IServiceCollection services)
{
services.AddTransient<ITodoService, TodoService>();
}
}
}
159 changes: 159 additions & 0 deletions Applications/TodoApi/Controllers/TodoItemsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
using AutoMapper;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using TodoApiDto.Services.Interfaces;
using TodoApiDto.StrongId;
using ApiData = TodoApiDto.Shared.Api.Data;
using ServiceData = TodoApiDto.Services.Data;

namespace TodoApi.Controllers
{
[Route("api/[controller]"), ApiController, Produces("application/json")]
public class TodoItemsController : ControllerBase
{
private readonly ITodoService _todoService;
private readonly IMapper _mapper;

public TodoItemsController(
ITodoService todoService,
IMapper mapper)
{
_todoService = todoService;
_mapper = mapper;
}

/// <summary>
/// Get all TodoItems
/// </summary>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<IEnumerable<ApiData.TodoItemViewModel>>> GetTodoItems()
{
var serviceResult = await _todoService.GetAllAsync();

if (serviceResult.IsError)
{
return StatusCode(StatusCodes.Status500InternalServerError, new { message = "Internal server error" });
}

var apiData = _mapper.Map<IReadOnlyCollection<ApiData.TodoItemViewModel>>(serviceResult.Result);

return Ok(apiData);
}

/// <summary>
/// Get TodoItem by <paramref name="id"/>
/// </summary>
/// <param name="id">Record id</param>
[HttpGet("{id:long}")]
public async Task<ActionResult<ApiData.TodoItemViewModel>> GetTodoItem(long id)
{
var serviceResult = await _todoService.GetByIdAsync(new TodoId(id));

if (serviceResult.IsError)
{
return StatusCode(StatusCodes.Status500InternalServerError, new { message = "Internal server error" });
}

if (serviceResult.IsNotFound)
{
return NotFound();
}

var apiData = _mapper.Map<ApiData.TodoItemViewModel>(serviceResult.Result);

return Ok(apiData);
}

/// <summary>
/// Update TodoItem
/// </summary>
/// <param name="id">Record id</param>
/// <param name="request">Update request</param>
[HttpPut("{id:long}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> UpdateTodoItem(
long id,
[FromBody] ApiData.Requests.TodoItemUpdateRequest request)
{
if (id != request?.Id)
{
return BadRequest();
}

var serviceUpdateModel = _mapper.Map<ServiceData.TodoItemUpdateModel>(request);
var serviceResult = await _todoService.UpdateAsync(serviceUpdateModel);

if (serviceResult.IsError)
{
return StatusCode(StatusCodes.Status500InternalServerError, new { message = "Internal server error" });
}

if (serviceResult.IsNotFound)
{
return NotFound();
}

return Ok();
}

/// <summary>
/// Create new TodoItem
/// </summary>
/// <param name="request">Create request</param>
[HttpPost]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[ProducesResponseType(StatusCodes.Status201Created)]
public async Task<ActionResult<ApiData.TodoItemViewModel>> CreateTodoItem(
[FromBody] ApiData.Requests.TodoItemCreateRequest request)
{
if (request is null)
{
return BadRequest();
}

var serviceCreateModel = _mapper.Map<ServiceData.TodoItemCreateModel>(request);
var serviceResult = await _todoService.CreateAsync(serviceCreateModel);

if (serviceResult.IsError)
{
return StatusCode(StatusCodes.Status500InternalServerError, new { message = "Internal server error" });
}

var apiData = _mapper.Map<ApiData.TodoItemViewModel>(serviceResult.Result);

return CreatedAtAction(nameof(GetTodoItem), new { id = apiData.Id }, apiData);
}

/// <summary>
/// Delete TodoItem by <paramref name="id"/>
/// </summary>
/// <param name="id">Record id</param>
[HttpDelete("{id:long}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var serviceResult = await _todoService.RemoveAsync(new TodoId(id));

if (serviceResult.IsError)
{
return StatusCode(StatusCodes.Status500InternalServerError, new { message = "Internal server error" });
}

if (serviceResult.IsNotFound)
{
return NotFound();
}

return Ok();
}
}
}
51 changes: 51 additions & 0 deletions Applications/TodoApi/Middleware/ExceptionHandlerMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Net;
using System.Threading.Tasks;

namespace TodoApi.Middleware
{
public class ExceptionHandlerMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionHandlerMiddleware> _logger;

public ExceptionHandlerMiddleware(
RequestDelegate next,
ILogger<ExceptionHandlerMiddleware> logger)
{
_logger = logger;
_next = next;
}

public async Task Invoke(HttpContext context)
{
try
{
await _next.Invoke(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "Unhandled exception");

await HandleExceptionMessageAsync(context);
}
}

private static Task HandleExceptionMessageAsync(HttpContext context)
{
context.Response.ContentType = "application/json";
const int statusCode = (int)HttpStatusCode.InternalServerError;
var result = JsonConvert.SerializeObject(new
{
StatusCode = statusCode,
ErrorMessage = "Internal server error",
});
context.Response.ContentType = "application/json";
context.Response.StatusCode = statusCode;
return context.Response.WriteAsync(result);
}
}
}
30 changes: 30 additions & 0 deletions Applications/TodoApi/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Serilog;
using System.IO;

namespace TodoApi
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); })
.UseSerilog((context, services, configuration) =>
{
configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext()
.MinimumLevel.Information()
.WriteTo.File(Path.Combine(Directory.GetCurrentDirectory(), "Log.txt"));
});
}
}
}
27 changes: 27 additions & 0 deletions Applications/TodoApi/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:56416/",
"sslPort": 44331
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"TodoApiDTO": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:5001;http://localhost:5000"
}
}
}
Loading