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
101 changes: 101 additions & 0 deletions exercise.wwwapi/Controllers/UsersController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using api_cinema_challenge.Data;
using api_cinema_challenge.DataTransfer.Requests;
using api_cinema_challenge.DataTransfer.Response;
using api_cinema_challenge.Enums;
using api_cinema_challenge.Models;
using api_cinema_challenge.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Data;

namespace exercise.wwwapi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly CinemaContext _context;
private readonly TokenService _tokenService;

public UsersController(UserManager<ApplicationUser> userManager, CinemaContext context,
TokenService tokenService, ILogger<UsersController> logger)
{
_userManager = userManager;
_context = context;
_tokenService = tokenService;
}


[HttpPost]
[Route("register")]
public async Task<IActionResult> Register(RegistrationRequest request)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

var result = await _userManager.CreateAsync(
new ApplicationUser { UserName = request.Username, Email = request.Email, Role = request.Role },
request.Password!
);

if (result.Succeeded)
{
request.Password = "";
return CreatedAtAction(nameof(Register), new { email = request.Email, role = Role.User }, request);
}

foreach (var error in result.Errors)
{
ModelState.AddModelError(error.Code, error.Description);
}

return BadRequest(ModelState);
}


[HttpPost]
[Route("login")]
public async Task<ActionResult<AuthResponse>> Authenticate([FromBody] AuthRequest request)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

var managedUser = await _userManager.FindByEmailAsync(request.Email!);

if (managedUser == null)
{
return BadRequest("Bad credentials");
}

var isPasswordValid = await _userManager.CheckPasswordAsync(managedUser, request.Password!);

if (!isPasswordValid)
{
return BadRequest("Bad credentials");
}

var userInDb = _context.Users.FirstOrDefault(u => u.Email == request.Email);

if (userInDb is null)
{
return Unauthorized();
}

var accessToken = _tokenService.CreateToken(userInDb);
await _context.SaveChangesAsync();

return Ok(new AuthResponse
{
Username = userInDb.UserName,
Email = userInDb.Email,
Token = accessToken,
});
}
}
}
11 changes: 11 additions & 0 deletions exercise.wwwapi/DTOs/CustomerPost.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace api_cinema_challenge.DTOs
{
public class CustomerPost
{
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
//public DateTime CreatedAt { get; set; }
//public DateTime UpdatedAt { get; set; }
}
}
9 changes: 9 additions & 0 deletions exercise.wwwapi/DTOs/CustomerPut.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace api_cinema_challenge.DTOs
{
public class CustomerPut
{
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
}
}
72 changes: 72 additions & 0 deletions exercise.wwwapi/Data/CinemaContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using api_cinema_challenge.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq;

namespace api_cinema_challenge.Data
{
public class CinemaContext : IdentityUserContext<ApplicationUser>
{
private string _connectionString;
public CinemaContext(DbContextOptions<CinemaContext> options) : base(options)
{
var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
_connectionString = configuration.GetValue<string>("ConnectionStrings:DefaultConnectionString")!;
this.Database.EnsureCreated();
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseNpgsql(_connectionString);
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Customers
Customer customer = new Customer
{
Id = 1,
Name = "Jonatan",
Email = "jonnabr@hotmail.com",
Phone = "+4793277670",
CreatedAt = new DateTime(2025, 01, 20, 12, 00, 00, DateTimeKind.Utc),
UpdatedAt = new DateTime(2025, 01, 21, 15, 00, 00, DateTimeKind.Utc)
};

Customer customer2 = new Customer
{
Id = 2,
Name = "Isabel",
Email = "Isabel@hotmail.com",
Phone = "+4792088620",
CreatedAt = new DateTime(2025, 01, 20, 12, 00, 00, DateTimeKind.Utc),
UpdatedAt = new DateTime(2025, 01, 21, 15, 00, 00, DateTimeKind.Utc)
};

Customer customer3 = new Customer
{
Id = 3,
Name = "Marius",
Email = "marius@hotmail.com",
Phone = "+4798765432",
CreatedAt = new DateTime(2025, 01, 19, 12, 00, 00, DateTimeKind.Utc),
UpdatedAt = new DateTime(2025, 01, 21, 17, 00, 00, DateTimeKind.Utc)
};

Customer customer4 = new Customer
{
Id = 4,
Name = "Emma",
Email = "emma@hotmail.com",
Phone = "+4791234567",
CreatedAt = new DateTime(2025, 01, 18, 12, 00, 00, DateTimeKind.Utc),
UpdatedAt = new DateTime(2025, 01, 21, 18, 00, 00, DateTimeKind.Utc)
};

modelBuilder.Entity<Customer>().HasData([customer, customer2, customer3, customer4]);
base.OnModelCreating(modelBuilder);
}

public DbSet<Customer> Customers { get; set; }
}
}
13 changes: 13 additions & 0 deletions exercise.wwwapi/DataTransfer/Requests/AuthRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace api_cinema_challenge.DataTransfer.Requests
{
public class AuthRequest
{
public string? Email { get; set; }
public string? Password { get; set; }

public bool IsValid()
{
return true;
}
}
}
19 changes: 19 additions & 0 deletions exercise.wwwapi/DataTransfer/Requests/RegistrationRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using api_cinema_challenge.Enums;
using System.ComponentModel.DataAnnotations;

namespace api_cinema_challenge.DataTransfer.Requests
{
public class RegistrationRequest
{
[Required]
public string? Email { get; set; }

[Required]
public string? Username { get { return this.Email; } set { } }

[Required]
public string? Password { get; set; }

public Role Role { get; set; } = Role.User;
}
}
9 changes: 9 additions & 0 deletions exercise.wwwapi/DataTransfer/Response/AuthResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace api_cinema_challenge.DataTransfer.Response
{
public class AuthResponse
{
public string? Username { get; set; }
public string? Email { get; set; }
public string? Token { get; set; }
}
}
123 changes: 123 additions & 0 deletions exercise.wwwapi/Endpoints/CustomerEndpoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
using api_cinema_challenge.DTOs;
using api_cinema_challenge.Models;
using api_cinema_challenge.Repository;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Runtime.CompilerServices;

namespace api_cinema_challenge.Endpoints
{
public static class CustomerEndpoint
{
public static void ConfigureCustomerEndpoint(this WebApplication app)
{
var customerGroup = app.MapGroup("customers/").RequireAuthorization();
customerGroup.MapGet("/", GetCustomers);
customerGroup.MapGet("/{id}", GetCustomerById);
customerGroup.MapPost("", CreateCustomer);
customerGroup.MapPut("", UpdateCustomer);
customerGroup.MapDelete("", DeleteCustomer);
}


[ProducesResponseType(StatusCodes.Status200OK)]
public static async Task<IResult> GetCustomers(IRepository repository)
{
var entities = await repository.GetCustomers();
List<Customer> result = new List<Customer>();
foreach (var entity in entities)
{
result.Add(entity);
}
return TypedResults.Ok(new
{
status = "success",
data = result
});
}

[ProducesResponseType(StatusCodes.Status200OK)]
public static async Task<IResult> GetCustomerById(IRepository repository, int id)
{
string statusString = "success";
var entity = await repository.GetCustomerById(id);
if (entity == null) statusString = "NotFound";

return TypedResults.Ok(new
{
status = statusString,
data = entity
});
}

[ProducesResponseType(StatusCodes.Status201Created)]
public static async Task<IResult> CreateCustomer(IRepository repository, CustomerPost model)
{
Customer customer = new Customer();
customer.Name = model.Name;
customer.Email = model.Email;
customer.Phone = model.Phone;
customer.CreatedAt = DateTime.UtcNow;
customer.UpdatedAt = DateTime.UtcNow;

try
{
var entity = await repository.CreateCustomer(customer);
return TypedResults.Created($"", new
{
status = "success",
data = entity
});
}
catch (Exception e)
{
return TypedResults.Created($"", new
{
status = "failed",
data = new Customer()
});
}
}

[ProducesResponseType(StatusCodes.Status200OK)]
public static async Task<IResult> UpdateCustomer(IRepository repository, int id, CustomerPut model)
{
var customer = await repository.GetCustomerById(id);
//if (entity == null) return TypedResults.NotFound(new {Error = $"Did not find a customer with id '{id}'."});
if (customer == null)
{
return TypedResults.Ok(new
{
status = "NotFound",
data = customer
});
}

if (model.Name != null) customer.Name = model.Name;
if (model.Email != null) customer.Email = model.Email;
if (model.Phone != null) customer.Phone = model.Phone;

customer.UpdatedAt = DateTime.UtcNow;
var result = await repository.UpdateCustomer(id, customer);

return TypedResults.Ok(new
{
status = "success",
data = result
});
}

public static async Task<IResult> DeleteCustomer(IRepository repository, int id)
{
var entity = await repository.DeleteCustomer(id);
string statusString = "success";
if (entity == null) statusString = "NotFound"; // return TypedResults.NotFound(new { Error = $"Did not find a customer with id '{id}'." });

return TypedResults.Ok(new
{
status = statusString,
data = entity
});
}
}
}
8 changes: 8 additions & 0 deletions exercise.wwwapi/Enums/Role.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace api_cinema_challenge.Enums
{
public enum Role
{
Admin,
User
}
}
10 changes: 10 additions & 0 deletions exercise.wwwapi/Models/ApplicationUser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using api_cinema_challenge.Enums;
using Microsoft.AspNetCore.Identity;

namespace api_cinema_challenge.Models
{
public class ApplicationUser : IdentityUser
{
public Role Role { get; set; }
}
}
Loading