From c662c48e595c114d6132acf9b6bc4c655c39d4be Mon Sep 17 00:00:00 2001 From: haraldnilsen Date: Tue, 19 Sep 2023 09:48:59 +0200 Subject: [PATCH 01/10] :construction: working on get-endpoint Co-authored-by: Sindre Kjelsrud --- backend/Controllers/MovieController.cs | 17 ++++------ .../Controllers/WeatherForecastController.cs | 32 ------------------- 2 files changed, 6 insertions(+), 43 deletions(-) delete mode 100644 backend/Controllers/WeatherForecastController.cs diff --git a/backend/Controllers/MovieController.cs b/backend/Controllers/MovieController.cs index b58c0a8..c1befae 100644 --- a/backend/Controllers/MovieController.cs +++ b/backend/Controllers/MovieController.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Mvc; +using Microsoft.OpenApi.Writers; namespace backend.Controllers; @@ -6,24 +7,18 @@ namespace backend.Controllers; [Route("[controller]")] public class MovieController: ControllerBase { + private readonly MovieDbContext _context; private readonly ILogger _logger; - public MovieController(ILogger logger) + public MovieController(ILogger logger, MovieDbContext context) { _logger = logger; + _context = context; } [HttpGet(Name = "GetMovie")] - public IEnumerable Get() + public IEnumerable Get() { - return Enumerable.Range(1, 5).Select(index => new Movie - { - Title = "Title", - Year = "Year", - imdbID = "imdbID", - Type = "Type", - Poster = "Poster" - }) - .ToArray(); + return _context.Movies.ToList(); } } \ No newline at end of file diff --git a/backend/Controllers/WeatherForecastController.cs b/backend/Controllers/WeatherForecastController.cs deleted file mode 100644 index 44d1739..0000000 --- a/backend/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace backend.Controllers; - -[ApiController] -[Route("[controller]")] -public class WeatherForecastController : ControllerBase -{ - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet(Name = "GetWeatherForecast")] - public IEnumerable 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(); - } -} From f2d00947c86b121433ea2f5a9213ca735b49637f Mon Sep 17 00:00:00 2001 From: haraldnilsen Date: Tue, 19 Sep 2023 10:14:35 +0200 Subject: [PATCH 02/10] :goal_net: added error handling for get-endpoint Co-authored-by: Sindre Kjelsrud --- backend/Controllers/MovieController.cs | 27 +++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/backend/Controllers/MovieController.cs b/backend/Controllers/MovieController.cs index c1befae..2dc8e4e 100644 --- a/backend/Controllers/MovieController.cs +++ b/backend/Controllers/MovieController.cs @@ -16,9 +16,30 @@ public class MovieController: ControllerBase _context = context; } - [HttpGet(Name = "GetMovie")] - public IEnumerable Get() + [HttpGet(Name = "GetMovies")] + public ActionResult> Get([FromQuery]int pageNumber = 1, [FromQuery] int pageSize = 5) { - return _context.Movies.ToList(); + try + { + var movies = _context.Movies.ToList(); + return Ok(movies); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error fetching movies"); + return StatusCode(500, "Internal server error"); + } } + + // [HttpPost(Name = "PostMovie")] + // public ActionResult<> Post([FromQuery] string movieTitle) { + // try + // { + + // } + // catch (Exception ex) + // { + + // } + // } } \ No newline at end of file From 6ef2a4f8afc3c3c80da1445a8d4b64ff1673dd22 Mon Sep 17 00:00:00 2001 From: Sindre Kjelsrud Date: Tue, 19 Sep 2023 11:14:59 +0200 Subject: [PATCH 03/10] :sparkles: introducing pagination in the api! Co-authored-by: haraldnilsen --- backend/Controllers/MovieController.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/Controllers/MovieController.cs b/backend/Controllers/MovieController.cs index 2dc8e4e..32b77db 100644 --- a/backend/Controllers/MovieController.cs +++ b/backend/Controllers/MovieController.cs @@ -22,7 +22,14 @@ public class MovieController: ControllerBase try { var movies = _context.Movies.ToList(); - return Ok(movies); + + var totalMovies = movies.Count; + var totalPages = Math.Ceiling((double)totalMovies / pageSize); + + IEnumerable resultSkip = movies.Skip(pageSize * (pageNumber - 1)); + IEnumerable resultTake = resultSkip.Take(pageSize); + + return Ok(resultTake); } catch (Exception ex) { From a2557d2201bf7c5e7dd5ba44f59695072c13f843 Mon Sep 17 00:00:00 2001 From: Sindre Kjelsrud Date: Tue, 19 Sep 2023 11:21:13 +0200 Subject: [PATCH 04/10] :goal_net: catching 'No Content' error Co-authored-by: haraldnilsen --- backend/Controllers/MovieController.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/Controllers/MovieController.cs b/backend/Controllers/MovieController.cs index 32b77db..032d649 100644 --- a/backend/Controllers/MovieController.cs +++ b/backend/Controllers/MovieController.cs @@ -29,6 +29,10 @@ public class MovieController: ControllerBase IEnumerable resultSkip = movies.Skip(pageSize * (pageNumber - 1)); IEnumerable resultTake = resultSkip.Take(pageSize); + if (resultTake.ToList().Count == 0) { + return StatusCode(204, "No Content"); + } + return Ok(resultTake); } catch (Exception ex) From 942154e9773041b359872d8ab1ee78c9d0b7a9f3 Mon Sep 17 00:00:00 2001 From: Sindre Kjelsrud Date: Tue, 19 Sep 2023 11:58:43 +0200 Subject: [PATCH 05/10] :sparkles: introducing filtering to get Co-authored-by: haraldnilsen --- backend/Controllers/MovieController.cs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/backend/Controllers/MovieController.cs b/backend/Controllers/MovieController.cs index 032d649..133640e 100644 --- a/backend/Controllers/MovieController.cs +++ b/backend/Controllers/MovieController.cs @@ -17,13 +17,30 @@ public class MovieController: ControllerBase } [HttpGet(Name = "GetMovies")] - public ActionResult> Get([FromQuery]int pageNumber = 1, [FromQuery] int pageSize = 5) - { + public ActionResult> Get( + [FromQuery] string? s, + [FromQuery] string? type, + [FromQuery] string? y, + [FromQuery] int pageNumber = 1, + [FromQuery] int pageSize = 5 + ) { try { - var movies = _context.Movies.ToList(); + if (s == "") { + return StatusCode(400, "Bad Request"); + } - var totalMovies = movies.Count; + var movies = _context.Movies.Where(m => m.Title.Contains(s.ToLower())); + + if (type != null) { + movies = movies.Where(m => m.Type == type); + } + + if (y != null) { + movies = movies.Where(m => m.Year == y); + } + + var totalMovies = movies.Count(); var totalPages = Math.Ceiling((double)totalMovies / pageSize); IEnumerable resultSkip = movies.Skip(pageSize * (pageNumber - 1)); From d5a587f9bd6225806202615780a416de917fb541 Mon Sep 17 00:00:00 2001 From: haraldnilsen Date: Tue, 19 Sep 2023 13:01:53 +0200 Subject: [PATCH 06/10] :memo: created openapi swagger file Co-authored-by: Sindre Kjelsrud --- backend/openapi.yaml | 196 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 backend/openapi.yaml diff --git a/backend/openapi.yaml b/backend/openapi.yaml new file mode 100644 index 0000000..81bb8aa --- /dev/null +++ b/backend/openapi.yaml @@ -0,0 +1,196 @@ +swagger: '2.0' +info: + description: 'This API is made for our task for WA.works.' + version: '1.0' + title: Cinemateket API + license: + name: Github-repository + url: https://github.com/SindreKjelsrud/Cinemateket/ +host: github.com +basePath: '/SindreKjelsrud/Cinemateket/' +tags: + - name: Search Parameter + description: e.g. ?s=title +schemes: + - http + - https +paths: + '/?s': + get: + tags: + - Search Parameter + summary: Returns an array of results for a given title + operationId: titleSearch + parameters: + - name: s + in: query + description: Title of movie or series + required: true + type: string + - name: y + in: query + description: Year of release + required: false + type: integer + - name: type + in: query + description: Return movie or series + required: false + type: string + enum: + - movie + - series + - name: sort + in: query + description: Choose sorted order to return + required: false + type: string + enum: + - titleasc + - titledesc + - yearasc + - yeardesc + - name: pageNumber + in: query + description: Page number to return + required: false + type: integer + - name: pageSize + in: query + description: Size of pages to return + type: integer + responses: + '200': + description: Successful operation + '204': + description: No content + '400': + description: Bad request + '500': + description: Internal server error + post: + tags: + - Search Parameter + summary: Inserts a new movie to the database + operationId: titleSearch + parameters: + - name: s + in: query + description: Title of movie or series + required: true + type: string + - name: y + in: query + description: Year of release + required: false + type: integer + - name: type + in: query + description: Return movie or series + required: false + type: string + enum: + - movie + - series + - name: pageNumber + in: query + description: Page number to return + required: false + type: integer + - name: pageSize + in: query + description: Size of pages to return + type: integer + responses: + '200': + description: Successful operation + '204': + description: No content + '400': + description: Bad request + '500': + description: Internal server error + put: + tags: + - Search Parameter + summary: Make changes to an existing movie + operationId: titleSearch + parameters: + - name: s + in: query + description: Title of movie or series + required: true + type: string + - name: y + in: query + description: Year of release + required: false + type: integer + - name: type + in: query + description: Return movie or series + required: false + type: string + enum: + - movie + - series + - name: pageNumber + in: query + description: Page number to return + required: false + type: integer + - name: pageSize + in: query + description: Size of pages to return + type: integer + responses: + '200': + description: Successful operation + '204': + description: No content + '400': + description: Bad request + '500': + description: Internal server error + delete: + tags: + - Search Parameter + summary: Deletes a movie with a given title or imdbID + operationId: titleSearch + parameters: + - name: s + in: query + description: Title of movie or series + required: true + type: string + - name: y + in: query + description: Year of release + required: false + type: integer + - name: type + in: query + description: Return movie or series + required: false + type: string + enum: + - movie + - series + - name: pageNumber + in: query + description: Page number to return + required: false + type: integer + - name: pageSize + in: query + description: Size of pages to return + type: integer + responses: + '200': + description: Successful operation + '204': + description: No content + '400': + description: Bad request + '500': + description: Internal server error From 4f063495f2ee37c785a72cec993a120dad474aac Mon Sep 17 00:00:00 2001 From: haraldnilsen Date: Tue, 19 Sep 2023 13:43:21 +0200 Subject: [PATCH 07/10] :safety_vest: added validators for query-params Co-authored-by: Sindre Kjelsrud --- backend/Util/Validators.cs | 56 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 backend/Util/Validators.cs diff --git a/backend/Util/Validators.cs b/backend/Util/Validators.cs new file mode 100644 index 0000000..77b9a57 --- /dev/null +++ b/backend/Util/Validators.cs @@ -0,0 +1,56 @@ +namespace backend; + +public static class QueryParameterValidators +{ + private static readonly string[] sortTypes = new[] + { + "titleasc", "titledesc", "yearasc", "yeardesc" + }; + + private static readonly string[] movieTypes = new[] + { + "movie", "series", "episode" + }; + + + // Assuming `s` is a non-nullable parameter and can be any string + // If there are specific requirements for `s`, additional validation can be added + public static bool IsValidS(string s) + { + return !string.IsNullOrWhiteSpace(s); + } + + // Assuming `type` can be any string but you might want to limit it to specific values + // If there are predefined types, you can replace the body with something like: return new List { "type1", "type2" }.Contains(type); + public static bool IsValidType(string? type) + { + return string.IsNullOrWhiteSpace(type) || movieTypes.Contains(type.ToLower()); + } + + // For `year`, assuming it's a 4 digit representation of a year + public static bool IsValidYear(string? year) + { + if (string.IsNullOrWhiteSpace(year)) return true; + + return int.TryParse(year, out int parsedYear) && parsedYear > 1900 && parsedYear <= DateTime.Now.Year; + } + + // Assuming `sort` can be any string but you might want to limit it to specific values + // If there are predefined sorts, you can replace the body with something like: return new List { "asc", "desc" }.Contains(sort); + public static bool IsValidSort(string? sort) + { + return string.IsNullOrWhiteSpace(sort) || sortTypes.Contains(sort.ToLower()); + } + + // Assuming `pageNumber` should be 1 or more + public static bool IsValidPageNumber(int pageNumber) + { + return pageNumber > 0; + } + + // Assuming `pageSize` should be between 1 to 100 (or any other limits you might want to set) + public static bool IsValidPageSize(int pageSize) + { + return pageSize > 0 && pageSize <= 100; // you can adjust the upper limit as per requirements + } +} \ No newline at end of file From 983a6af41a9aa5683a727b9da5027334aa090da7 Mon Sep 17 00:00:00 2001 From: haraldnilsen Date: Tue, 19 Sep 2023 13:45:34 +0200 Subject: [PATCH 08/10] :goal_net: handled input errors in API Co-authored-by: Sindre Kjelsrud --- backend/Controllers/MovieController.cs | 27 +++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/backend/Controllers/MovieController.cs b/backend/Controllers/MovieController.cs index 133640e..b87ac19 100644 --- a/backend/Controllers/MovieController.cs +++ b/backend/Controllers/MovieController.cs @@ -1,7 +1,8 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.OpenApi.Writers; -namespace backend.Controllers; +namespace backend; +using static backend.QueryParameterValidators; [ApiController] [Route("[controller]")] @@ -9,6 +10,10 @@ public class MovieController: ControllerBase { private readonly MovieDbContext _context; private readonly ILogger _logger; + private static readonly string[] sortTypes = new[] + { + "titleasc", "titledesc", "yearasc", "yeardesc" + }; public MovieController(ILogger logger, MovieDbContext context) { @@ -18,31 +23,39 @@ public class MovieController: ControllerBase [HttpGet(Name = "GetMovies")] public ActionResult> Get( - [FromQuery] string? s, + [FromQuery] string s, [FromQuery] string? type, [FromQuery] string? y, + [FromQuery] string? sort, [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 5 ) { try { - if (s == "") { - return StatusCode(400, "Bad Request"); - } - + if (IsValidS(s)) return StatusCode(400, "Bad Request: Invalid title"); var movies = _context.Movies.Where(m => m.Title.Contains(s.ToLower())); if (type != null) { + if (!IsValidType(type)) return StatusCode(400, "Bad Request: Invalid type"); movies = movies.Where(m => m.Type == type); } if (y != null) { + if (!IsValidYear(y)) return StatusCode(400, "Bad Request: Invalid year"); movies = movies.Where(m => m.Year == y); } + if (sort != null) { + if (!IsValidSort(sort)) return StatusCode(400, "Bad Request: Invalid sort-type"); + + } + + if (!IsValidPageNumber(pageNumber) || !IsValidPageSize(pageSize)) + return StatusCode(400, "Bad Request: Invalid page-size or page-number"); + var totalMovies = movies.Count(); var totalPages = Math.Ceiling((double)totalMovies / pageSize); - + IEnumerable resultSkip = movies.Skip(pageSize * (pageNumber - 1)); IEnumerable resultTake = resultSkip.Take(pageSize); From 0354b7c0989711ff664438d6f5805176ceaf2d6f Mon Sep 17 00:00:00 2001 From: haraldnilsen Date: Tue, 19 Sep 2023 13:53:05 +0200 Subject: [PATCH 09/10] :construction: working on sorting in api Co-authored-by: Sindre Kjelsrud --- backend/Controllers/MovieController.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/backend/Controllers/MovieController.cs b/backend/Controllers/MovieController.cs index b87ac19..980265e 100644 --- a/backend/Controllers/MovieController.cs +++ b/backend/Controllers/MovieController.cs @@ -47,8 +47,12 @@ public class MovieController: ControllerBase if (sort != null) { if (!IsValidSort(sort)) return StatusCode(400, "Bad Request: Invalid sort-type"); - - } + if (sort == "titleasc") movies = movies.OrderBy(m => m.Title); + if (sort == "titledesc") movies = movies.OrderByDescending(m => m.Title); + if (sort == "yearasc") movies = movies.OrderBy(m => m.Year); + if (sort == "yeardesc") movies = movies.OrderByDescending(m => m.Year); + } + if (!IsValidPageNumber(pageNumber) || !IsValidPageSize(pageSize)) return StatusCode(400, "Bad Request: Invalid page-size or page-number"); From b76bf74b1ba4ba797fc9947c318d0e737b22eb0f Mon Sep 17 00:00:00 2001 From: haraldnilsen Date: Tue, 19 Sep 2023 13:57:44 +0200 Subject: [PATCH 10/10] :sparkles: working get-endpoint in api! Co-authored-by: Sindre Kjelsrud --- backend/Controllers/MovieController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/Controllers/MovieController.cs b/backend/Controllers/MovieController.cs index 980265e..c720c66 100644 --- a/backend/Controllers/MovieController.cs +++ b/backend/Controllers/MovieController.cs @@ -32,8 +32,8 @@ public class MovieController: ControllerBase ) { try { - if (IsValidS(s)) return StatusCode(400, "Bad Request: Invalid title"); - var movies = _context.Movies.Where(m => m.Title.Contains(s.ToLower())); + if (!IsValidS(s)) return StatusCode(400, "Bad Request: Invalid title"); + var movies = _context.Movies.Where(m => m.Title.ToLower().Contains(s.ToLower())); if (type != null) { if (!IsValidType(type)) return StatusCode(400, "Bad Request: Invalid type");