diff --git a/WatchIt.Common/WatchIt.Common.Model/Series/Series.cs b/WatchIt.Common/WatchIt.Common.Model/Series/Series.cs new file mode 100644 index 0000000..08978de --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Series/Series.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace WatchIt.Common.Model.Series; + +public class Series : Media.Media +{ + [JsonPropertyName("has_ended")] + public bool HasEnded { get; set; } +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Series/SeriesQueryParameters.cs b/WatchIt.Common/WatchIt.Common.Model/Series/SeriesQueryParameters.cs new file mode 100644 index 0000000..d8797be --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Series/SeriesQueryParameters.cs @@ -0,0 +1,62 @@ +using Microsoft.AspNetCore.Mvc; +using WatchIt.Common.Query; + +namespace WatchIt.Common.Model.Series; + +public class SeriesQueryParameters : QueryParameters +{ + #region PROPERTIES + + [FromQuery(Name = "title")] + public string? Title { get; set; } + + [FromQuery(Name = "original_title")] + public string? OriginalTitle { get; set; } + + [FromQuery(Name = "description")] + public string? Description { get; set; } + + [FromQuery(Name = "release_date")] + public DateOnly? ReleaseDate { get; set; } + + [FromQuery(Name = "release_date_from")] + public DateOnly? ReleaseDateFrom { get; set; } + + [FromQuery(Name = "release_date_to")] + public DateOnly? ReleaseDateTo { get; set; } + + [FromQuery(Name = "length")] + public short? Length { get; set; } + + [FromQuery(Name = "length_from")] + public short? LengthFrom { get; set; } + + [FromQuery(Name = "length_to")] + public short? LengthTo { get; set; } + + [FromQuery(Name = "has_ended")] + public bool? HasEnded { get; set; } + + #endregion + + + + #region PUBLIC METHODS + + public override bool IsMeetingConditions(SeriesResponse item) => + ( + TestString(item.Title, Title) + && + TestString(item.OriginalTitle, OriginalTitle) + && + TestString(item.Description, Description) + && + TestComparable(item.ReleaseDate, ReleaseDate, ReleaseDateFrom, ReleaseDateTo) + && + TestComparable(item.Length, Length, LengthFrom, LengthTo) + && + TestBoolean(item.HasEnded, HasEnded) + ); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Series/SeriesRequest.cs b/WatchIt.Common/WatchIt.Common.Model/Series/SeriesRequest.cs new file mode 100644 index 0000000..baab8c2 --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Series/SeriesRequest.cs @@ -0,0 +1,59 @@ +using System.Diagnostics.CodeAnalysis; +using WatchIt.Database.Model.Media; + +namespace WatchIt.Common.Model.Series; + +public class SeriesRequest : Series +{ + #region CONSTRUCTORS + + [SetsRequiredMembers] + public SeriesRequest(SeriesResponse initData) + { + Title = initData.Title; + OriginalTitle = initData.OriginalTitle; + Description = initData.Description; + ReleaseDate = initData.ReleaseDate; + Length = initData.Length; + HasEnded = initData.HasEnded; + } + + public SeriesRequest() {} + + #endregion + + + + #region PUBLIC METHODS + + public Database.Model.Media.Media CreateMedia() => new Database.Model.Media.Media + { + Title = Title, + OriginalTitle = OriginalTitle, + Description = Description, + ReleaseDate = ReleaseDate, + Length = Length, + }; + + public MediaSeries CreateMediaSeries(long id) => new MediaSeries + { + Id = id, + HasEnded = HasEnded, + }; + + public void UpdateMedia(Database.Model.Media.Media media) + { + media.Title = Title; + media.OriginalTitle = OriginalTitle; + media.Description = Description; + media.ReleaseDate = ReleaseDate; + media.Length = Length; + } + + public void UpdateMediaSeries(MediaSeries mediaSeries) + { + mediaSeries.HasEnded = HasEnded; + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Series/SeriesResponse.cs b/WatchIt.Common/WatchIt.Common.Model/Series/SeriesResponse.cs new file mode 100644 index 0000000..2e44003 --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Series/SeriesResponse.cs @@ -0,0 +1,36 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; +using WatchIt.Database.Model.Media; + +namespace WatchIt.Common.Model.Series; + +public class SeriesResponse : Series +{ + #region PROPERTIES + + [JsonPropertyName("id")] + public long Id { get; set; } + + #endregion + + + + #region CONSTRUCTORS + + [JsonConstructor] + public SeriesResponse() {} + + [SetsRequiredMembers] + public SeriesResponse(MediaSeries mediaSeries) + { + Id = mediaSeries.Media.Id; + Title = mediaSeries.Media.Title; + OriginalTitle = mediaSeries.Media.OriginalTitle; + Description = mediaSeries.Media.Description; + ReleaseDate = mediaSeries.Media.ReleaseDate; + Length = mediaSeries.Media.Length; + HasEnded = mediaSeries.HasEnded; + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/MoviesController.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/MoviesController.cs index 5f87add..dbad719 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/MoviesController.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/MoviesController.cs @@ -57,7 +57,7 @@ public class MoviesController : ControllerBase [HttpPut("{id}")] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - [ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)] + [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/SeriesController.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/SeriesController.cs new file mode 100644 index 0000000..b5e82d5 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/SeriesController.cs @@ -0,0 +1,75 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using WatchIt.Common.Model.Series; +using WatchIt.Database; +using WatchIt.WebAPI.Services.Controllers.Series; + +namespace WatchIt.WebAPI.Controllers; + +[ApiController] +[Route("series")] +public class SeriesController : ControllerBase +{ + #region SERVICES + + private readonly ISeriesControllerService _seriesControllerService; + + #endregion + + + + #region CONSTRUCTORS + + public SeriesController(ISeriesControllerService seriesControllerService) + { + _seriesControllerService = seriesControllerService; + } + + #endregion + + + + #region PUBLIC METHODS + + #region Main + + [HttpGet] + [AllowAnonymous] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetAllSeries(SeriesQueryParameters query) => await _seriesControllerService.GetAllSeries(query); + + [HttpGet("{id}")] + [AllowAnonymous] + [ProducesResponseType(typeof(SeriesResponse), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetSeries([FromRoute] long id) => await _seriesControllerService.GetSeries(id); + + [HttpPost] + [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + [ProducesResponseType(typeof(SeriesResponse), StatusCodes.Status201Created)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + public async Task PostSeries([FromBody] SeriesRequest body) => await _seriesControllerService.PostSeries(body); + + [HttpPut("{id}")] + [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + public async Task PutSeries([FromRoute] long id, [FromBody]SeriesRequest body) => await _seriesControllerService.PutSeries(id, body); + + [HttpDelete("{id}")] + [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + [ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + public async Task DeleteSeries([FromRoute] long id) => await _seriesControllerService.DeleteSeries(id); + + #endregion + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/WatchIt.WebAPI.Controllers.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/WatchIt.WebAPI.Controllers.csproj index 12e6512e..1b46756 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/WatchIt.WebAPI.Controllers.csproj +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/WatchIt.WebAPI.Controllers.csproj @@ -17,6 +17,7 @@ + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/MoviesControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/MoviesControllerService.cs index cc5d69e..50256b0 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/MoviesControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/MoviesControllerService.cs @@ -31,6 +31,7 @@ public class MoviesControllerService : IMoviesControllerService #endregion + #region PUBLIC METHODS #region Main @@ -90,7 +91,7 @@ public class MoviesControllerService : IMoviesControllerService data.UpdateMedia(item.Media); await _database.SaveChangesAsync(); - return RequestResult.NoContent(); + return RequestResult.Ok(); } public async Task DeleteMovie(long id) diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Series/ISeriesControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Series/ISeriesControllerService.cs new file mode 100644 index 0000000..3c78ae5 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Series/ISeriesControllerService.cs @@ -0,0 +1,13 @@ +using WatchIt.Common.Model.Series; +using WatchIt.WebAPI.Services.Controllers.Common; + +namespace WatchIt.WebAPI.Services.Controllers.Series; + +public interface ISeriesControllerService +{ + Task GetAllSeries(SeriesQueryParameters query); + Task GetSeries(long id); + Task PostSeries(SeriesRequest data); + Task PutSeries(long id, SeriesRequest data); + Task DeleteSeries(long id); +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Series/SeriesControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Series/SeriesControllerService.cs new file mode 100644 index 0000000..5117aec --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Series/SeriesControllerService.cs @@ -0,0 +1,135 @@ +using Microsoft.EntityFrameworkCore; +using WatchIt.Common.Model.Series; +using WatchIt.Database; +using WatchIt.Database.Model.Media; +using WatchIt.WebAPI.Services.Controllers.Common; +using WatchIt.WebAPI.Services.Utility.User; + +namespace WatchIt.WebAPI.Services.Controllers.Series; + +public class SeriesControllerService : ISeriesControllerService +{ + #region SERVICES + + private readonly DatabaseContext _database; + + private readonly IUserService _userService; + + #endregion + + + + #region CONSTRUCTORS + + public SeriesControllerService(DatabaseContext database, IUserService userService) + { + _database = database; + + _userService = userService; + } + + #endregion + + + + #region PUBLIC METHODS + + public async Task GetAllSeries(SeriesQueryParameters query) + { + IEnumerable data = await _database.MediaSeries.Select(x => new SeriesResponse(x)).ToListAsync(); + data = query.PrepareData(data); + return RequestResult.Ok(data); + } + + public async Task GetSeries(long id) + { + MediaSeries? item = await _database.MediaSeries.FirstOrDefaultAsync(x => x.Id == id); + if (item is null) + { + return RequestResult.NotFound(); + } + + SeriesResponse data = new SeriesResponse(item); + return RequestResult.Ok(data); + } + + public async Task PostSeries(SeriesRequest data) + { + UserValidator validator = _userService.GetValidator().MustBeAdmin(); + if (!validator.IsValid) + { + return RequestResult.Forbidden(); + } + + Media mediaItem = data.CreateMedia(); + await _database.Media.AddAsync(mediaItem); + await _database.SaveChangesAsync(); + MediaSeries mediaSeriesItem = data.CreateMediaSeries(mediaItem.Id); + await _database.MediaSeries.AddAsync(mediaSeriesItem); + await _database.SaveChangesAsync(); + + return RequestResult.Created($"series/{mediaItem.Id}", new SeriesResponse(mediaSeriesItem)); + } + + public async Task PutSeries(long id, SeriesRequest data) + { + UserValidator validator = _userService.GetValidator().MustBeAdmin(); + if (!validator.IsValid) + { + return RequestResult.Forbidden(); + } + + MediaSeries? item = await _database.MediaSeries.FirstOrDefaultAsync(x => x.Id == id); + if (item is null) + { + return RequestResult.NotFound(); + } + + data.UpdateMediaSeries(item); + data.UpdateMedia(item.Media); + await _database.SaveChangesAsync(); + + return RequestResult.Ok(); + } + + public async Task DeleteSeries(long id) + { + UserValidator validator = _userService.GetValidator().MustBeAdmin(); + if (!validator.IsValid) + { + return RequestResult.Forbidden(); + } + + MediaSeries? item = await _database.MediaSeries.FirstOrDefaultAsync(x => x.Id == id); + if (item is null) + { + return RequestResult.NotFound(); + } + + _database.MediaSeries.Attach(item); + _database.MediaSeries.Remove(item); + _database.MediaPosterImages.Attach(item.Media.MediaPosterImage!); + _database.MediaPosterImages.Remove(item.Media.MediaPosterImage!); + _database.MediaPhotoImages.AttachRange(item.Media.MediaPhotoImages); + _database.MediaPhotoImages.RemoveRange(item.Media.MediaPhotoImages); + _database.MediaGenres.AttachRange(item.Media.MediaGenres); + _database.MediaGenres.RemoveRange(item.Media.MediaGenres); + _database.MediaProductionCountries.AttachRange(item.Media.MediaProductionCountries); + _database.MediaProductionCountries.RemoveRange(item.Media.MediaProductionCountries); + _database.PersonActorRoles.AttachRange(item.Media.PersonActorRoles); + _database.PersonActorRoles.RemoveRange(item.Media.PersonActorRoles); + _database.PersonCreatorRoles.AttachRange(item.Media.PersonCreatorRoles); + _database.PersonCreatorRoles.RemoveRange(item.Media.PersonCreatorRoles); + _database.RatingsMedia.AttachRange(item.Media.RatingMedia); + _database.RatingsMedia.RemoveRange(item.Media.RatingMedia); + _database.ViewCountsMedia.AttachRange(item.Media.ViewCountsMedia); + _database.ViewCountsMedia.RemoveRange(item.Media.ViewCountsMedia); + _database.Media.Attach(item.Media); + _database.Media.Remove(item.Media); + await _database.SaveChangesAsync(); + + return RequestResult.NoContent(); + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Series/WatchIt.WebAPI.Services.Controllers.Series.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Series/WatchIt.WebAPI.Services.Controllers.Series.csproj new file mode 100644 index 0000000..9e4603e --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Series/WatchIt.WebAPI.Services.Controllers.Series.csproj @@ -0,0 +1,16 @@ + + + + net8.0 + enable + enable + + + + + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI/Program.cs b/WatchIt.WebAPI/WatchIt.WebAPI/Program.cs index 215523c..751b900 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI/Program.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI/Program.cs @@ -13,6 +13,7 @@ using WatchIt.WebAPI.Services.Controllers.Accounts; using WatchIt.WebAPI.Services.Controllers.Genres; using WatchIt.WebAPI.Services.Controllers.Media; using WatchIt.WebAPI.Services.Controllers.Movies; +using WatchIt.WebAPI.Services.Controllers.Series; using WatchIt.WebAPI.Services.Utility.Configuration; using WatchIt.WebAPI.Services.Utility.Tokens; using WatchIt.WebAPI.Services.Utility.User; @@ -149,6 +150,7 @@ public static class Program builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); + builder.Services.AddTransient(); return builder; } diff --git a/WatchIt.sln b/WatchIt.sln index ecc00b2..75ac7f3 100644 --- a/WatchIt.sln +++ b/WatchIt.sln @@ -80,6 +80,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.Ut EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.Utility.Authentication", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.Utility\WatchIt.Website.Services.Utility.Authentication\WatchIt.Website.Services.Utility.Authentication.csproj", "{8720AECA-7084-429A-BA15-49B6622C1A32}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Series", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Series\WatchIt.WebAPI.Services.Controllers.Series.csproj", "{F8FCEF7B-72EA-48BC-AC68-E11244B067DD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -122,6 +124,7 @@ Global {2D62ED42-489E-4888-9479-E5A50A0E7D70} = {46E3711F-18BD-4004-AF53-EA4D8643D92F} {77FDAFDD-E97E-4059-A935-B563B6B0D555} = {130BC8F5-82CE-4EDF-AECB-21594DD41849} {8720AECA-7084-429A-BA15-49B6622C1A32} = {130BC8F5-82CE-4EDF-AECB-21594DD41849} + {F8FCEF7B-72EA-48BC-AC68-E11244B067DD} = {CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {23383776-1F27-4B5D-8C7C-57BFF75FA473}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -236,5 +239,9 @@ Global {8720AECA-7084-429A-BA15-49B6622C1A32}.Debug|Any CPU.Build.0 = Debug|Any CPU {8720AECA-7084-429A-BA15-49B6622C1A32}.Release|Any CPU.ActiveCfg = Release|Any CPU {8720AECA-7084-429A-BA15-49B6622C1A32}.Release|Any CPU.Build.0 = Release|Any CPU + {F8FCEF7B-72EA-48BC-AC68-E11244B067DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8FCEF7B-72EA-48BC-AC68-E11244B067DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8FCEF7B-72EA-48BC-AC68-E11244B067DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8FCEF7B-72EA-48BC-AC68-E11244B067DD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal