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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -368,3 +368,4 @@ FodyWeavers.xsd
*/**/bin/Release
*/**/obj/Debug
*/**/obj/Release
*/**/Migrations
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using api_cinema_challenge.DataModels;
using api_cinema_challenge.Enums;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using api_cinema_challenge.Data;
using api_cinema_challenge.DataTransfer.Requests;
using api_cinema_challenge.DataTransfer.Response;
using api_cinema_challenge.Services;

namespace api_cinema_challenge.Controllers
{

[ApiController]
[Route("/api/[controller]")]
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,
});
}
}
}
14 changes: 14 additions & 0 deletions api-cinema-challenge/api-cinema-challenge/DTOs/CustomerGet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;

namespace api_cinema_challenge.DTOs
{
public class CustomerGet
{
public int Id { get; set; }
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; }
}
}
12 changes: 12 additions & 0 deletions api-cinema-challenge/api-cinema-challenge/DTOs/CustomerPost.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;

namespace api_cinema_challenge.DTOs
{
public class CustomerPost
{
public string Name { get; set; }
[EmailAddress]
public string Email { get; set; }
public string Phone { get; set; }
}
}
12 changes: 12 additions & 0 deletions api-cinema-challenge/api-cinema-challenge/DTOs/CustomerPut.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;

namespace api_cinema_challenge.DTOs
{
public class CustomerPut
{
public string Name { get; set; }
[EmailAddress]
public string Email { get; set; }
public string Phone { get; set; }
}
}
38 changes: 38 additions & 0 deletions api-cinema-challenge/api-cinema-challenge/DTOs/MovieFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using api_cinema_challenge.Models;
using System.Reflection;

namespace api_cinema_challenge.DTOs
{
public static class MovieFactory
{
public static Movie NewMovie(MoviePost item)
{
if (item is null) return null;

return new Movie()
{
Title = item.Title,
Rating = item.Rating,
Description = item.Description,
RuntimeMins = item.RuntimeMins,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow,
};
}
public static MovieGet NewMovieGet(Movie item)
{
if (item is null) return null;

return new MovieGet()
{
Id = item.Id,
Title = item.Title,
Rating = item.Rating,
Description = item.Description,
RuntimeMins = item.RuntimeMins,
CreatedAt = item.CreatedAt,
UpdatedAt = item.UpdatedAt,
};
}
}
}
15 changes: 15 additions & 0 deletions api-cinema-challenge/api-cinema-challenge/DTOs/MovieGet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Http.HttpResults;

namespace api_cinema_challenge.DTOs
{
public class MovieGet
{
public int Id { get; set; }
public string Title { get; set; }
public string Rating { get; set; }
public string Description { get; set; }
public int RuntimeMins { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}
}
10 changes: 10 additions & 0 deletions api-cinema-challenge/api-cinema-challenge/DTOs/MoviePost.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace api_cinema_challenge.DTOs
{
public class MoviePost
{
public string Title { get; set; }
public string Rating { get; set; }
public string Description { get; set; }
public int RuntimeMins { get; set; }
}
}
12 changes: 12 additions & 0 deletions api-cinema-challenge/api-cinema-challenge/DTOs/MoviePut.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;

namespace api_cinema_challenge.DTOs
{
public class MoviePut
{
public string Title { get; set; }
public string Rating { get; set; }
public string Description { get; set; }
public int RuntimeMins { get; set; }
}
}
36 changes: 36 additions & 0 deletions api-cinema-challenge/api-cinema-challenge/DTOs/ScreeningFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using api_cinema_challenge.Models;

namespace api_cinema_challenge.DTOs
{
public static class ScreeningFactory
{
public static ScreeningGet NewScreeningGet(Screening item)
{
if (item == null) return null;

return new ScreeningGet()
{
Id = item.Id,
ScreenNumber = item.ScreenNumber,
Capacity = item.Capacity,
StartsAt = item.StartsAt,
CreatedAt = item.CreatedAt,
UpdatedAt = item.UpdatedAt,
};
}

public static Screening NewScreening(int movieId, ScreeningPost item)
{
if (item == null) return null;
return new Screening()
{
MovieId = movieId,
ScreenNumber = item.ScreenNumber,
Capacity = item.Capacity,
StartsAt = item.StartsAt,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow,
};
}
}
}
12 changes: 12 additions & 0 deletions api-cinema-challenge/api-cinema-challenge/DTOs/ScreeningGet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace api_cinema_challenge.DTOs
{
public class ScreeningGet
{
public int Id { get; set; }
public int ScreenNumber { get; set; }
public int Capacity { get; set; }
public DateTime StartsAt { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace api_cinema_challenge.DTOs
{
public class ScreeningPost
{
public int ScreenNumber { get; set; }
public int Capacity { get; set; }
public DateTime StartsAt { get; set; }
}
}
29 changes: 27 additions & 2 deletions api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
using Microsoft.EntityFrameworkCore;
using api_cinema_challenge.DataModels;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using api_cinema_challenge.Models;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;
using Newtonsoft.Json.Linq;

namespace api_cinema_challenge.Data
{
public class CinemaContext : DbContext
public class CinemaContext : IdentityUserContext<ApplicationUser>
{
private string _connectionString;
public CinemaContext(DbContextOptions<CinemaContext> options) : base(options)
Expand All @@ -20,7 +24,28 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
List<Customer> customers = new List<Customer>();
customers.Add(new Customer { Id = 1, Name = "Nigel", Email = "nigel@nigel.nigel", Phone = "+44729388192", CreatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc), UpdatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc) });
customers.Add(new Customer { Id = 2, Name = "Dave", Email = "dave@dave.dave", Phone = "+44729388180", CreatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc), UpdatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc) });
customers.Add(new Customer { Id = 3, Name = "Walter", Email = "walter@white.bb", Phone = "+44729383492", CreatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc), UpdatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc) });
modelBuilder.Entity<Customer>().HasData(customers);

List<Movie> movies = new List<Movie>();
movies.Add(new Movie {Id = 1, Title = "Dodgeball", Rating = "PG-13", Description = "Dodge this", RuntimeMins = 126, CreatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc), UpdatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc) });
movies.Add(new Movie {Id = 2, Title = "Dune: Part 3", Rating = "PG-100", Description = "Spice", RuntimeMins = 158, CreatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc), UpdatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc) });
movies.Add(new Movie { Id = 3, Title = "Return of the King", Rating = "PG-18", Description = "Epic", RuntimeMins = 240, CreatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc), UpdatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc) });
modelBuilder.Entity<Movie>().HasData(movies);

List<Screening> screenings = new List<Screening>();
screenings.Add(new Screening {Id = 1, MovieId = 2, ScreenNumber=1, Capacity=100, StartsAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc), CreatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc), UpdatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc) });
screenings.Add(new Screening {Id = 2, MovieId = 2, ScreenNumber=2, Capacity=150, StartsAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc), CreatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc), UpdatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc) });
screenings.Add(new Screening { Id = 3, MovieId = 3, ScreenNumber = 3, Capacity = 60, StartsAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc), CreatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc), UpdatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc) });
screenings.Add(new Screening { Id = 4, MovieId = 1, ScreenNumber = 4, Capacity = 700, StartsAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc), CreatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc), UpdatedAt = new DateTime(2025, 08, 25, 10, 00, 00, 00, DateTimeKind.Utc) });
modelBuilder.Entity<Screening>().HasData(screenings);
}

public DbSet<Customer> Customers { get; set; }
public DbSet<Movie> Movies { get; set; }
public DbSet<Screening> Screenings { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace api_cinema_challenge.DataTransfer.Requests;

public class AuthRequest
{
public string? Email { get; set; }
public string? Password { get; set; }

public bool IsValid()
{
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@


using System.ComponentModel.DataAnnotations;
using api_cinema_challenge.Enums;


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;
}
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; }
}
Loading