Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ловыгин Букина ФТ-302 #32

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
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
138 changes: 132 additions & 6 deletions WebApi/Controllers/UsersController.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using Game.Domain;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Newtonsoft.Json;
using WebApi.Models;

namespace WebApi.Controllers
Expand All @@ -9,21 +15,141 @@ namespace WebApi.Controllers
[ApiController]
public class UsersController : Controller
{
// Чтобы ASP.NET положил что-то в userRepository требуется конфигурация
public UsersController(IUserRepository userRepository)
private readonly IUserRepository userRepository;
private readonly IMapper mapper;
private readonly LinkGenerator linkGenerator;

public UsersController(IUserRepository userRepository, IMapper mapper, LinkGenerator linkGenerator)
{
this.userRepository = userRepository;
this.mapper = mapper;
this.linkGenerator = linkGenerator;
}

[HttpGet("{userId}")]
[HttpGet("{userId}", Name = nameof(GetUserById))]
[HttpHead("{userId}")]
[Produces("application/json", "application/xml")]
public ActionResult<UserDto> GetUserById([FromRoute] Guid userId)
{
throw new NotImplementedException();
var user = userRepository.FindById(userId);

if (user == null)
return NotFound();

return Ok(mapper.Map<UserDto>(user));
}

[HttpPost]
public IActionResult CreateUser([FromBody] object user)
[Produces("application/json", "application/xml")]
public IActionResult CreateUser([FromBody] UserToCreateDto userDto)
{
if (userDto == null)
return BadRequest();

if (!ModelState.IsValid)
return UnprocessableEntity(ModelState);

if (!userDto.Login.All(char.IsLetterOrDigit))
{
ModelState.AddModelError("Login", "Login should contain only letters or digits");
return UnprocessableEntity(ModelState);
}

var user = mapper.Map<UserEntity>(userDto);
user = userRepository.Insert(user);

return CreatedAtRoute(
nameof(GetUserById),
new { userId = user.Id },
user.Id);
}

[HttpPut("{userId}")]
[Produces("application/json", "application/xml")]
public IActionResult UpdateUser([FromBody] UserToUpdateDto userDto, [FromRoute] Guid userId)
{
if (userDto == null || userId == Guid.Empty)
return BadRequest();

if (!ModelState.IsValid)
return UnprocessableEntity(ModelState);

var user = mapper.Map(userDto, new UserEntity(userId));
userRepository.UpdateOrInsert(user, out var isInserted);

return isInserted ? CreatedAtRoute(nameof(GetUserById), new { userId = user.Id }, user.Id) : NoContent();
}

[HttpPatch("{userId}")]
[Produces("application/json", "application/xml")]
public IActionResult PartiallyUpdateUser([FromBody] JsonPatchDocument<UserToUpdateDto> patchDoc, [FromRoute] Guid userId)
{
if (patchDoc == null)
return BadRequest();

var user = userRepository.FindById(userId);
if (user == null)
return NotFound();

var userDto = mapper.Map<UserToUpdateDto>(user);
patchDoc.ApplyTo(userDto, ModelState);

if (!TryValidateModel(userDto))
return UnprocessableEntity(ModelState);

user = mapper.Map<UserEntity>(userDto);
userRepository.Update(user);
return NoContent();
}

[HttpDelete("{userId}")]
[Produces("application/json", "application/xml")]
public IActionResult DeleteUser([FromRoute] Guid userId)
{
var user = userRepository.FindById(userId);
if (user == null)
return NotFound();

userRepository.Delete(userId);
return NoContent();
}

[HttpGet(Name = nameof(GetUsers))]
[Produces("application/json", "application/xml")]
public ActionResult<IEnumerable<UserDto>> GetUsers([FromQuery] int? pageNumber, [FromQuery] int? pageSize)
{
var intPageNumber = pageNumber != null ? Math.Max(1, pageNumber!.Value) : 1;

var intPageSize = pageSize != null ? Math.Min(20, Math.Max(1, pageSize!.Value)) : 10;

var pageList = userRepository.GetPage(intPageNumber, intPageSize);

var paginationHeader = new
{
previousPageLink = pageList.HasPrevious ? GetUriUsers(intPageNumber - 1, intPageSize) : null,
nextPageLink = pageList.HasNext ? GetUriUsers(intPageNumber + 1, intPageSize) : null,
totalCount = pageList.TotalCount,
pageSize = pageList.PageSize,
currentPage = pageList.CurrentPage,
totalPages = pageList.TotalPages,
};

Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(paginationHeader));

return Ok(paginationHeader);
}

private string GetUriUsers(int pageNumber, int pageSize)
{
return linkGenerator.GetUriByRouteValues(HttpContext, nameof(GetUsers), new { pageSize, pageNumber });
}

[HttpOptions]
[Produces("application/json", "application/xml")]
public IActionResult Options()
{
throw new NotImplementedException();
Response.Headers.Add("Allow", "POST, GET, OPTIONS");
return Ok();
}
}
}
17 changes: 17 additions & 0 deletions WebApi/Models/UserToCreateDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace WebApi.Models
{
public class UserToCreateDto
{
[Required]
public string Login { get; set; }

[DefaultValue("John")]
public string FirstName { get; set; }

[DefaultValue("Doe")]
public string LastName { get; set; }
}
}
17 changes: 17 additions & 0 deletions WebApi/Models/UserToUpdateDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.ComponentModel.DataAnnotations;

namespace WebApi.Models
{
public class UserToUpdateDto
{
[Required]
[RegularExpression("^[0-9\\p{L}]*$", ErrorMessage = "Login should contain only letters or digits")]
public string Login { get; set; }

[Required]
public string FirstName { get; set; }

[Required]
public string LastName { get; set; }
}
}
38 changes: 34 additions & 4 deletions WebApi/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
using System;
using System.Reflection;
using AutoMapper;
using Game.Domain;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using WebApi.Models;

namespace WebApi
{
Expand All @@ -14,14 +22,36 @@ public Startup(IConfiguration configuration)

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.ConfigureApiBehaviorOptions(options => {
services.AddControllers(options =>
{
options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
options.ReturnHttpNotAcceptable = true;
options.RespectBrowserAcceptHeader = true;
})
.ConfigureApiBehaviorOptions(options =>
{
options.SuppressModelStateInvalidFilter = true;
options.SuppressMapClientErrors = true;
})
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
options.SerializerSettings.DefaultValueHandling = DefaultValueHandling.Populate;
});
services.AddSingleton<IUserRepository, InMemoryUserRepository>();
services.AddAutoMapper(cfg =>
{
cfg.CreateMap<UserEntity, UserDto>()
.ForMember(
dest => dest.FullName,
opt => opt.MapFrom(src => $"{src.LastName} {src.FirstName}")
);
cfg.CreateMap<UserToCreateDto, UserEntity>();
cfg.CreateMap<UserToUpdateDto, UserEntity>();
cfg.CreateMap<UserEntity, UserToUpdateDto>();
}, Array.Empty<Assembly>());
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand All @@ -39,4 +69,4 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
});
}
}
}
}