initial commit
This commit is contained in:
88
CityInfo.API/Controllers/AuthenticationController.cs
Normal file
88
CityInfo.API/Controllers/AuthenticationController.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace CityInfo.API.Controllers
|
||||
{
|
||||
[Route("api/authentication")]
|
||||
[ApiController]
|
||||
public class AuthenticationController : ControllerBase
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public class AuthenticationRequestBody
|
||||
{
|
||||
public string? UserName { get; set; }
|
||||
public string? Password { get; set; }
|
||||
|
||||
}
|
||||
internal class CityInfoUser
|
||||
{
|
||||
public int UserId { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string City { get; set; }
|
||||
public CityInfoUser(
|
||||
int userId, string userName, string firstName, string lastName, string city)
|
||||
{
|
||||
UserId = userId;
|
||||
UserName = userName;
|
||||
FirstName = firstName;
|
||||
LastName = lastName;
|
||||
City = city;
|
||||
}
|
||||
}
|
||||
|
||||
public AuthenticationController(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
[HttpPost("authenticate")]
|
||||
public ActionResult<string> Authenticate(AuthenticationRequestBody authenticationRequestBody)
|
||||
{
|
||||
var user = ValidateUserCredentials(authenticationRequestBody.UserName, authenticationRequestBody.Password);
|
||||
if (user == null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
var securityKey = new SymmetricSecurityKey(Convert.FromBase64String(_configuration["Authentication:SecretForKey"]));
|
||||
var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var claimsForToken = new List<Claim>
|
||||
{
|
||||
new Claim("sub", user.UserId.ToString()),
|
||||
new Claim("given_name", user.FirstName),
|
||||
new Claim("family_name", user.LastName),
|
||||
new Claim("city", user.City)
|
||||
};
|
||||
|
||||
var jwtSecurityToken = new JwtSecurityToken(
|
||||
_configuration["Authentication:Issuer"],
|
||||
_configuration["Authentication:Audience"],
|
||||
claimsForToken,
|
||||
DateTime.UtcNow,
|
||||
DateTime.UtcNow.AddHours(1),
|
||||
signingCredentials);
|
||||
|
||||
var tokenToReturn = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
|
||||
|
||||
return Ok(tokenToReturn);
|
||||
}
|
||||
|
||||
private CityInfoUser ValidateUserCredentials(string? userName, string? password)
|
||||
{
|
||||
return new CityInfoUser(
|
||||
1,
|
||||
userName ?? "",
|
||||
"Nathan",
|
||||
"Pire",
|
||||
"Charleroi");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
63
CityInfo.API/Controllers/CitiesController.cs
Normal file
63
CityInfo.API/Controllers/CitiesController.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using AutoMapper;
|
||||
using CityInfo.API.Models;
|
||||
using CityInfo.API.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace CityInfo.API.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("api/cities")]
|
||||
public class CitiesController: ControllerBase
|
||||
{
|
||||
private readonly ICityInfoRepository _cityInfoRepository;
|
||||
private readonly IMapper _mapper;
|
||||
const int maximumPageSize = 20;
|
||||
|
||||
public CitiesController(
|
||||
ICityInfoRepository cityInfoRepository,
|
||||
IMapper mapper)
|
||||
{
|
||||
_cityInfoRepository = cityInfoRepository ?? throw new ArgumentNullException(nameof(cityInfoRepository));
|
||||
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
|
||||
}
|
||||
[HttpGet()]
|
||||
public async Task<ActionResult<IEnumerable<CityWithoutPointsOfInterestDto>>> GetCities(
|
||||
string? name, string? searchQuery, int pageNumber = 1, int pageSize = 10)
|
||||
{
|
||||
if(pageSize > maximumPageSize)
|
||||
{
|
||||
pageSize = maximumPageSize;
|
||||
}
|
||||
|
||||
var (cityEntities, paginationMetaData) = await _cityInfoRepository
|
||||
.GetCitiesAsync(name, searchQuery,pageNumber, pageSize);
|
||||
|
||||
Response.Headers.Append("X-Pagination", JsonSerializer.Serialize(paginationMetaData));
|
||||
|
||||
return Ok(_mapper.Map<IEnumerable<CityWithoutPointsOfInterestDto>>(cityEntities));
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetCity(int id, bool includePointsOfInterest = false)
|
||||
{
|
||||
var city = await _cityInfoRepository.GetCityAsync(id, includePointsOfInterest);
|
||||
|
||||
if (city == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (includePointsOfInterest)
|
||||
{
|
||||
return Ok(_mapper.Map<CityDto>(city));
|
||||
}
|
||||
|
||||
return Ok(_mapper.Map<CityWithoutPointsOfInterestDto>(city));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
54
CityInfo.API/Controllers/FilesController.cs
Normal file
54
CityInfo.API/Controllers/FilesController.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
|
||||
namespace CityInfo.API.Controllers
|
||||
{
|
||||
[Route("api/files")]
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
public class FilesController : ControllerBase
|
||||
{
|
||||
private readonly FileExtensionContentTypeProvider _fileExtensionContentTypeProvide;
|
||||
public FilesController(
|
||||
FileExtensionContentTypeProvider fileExtensionContentTypeProvider)
|
||||
{
|
||||
_fileExtensionContentTypeProvide = fileExtensionContentTypeProvider
|
||||
?? throw new System.ArgumentNullException(
|
||||
nameof(FileExtensionContentTypeProvider));
|
||||
}
|
||||
[HttpGet("{fileId}")]
|
||||
public ActionResult GetFile(string fileId)
|
||||
{
|
||||
var pathToFile = "email_template.pdf";
|
||||
if (!System.IO.File.Exists(pathToFile))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
if (!_fileExtensionContentTypeProvide
|
||||
.TryGetContentType(pathToFile, out var contentType))
|
||||
{
|
||||
contentType = "application/octet-stream";
|
||||
}
|
||||
var bytes = System.IO.File.ReadAllBytes(pathToFile);
|
||||
return File(bytes, contentType , Path.GetFileName(pathToFile));
|
||||
}
|
||||
[HttpPost]
|
||||
public async Task<ActionResult> CreateFile(IFormFile file)
|
||||
{
|
||||
if(file.Length == 0 || file.Length > 20971520 || file.ContentType != "application/pdf")
|
||||
{
|
||||
return BadRequest("No file or an invalid one has been inputted.");
|
||||
}
|
||||
var path = Path.Combine(
|
||||
Directory.GetCurrentDirectory(),
|
||||
$"uploaded_file_{Guid.NewGuid()}.pdf");
|
||||
using (var stream = new FileStream(path, FileMode.Create))
|
||||
{
|
||||
await file.CopyToAsync(stream);
|
||||
}
|
||||
return Ok("Your file has been uploaded successfully.");
|
||||
}
|
||||
}
|
||||
}
|
||||
183
CityInfo.API/Controllers/PointsOfInterestController.cs
Normal file
183
CityInfo.API/Controllers/PointsOfInterestController.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using AutoMapper;
|
||||
using CityInfo.API.Entities;
|
||||
using CityInfo.API.Models;
|
||||
using CityInfo.API.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.JsonPatch;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CityInfo.API.Controllers
|
||||
{
|
||||
[Route("api/cities/{cityId}/pointsofinterest/", Name = "GetPointsOfInterest")]
|
||||
[Authorize(Policy = "MustBeFromCharleroi")]
|
||||
[ApiController]
|
||||
public class PointsOfInterestController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<PointsOfInterestController> _logger;
|
||||
private readonly IMailService _mailService;
|
||||
private readonly ICityInfoRepository _cityInfoRepository;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public PointsOfInterestController(
|
||||
ILogger<PointsOfInterestController> logger,
|
||||
IMailService mailService,
|
||||
ICityInfoRepository cityInfoRepository,
|
||||
IMapper mapper)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_mailService = mailService ?? throw new ArgumentNullException(nameof(mailService));
|
||||
_cityInfoRepository = cityInfoRepository ?? throw new ArgumentNullException(nameof(cityInfoRepository));
|
||||
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IEnumerable<PointOfInterestDto>>> GetPointsOfInterest(int cityId)
|
||||
{
|
||||
//throw new Exception("pipi");
|
||||
try
|
||||
{
|
||||
//throw new Exception("caca prout");
|
||||
var cityName = User.Claims.FirstOrDefault(claim => claim.Type == "city")?.Value;
|
||||
if (!await _cityInfoRepository.CityNameMatchesCityId(cityName, cityId))
|
||||
{
|
||||
return Forbid();
|
||||
}
|
||||
|
||||
if(!await _cityInfoRepository.CityExistAsync(cityId))
|
||||
{
|
||||
_logger.LogInformation($"City with Id {cityId} wasn't found when accessing points of interest.");
|
||||
return NotFound();
|
||||
}
|
||||
var pointsOfInterest = await _cityInfoRepository.GetPointsOfinterestForCityAsync(cityId);
|
||||
return Ok(_mapper.Map<IEnumerable<PointOfInterestDto>>(pointsOfInterest));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogCritical($"Exception while getting points of interest for city with id {cityId}", ex);
|
||||
return StatusCode(500, "A problem happened while handling your request.");
|
||||
}
|
||||
|
||||
}
|
||||
[HttpGet("{pointOfInterestId}", Name = "GetPointOfInterest")]
|
||||
public async Task<ActionResult<PointOfInterestDto>> GetPointOfInterest(int cityId, int pointOfInterestId)
|
||||
{
|
||||
if (!await _cityInfoRepository.CityExistAsync(cityId))
|
||||
{
|
||||
_logger.LogInformation($"City with Id {cityId} wasn't found when accessing point of interest.");
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var pointOfInterest = await _cityInfoRepository.GetPointOfInterestForCityAsync(cityId, pointOfInterestId);
|
||||
|
||||
if (pointOfInterest == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return Ok(_mapper.Map<PointOfInterestDto>(pointOfInterest));
|
||||
}
|
||||
|
||||
[HttpPost(Name = "CreatePointOfInterest")]
|
||||
public async Task<ActionResult<PointOfInterestDto>> CreatePointOfInterest(
|
||||
int cityId,
|
||||
PointOfInterestForCreationDto pointOfInterest)
|
||||
{
|
||||
if (!await _cityInfoRepository.CityExistAsync(cityId))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var finalPointOfInterest = _mapper.Map<PointOfInterest>(pointOfInterest);
|
||||
await _cityInfoRepository.CreatePointOfInterestForCityAsync(cityId, finalPointOfInterest);
|
||||
|
||||
await _cityInfoRepository.SaveChangesAsync();
|
||||
var createdPointOfInterestToReturn = _mapper.Map<PointOfInterestDto>(finalPointOfInterest);
|
||||
|
||||
return CreatedAtRoute("GetPointOfInterest",
|
||||
new
|
||||
{
|
||||
cityId = cityId,
|
||||
pointOfInterestId = createdPointOfInterestToReturn.Id
|
||||
},
|
||||
createdPointOfInterestToReturn);
|
||||
}
|
||||
[HttpPut("{pointOfInterestId}", Name = "UpdatePointOfInterest")]
|
||||
public async Task<ActionResult> UpdatePointOfInterest(
|
||||
int cityId,
|
||||
int pointOfInterestId,
|
||||
PointOfInterestForUpdateDto pointOfInterest)
|
||||
{
|
||||
if (!await _cityInfoRepository.CityExistAsync(cityId))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
var pointofInterestEntity = await _cityInfoRepository.GetPointOfInterestForCityAsync(cityId, pointOfInterestId);
|
||||
|
||||
if (pointofInterestEntity == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
_mapper.Map(pointOfInterest, pointofInterestEntity);
|
||||
await _cityInfoRepository.SaveChangesAsync();
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPatch("{pointOfInterestId}", Name = "PartiallyUpdatePointOfInterest")]
|
||||
public async Task<ActionResult> PartiallyUpdatePointOfInterest(
|
||||
int cityId,
|
||||
int pointOfInterestId,
|
||||
JsonPatchDocument<PointOfInterestForUpdateDto> patchDocument)
|
||||
{
|
||||
if (!await _cityInfoRepository.CityExistAsync(cityId))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var pointofInterestEntity = await _cityInfoRepository.GetPointOfInterestForCityAsync(cityId, pointOfInterestId);
|
||||
|
||||
if (pointofInterestEntity == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var pointOfInterestToPatch = _mapper.Map<PointOfInterestForUpdateDto>(pointofInterestEntity);
|
||||
|
||||
patchDocument.ApplyTo(pointOfInterestToPatch, ModelState);
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
if (!TryValidateModel(pointOfInterestToPatch))
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
_mapper.Map(pointOfInterestToPatch, pointofInterestEntity);
|
||||
await _cityInfoRepository.SaveChangesAsync();
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
[HttpDelete("{pointOfInterestId}")]
|
||||
public async Task<ActionResult> DeletePointOfInterest(
|
||||
int cityId,
|
||||
int pointOfInterestId)
|
||||
{
|
||||
if (!await _cityInfoRepository.CityExistAsync(cityId))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var pointofInterestEntity = await _cityInfoRepository.GetPointOfInterestForCityAsync(cityId, pointOfInterestId);
|
||||
|
||||
if (pointofInterestEntity == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
_cityInfoRepository.DeletePointOfInterest(pointofInterestEntity);
|
||||
await _cityInfoRepository.SaveChangesAsync();
|
||||
_mailService.Send("Point of interest deleted",
|
||||
$"Point of Interest {pointofInterestEntity.Name} with Id {pointofInterestEntity.Id} was deleted.");
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CityInfo.API.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class WeatherForecastController : ControllerBase
|
||||
{
|
||||
private static readonly string[] Summaries =
|
||||
[
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
];
|
||||
|
||||
[HttpGet(Name = "GetWeatherForecast")]
|
||||
public IEnumerable<WeatherForecast> Get()
|
||||
{
|
||||
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
||||
{
|
||||
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||
TemperatureC = Random.Shared.Next(-20, 55),
|
||||
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user