diff --git a/.drone.yml b/.drone.yml index 7fdb81f..89479ad 100644 --- a/.drone.yml +++ b/.drone.yml @@ -46,6 +46,7 @@ steps: from_secret: SECRET_SONAR_LOGIN depends_on: [tests] # docker image build + - name: docker-build-and-push image: plugins/docker settings: @@ -57,6 +58,17 @@ steps: from_secret: SECRET_REGISTRY_USERNAME password: from_secret: SECRET_REGISTRY_PASSWORD + + # container deployment + - name: deploy-container + image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest + environment: + IMAGENAME: hub.codefirst.iut.uca.fr/emre.kartal/lolproject:latest + CONTAINERNAME: lolApi + COMMAND: create + OVERWRITE: true + depends_on: [ docker-build-and-push ] + - name: generate-and-deploy-docs image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-docdeployer failure: ignore @@ -74,4 +86,4 @@ steps: #volumes: #- name: docs - # temp: {} + # temp: {} \ No newline at end of file diff --git a/.vs/LolProject/v17/.suo b/.vs/LolProject/v17/.suo index cb24cbd..25c0511 100644 Binary files a/.vs/LolProject/v17/.suo and b/.vs/LolProject/v17/.suo differ diff --git a/.vs/LolProjects/v17/.suo b/.vs/LolProjects/v17/.suo new file mode 100644 index 0000000..ba24d1a Binary files /dev/null and b/.vs/LolProjects/v17/.suo differ diff --git a/README.md b/README.md index d8f3407..b88ddc8 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,12 @@ -**Thème du projet** : Réalisation d'une API et d'un ORM(Entity Framework) qui seront reliés à une base de données dans le thème de League of Legends +**Thème du projet** : Réalisation d'une API et d'un ORM (Entity Framework) qui seront reliés à une base de données dans le thème de League of Legends
-La racine de notre gitlab est composée de deux dossiers essentiels au projet: +La racine de mon GitLab contient deux dossiers essentiels pour le projet : [**src**](src) : **Toute la partie codage de l'application** @@ -23,7 +23,7 @@ La racine de notre gitlab est composée de deux dossiers essentiels au projet: - ### Comment récupérer le projet ? -Tout d'abord si ce n'est pas fait cloner le dépôt de la branche **master/main**, pour cela copier le lien URL du dépôt git : +Tout d'abord, si ce n'est pas déjà fait, clonez le dépôt de la branche **master/main**. Pour ce faire, copiez le lien URL du dépôt git :
@@ -31,7 +31,7 @@ Tout d'abord si ce n'est pas fait cloner le dépôt de la branche **master/main*
-Vous pouvez le cloner via un terminal dans le répertoire que vous souhaitez en tapant la commande : **git clone https://codefirst.iut.uca.fr/git/emre.kartal/LolProject.git** ou utiliser Visual Studio et cloner en entrant le lien : +Vous pouvez le cloner via un terminal dans le répertoire de votre choix en utilisant la commande : **git clone https://codefirst.iut.uca.fr/git/emre.kartal/LolProject.git**. Sinon, vous pouvez utiliser Visual Studio et cloner le dépôt en entrant le lien :
@@ -39,16 +39,31 @@ Vous pouvez le cloner via un terminal dans le répertoire que vous souhaitez en
-:information_source: *Si vous ne disposez pas de Visual Studio, allé sur le site [Microsoft Visual Studio](https://visualstudio.microsoft.com/fr/downloads/) pour pouvoir le télécharger !!!* +:information_source: *Si vous ne disposez pas de Visual Studio, allez sur le site [Microsoft Visual Studio](https://visualstudio.microsoft.com/fr/downloads/) pour pouvoir le télécharger !!!* +- ### Comment lancer l'API dotnet ? + +Pour pouvoir utiliser toutes les requêtes de l'API, il est nécessaire de récupérer le projet si cela n'a pas été fait, puis de le lancer à partir de Visual Studio. Vous pouvez directement cliquer sur la solution du projet. Ensuite, dans les projets de démarrage, choisissez "Api-Lol" : + +
+ +![Lancer l'API](doc/Images/Api_Launcher.png) + +
+ +Normalement, tout est bon et vous serez redirigé vers Swagger, qui est l'interface graphique permettant d'effectuer facilement des requêtes. Il vous suffit ensuite de choisir la version en haut à droite. La version 3.0 est normalement la plus récente et la plus performante en termes de gestion d'erreur : + +
+ +![Version API](doc/Images/Swagger_Version.png) + +
- ### Comment lancer Le projet Entity Framework ? -Afin de générer les migrations et les tables. -
-Vous devez avoir installé correctement EntityFrameworkCore, pour cela il existe la commande : **dotnet tool install --global dotnet-ef** qui peut être lancé à partir d'un terminal, si il est déjà installer mais n'a pas la bonne version : **dotnet tool update --global dotnet-ef** (oui y que le *install* qui change vous êtes perspicace)! +Afin de générer les migrations et les tables, vous devez avoir installé correctement EntityFrameworkCore. Pour cela il existe la commande : **dotnet tool install --global dotnet-ef**, qui peut être lancé à partir d'un terminal. Si elle est déjà installée mais n'a pas la bonne version : **dotnet tool update --global dotnet-ef** (oui, il n'y a que l'*install* qui change. Vous êtes perspicace !). -Aussi assurer vous d'avoir installé sur Visual Studio au préalable les package Nuget suivants : +Assurez-vous également d'avoir installé les packages Nuget suivants sur Visual Studio au préalable :
@@ -56,13 +71,13 @@ Aussi assurer vous d'avoir installé sur Visual Studio au préalable les package
-Ensuite sur le terminal PowerShell ou Visual Studio, lancer la migration via la commande : **dotnet ef migrations add monNomDeMigration** (n'oublier pas de vous situer dans le dossier "/MyFlib" lorsque vous l'exécuter)! +Ensuite, sur le terminal PowerShell ou Visual Studio, lancer la migration via la commande : **dotnet ef migrations add monNomDeMigration** (n'oubliez pas de vous situer dans le dossier "/MyFlib" lorsque vous l'exécutez)! - ### Comment voir la base de données ? -C'est bien beau toutes ces étapes mais s’il n'y a pas de résultat à quoi cela sert ! +C'est bien beau toutes ces étapes, mais s’il n'y a pas de résultat, à quoi cela sert ! -Tout d'abord veuillez regarder dans l'onglet extension, si l'outil **SQLite and SQL Server Compact Toolbax** est bien installé. +Tout d'abord, veuillez regarder dans l'onglet Extensions si l'outil **SQLite and SQL Server Compact Toolbax** est bien installé. Puis, afin de visualiser la migration dans la base de données, cliquer dans l'onglet **Outils**->**SQLLite/ SQL Server compact Toolbox** : @@ -72,7 +87,7 @@ Puis, afin de visualiser la migration dans la base de données, cliquer dans l'o -Et enfin cliquer sur l'icône ci-dessous pour faire la connexion à la solution courent : +Et enfin cliquez sur l'icône ci-dessous pour établir la connexion à la solution en cours :
@@ -80,14 +95,14 @@ Et enfin cliquer sur l'icône ci-dessous pour faire la connexion à la solution
-Vous pouvez dorénavant voir toutes les tables qui y sont enregistrées ! Si vous souhaitez ajouter des modifications à la base de données et les visualiser, -réaliser à nouveau la migration (ou *updater* celui actuel), puis supprimer toutes les tables et lancer la commande : **dotnet ef database update** et enfin rafraichissez la BD ! +Vous pouvez désormais voir toutes les tables qui y sont enregistrées ! Si vous souhaitez ajouter des modifications à la base de données et les visualiser, +réalisez à nouveau la migration (ou mettez à jour celle actuelle), puis supprimez toutes les tables et lancez la commande : **dotnet ef database update** et enfin rafraîchissez la base de données ! -:information_source: *Notez qu'il est également possible grâce à SQLLite d'ajouter, modifier ou supprimer des données dans les tables.* +:information_source: *Notez qu'il est également possible, grâce à SQLLite, d'ajouter, modifier ou supprimer des données dans les tables.* -Mon environnement de travail se base sur un outil et un langage en particulier :👇 +Mon environnement de travail est basé sur un outil et un langage en particulier : 👇
diff --git a/doc/Images/Api_Launcher.png b/doc/Images/Api_Launcher.png new file mode 100644 index 0000000..deceb08 Binary files /dev/null and b/doc/Images/Api_Launcher.png differ diff --git a/doc/Images/Swagger_Version.png b/doc/Images/Swagger_Version.png new file mode 100644 index 0000000..bb0c3b0 Binary files /dev/null and b/doc/Images/Swagger_Version.png differ diff --git a/src/EntityFramework_LoL/Sources/ApiLol/ApiLol.csproj b/src/EntityFramework_LoL/Sources/ApiLol/ApiLol.csproj index 57d85a9..f765ca4 100644 --- a/src/EntityFramework_LoL/Sources/ApiLol/ApiLol.csproj +++ b/src/EntityFramework_LoL/Sources/ApiLol/ApiLol.csproj @@ -9,6 +9,8 @@ + + diff --git a/src/EntityFramework_LoL/Sources/ApiLol/Controllers/ChampionsController.cs b/src/EntityFramework_LoL/Sources/ApiLol/Controllers/ChampionsController.cs deleted file mode 100644 index 1680ad1..0000000 --- a/src/EntityFramework_LoL/Sources/ApiLol/Controllers/ChampionsController.cs +++ /dev/null @@ -1,75 +0,0 @@ -using ApiLol.Mapper; -using DTO; -using Microsoft.AspNetCore.Mvc; -using Microsoft.IdentityModel.Tokens; -using Model; - -// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 - -namespace ApiLol.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class ChampionsController : ControllerBase - { - private readonly IDataManager _manager; - public ChampionsController(IDataManager dataManager) - { - this._manager = dataManager; - } - - // GET: api/ - [HttpGet] - public async Task Get() - { - IEnumerable dtos = (await _manager.ChampionsMgr.GetItems(0, await _manager.ChampionsMgr.GetNbItems())) - .Select(x => x.ToDto()); - return Ok(dtos); - } - - // GET api//5 - [HttpGet("{name}")] - public async Task Get(string name) - { - var dtos = (await _manager.ChampionsMgr.GetItemsByName(name,0, await _manager.ChampionsMgr.GetNbItems())) - .Select(x => x.ToDto()); - if(dtos.IsNullOrEmpty()) - { - return NotFound(); - } - return Ok(dtos); - } - - // POST api/ - [HttpPost] - public async Task Post([FromBody] ChampionDto champion) - { - return CreatedAtAction(nameof(Get), - (await _manager.ChampionsMgr.AddItem(champion.ToModel())).ToDto()); - } - - // PUT api//5 - [HttpPut("{name}")] - public async Task Put(string name, [FromBody] ChampionDto champion) - { - var dtos = (await _manager.ChampionsMgr.GetItemsByName(name, 0, await _manager.ChampionsMgr.GetNbItems())); - if(dtos.IsNullOrEmpty()) - { - return BadRequest(); - } - return Ok(await _manager.ChampionsMgr.UpdateItem(dtos.First(), champion.ToModel())); - } - - // DELETE api//5 - [HttpDelete("{name}")] - public async Task Delete(string name) - { - var dtos = (await _manager.ChampionsMgr.GetItemsByName(name, 0, await _manager.ChampionsMgr.GetNbItems())); - if (dtos.IsNullOrEmpty()) - { - return BadRequest(); - } - return Ok(await _manager.ChampionsMgr.DeleteItem(dtos.First())); - } - } -} diff --git a/src/EntityFramework_LoL/Sources/ApiLol/Controllers/SkinsController.cs b/src/EntityFramework_LoL/Sources/ApiLol/Controllers/SkinsController.cs new file mode 100644 index 0000000..bef0ede --- /dev/null +++ b/src/EntityFramework_LoL/Sources/ApiLol/Controllers/SkinsController.cs @@ -0,0 +1,76 @@ +using ApiLol.Mapper; +using DTO; +using Microsoft.AspNetCore.Mvc; +using Model; + +// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 + +namespace ApiLol.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class SkinsController : ControllerBase + { + private readonly IDataManager _manager; + private readonly ILogger _logger; + + public SkinsController(IDataManager dataManager, ILogger logger) + { + _logger = logger; + this._manager = dataManager; + } + + // GET: api/ + [HttpGet] + public async Task Get([FromQuery] PageRequest pageRequest) + { + try + { + int nbTotal = await _manager.ChampionsMgr.GetNbItems(); + if (pageRequest.count + pageRequest.index > nbTotal) + { + _logger.LogWarning($"too many, maximum {nbTotal}"); + return BadRequest($"Champion limit exceed, max {nbTotal}"); + } + + _logger.LogInformation($"method Get call"); + IEnumerable dtos = (await _manager.SkinsMgr.GetItems(pageRequest.index, pageRequest.count)) + .Select(x => x.ToDtoC()); + return Ok(dtos); + + } + catch (Exception e) + { + return BadRequest(e.Message); + } + } + + // GET api//5 + [HttpGet("{name}")] + public async Task Get(string name) + { + return Ok(); + } + + // POST api/ + [HttpPost] + public async Task Post([FromBody] string value) + { + return Ok(); + } + + // PUT api//5 + [HttpPut("{name}")] + public async Task Put(int id, [FromBody] string value) + { + return Ok(); + } + + // DELETE api//5 + [HttpDelete("{name}")] + public async Task Delete(string name) + { + return Ok(); + } + } +} diff --git a/src/EntityFramework_LoL/Sources/ApiLol/Controllers/v1/ChampionsController.cs b/src/EntityFramework_LoL/Sources/ApiLol/Controllers/v1/ChampionsController.cs new file mode 100644 index 0000000..53c9853 --- /dev/null +++ b/src/EntityFramework_LoL/Sources/ApiLol/Controllers/v1/ChampionsController.cs @@ -0,0 +1,82 @@ +using ApiLol.Mapper; +using DTO; +using Microsoft.AspNetCore.Mvc; +using Microsoft.IdentityModel.Tokens; +using Model; + +// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 + +namespace ApiLol.Controllers.v1 +{ + [ApiVersion("1.0")] + [Route("api/v{version:apiVersion}/[controller]")] + [ApiController] + public class ChampionsController : ControllerBase + { + private readonly IDataManager _manager; + private readonly ILogger _logger; + public ChampionsController(IDataManager dataManager, ILogger logger) + { + _logger = logger; + this._manager = dataManager; + } + + // GET: api/ + [HttpGet] + public async Task Get([FromQuery] PageRequest pageRequest) + { + _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(Get), pageRequest); + IEnumerable dtos = (await _manager.ChampionsMgr.GetItems(pageRequest.index, pageRequest.count, pageRequest.orderingPropertyName, pageRequest.descending)) + .Select(x => x.ToDto()); + return Ok(dtos); + + } + + // GET api//5 + [HttpGet("{name}")] + public async Task Get(string name) + { + _logger.LogInformation("method {Action} call with {name}", nameof(Get), name); + var dtos = (await _manager.ChampionsMgr.GetItemByName(name, 0, await _manager.ChampionsMgr.GetNbItems())) + .Select(x => x.ToDto()); + + return Ok(dtos.First()); + + } + + // POST api/ + [HttpPost] + public async Task Post([FromBody] ChampionDto champion) + { + + _logger.LogInformation("method {Action} call with {item}", nameof(Post), champion); + return CreatedAtAction(nameof(Get), + (await _manager.ChampionsMgr.AddItem(champion.ToModel())).ToDto()); + + } + + // PUT api//5 + [HttpPut("{name}")] + public async Task Put(string name, [FromBody] ChampionDto champion) + { + + _logger.LogInformation("method {Action} call with {name} and {item}", nameof(Put), name, champion); + var dtos = (await _manager.ChampionsMgr.GetItemByName(name, 0, await _manager.ChampionsMgr.GetNbItems())); + + return Ok((await _manager.ChampionsMgr.UpdateItem(dtos.First(), champion.ToModel())).ToDto()); + + } + + // DELETE api//5 + [HttpDelete("{name}")] + public async Task Delete(string name) + { + + _logger.LogInformation("method {Action} call with {name}", nameof(Delete), name); + var dtos = (await _manager.ChampionsMgr.GetItemByName(name, 0, await _manager.ChampionsMgr.GetNbItems())); + + return Ok(await _manager.ChampionsMgr.DeleteItem(dtos.First())); + + } + } +} \ No newline at end of file diff --git a/src/EntityFramework_LoL/Sources/ApiLol/Controllers/v2/ChampionsController.cs b/src/EntityFramework_LoL/Sources/ApiLol/Controllers/v2/ChampionsController.cs new file mode 100644 index 0000000..838fe00 --- /dev/null +++ b/src/EntityFramework_LoL/Sources/ApiLol/Controllers/v2/ChampionsController.cs @@ -0,0 +1,211 @@ +using ApiLol.Mapper; +using Azure.Core; +using DTO; +using Microsoft.AspNetCore.Mvc; +using Microsoft.IdentityModel.Tokens; +using Model; + +// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 + +namespace ApiLol.Controllers.v2 +{ + [ApiVersion("2.0")] + [ApiVersion("3.0")] + [Route("api/v{version:apiVersion}/[controller]")] + [ApiController] + public class ChampionsController : ControllerBase + { + private readonly IDataManager _manager; + private readonly ILogger _logger; + public ChampionsController(IDataManager dataManager, ILogger logger) + { + _logger = logger; + this._manager = dataManager; + } + + // GET: api/ + [HttpGet] + public async Task Get([FromQuery] PageRequest pageRequest) + { + try + { + int nbTotal = await _manager.ChampionsMgr.GetNbItems(); + if (pageRequest.count == 0) + { + pageRequest = new PageRequest() { index = 0, count = nbTotal }; + } + else if (pageRequest.count * pageRequest.index >= nbTotal) + { + _logger.LogWarning($"too many, maximum {nbTotal}"); + return BadRequest($"Champion limit exceed, max {nbTotal}"); + } + + _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(Get), pageRequest.count); + IEnumerable dtos = (await _manager.ChampionsMgr.GetItems(pageRequest.index, pageRequest.count)) + .Select(x => x.ToDto()); + return Ok(dtos); + } + catch (Exception error) + { + return BadRequest(error.Message); + } + } + + // GET: api/ + [HttpGet, MapToApiVersion("3.0")] + public async Task GetV3([FromQuery] PageRequest pageRequest) + { + try + { + int nbTotal = await _manager.ChampionsMgr.GetNbItems(); + if (pageRequest.count == 0) + { + pageRequest = new PageRequest() { index = 0, count = nbTotal }; + } + else if (pageRequest.count * pageRequest.index >= nbTotal || pageRequest.count > nbTotal) + { + _logger.LogWarning("too many, maximum {number}", nbTotal); + return BadRequest($"Champion limit exceed, max {nbTotal}"); + } + + _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(Get), pageRequest); + IEnumerable dtos = (await _manager.ChampionsMgr.GetItems(pageRequest.index, pageRequest.count, pageRequest.orderingPropertyName, pageRequest.descending)) + .Select(x => x.ToDto()); + return Ok(new { Data = dtos, index = pageRequest.index, count = pageRequest.count, total = nbTotal}); + } + catch (Exception error) + { + return BadRequest(error.Message); + } + } + + // GET api//5 + [HttpGet("{name}")] + public async Task Get(string name) + { + try + { + _logger.LogInformation("method {Action} call with {name}", nameof(Get), name); + var dtos = (await _manager.ChampionsMgr.GetItemByName(name, 0, await _manager.ChampionsMgr.GetNbItems())) + .Select(x => x.ToDto()); + if (dtos.IsNullOrEmpty()) + { + _logger.LogWarning($"{name} was not found"); + return NotFound(); + } + return Ok(dtos.First()); + } + catch (Exception error) + { + return BadRequest(error.Message); + } + } + + // POST api/ + [HttpPost] + public async Task Post([FromBody] ChampionDto champion) + { + try + { + _logger.LogInformation("method {Action} call with {item}", nameof(Post), champion); + var dtos = (await _manager.ChampionsMgr.GetItemByName(champion.Name, 0, await _manager.ChampionsMgr.GetNbItems())); + if (!dtos.IsNullOrEmpty()) + { + return BadRequest("Name is already exist"); + } + return CreatedAtAction(nameof(Get), + (await _manager.ChampionsMgr.AddItem(champion.ToModel())).ToDto()); + } + catch (Exception error) + { + return BadRequest(error.Message); + } + } + + // PUT api//5 + [HttpPut("{name}")] + public async Task Put(string name, [FromBody] ChampionDto champion) + { + try + { + _logger.LogInformation("method {Action} call with {name} and {item}", nameof(Put), name, champion); + var dtos = (await _manager.ChampionsMgr.GetItemByName(name, 0, await _manager.ChampionsMgr.GetNbItems())); + if (dtos.IsNullOrEmpty()) + { + return NotFound($"Name {name} not exist"); + } + // Checks if the new name exists + if (name != champion.Name) + { + var dtos2 = (await _manager.ChampionsMgr.GetItemByName(champion.Name, 0, await _manager.ChampionsMgr.GetNbItems())); + if (dtos2.IsNullOrEmpty() || dtos2.Count() > 0) + { + return BadRequest("Name is already exist"); + } + } + return Ok((await _manager.ChampionsMgr.UpdateItem(dtos.First(), champion.ToModel())).ToDto()); + } + catch (Exception error) + { + return BadRequest(error.Message); + } + } + + [HttpGet("/{name}/skins")] + public async Task> GetChampionsSkins(string name) + { + try + { + _logger.LogInformation("method {Action} call with {name}", nameof(GetChampionsSkins), name); + var champions = await _manager.ChampionsMgr.GetItemByName(name, 0, await _manager.ChampionsMgr.GetNbItems()); + //skinsDTO + IEnumerable res = champions.First().Skins.Select(e => e.ToDto()); + + return Ok(res); + } + catch (Exception error) + { + return BadRequest(error.Message); + } + } + + [HttpGet("/{name}/skills")] + public async Task> GetChampionsSkills(string name) + { + try + { + _logger.LogInformation("method {Action} call with {name}", nameof(GetChampionsSkills), name); + var champions = await _manager.ChampionsMgr.GetItemByName(name, 0, await _manager.ChampionsMgr.GetNbItems()); + //SkillDTO + IEnumerable res = champions.First().Skills.Select(e => e.ToDto()); + + return Ok(res); + } + catch (Exception error) + { + return BadRequest(error.Message); + } + } + + // DELETE api//5 + [HttpDelete("{name}")] + public async Task Delete(string name) + { + try + { + _logger.LogInformation("method {Action} call with {name}", nameof(Delete), name); + var dtos = (await _manager.ChampionsMgr.GetItemByName(name, 0, await _manager.ChampionsMgr.GetNbItems())); + if (dtos.IsNullOrEmpty()) + { + _logger.LogWarning("{name} was not found", name); + return BadRequest(); + } + return Ok(await _manager.ChampionsMgr.DeleteItem(dtos.First())); + } + catch (Exception error) + { + return BadRequest(error.Message); + } + } + } +} \ No newline at end of file diff --git a/src/EntityFramework_LoL/Sources/ApiLol/Mapper/ChampionMapper.cs b/src/EntityFramework_LoL/Sources/ApiLol/Mapper/ChampionMapper.cs index dbb9679..094739d 100644 --- a/src/EntityFramework_LoL/Sources/ApiLol/Mapper/ChampionMapper.cs +++ b/src/EntityFramework_LoL/Sources/ApiLol/Mapper/ChampionMapper.cs @@ -13,13 +13,20 @@ namespace ApiLol.Mapper Bio = champion.Bio, Class = champion.Class.ToDto(), Icon = champion.Icon, - Image = champion.Image.Base64 + Image = champion.Image.ToDto(), + Skins = champion.Skins.Select(e => e.ToDto()), + Skills = champion.Skills.Select(e => e.ToDto()) }; } public static Champion ToModel(this ChampionDto championDto) { - return new Champion(championDto.Name, championDto.Class.ToModel(), championDto.Icon, championDto.Image,championDto.Bio); + var champ = new Champion(championDto.Name, championDto.Class.ToModel(), championDto.Icon, championDto.Image.Base64, championDto.Bio); + foreach(var skin in championDto.Skins) + { + champ.AddSkin(skin.ToModel(champ)); + } + return champ; } } diff --git a/src/EntityFramework_LoL/Sources/ApiLol/Mapper/LargeImageMapper.cs b/src/EntityFramework_LoL/Sources/ApiLol/Mapper/LargeImageMapper.cs new file mode 100644 index 0000000..6bebb6f --- /dev/null +++ b/src/EntityFramework_LoL/Sources/ApiLol/Mapper/LargeImageMapper.cs @@ -0,0 +1,21 @@ +using DTO; +using Model; + +namespace ApiLol.Mapper +{ + public static class LargeImageMapper + { + public static LargeImageDto ToDto(this LargeImage largeImage) + { + return new LargeImageDto() + { + Base64 = largeImage.Base64 + }; + } + + public static LargeImage ToModel(this LargeImageDto largeImageDto) + { + return new LargeImage(largeImageDto.Base64); + } + } +} \ No newline at end of file diff --git a/src/EntityFramework_LoL/Sources/ApiLol/Mapper/RuneMapper.cs b/src/EntityFramework_LoL/Sources/ApiLol/Mapper/RuneMapper.cs index fcd88a6..276d0ac 100644 --- a/src/EntityFramework_LoL/Sources/ApiLol/Mapper/RuneMapper.cs +++ b/src/EntityFramework_LoL/Sources/ApiLol/Mapper/RuneMapper.cs @@ -14,7 +14,7 @@ namespace ApiLol.Mapper }; } -/* public static Rune ToModel(this RuneDto rune) +/* public static Rune ToModel(this RuneDto runeDto) { return new Rune(rune.Name) { diff --git a/src/EntityFramework_LoL/Sources/ApiLol/Mapper/SkillMapper.cs b/src/EntityFramework_LoL/Sources/ApiLol/Mapper/SkillMapper.cs index 768caf7..20c0787 100644 --- a/src/EntityFramework_LoL/Sources/ApiLol/Mapper/SkillMapper.cs +++ b/src/EntityFramework_LoL/Sources/ApiLol/Mapper/SkillMapper.cs @@ -11,15 +11,13 @@ namespace ApiLol.Mapper { Name = skill.Name, Description = skill.Description, + Type = skill.Type.ToDto() }; } -/* public static Skill ToModel(this SkillDto skill) + public static Skill ToModel(this SkillDto skillDto) { - return new Skill(skill.Name) - { - Description = skill.Description - }; - }*/ + return new Skill(skillDto.Name, skillDto.Type.ToModel(), skillDto.Description); + } } } diff --git a/src/EntityFramework_LoL/Sources/ApiLol/Mapper/SkinMapper.cs b/src/EntityFramework_LoL/Sources/ApiLol/Mapper/SkinMapper.cs index a093e12..9b5273b 100644 --- a/src/EntityFramework_LoL/Sources/ApiLol/Mapper/SkinMapper.cs +++ b/src/EntityFramework_LoL/Sources/ApiLol/Mapper/SkinMapper.cs @@ -1,18 +1,39 @@ using DTO; +using Model; namespace ApiLol.Mapper { public static class SkinMapper { - public static SkinDto ToDto(this SkinDto skin) + public static SkinDto ToDto(this Skin skin) { return new SkinDto() { Name = skin.Name, Description = skin.Description, Icon = skin.Icon, + Image = skin.Image.ToDto(), Price = skin.Price }; } + + public static SkinDtoC ToDtoC(this Skin skin) + { + return new SkinDtoC() + { + Name = skin.Name, + Description = skin.Description, + Icon = skin.Icon, + Image = skin.Image.ToDto(), + Price = skin.Price, + ChampionName = skin.Champion.Name + }; + } + + public static Skin ToModel(this SkinDto skinDto, Champion champ) + { + return new Skin(skinDto.Name, champ, skinDto.Price, skinDto.Icon, skinDto.Image.Base64, skinDto.Description); + } + } -} +} \ No newline at end of file diff --git a/src/EntityFramework_LoL/Sources/ApiLol/Mapper/enums/ChampionClassMapper.cs b/src/EntityFramework_LoL/Sources/ApiLol/Mapper/enums/ChampionClassMapper.cs index c0c52ed..a96d7bf 100644 --- a/src/EntityFramework_LoL/Sources/ApiLol/Mapper/enums/ChampionClassMapper.cs +++ b/src/EntityFramework_LoL/Sources/ApiLol/Mapper/enums/ChampionClassMapper.cs @@ -9,9 +9,9 @@ namespace ApiLol.Mapper { return (ChampionClassDto) championClass; } - public static ChampionClass ToModel(this ChampionClassDto championClass) + public static ChampionClass ToModel(this ChampionClassDto championClassDto) { - return (ChampionClass) championClass; + return (ChampionClass) championClassDto; } } } diff --git a/src/EntityFramework_LoL/Sources/ApiLol/Mapper/enums/SkillTypeMapper.cs b/src/EntityFramework_LoL/Sources/ApiLol/Mapper/enums/SkillTypeMapper.cs new file mode 100644 index 0000000..ace0c98 --- /dev/null +++ b/src/EntityFramework_LoL/Sources/ApiLol/Mapper/enums/SkillTypeMapper.cs @@ -0,0 +1,57 @@ +using DTO; +using Model; + +namespace ApiLol.Mapper +{ + public static class SkillTypeMapper + { + public static SkillTypeDto ToDto(this SkillType skillType) + { + if (skillType == SkillType.Unknown) + { + return SkillTypeDto.Unknown; + } + if (skillType == SkillType.Basic) + { + return SkillTypeDto.Basic; + } + if (skillType == SkillType.Passive) + { + return SkillTypeDto.Passive; + } + if (skillType == SkillType.Ultimate) + { + return SkillTypeDto.Ultimate; + } + else + { + return SkillTypeDto.Unknown; + } + + } + public static SkillType ToModel(this SkillTypeDto skillTypeDto) + { + if (skillTypeDto == SkillTypeDto.Unknown) + { + return SkillType.Unknown; + } + if (skillTypeDto == SkillTypeDto.Basic) + { + return SkillType.Basic; + } + if (skillTypeDto == SkillTypeDto.Passive) + { + return SkillType.Passive; + } + if (skillTypeDto == SkillTypeDto.Ultimate) + { + return SkillType.Ultimate; + } + else + { + return SkillType.Unknown; + } + + } + } +} \ No newline at end of file diff --git a/src/EntityFramework_LoL/Sources/ApiLol/Program.cs b/src/EntityFramework_LoL/Sources/ApiLol/Program.cs index e6ed0a5..98e6f6c 100644 --- a/src/EntityFramework_LoL/Sources/ApiLol/Program.cs +++ b/src/EntityFramework_LoL/Sources/ApiLol/Program.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.Versioning; using Model; using StubLib; @@ -9,16 +11,41 @@ builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); + + +builder.Services.AddApiVersioning(opt => + { + opt.DefaultApiVersion = new Microsoft.AspNetCore.Mvc.ApiVersion(1, 0); + opt.AssumeDefaultVersionWhenUnspecified = true; + opt.ReportApiVersions = true; + opt.ApiVersionReader = ApiVersionReader.Combine(new UrlSegmentApiVersionReader(), + new HeaderApiVersionReader("x-api-version"), + new MediaTypeApiVersionReader("x-api-version")); + }); +// Add ApiExplorer to discover versions +builder.Services.AddVersionedApiExplorer(setup => +{ + setup.GroupNameFormat = "'v'VVV"; + setup.SubstituteApiVersionInUrl = true; +}); + builder.Services.AddSingleton(); var app = builder.Build(); // Configure the HTTP request pipeline. -if (app.Environment.IsDevelopment()) +var apiVersionDescriptionProvider = app.Services.GetRequiredService(); + +// Configure the HTTP request pipeline. +app.UseSwagger(); +app.UseSwaggerUI(options => { - app.UseSwagger(); - app.UseSwaggerUI(); -} + foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions) + { + options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", + description.GroupName.ToUpperInvariant()); + } +}); app.UseHttpsRedirection(); diff --git a/src/EntityFramework_LoL/Sources/Client/ChampionHttpClient.cs b/src/EntityFramework_LoL/Sources/Client/ChampionHttpClient.cs index 2fa8957..deae099 100644 --- a/src/EntityFramework_LoL/Sources/Client/ChampionHttpClient.cs +++ b/src/EntityFramework_LoL/Sources/Client/ChampionHttpClient.cs @@ -18,19 +18,19 @@ namespace Client httpClient.BaseAddress = new Uri("https://localhost:7252;http://localhost:5252"); } - public async Task> GetChampion() + public async Task> GetChampion(int index, int count) { - var champions = await _httpClient.GetFromJsonAsync>(ApiChampions); - return champions; + var url = $"{ApiChampions}?index={index}&count={count}"; + return await _httpClient.GetFromJsonAsync>(url); } - public async void Add(ChampionDto champion) +/* public async void Add(ChampionDto champion) { await _httpClient.PostAsJsonAsync(ApiChampions, champion); - } + }*/ /* public async void Delete(ChampionDto champion) { - await _httpClient.DeleteAsync(champion); + await _httpClient.DeleteAsync(champion.Name); } public async void Update(ChampionDto champion) diff --git a/src/EntityFramework_LoL/Sources/Client/Program.cs b/src/EntityFramework_LoL/Sources/Client/Program.cs index 3751555..fdff83d 100644 --- a/src/EntityFramework_LoL/Sources/Client/Program.cs +++ b/src/EntityFramework_LoL/Sources/Client/Program.cs @@ -1,2 +1,38 @@ // See https://aka.ms/new-console-template for more information +using Client; +using DTO; + Console.WriteLine("Hello, World!"); + +var championClient = new ChampionHttpClient(new HttpClient()); + +// Get all champions +var champions = await championClient.GetChampion(0,6); +Console.WriteLine("All champions:"); +foreach (var champion in champions) +{ + Console.WriteLine($"{champion.Name} ({champion.Bio})"); +} + +/*// Add a new champion +var newChampion = new ChampionDto { Name = "Akali", Role = "Assassin" }; +championClient.Add(newChampion); + +// Delete a champion +var championToDelete = champions.FirstOrDefault(c => c.Name == "Riven"); +if (championToDelete != null) +{ + championClient.Delete(championToDelete); + Console.WriteLine($"{championToDelete.Name} deleted."); +} + +// Update a champion +var championToUpdate = champions.FirstOrDefault(c => c.Name == "Ashe"); +if (championToUpdate != null) +{ + championToUpdate.Role = "Marksman"; + championClient.Update(championToUpdate); + Console.WriteLine($"{championToUpdate.Name} updated."); +} + +*/ \ No newline at end of file diff --git a/src/EntityFramework_LoL/Sources/DTO/ChampionDto.cs b/src/EntityFramework_LoL/Sources/DTO/ChampionDto.cs index 09f009b..e9b002f 100644 --- a/src/EntityFramework_LoL/Sources/DTO/ChampionDto.cs +++ b/src/EntityFramework_LoL/Sources/DTO/ChampionDto.cs @@ -6,8 +6,9 @@ public string Bio { get; set; } public ChampionClassDto Class { get; set; } public string Icon { get; set; } - public string Image { get; set; } + public LargeImageDto Image { get; set; } public IEnumerable Skins { get; set; } + public IEnumerable Skills { get; set; } } } \ No newline at end of file diff --git a/src/EntityFramework_LoL/Sources/DTO/LargeImageDto.cs b/src/EntityFramework_LoL/Sources/DTO/LargeImageDto.cs new file mode 100644 index 0000000..133ac67 --- /dev/null +++ b/src/EntityFramework_LoL/Sources/DTO/LargeImageDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DTO +{ + public class LargeImageDto + { + public string Base64 { get; set; } + } +} diff --git a/src/EntityFramework_LoL/Sources/DTO/PageRequest.cs b/src/EntityFramework_LoL/Sources/DTO/PageRequest.cs new file mode 100644 index 0000000..a947eb9 --- /dev/null +++ b/src/EntityFramework_LoL/Sources/DTO/PageRequest.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DTO +{ + public class PageRequest + { + public int index { get; set; } = 0; + public int count { get; set; } = 0; + public string? orderingPropertyName { get; set; } = "Name"; + public bool descending { get; set; } = false; + } +} diff --git a/src/EntityFramework_LoL/Sources/DTO/RuneDto.cs b/src/EntityFramework_LoL/Sources/DTO/RuneDto.cs index f5afb80..5f463b6 100644 --- a/src/EntityFramework_LoL/Sources/DTO/RuneDto.cs +++ b/src/EntityFramework_LoL/Sources/DTO/RuneDto.cs @@ -1,4 +1,5 @@ -using System; +using DTO.enums; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -10,6 +11,8 @@ namespace DTO { public string Name { get; set; } public string Description { get; set; } + public RuneFamilyDto Family { get; set; } + public LargeImageDto Image { get; set; } } } diff --git a/src/EntityFramework_LoL/Sources/DTO/SkillDto.cs b/src/EntityFramework_LoL/Sources/DTO/SkillDto.cs index 77372ab..f0e1919 100644 --- a/src/EntityFramework_LoL/Sources/DTO/SkillDto.cs +++ b/src/EntityFramework_LoL/Sources/DTO/SkillDto.cs @@ -10,5 +10,6 @@ namespace DTO { public string Name { get; set; } public string Description { get; set; } + public SkillTypeDto Type { get; set; } } } diff --git a/src/EntityFramework_LoL/Sources/DTO/SkinDto.cs b/src/EntityFramework_LoL/Sources/DTO/SkinDto.cs index 2c3962b..8ff7cca 100644 --- a/src/EntityFramework_LoL/Sources/DTO/SkinDto.cs +++ b/src/EntityFramework_LoL/Sources/DTO/SkinDto.cs @@ -11,6 +11,7 @@ namespace DTO public string Name { get; set; } public string Description { get; set; } public string Icon { get; set; } + public LargeImageDto Image { get; set; } public float Price { get; set; } } diff --git a/src/EntityFramework_LoL/Sources/DTO/SkinDtoC.cs b/src/EntityFramework_LoL/Sources/DTO/SkinDtoC.cs new file mode 100644 index 0000000..656fb33 --- /dev/null +++ b/src/EntityFramework_LoL/Sources/DTO/SkinDtoC.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DTO +{ + public class SkinDtoC + { + public string Name { get; set; } + public string Description { get; set; } + public string Icon { get; set; } + public LargeImageDto Image { get; set; } + public float Price { get; set; } + public string ChampionName { get; set; } + + + } +} diff --git a/src/EntityFramework_LoL/Sources/DTO/enums/RuneFamilyDto.cs b/src/EntityFramework_LoL/Sources/DTO/enums/RuneFamilyDto.cs new file mode 100644 index 0000000..053249c --- /dev/null +++ b/src/EntityFramework_LoL/Sources/DTO/enums/RuneFamilyDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DTO.enums +{ + public enum RuneFamilyDto + { + Unknown, + Precision, + Domination + } +} diff --git a/src/EntityFramework_LoL/Sources/DTO/enums/SkillTypeDto.cs b/src/EntityFramework_LoL/Sources/DTO/enums/SkillTypeDto.cs new file mode 100644 index 0000000..f416bdd --- /dev/null +++ b/src/EntityFramework_LoL/Sources/DTO/enums/SkillTypeDto.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DTO +{ + public enum SkillTypeDto + { + Unknown, + Basic, + Passive, + Ultimate + } +} diff --git a/src/EntityFramework_LoL/Sources/Model/Champion.cs b/src/EntityFramework_LoL/Sources/Model/Champion.cs index b8b369b..313e607 100644 --- a/src/EntityFramework_LoL/Sources/Model/Champion.cs +++ b/src/EntityFramework_LoL/Sources/Model/Champion.cs @@ -62,7 +62,7 @@ public class Champion : IEquatable public ImmutableHashSet Skills => skills.ToImmutableHashSet(); private HashSet skills = new HashSet(); - internal bool AddSkin(Skin skin) + public bool AddSkin(Skin skin) { if (skins.Contains(skin)) return false; @@ -70,7 +70,7 @@ public class Champion : IEquatable return true; } - internal bool RemoveSkin(Skin skin) + public bool RemoveSkin(Skin skin) => skins.Remove(skin); public bool AddSkill(Skill skill) diff --git a/src/EntityFramework_LoL/Sources/Shared/IGenericDataManager.cs b/src/EntityFramework_LoL/Sources/Shared/IGenericDataManager.cs index ed3ac48..a3b4ee4 100644 --- a/src/EntityFramework_LoL/Sources/Shared/IGenericDataManager.cs +++ b/src/EntityFramework_LoL/Sources/Shared/IGenericDataManager.cs @@ -5,7 +5,8 @@ public interface IGenericDataManager Task> GetItems(int index, int count, string? orderingPropertyName = null, bool descending = false); Task GetNbItemsByName(string substring); Task> GetItemsByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false); - Task UpdateItem(T oldItem, T newItem); + Task> GetItemByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false); + Task UpdateItem(T oldItem, T newItem); Task AddItem(T item); Task DeleteItem(T item); } diff --git a/src/EntityFramework_LoL/Sources/StubLib/StubData.Champions.cs b/src/EntityFramework_LoL/Sources/StubLib/StubData.Champions.cs index ad19275..d7f4bf1 100644 --- a/src/EntityFramework_LoL/Sources/StubLib/StubData.Champions.cs +++ b/src/EntityFramework_LoL/Sources/StubLib/StubData.Champions.cs @@ -3,19 +3,19 @@ using Model; namespace StubLib { - public partial class StubData - { - private List champions = new() - { - new Champion("Akali", ChampionClass.Assassin), - new Champion("Aatrox", ChampionClass.Fighter), - new Champion("Ahri", ChampionClass.Mage), - new Champion("Akshan", ChampionClass.Marksman), - new Champion("Bard", ChampionClass.Support), - new Champion("Alistar", ChampionClass.Tank), - }; - - public class ChampionsManager : IChampionsManager + public partial class StubData + { + private List champions = new() + { + new Champion("Akali", ChampionClass.Assassin), + new Champion("Aatrox", ChampionClass.Fighter), + new Champion("Ahri", ChampionClass.Mage), + new Champion("Akshan", ChampionClass.Marksman), + new Champion("Bard", ChampionClass.Support), + new Champion("Alistar", ChampionClass.Tank), + }; + + public class ChampionsManager : IChampionsManager { private readonly StubData parent; @@ -82,19 +82,24 @@ namespace StubLib (parent.championsAndRunePages .Where(tuple => tuple.Item2.Equals(runePage)) .Select(tuple => tuple.Item1) - .Skip(index*count).Take(count)); + .Skip(index * count).Take(count)); + + private Func filterByName = (champ, substring) => champ.Name.Equals(substring, StringComparison.InvariantCultureIgnoreCase); - private Func filterByName = (champ, substring) => champ.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase); + private Func filterByNameContains = (champ, substring) => champ.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase); public Task GetNbItemsByName(string substring) => parent.champions.GetNbItemsWithFilter(champ => filterByName(champ, substring)); public Task> GetItemsByName(string substring, int index, int count, string? orderingPropertyName, bool descending = false) + => parent.champions.GetItemsWithFilterAndOrdering(champ => filterByNameContains(champ, substring), index, count, orderingPropertyName, descending); + + public Task> GetItemByName(string substring, int index, int count, string? orderingPropertyName, bool descending = false) => parent.champions.GetItemsWithFilterAndOrdering(champ => filterByName(champ, substring), index, count, orderingPropertyName, descending); public Task UpdateItem(Champion? oldItem, Champion? newItem) => parent.champions.UpdateItem(oldItem, newItem); } - } + } } diff --git a/src/EntityFramework_LoL/Sources/StubLib/StubData.RunePages.cs b/src/EntityFramework_LoL/Sources/StubLib/StubData.RunePages.cs index a08a947..2ec8285 100644 --- a/src/EntityFramework_LoL/Sources/StubLib/StubData.RunePages.cs +++ b/src/EntityFramework_LoL/Sources/StubLib/StubData.RunePages.cs @@ -27,6 +27,9 @@ namespace StubLib => this.parent = parent; private static Func filterByName + = (rp, substring) => rp.Name.Equals(substring, StringComparison.InvariantCultureIgnoreCase); + + private static Func filterByNameContains = (rp, substring) => rp.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase); private static Func filterByRune @@ -51,6 +54,11 @@ namespace StubLib .Skip(index*count).Take(count)); public Task> GetItemsByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false) + => parent.runePages.GetItemsWithFilterAndOrdering( + rp => filterByNameContains(rp, substring), + index, count, orderingPropertyName, descending); + + public Task> GetItemByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false) => parent.runePages.GetItemsWithFilterAndOrdering( rp => filterByName(rp, substring), index, count, orderingPropertyName, descending); diff --git a/src/EntityFramework_LoL/Sources/StubLib/StubData.Runes.cs b/src/EntityFramework_LoL/Sources/StubLib/StubData.Runes.cs index f0e8802..5ce8306 100644 --- a/src/EntityFramework_LoL/Sources/StubLib/StubData.Runes.cs +++ b/src/EntityFramework_LoL/Sources/StubLib/StubData.Runes.cs @@ -37,6 +37,9 @@ namespace StubLib = (rune, family) => rune.Family == family; private static Func filterByName + = (rune, substring) => rune.Name.Equals(substring, StringComparison.InvariantCultureIgnoreCase); + + private static Func filterByNameContains = (rune, substring) => rune.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase); public Task> GetItemsByFamily(RuneFamily family, int index, int count, string? orderingPropertyName = null, bool descending = false) @@ -45,6 +48,11 @@ namespace StubLib index, count, orderingPropertyName, descending); public Task> GetItemsByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false) + => parent.runes.GetItemsWithFilterAndOrdering( + rune => filterByNameContains(rune, substring), + index, count, orderingPropertyName, descending); + + public Task> GetItemByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false) => parent.runes.GetItemsWithFilterAndOrdering( rune => filterByName(rune, substring), index, count, orderingPropertyName, descending); diff --git a/src/EntityFramework_LoL/Sources/StubLib/StubData.Skins.cs b/src/EntityFramework_LoL/Sources/StubLib/StubData.Skins.cs index ff5fc08..5c44dfe 100644 --- a/src/EntityFramework_LoL/Sources/StubLib/StubData.Skins.cs +++ b/src/EntityFramework_LoL/Sources/StubLib/StubData.Skins.cs @@ -48,7 +48,9 @@ namespace StubLib private static Func filterByChampion = (skin, champion) => champion != null && skin.Champion.Equals(champion!); - private static Func filterByName = (skin, substring) => skin.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase); + private static Func filterByName = (skin, substring) => skin.Name.Equals(substring, StringComparison.InvariantCultureIgnoreCase); + + private static Func filterByNameContains = (skin, substring) => skin.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase); public Task> GetItemsByChampion(Champion? champion, int index, int count, string? orderingPropertyName = null, bool descending = false) => parent.skins.GetItemsWithFilterAndOrdering( @@ -56,6 +58,11 @@ namespace StubLib index, count, orderingPropertyName, descending); public Task> GetItemsByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false) + => parent.skins.GetItemsWithFilterAndOrdering( + skin => filterByNameContains(skin, substring), + index, count, orderingPropertyName, descending); + + public Task> GetItemByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false) => parent.skins.GetItemsWithFilterAndOrdering( skin => filterByName(skin, substring), index, count, orderingPropertyName, descending); diff --git a/src/EntityFramework_LoL/Sources/Tests/ApiTests/ChampionsControllerTest.cs b/src/EntityFramework_LoL/Sources/Tests/ApiTests/ChampionsControllerTest.cs index 5546a25..078a64a 100644 --- a/src/EntityFramework_LoL/Sources/Tests/ApiTests/ChampionsControllerTest.cs +++ b/src/EntityFramework_LoL/Sources/Tests/ApiTests/ChampionsControllerTest.cs @@ -1,7 +1,9 @@ using ApiLol.Controllers; +using ApiLol.Controllers.v2; using DTO; using Microsoft.AspNetCore.Mvc; -using Model; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using StubLib; namespace ApiTests @@ -14,7 +16,7 @@ namespace ApiTests public ChampionsControllerTest() { stub = new StubData(); - champs = new ChampionsController(stub); + champs = new ChampionsController(stub, new NullLogger()); } [TestMethod] @@ -23,7 +25,8 @@ namespace ApiTests //Arrange //Act - var champion = await champs.Get(); + var total = await stub.ChampionsMgr.GetNbItems(); + var champion = await champs.Get(new PageRequest()); //Assert var objectResult = champion as OkObjectResult; @@ -32,7 +35,7 @@ namespace ApiTests var champions = objectResult?.Value as IEnumerable; Assert.IsNotNull(champions); - Assert.AreEqual(champions.Count(), await stub.ChampionsMgr.GetNbItems()); + Assert.AreEqual(champions.Count(), total); } @@ -43,7 +46,11 @@ namespace ApiTests var ChampionDto = new ChampionDto { Name = "Sylas", - Bio = "Good" + Bio = "Good", + Class = ChampionClassDto.Tank, + Icon = "", + Image = new LargeImageDto() { Base64 = "" }, + Skins = new List() }; //Act @@ -56,6 +63,64 @@ namespace ApiTests var champions = objectResult?.Value as ChampionDto; Assert.IsNotNull(champions); + } + + [TestMethod] + public async Task TestPutChampion() + { + //Arange + var ChampionDto = new ChampionDto + { + Name = "Sylas", + Bio = "Good", + Class = ChampionClassDto.Tank, + Icon = "", + Image = new LargeImageDto() { Base64 = "" }, + Skins = new List() + }; + var ChampionDtoPut = new ChampionDto + { + Name = "Sylas", + Bio = "Bad", + Class = ChampionClassDto.Tank, + Icon = "", + Image = new LargeImageDto() { Base64 = "" }, + Skins = new List() + }; + + //Act + await champs.Post(ChampionDto); + var championsResult = await champs.Put(ChampionDto.Name, ChampionDtoPut); + + //Assert + var objectResult = championsResult as OkObjectResult; + Assert.IsNotNull(objectResult); + + var champions = objectResult?.Value as ChampionDto; + Assert.IsNotNull(champions); + + Assert.AreNotEqual(ChampionDto.Bio, champions.Bio); + Assert.AreEqual(ChampionDtoPut.Bio, champions.Bio); + + } + + [TestMethod] + public async Task TestDeleteChampion() + { + //Arange + + + //Act + var total = await stub.ChampionsMgr.GetNbItems(); + var championsResult = await champs.Delete("Akali"); + + //Assert + var objectResult = championsResult as OkObjectResult; + Assert.IsNotNull(objectResult); + + Assert.AreEqual(objectResult.Value, true); + Assert.AreNotEqual(await stub.ChampionsMgr.GetNbItems(), total); + } } diff --git a/src/EntityFramework_LoL/Sources/Tests/ConsoleTests/Program.cs b/src/EntityFramework_LoL/Sources/Tests/ConsoleTests/Program.cs index 28dc26b..80c9d9f 100644 --- a/src/EntityFramework_LoL/Sources/Tests/ConsoleTests/Program.cs +++ b/src/EntityFramework_LoL/Sources/Tests/ConsoleTests/Program.cs @@ -1,338 +1,338 @@ using System.Collections.Immutable; -using System.Diagnostics; -using Microsoft.Extensions.DependencyInjection; -using Model; -using StubLib; -using static System.Console; - -namespace ConsoleTests -{ - static class Program - { - static IDataManager dataManager = null!; - - static async Task Main(string[] args) - { - try - { - using var servicesProvider = new ServiceCollection() - .AddSingleton() - .BuildServiceProvider(); - - dataManager = servicesProvider.GetRequiredService(); - - await DisplayMainMenu(); - - Console.ReadLine(); - } - catch (Exception ex) - { - Debug.WriteLine(ex, "Stopped program because of exception"); - throw; - } - } - - public static async Task DisplayMainMenu() - { - Dictionary choices = new Dictionary() - { - [1] = "1- Manage Champions", - [2] = "2- Manage Skins", - [3] = "3- Manage Runes", - [4] = "4- Manage Rune Pages", - [99] = "99- Quit" - }; - - while(true) - { - int input = DisplayAMenu(choices); - - switch(input) - { - case 1: - await DisplayChampionsMenu(); - break; - case 2: - break; - case 3: - break; - case 4: - break; - case 99: - WriteLine("Bye bye!"); - return; - default: - break; - } - } - } - - private static int DisplayAMenu(Dictionary choices) - { - int input=-1; - while(true) - { - WriteLine("What is your choice?"); - WriteLine("--------------------"); - foreach(var choice in choices.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value)) - { - WriteLine(choice); - } - if(!int.TryParse(ReadLine(), out input) || input == -1) - { - WriteLine("I do not understand what your choice is. Please try again."); - continue; - } - break; - } - WriteLine($"You have chosen: {choices[input]}"); - WriteLine(); - return input; - } - - public static async Task DisplayChampionsMenu() - { - Dictionary choices = new Dictionary() - { - [0] = "0- Get number of champions", - [1] = "1- Get champions", - [2] = "2- Find champions by name", - [3] = "3- Find champions by characteristic", - [4] = "4- Find champions by class", - [5] = "5- Find champions by skill", - [6] = "6- Add new champion", - [7] = "7- Delete a champion", - [8] = "8- Update a champion", - }; - - int input = DisplayAMenu(choices); - - switch(input) - { - case 0: - int nb = await dataManager.ChampionsMgr.GetNbItems(); - WriteLine($"There are {nb} champions"); - WriteLine("**********************"); - break; - case 1: - { - int index = ReadAnInt("Please enter the page index"); - int count = ReadAnInt("Please enter the number of elements to display"); - WriteLine($"{count} champions of page {index+1}"); - var champions = await dataManager.ChampionsMgr.GetItems(index, count, nameof(Champion.Name)); - foreach(var champion in champions) - { - WriteLine($"\t{champion}"); - } - WriteLine("**********************"); - } - break; - case 2: - { - string substring = ReadAString("Please enter the substring to look for in the name of a champion"); - int index = ReadAnInt("Please enter the page index"); - int count = ReadAnInt("Please enter the number of elements to display"); - var champions = await dataManager.ChampionsMgr.GetItemsByName(substring, index, count, nameof(Champion.Name)); - foreach(var champion in champions) - { - WriteLine($"\t{champion}"); - } - WriteLine("**********************"); - } - break; - case 3: - { - string substring = ReadAString("Please enter the substring to look for in the characteristics of champions"); - int index = ReadAnInt("Please enter the page index"); - int count = ReadAnInt("Please enter the number of elements to display"); - var champions = await dataManager.ChampionsMgr.GetItemsByCharacteristic(substring, index, count, nameof(Champion.Name)); - foreach(var champion in champions) - { - WriteLine($"\t{champion}"); - } - WriteLine("**********************"); - } - break; - case 4: - { - ChampionClass championClass = ReadAnEnum($"Please enter the champion class (possible values are: {Enum.GetNames().Aggregate("", (name, chaine) => $"{chaine} {name}")}):"); - int index = ReadAnInt("Please enter the page index"); - int count = ReadAnInt("Please enter the number of elements to display"); - var champions = await dataManager.ChampionsMgr.GetItemsByClass(championClass, index, count, nameof(Champion.Name)); - foreach(var champion in champions) - { - WriteLine($"\t{champion}"); - } - WriteLine("**********************"); - } - break; - case 5: - { - string substring = ReadAString("Please enter the substring to look for in the skills of champions"); - int index = ReadAnInt("Please enter the page index"); - int count = ReadAnInt("Please enter the number of elements to display"); - var champions = await dataManager.ChampionsMgr.GetItemsBySkill(substring, index, count, nameof(Champion.Name)); - foreach(var champion in champions) - { - WriteLine($"\t{champion}"); - } - WriteLine("**********************"); - } - break; - case 6: - { - WriteLine("You are going to create a new champion."); - string name = ReadAString("Please enter the champion name:"); - ChampionClass championClass = ReadAnEnum($"Please enter the champion class (possible values are: {Enum.GetNames().Aggregate("", (name, chaine) => $"{chaine} {name}")}):"); - string bio = ReadAString("Please enter the champion bio:"); - Champion champion = new Champion(name, championClass, bio: bio); - DisplayCreationChampionMenu(champion); - _ = await dataManager.ChampionsMgr.AddItem(champion); - } - break; - case 7: - { - WriteLine("You are going to delete a champion."); - string name = ReadAString("Please enter the champion name:"); - var somechampions = await dataManager.ChampionsMgr.GetItemsByName(name, 0, 10, nameof(Champion.Name)); - var someChampionNames = somechampions.Select(c => c!.Name); - var someChampionNamesAsOneString = someChampionNames.Aggregate("", (name, chaine) => $"{chaine} {name}"); - string champName = ReadAStringAmongPossibleValues($"Who do you want to delete among these champions? (type \"Cancel\" to ... cancel) {someChampionNamesAsOneString}", - someChampionNames.ToArray()); - if(champName != "Cancel") - { - await dataManager.ChampionsMgr.DeleteItem(somechampions.Single(c => c!.Name == champName)); - } - } - break; - case 8: - { - WriteLine("You are going to update a champion."); - string name = ReadAString("Please enter the champion name:"); - var somechampions = await dataManager.ChampionsMgr.GetItemsByName(name, 0, 10, nameof(Champion.Name)); - var someChampionNames = somechampions.Select(c => c!.Name); - var someChampionNamesAsOneString = someChampionNames.Aggregate("", (name, chaine) => $"{chaine} {name}"); - string champName = ReadAStringAmongPossibleValues($"Who do you want to update among these champions? (type \"Cancel\" to ... cancel) {someChampionNamesAsOneString}", - someChampionNames.ToArray()); - if(champName == "Cancel") break; - ChampionClass championClass = ReadAnEnum($"Please enter the champion class (possible values are: {Enum.GetNames().Aggregate("", (name, chaine) => $"{chaine} {name}")}):"); - string bio = ReadAString("Please enter the champion bio:"); - Champion champion = new Champion(champName, championClass, bio: bio); - DisplayCreationChampionMenu(champion); - await dataManager.ChampionsMgr.UpdateItem(somechampions.Single(c => c!.Name == champName), champion); - } - break; - default: - break; - } - - } - - public static void DisplayCreationChampionMenu(Champion champion) - { - Dictionary choices = new Dictionary() - { - [1] = "1- Add a skill", - [2] = "2- Add a skin", - [3] = "3- Add a characteristic", - [99] = "99- Finish" - }; - - while(true) - { - int input = DisplayAMenu(choices); - - switch(input) - { - case 1: - string skillName = ReadAString("Please enter the skill name:"); - SkillType skillType = ReadAnEnum($"Please enter the skill type (possible values are: {Enum.GetNames().Aggregate("", (name, chaine) => $"{chaine} {name}")}):"); - string skillDescription = ReadAString("Please enter the skill description:"); - Skill skill = new Skill(skillName, skillType, skillDescription); - champion.AddSkill(skill); - break; - case 2: - string skinName = ReadAString("Please enter the skin name:"); - string skinDescription = ReadAString("Please enter the skin description:"); - float skinPrice = ReadAFloat("Please enter the price of this skin:"); - Skin skin = new Skin(skinName, champion, skinPrice, description: skinDescription); - break; - case 3: - string characteristic = ReadAString("Please enter the characteristic:"); - int value = ReadAnInt("Please enter the value associated to this characteristic:"); - champion.AddCharacteristics(Tuple.Create(characteristic, value)); - break; - case 99: - return; - default: - break; - } - } - } - - private static int ReadAnInt(string message) - { - while(true) - { - WriteLine(message); - if(!int.TryParse(ReadLine(), out int result)) - { - continue; - } - return result; - } - } - - private static float ReadAFloat(string message) - { - while(true) - { - WriteLine(message); - if(!float.TryParse(ReadLine(), out float result)) - { - continue; - } - return result; - } - } - - private static string ReadAString(string message) - { - while(true) - { - WriteLine(message); - string? line = ReadLine(); - if(line == null) - { - continue; - } - return line!; - } - } - - private static TEnum ReadAnEnum(string message) where TEnum :struct - { - while(true) - { - WriteLine(message); - if(!Enum.TryParse(ReadLine(), out TEnum result)) - { - continue; - } - return result; - } - } - - private static string ReadAStringAmongPossibleValues(string message, params string[] possibleValues) - { - while(true) - { - WriteLine(message); - string? result = ReadLine(); - if(result == null) continue; - if(result != "Cancel" && !possibleValues.Contains(result!)) continue; - return result!; - } - } - } +using System.Diagnostics; +using Microsoft.Extensions.DependencyInjection; +using Model; +using StubLib; +using static System.Console; + +namespace ConsoleTests +{ + static class Program + { + static IDataManager dataManager = null!; + + static async Task Main(string[] args) + { + try + { + using var servicesProvider = new ServiceCollection() + .AddSingleton() + .BuildServiceProvider(); + + dataManager = servicesProvider.GetRequiredService(); + + await DisplayMainMenu(); + + Console.ReadLine(); + } + catch (Exception ex) + { + Debug.WriteLine(ex, "Stopped program because of exception"); + throw; + } + } + + public static async Task DisplayMainMenu() + { + Dictionary choices = new Dictionary() + { + [1] = "1- Manage Champions", + [2] = "2- Manage Skins", + [3] = "3- Manage Runes", + [4] = "4- Manage Rune Pages", + [99] = "99- Quit" + }; + + while(true) + { + int input = DisplayAMenu(choices); + + switch(input) + { + case 1: + await DisplayChampionsMenu(); + break; + case 2: + break; + case 3: + break; + case 4: + break; + case 99: + WriteLine("Bye bye!"); + return; + default: + break; + } + } + } + + private static int DisplayAMenu(Dictionary choices) + { + int input=-1; + while(true) + { + WriteLine("What is your choice?"); + WriteLine("--------------------"); + foreach(var choice in choices.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value)) + { + WriteLine(choice); + } + if(!int.TryParse(ReadLine(), out input) || input == -1) + { + WriteLine("I do not understand what your choice is. Please try again."); + continue; + } + break; + } + WriteLine($"You have chosen: {choices[input]}"); + WriteLine(); + return input; + } + + public static async Task DisplayChampionsMenu() + { + Dictionary choices = new Dictionary() + { + [0] = "0- Get number of champions", + [1] = "1- Get champions", + [2] = "2- Find champions by name", + [3] = "3- Find champions by characteristic", + [4] = "4- Find champions by class", + [5] = "5- Find champions by skill", + [6] = "6- Add new champion", + [7] = "7- Delete a champion", + [8] = "8- Update a champion", + }; + + int input = DisplayAMenu(choices); + + switch(input) + { + case 0: + int nb = await dataManager.ChampionsMgr.GetNbItems(); + WriteLine($"There are {nb} champions"); + WriteLine("**********************"); + break; + case 1: + { + int index = ReadAnInt("Please enter the page index"); + int count = ReadAnInt("Please enter the number of elements to display"); + WriteLine($"{count} champions of page {index+1}"); + var champions = await dataManager.ChampionsMgr.GetItems(index, count, nameof(Champion.Name)); + foreach(var champion in champions) + { + WriteLine($"\t{champion}"); + } + WriteLine("**********************"); + } + break; + case 2: + { + string substring = ReadAString("Please enter the substring to look for in the name of a champion"); + int index = ReadAnInt("Please enter the page index"); + int count = ReadAnInt("Please enter the number of elements to display"); + var champions = await dataManager.ChampionsMgr.GetItemsByName(substring, index, count, nameof(Champion.Name)); + foreach(var champion in champions) + { + WriteLine($"\t{champion}"); + } + WriteLine("**********************"); + } + break; + case 3: + { + string substring = ReadAString("Please enter the substring to look for in the characteristics of champions"); + int index = ReadAnInt("Please enter the page index"); + int count = ReadAnInt("Please enter the number of elements to display"); + var champions = await dataManager.ChampionsMgr.GetItemsByCharacteristic(substring, index, count, nameof(Champion.Name)); + foreach(var champion in champions) + { + WriteLine($"\t{champion}"); + } + WriteLine("**********************"); + } + break; + case 4: + { + ChampionClass championClass = ReadAnEnum($"Please enter the champion class (possible values are: {Enum.GetNames().Aggregate("", (name, chaine) => $"{chaine} {name}")}):"); + int index = ReadAnInt("Please enter the page index"); + int count = ReadAnInt("Please enter the number of elements to display"); + var champions = await dataManager.ChampionsMgr.GetItemsByClass(championClass, index, count, nameof(Champion.Name)); + foreach(var champion in champions) + { + WriteLine($"\t{champion}"); + } + WriteLine("**********************"); + } + break; + case 5: + { + string substring = ReadAString("Please enter the substring to look for in the skills of champions"); + int index = ReadAnInt("Please enter the page index"); + int count = ReadAnInt("Please enter the number of elements to display"); + var champions = await dataManager.ChampionsMgr.GetItemsBySkill(substring, index, count, nameof(Champion.Name)); + foreach(var champion in champions) + { + WriteLine($"\t{champion}"); + } + WriteLine("**********************"); + } + break; + case 6: + { + WriteLine("You are going to create a new champion."); + string name = ReadAString("Please enter the champion name:"); + ChampionClass championClass = ReadAnEnum($"Please enter the champion class (possible values are: {Enum.GetNames().Aggregate("", (name, chaine) => $"{chaine} {name}")}):"); + string bio = ReadAString("Please enter the champion bio:"); + Champion champion = new Champion(name, championClass, bio: bio); + DisplayCreationChampionMenu(champion); + _ = await dataManager.ChampionsMgr.AddItem(champion); + } + break; + case 7: + { + WriteLine("You are going to delete a champion."); + string name = ReadAString("Please enter the champion name:"); + var somechampions = await dataManager.ChampionsMgr.GetItemsByName(name, 0, 10, nameof(Champion.Name)); + var someChampionNames = somechampions.Select(c => c!.Name); + var someChampionNamesAsOneString = someChampionNames.Aggregate("", (name, chaine) => $"{chaine} {name}"); + string champName = ReadAStringAmongPossibleValues($"Who do you want to delete among these champions? (type \"Cancel\" to ... cancel) {someChampionNamesAsOneString}", + someChampionNames.ToArray()); + if(champName != "Cancel") + { + await dataManager.ChampionsMgr.DeleteItem(somechampions.Single(c => c!.Name == champName)); + } + } + break; + case 8: + { + WriteLine("You are going to update a champion."); + string name = ReadAString("Please enter the champion name:"); + var somechampions = await dataManager.ChampionsMgr.GetItemsByName(name, 0, 10, nameof(Champion.Name)); + var someChampionNames = somechampions.Select(c => c!.Name); + var someChampionNamesAsOneString = someChampionNames.Aggregate("", (name, chaine) => $"{chaine} {name}"); + string champName = ReadAStringAmongPossibleValues($"Who do you want to update among these champions? (type \"Cancel\" to ... cancel) {someChampionNamesAsOneString}", + someChampionNames.ToArray()); + if(champName == "Cancel") break; + ChampionClass championClass = ReadAnEnum($"Please enter the champion class (possible values are: {Enum.GetNames().Aggregate("", (name, chaine) => $"{chaine} {name}")}):"); + string bio = ReadAString("Please enter the champion bio:"); + Champion champion = new Champion(champName, championClass, bio: bio); + DisplayCreationChampionMenu(champion); + await dataManager.ChampionsMgr.UpdateItem(somechampions.Single(c => c!.Name == champName), champion); + } + break; + default: + break; + } + + } + + public static void DisplayCreationChampionMenu(Champion champion) + { + Dictionary choices = new Dictionary() + { + [1] = "1- Add a skill", + [2] = "2- Add a skin", + [3] = "3- Add a characteristic", + [99] = "99- Finish" + }; + + while(true) + { + int input = DisplayAMenu(choices); + + switch(input) + { + case 1: + string skillName = ReadAString("Please enter the skill name:"); + SkillType skillType = ReadAnEnum($"Please enter the skill type (possible values are: {Enum.GetNames().Aggregate("", (name, chaine) => $"{chaine} {name}")}):"); + string skillDescription = ReadAString("Please enter the skill description:"); + Skill skill = new Skill(skillName, skillType, skillDescription); + champion.AddSkill(skill); + break; + case 2: + string skinName = ReadAString("Please enter the skin name:"); + string skinDescription = ReadAString("Please enter the skin description:"); + float skinPrice = ReadAFloat("Please enter the price of this skin:"); + Skin skin = new Skin(skinName, champion, skinPrice, description: skinDescription); + break; + case 3: + string characteristic = ReadAString("Please enter the characteristic:"); + int value = ReadAnInt("Please enter the value associated to this characteristic:"); + champion.AddCharacteristics(Tuple.Create(characteristic, value)); + break; + case 99: + return; + default: + break; + } + } + } + + private static int ReadAnInt(string message) + { + while(true) + { + WriteLine(message); + if(!int.TryParse(ReadLine(), out int result)) + { + continue; + } + return result; + } + } + + private static float ReadAFloat(string message) + { + while(true) + { + WriteLine(message); + if(!float.TryParse(ReadLine(), out float result)) + { + continue; + } + return result; + } + } + + private static string ReadAString(string message) + { + while(true) + { + WriteLine(message); + string? line = ReadLine(); + if(line == null) + { + continue; + } + return line!; + } + } + + private static TEnum ReadAnEnum(string message) where TEnum :struct + { + while(true) + { + WriteLine(message); + if(!Enum.TryParse(ReadLine(), out TEnum result)) + { + continue; + } + return result; + } + } + + private static string ReadAStringAmongPossibleValues(string message, params string[] possibleValues) + { + while(true) + { + WriteLine(message); + string? result = ReadLine(); + if(result == null) continue; + if(result != "Cancel" && !possibleValues.Contains(result!)) continue; + return result!; + } + } + } } \ No newline at end of file