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
8 changes: 8 additions & 0 deletions api-cinema-challenge/api-cinema-challenge/DTO/BookingDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
public class BookingDto
{
public int CustomerId { get; set; }
public int MovieId { get; set; }
public DateTime BookedAt { get; set; }
public string? CustomerName { get; set; }
public string? MovieTitle { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public class CreateBookingDto
{
public int CustomerId { get; set; }
public int MovieId { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
public class RegisterCustomerDto
{
public string Name { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public string Phonenumber { get; set; } = string.Empty;
}
32 changes: 30 additions & 2 deletions api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@ namespace api_cinema_challenge.Data
public class CinemaContext : DbContext
{
private string _connectionString;
public DbSet<Customer> Customers { get; set; } = null!;
public DbSet<Movie> Movies { get; set; } = null!;
public DbSet<Screening> Screenings { get; set; } = null!;
public DbSet<User> Users { get; set; } = null!;
public DbSet<Booking> Bookings { get; set; } = null!;


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)
Expand All @@ -20,7 +26,29 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Booking>()
.HasKey(b => new { b.CustomerId, b.MovieId });

modelBuilder.Entity<Customer>()
.Property(c => c.CreatedAt)
.HasDefaultValueSql("CURRENT_TIMESTAMP");

}
modelBuilder.Entity<Customer>()
.Property(c => c.UpdatedAt)
.HasDefaultValueSql("CURRENT_TIMESTAMP");

modelBuilder.Entity<Booking>()
.HasKey(b => new { b.CustomerId, b.MovieId });

modelBuilder.Entity<Booking>()
.HasOne(b => b.Customer)
.WithMany(c => c.Bookings)
.HasForeignKey(b => b.CustomerId);

modelBuilder.Entity<Booking>()
.HasOne(b => b.Movie)
.WithMany(m => m.Bookings)
.HasForeignKey(b => b.MovieId);
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions api-cinema-challenge/api-cinema-challenge/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src

# Copy csproj and restore dependencies
COPY *.csproj ./
RUN dotnet restore

# Copy everything else and build
COPY . .
RUN dotnet publish -c Release -o /app/publish

# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final
WORKDIR /app
COPY --from=build /app/publish .

# Expose port
EXPOSE 8080

# Run your app
ENTRYPOINT ["dotnet", "api-cinema-challenge.dll"]


# Create the image with
# sudo docker build -t dockerimage .

# Run a container
# sudo docker run -p 8080:8080 -e ASPNETCORE_URLS="http://+:8080" -e DOTNET_ENVIRONMENT=Development dockerimage
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using Microsoft.AspNetCore.Mvc;

public static class BookingEndpoints
{
public static void MapBookingEndpoints(this WebApplication app)
{
app.MapGet("/bookings", async ([FromServices] IBookingRepository repo) =>
{
var bookings = await repo.GetAllAsync();

var dtoList = bookings.Select(b => new BookingDto
{
CustomerId = b.CustomerId,
MovieId = b.MovieId,
BookedAt = b.BookedAt,
MovieTitle = b.Movie?.Title
});

return Results.Ok(dtoList);
})
.WithTags("Bookings");


app.MapPost("/bookings", async (CreateBookingDto dto, [FromServices] IBookingRepository repo) =>
{
var booking = new Booking
{
CustomerId = dto.CustomerId,
MovieId = dto.MovieId
};

var createdBooking = await repo.CreateAsync(booking);

var responseDto = new BookingDto
{
CustomerId = createdBooking.CustomerId,
MovieId = createdBooking.MovieId,
};


return Results.Created(
$"/bookings/{booking.CustomerId}/{booking.MovieId}",
responseDto
);
})
.WithTags("Bookings");


}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

public static class CustomerEndpoints
{
public static void MapCustomerEndpoints(this WebApplication app)
{
app.MapGet("/customers", [Authorize] async ([FromServices] ICustomerRepository repo) =>
{
var customers = await repo.GetAllAsync();
return Results.Ok(customers);
})
.WithTags("Customers");

app.MapGet("/customers/{id}", [Authorize] async (int id, [FromServices] ICustomerRepository repo) =>
{
var customer = await repo.GetByIdAsync(id);
return customer is not null ? Results.Ok(customer) : Results.NotFound();
})
.WithTags("Customers");

app.MapPost("/customers", [Authorize] async (Customer customer, [FromServices] ICustomerRepository repo) =>
{
var createdCustomer = await repo.CreateAsync(customer);
return Results.Created($"/customers/{createdCustomer.Id}", createdCustomer);
})
.WithTags("Customers");

app.MapPut("/customers/{id}", [Authorize] async (int id, Customer customer, [FromServices] ICustomerRepository repo) =>
{
var updatedCustomer = await repo.UpdateAsync(id, customer);
return updatedCustomer is not null ? Results.Ok(updatedCustomer) : Results.NotFound();
})
.WithTags("Customers");

app.MapDelete("/customers/{id}", [Authorize] async (int id, [FromServices] ICustomerRepository repo) =>
{
var deleted = await repo.DeleteAsync(id);
return deleted ? Results.Ok() : Results.NotFound();
})
.WithTags("Customers");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using api_cinema_challenge.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;

public static class LoginEndpoints
{
public static void MapLoginEndpoints(this WebApplication app, IConfiguration config)
{
var jwtSettings = config.GetSection("Jwt");

app.MapPost("/login", async (LoginRequest login, CinemaContext db) =>
{
var user = await db.Customers.SingleOrDefaultAsync(u => u.Name == login.Name);
if (user == null || user.Password != login.Password)
return Results.Unauthorized();

var key = Encoding.UTF8.GetBytes(jwtSettings.GetValue<string>("Key"));

var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new System.Security.Claims.ClaimsIdentity(new[]
{
new System.Security.Claims.Claim("id", user.Id.ToString())
}),

Expires = DateTime.UtcNow.AddMinutes(jwtSettings.GetValue<int>("ExpiryMinutes")),
Issuer = jwtSettings["Issuer"],
Audience = jwtSettings["Audience"],
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};


var token = tokenHandler.CreateToken(tokenDescriptor);
var jwt = tokenHandler.WriteToken(token);

return Results.Ok(new { Token = jwt });
})
.WithTags("Authentication");

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

public static class MovieEndpoints
{
public static void MapMovieEndpoints(this WebApplication app)
{
app.MapGet("/movies", [Authorize] async ([FromServices] IMovieRepository repo) =>
{
var movies = await repo.GetAllAsync();
return Results.Ok(movies);
})
.WithTags("Movies");

app.MapGet("/movies/{id}", [Authorize] async (int id, [FromServices] IMovieRepository repo) =>
{
var movie = await repo.GetByIdAsync(id);
return movie is not null ? Results.Ok(movie) : Results.NotFound();
})
.WithTags("Movies");

app.MapPost("/movies", [Authorize] async (Movie movie, [FromServices] IMovieRepository repo) =>
{
var createdMovie = await repo.CreateAsync(movie);
return Results.Created($"/movies/{createdMovie.Id}", createdMovie);
})
.WithTags("Movies");

app.MapPut("/movies/{id}", [Authorize] async (int id, Movie movie, [FromServices] IMovieRepository repo) =>
{
var updatedMovie = await repo.UpdateAsync(id, movie);
return updatedMovie is not null ? Results.Ok(updatedMovie) : Results.NotFound();
})
.WithTags("Movies");

app.MapDelete("/movies/{id}", [Authorize] async (int id, [FromServices] IMovieRepository repo) =>
{
var deleted = await repo.DeleteAsync(id);
return deleted ? Results.Ok() : Results.NotFound();
})
.WithTags("Movies");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using api_cinema_challenge.Data;
using Microsoft.AspNetCore.Mvc;

public static class RegisterEndpoints
{
public static void MapRegisterEndpoints(this WebApplication app)
{
app.MapPost("/register", async (RegisterCustomerDto dto, CinemaContext db) =>
{
var customer = new Customer
{
Name = dto.Name,
Email = dto.Email,
Password = dto.Password,
Phonenumber = dto.Phonenumber,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};

db.Customers.Add(customer);
await db.SaveChangesAsync();

return Results.Ok(new { customer.Id, customer.Name, customer.Email });
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

public static class ScreeningEndpoints
{
public static void MapScreeningEndpoints(this WebApplication app)
{
app.MapGet("/screenings", [Authorize] async ([FromServices] IScreeningRepository repo) =>
{
var screening = await repo.GetAllAsync();
return Results.Ok(screening);
})
.WithTags("Screenings");

app.MapPost("/screenings", [Authorize] async (Screening screening, [FromServices] IScreeningRepository repo) =>
{
var createdMovie = await repo.CreateAsync(screening);
return Results.Created($"/screenings/{createdMovie.Id}", createdMovie);
})
.WithTags("Screenings");
}
}
Loading