Compare commits

...

71 Commits

Author SHA1 Message Date
Emre KARTAL 5ba7ffaea9 Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL ab3a4bba0c Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is failing Details
2 years ago
Emre KARTAL 998dff6490 Update README
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 2c630c77d0 Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL de4a02bcc8 Merge pull request 'ApiManager' (#11) from ApiManager into master
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 8f28944450 Correct some errors 🐛
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL f66fc1a48d completion of the ApiManager
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL dded54568b Beginning ApiManager
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL c2372ec447 Update README
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 8c5a9d7b49 Update README
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 40da19ed6f Correct error CI 🐛
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 91c36e266c correct some errors 🐛
continuous-integration/drone/push Build is failing Details
2 years ago
Emre KARTAL 43cf7792e6 Update README
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL f929c72194 Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 51afc1d03f Merge pull request 'MAUI_Settings' (#10) from MAUI_Settings into master
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 75f43c319f Merge branch 'MAUI_Settings' of https://codefirst.iut.uca.fr/git/emre.kartal/LolProject into MAUI_Settings
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL cf320b341b DbManager and MAUI Done
2 years ago
Emre KARTAL da5d3d7bef Update README
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL b53c1e10e4 Update README
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL ac20890cb6 Update README and completion of the DbManager Champion
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 9dfa473ad3 Mise à jour de 'README.md'
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL f5155dafb8 Mise à jour de 'README.md'
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 0b19d8b016 Mise à jour de 'README.md'
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL cb0c9e4071 Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL ceeae34769 Mise à jour de 'README.md'
continuous-integration/drone/push Build is failing Details
2 years ago
Emre KARTAL 7bd6b69cd0 Mise à jour de 'README.md'
continuous-integration/drone/push Build is failing Details
2 years ago
Emre KARTAL 8da497bd6e Add Client MAUI
continuous-integration/drone/push Build is failing Details
2 years ago
Emre KARTAL 00c5ad1c24 Changes to achievements
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL f1142a0f0e Merge pull request 'UT_EF_Manager' (#9) from UT_EF_Manager into master
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 0384119f22 Added Mapper to Database
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 620d50c97d Create DbManager 🔨
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 82984ee4d6 Added more UT for the Entities 👍
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 30542716df Many to Many and the dictionaries done 🐵
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 78e19b25d5 Update README
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL cfae72db6c Update README
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL f0d1451095 Merge pull request 'API_RunePage_ReadMe_UT' (#8) from API_RunePage_ReadMe_UT into master
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 195db06bfd RunePagesController and UT done
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 48f4b27ae3 Addition of modifications of Mr Chevaldonne and rehabilitation of unit tests 🐛
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL b603bcdd0d Correct Error for UT
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 8e6fd3984a Again correct error ⚠️
continuous-integration/drone/push Build is failing Details
2 years ago
Emre KARTAL c7f434a29c Correct error ⚠️
continuous-integration/drone/push Build is failing Details
2 years ago
Emre KARTAL b937756b4b Dictionary for ChampionDto done and beginning the RunePageController 🙈
continuous-integration/drone/push Build is failing Details
2 years ago
Emre KARTAL 6d2d6f9ffd Update README
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 43001258c8 Merge branch 'API_RunePage_ReadMe_UT' of https://codefirst.iut.uca.fr/git/emre.kartal/LolProject into API_RunePage_ReadMe_UT
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 3794752e70 More UT and correct error
2 years ago
Emre KARTAL 0d4056e4fc Correction of errors in the README 🚩
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 7d8152215a Explanation of the achitecture in the README done ✏️
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL b1ac2fccd3 More API UT and create a pageResponse DTO 🔋
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL d71c2d9138 Merge branch 'NextStep_Controllers' 🌊
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL a9ebafdc69 Add the global architecture of the project 👾
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 9408cee2da Mise à jour de 'README.md'
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 299f97c73f Merge branch 'NextStep_Controllers' of https://codefirst.iut.uca.fr/git/emre.kartal/LolProject into NextStep_Controllers
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 7759337bcf just update Controllers 📀
2 years ago
Emre KARTAL 09ff4c71ac Mise à jour de 'README.md'
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 2435c10820 Mise à jour de 'README.md'
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 49a795992b Mise à jour de 'README.md'
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 701f9b4494 New Mappers, Controllers and beginning SkinsControllerTest 🎉
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 32d41969f1 Merge pull request (#6) from UT_Entity_Relation_NextStep into master
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL a1dadd05d7 just better code
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL b7f49bcdf7 Mise à jour de 'README.md'
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 011ef659a4 Merge pull request 'Logs_Version_Filtrage_Pagination' (#5) from Logs_Version_Filtrage_Pagination into master
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 392fc430cc Finish Champions Controller (filtrage, pagination ...) and add function for UTChampion
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL fa57eba9b8 Updating ChampionsController but not finish
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 0b5a3f1d03 Updating logs from ChampionsController class and create a new function for ChampionStub
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 074892b3a7 Changed program.cs for versioning in the swagger, also fixed uniqueness issues and created 3 versions of the ChampionController
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL c88eac1354 Add new functions for ChampionsController and create Skill Dto with mapper
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL afbd78428b Correct error
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 1a8f5c8c6c Add new DTO and mapper
continuous-integration/drone/push Build is failing Details
2 years ago
Emre KARTAL ec07596ea3 Realized Logs and pagination
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL 72dc884125 Correcting README and add deploy container into drone.yml
continuous-integration/drone/push Build is passing Details
2 years ago
Emre KARTAL b9b995a99b Comple ChampionHttpClient
continuous-integration/drone/push Build is passing Details
2 years ago

@ -1,6 +1,6 @@
kind: pipeline kind: pipeline
type: docker type: docker
name: default name: LolProject
trigger: trigger:
event: event:
@ -14,28 +14,28 @@ steps:
path: /docs path: /docs
commands: commands:
- cd src/EntityFramework_LoL/Sources/ - cd src/EntityFramework_LoL/Sources/
- dotnet restore LeagueOfLegends.sln - dotnet restore LeagueOfLegendsCI.sln
- dotnet build LeagueOfLegends.sln -c Release --no-restore - dotnet build LeagueOfLegendsCI.sln -c Release --no-restore
- dotnet publish LeagueOfLegends.sln -c Release --no-restore -o CI_PROJECT_DIR/build/release - dotnet publish LeagueOfLegendsCI.sln -c Release --no-restore -o CI_PROJECT_DIR/build/release
- name: tests - name: tests
image: mcr.microsoft.com/dotnet/sdk:6.0 image: mcr.microsoft.com/dotnet/sdk:6.0
commands: commands:
- cd src/EntityFramework_LoL/Sources/ - cd src/EntityFramework_LoL/Sources/
- dotnet restore LeagueOfLegends.sln - dotnet restore LeagueOfLegendsCI.sln
- dotnet test LeagueOfLegends.sln --no-restore - dotnet test LeagueOfLegendsCI.sln --no-restore
depends_on: [build] depends_on: [build]
- name: code-analysis - name: code-analysis
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dronesonarplugin-dotnet6 image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dronesonarplugin-dotnet6
commands: commands:
- cd src/EntityFramework_LoL/Sources/ - cd src/EntityFramework_LoL/Sources/
- dotnet restore LeagueOfLegends.sln - dotnet restore LeagueOfLegendsCI.sln
- dotnet sonarscanner begin /k:LolProjectIUT /d:sonar.host.url=$${PLUGIN_SONAR_HOST} /d:sonar.coverageReportPaths="coveragereport/SonarQube.xml" /d:sonar.coverage.exclusions="Tests/**" /d:sonar.login=$${PLUGIN_SONAR_TOKEN} - dotnet sonarscanner begin /k:LolProjectIUT /d:sonar.host.url=$${PLUGIN_SONAR_HOST} /d:sonar.coverageReportPaths="coveragereport/SonarQube.xml" /d:sonar.coverage.exclusions="Tests/**,DbManager/**,Client/**" /d:sonar.login=$${PLUGIN_SONAR_TOKEN}
- dotnet build LeagueOfLegends.sln -c Release --no-restore - dotnet build LeagueOfLegendsCI.sln -c Release --no-restore
- dotnet test LeagueOfLegends.sln --logger trx --no-restore /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura --collect "XPlat Code Coverage" - dotnet test LeagueOfLegendsCI.sln --logger trx --no-restore /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura --collect "XPlat Code Coverage"
- reportgenerator -reports:"**/coverage.cobertura.xml" -reporttypes:SonarQube -targetdir:"coveragereport" - reportgenerator -reports:"**/coverage.cobertura.xml" -reporttypes:SonarQube -targetdir:"coveragereport"
- dotnet publish LeagueOfLegends.sln -c Release --no-restore -o CI_PROJECT_DIR/build/release - dotnet publish LeagueOfLegendsCI.sln -c Release --no-restore -o CI_PROJECT_DIR/build/release
- dotnet sonarscanner end /d:sonar.login=$${PLUGIN_SONAR_TOKEN} - dotnet sonarscanner end /d:sonar.login=$${PLUGIN_SONAR_TOKEN}
secrets: [ SECRET_SONAR_LOGIN ] secrets: [ SECRET_SONAR_LOGIN ]
settings: settings:
@ -46,6 +46,7 @@ steps:
from_secret: SECRET_SONAR_LOGIN from_secret: SECRET_SONAR_LOGIN
depends_on: [tests] depends_on: [tests]
# docker image build # docker image build
- name: docker-build-and-push - name: docker-build-and-push
image: plugins/docker image: plugins/docker
settings: settings:
@ -57,6 +58,17 @@ steps:
from_secret: SECRET_REGISTRY_USERNAME from_secret: SECRET_REGISTRY_USERNAME
password: password:
from_secret: SECRET_REGISTRY_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 - name: generate-and-deploy-docs
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-docdeployer image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-docdeployer
failure: ignore failure: ignore

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -4,26 +4,67 @@
</div> </div>
<div align = center>
---
[![Duplicated Lines (%)](https://codefirst.iut.uca.fr/sonar/api/project_badges/measure?project=LolProjectIUT&metric=duplicated_lines_density&token=b65c8ed30f0fbe374e41e98340dc910942b77bba)](https://codefirst.iut.uca.fr/sonar/dashboard?id=LolProjectIUT)
[![Security Rating](https://codefirst.iut.uca.fr/sonar/api/project_badges/measure?project=LolProjectIUT&metric=security_rating&token=b65c8ed30f0fbe374e41e98340dc910942b77bba)](https://codefirst.iut.uca.fr/sonar/dashboard?id=LolProjectIUT)
[![Vulnerabilities](https://codefirst.iut.uca.fr/sonar/api/project_badges/measure?project=LolProjectIUT&metric=vulnerabilities&token=b65c8ed30f0fbe374e41e98340dc910942b77bba)](https://codefirst.iut.uca.fr/sonar/dashboard?id=LolProjectIUT)
[![Coverage](https://codefirst.iut.uca.fr/sonar/api/project_badges/measure?project=LolProjectIUT&metric=coverage&token=b65c8ed30f0fbe374e41e98340dc910942b77bba)](https://codefirst.iut.uca.fr/sonar/dashboard?id=LolProjectIUT)
[![Bugs](https://codefirst.iut.uca.fr/sonar/api/project_badges/measure?project=LolProjectIUT&metric=bugs&token=b65c8ed30f0fbe374e41e98340dc910942b77bba)](https://codefirst.iut.uca.fr/sonar/dashboard?id=LolProjectIUT)
[![Maintainability Rating](https://codefirst.iut.uca.fr/sonar/api/project_badges/measure?project=LolProjectIUT&metric=sqale_rating&token=b65c8ed30f0fbe374e41e98340dc910942b77bba)](https://codefirst.iut.uca.fr/sonar/dashboard?id=LolProjectIUT)
---
**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 <img src="https://logo-marque.com/wp-content/uploads/2020/11/League-of-Legends-Embleme.png" width="40" > </div>
**Thème du projet** : Réalisation d'une API Rest Full et d'un ORM (Entity Framework) qui seront reliés à une base de données (SQLite) dans le thème de League of Legends <img src="https://logo-marque.com/wp-content/uploads/2020/11/League-of-Legends-Embleme.png" width="40" >
</br> </br>
<img src="doc/Images/Title-Répartition.png" width="400"> <img src="doc/Images/Title-Répartition.png" width="400">
La racine de notre gitlab est composée de deux dossiers essentiels au projet: <u>La racine de mon GitLab contient deux dossiers :open_file_folder: essentiels pour le projet :</u>
[**src**](src) : **Toute la partie codage de l'application** [**src**](src) : **Toute la partie codage de l'application**
[**doc**](doc) : **Documentation de l'application** [**doc**](doc) : **Documentation de l'application** (Images + Schéma de l'architecture globale de l'application)
👉 [**Solution de l'application**](src/EntityFramework_LoL/Sources/LeagueOfLegends.sln) 👉 [**Solution de l'application**](src/EntityFramework_LoL/Sources/LeagueOfLegends.sln)
👉 [**Solution de l'application pour la CI**](src/EntityFramework_LoL/Sources/LeagueOfLegendsCI.sln)
<img src="doc/Images/Title-Fonctionnement.png" width="460" > <img src="doc/Images/Title-Fonctionnement.png" width="460" >
- ### :building_construction: Comment est structurée l'architecture globale de l'application ?
Voici un **schéma** pour mieux représenter mon architecture :
<img src="doc/Schémas/Architecture_Global.png" width="1000" >
</br>
</br>
:information_source: Tout d'abord, il y a la partie client, qui correspond à tous les appareils :computer: qui vont effectuer des requêtes à mon API et recevoir, sous format *.json*:envelope: (JavaScript Object Notation), des informations sur des champions, des skins, etc.
La web API est chargée de transformer les classes métiers du modèle en DTO (Data Transfer Object), afin de filtrer et renvoyer seulement les données essentielles aux utilisateurs. Pour obtenir les données qu'elle va transmettre, elle utilise d'abord l'IDataManager du modèle, qui est hérité par le stub (:pushpin:un regroupement de fausses données qui permet de s'assurer que tout fonctionne et d'éviter de bloquer le développement), puis le DbManager, qui contactera la base de données et assurera la persistance des données.
Grâce à l'injection de dépendances dans le projet, la Web API n'a pas besoin d'être modifiée lorsqu'on veut passer du StubLib au DbManager (si vous ne savez pas ce que c'est, je vous invite à voir cette vidéo qui explique très clairement l'[Injection de dépendances](https://www.youtube.com/watch?v=OeWgBNR1BLU&t=10s&ab_channel=BaptisteMobileDev)).
La partie ```bases de données``` est gérée par le DbManager, qui utilise l'ORM Entity Framework (plus précisément le DbContext) pour récupérer les données dans la base de données. De plus, grâce à une factory :factory: (oui, comme celle de la web API), elle transforme les données en tables (appelées "Entities") en classes du modèle (et inversement).
Le client MAUI pourra effectuer des requêtes HTTP à l'API via l'API Manager, qui hérite de IDataManager et possède une liste de fonctions responsables d'exécuter les requêtes. De cette manière, le client n'aura pas besoin de connaître l'API pour interagir avec elle. :bulb:
- ### Comment récupérer le 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 :
<div align = center> <div align = center>
@ -31,7 +72,7 @@ Tout d'abord si ce n'est pas fait cloner le dépôt de la branche **master/main*
</div> </div>
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 :
<div align = center> <div align = center>
@ -39,16 +80,33 @@ Vous pouvez le cloner via un terminal dans le répertoire que vous souhaitez en
</div> </div>
: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 :question:
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** :
<div align = center>
![Lancer l'API](doc/Images/Api_Launcher.png)
</div>
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 2.0``` est normalement la plus récente et la plus performante en termes de gestion d'erreur :
<div align = center>
![Version API](doc/Images/Swagger_Version.png)
</div>
:paperclip: Vous pouvez aussi utiliser l'API via cette url: https://codefirst.iut.uca.fr/containers/emrekartal-lolApi/Api/v3/champions
- ### Comment lancer Le projet Entity Framework ? - ### Comment lancer Le projet Entity Framework ?
Afin de générer les migrations et les tables. 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 !).
<br>
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)!
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 :
<div align = center> <div align = center>
@ -56,13 +114,13 @@ Aussi assurer vous d'avoir installé sur Visual Studio au préalable les package
</div> </div>
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 ? - ### Comment voir la base de données ?
C'est bien beau toutes ces étapes mais sil n'y a pas de résultat à quoi cela sert ! ```C'est bien beau toutes ces étapes, mais sil 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** : Puis, afin de visualiser la migration dans la base de données, cliquer dans l'onglet **Outils**->**SQLLite/ SQL Server compact Toolbox** :
@ -72,7 +130,7 @@ Puis, afin de visualiser la migration dans la base de données, cliquer dans l'o
</div> </div>
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 :
<div align = center> <div align = center>
@ -80,21 +138,119 @@ Et enfin cliquer sur l'icône ci-dessous pour faire la connexion à la solution
</div> </div>
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, 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é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 ! 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.*
<br>
:information_source: *Notez qu'il est également possible grâce à SQLLite d'ajouter, modifier ou supprimer des données dans les tables.* - ### Comment utiliser l'application MAUI ?
Si vous préférez éviter la manipulation de l'API, vous pouvez également utiliser le **client MAUI**. Celui-ci contacte directement l'ApiManager, qui se charge des requêtes HTTP à l'API, et vous permet de visualiser et de modifier les données grâce à une interface graphique.
Pour accomplir cela, veuillez configurer les propriétés de la solution en cliquant avec le bouton droit de la souris sur la solution, puis en sélectionnant "propriétés". De cette façon, vous pourrez lancer à la fois l'API et l'application League of Legends. Assurez-vous d'avoir les mêmes configurations :
<div align = center>
![HowToLaunch](doc/Images/HowToLaunch.png)
</div>
<u>Page **Home**:</u>
<div align = center>
![start BD](doc/Images/MauiClientHome.png)
</div>
<u>Page **Champions**:</u>
<div align = center>
![start BD](doc/Images/MauiClientChampions.gif)
</div>
Vous pouvez vous amuser à filtrer les champions sur cette page, visualiser leurs skins, les modifier, et même en ajouter ! :grin:
:information_source: *Pour utiliser cette application, vous devez avoir la dernière version de Visual Studio, avoir effectué la migration auparavant (comme indiqué ci-dessus), et avoir sélectionné la solution **LeagueOfLegends** plutôt que **LeagueOfLegendsCi**. Si vous avez besoin d'aide ou si vous souhaitez obtenir plus d'informations, vous pouvez contacter le technicien responsable de l'application (c'est-à-dire moi), en vous référant à mon mail ci-dessous. :email: Notez également qu'il est capable de lancer l'application soit à partir de votre téléphone Android, soit via un émulateur !*
- ### Tests Unitaires :triangular_flag_on_post:
Afin de garantir un code de qualité avec le moins d'erreurs possible, j'ai pris soin de réaliser des tests unitaires tout au long de ce projet à l'aide de la bibliothèque **XUnit**. J'ai ensuite intégré ces tests à mon processus d'intégration continue (CI) via l'outil ```Drone```, ce qui m'a permis de détecter rapidement toute erreur ou anomalie tout au long du projet. L'utilisation de tests unitaires et d'une intégration continue a été essentielle pour garantir la qualité du code, améliorer la maintenabilité et réduire le temps nécessaire pour détecter et corriger les erreurs.:bug:
Toute la partie EF et API a été testée correctement. Cependant, le DbManager et l'ApiManager qui les utilisent n'ont pas été testés et ont été retirés de la couverture de code sur **SonarQube**. Voici la couverture de code actuelle de chaque partie (le 26/03/23) :
<div align = center>
![Couverture de code](doc/Images/Tests-Coverage.png)
</div>
:information_source: *La partie API a été testée uniquement pour vérifier le bon fonctionnement de ses mécanismes de base. Cependant, je n'ai pas réalisé de tests pour vérifier les scénarios d'erreur possibles qui pourraient survenir lors de l'exécution de ces fonctions. De plus, la partie modèle et la bibliothèque Stublib ont été testées simultanément avec l'API.*
- ### Avancement du projet :construction_worker:
<u>Où en suis-je</u>:grey_question::grey_exclamation: (:white_check_mark: réalisé, :warning: presque abouti, :x: non commencé )
Côté API :
- :white_check_mark: [**Partie 1**](src/EntityFramework_Lol/Sources/ApiLol/Controllers) : **Mise en place de toutes les opérations CRUD**
- :white_check_mark: [**Partie 2**](src/EntityFramework_Lol/Sources/ApiLol/Controllers) : **Respect des règles de routage, utilisation des bons status code**
- :white_check_mark: [**Partie 3**](src/EntityFramework_Lol/Sources/ApiLol/Controllers) : **Versionnage de l'api**
- :white_check_mark: [**Partie 4**](src/EntityFramework_Lol/Sources/Tests/ApiTests) : **Tests unitaires**
- :white_check_mark: [**Partie 5**](.drone.yml) : **Dockerisation et Hébergement de l'API**
- :white_check_mark: [**Partie 6**](src/EntityFramework_Lol/Sources/ApiLol/Controllers) : **Logs**
- :white_check_mark: [**Partie 7**](src/EntityFramework_Lol/Sources/ApiLol/Controllers) : **Filtrage + Pagination des données**
- :x: [**Partie 8**](src/EntityFramework_Lol/Sources/ApiLol/) : **Utilisation des fichiers configurations**
- :warning: [**Partie 9**](src/EntityFramework_Lol/Sources/) : **Réalisation du client MAUI et liaison avec l'api**
- :white_check_mark: [**Partie 10**](src/EntityFramework_Lol/Sources/ApiLol/) : **Liaison avec la base de données**
Côté Entity_Framework :
- :white_check_mark: [**Partie 1**](src/EntityFramework_Lol/Sources/MyFlib/Entities) : **Réalisation des Entities**
- :white_check_mark: [**Partie 2**](src/EntityFramework_Lol/Sources/MyFlib/DataSeeder.cs) : **Data seeder**
- :white_check_mark: [**Partie 3**](src/EntityFramework_Lol/Sources/Tests/UT_EF) : **Tests unitaires**
- :white_check_mark: [**Partie 4**](src/EntityFramework_Lol/Sources/MyFlib) : **Many to many**
- :white_check_mark: [**Partie 5**](src/EntityFramework_Lol/Sources/) : **DbManager**
- :white_check_mark: [**Partie 6**](src/EntityFramework_Lol/Sources/) : **Mapping**
Autre :
- :white_check_mark: [**Partie 1**](.drone.yml) : **Mise en place de SonarQube**
- :white_check_mark: [**Partie 2**](.drone.yml) : **Schéma et description de l'architecture globale de l'application**
<img src="doc/Images/Title-Environnement.png" width="400" > <img src="doc/Images/Title-Environnement.png" width="400" >
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 : 👇
<div align = center> <div align = center>
--- ---
&nbsp; ![Docnet](https://img.shields.io/badge/Docnet-000?style=for-the-badge&logo=Docnet&logoColor=white&color=white) &nbsp; ![Docker](https://img.shields.io/badge/Docker-2496ED.svg?style=for-the-badge&logo=Docker&logoColor=white)
&nbsp; ![C#](https://img.shields.io/badge/Csharp-000?style=for-the-badge&logo=csharp&logoColor=white&color=blue) &nbsp; ![Dotnet](https://img.shields.io/badge/Core-000?style=for-the-badge&logo=Dotnet&logoColor=white&color=purple)
&nbsp; ![C#](https://img.shields.io/badge/C%20Sharp-239120.svg?style=for-the-badge&logo=C-Sharp&logoColor=white)
&nbsp; ![SQLite](https://img.shields.io/badge/SQLite-003B57.svg?style=for-the-badge&logo=SQLite&logoColor=white)
--- ---
@ -102,9 +258,19 @@ Mon environnement de travail se base sur un outil et un langage en particulier :
<img src="doc/Images/Title-Technicien.png" width="400" > <img src="doc/Images/Title-Technicien.png" width="400" >
⚙️ Emre KARTAL :mortar_board: **Emre KARTAL** - *Emre.kartal@etu.uca.fr*
<br> <br>
<div align = center> <div align = center>
<a href = "https://codefirst.iut.uca.fr/git/emre.kartal">
<img src="https://codefirst.iut.uca.fr/git/avatars/402cf312e853192f42c0135a888725c2?size=870" width="50" >
</br>
</a>
© PM2 © PM2
</div> </div>
<hr>
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />Ce(tte) œuvre est mise à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Licence Creative Commons Attribution - Pas d&#39;Utilisation Commerciale - Pas de Modification 4.0 International</a>.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

@ -9,12 +9,16 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.2" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.15.1" /> <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.15.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ApiMapping\ApiMapping.csproj" />
<ProjectReference Include="..\DbManager\DbManager.csproj" />
<ProjectReference Include="..\DTO\DTO.csproj" /> <ProjectReference Include="..\DTO\DTO.csproj" />
<ProjectReference Include="..\Model\Model.csproj" /> <ProjectReference Include="..\Model\Model.csproj" />
<ProjectReference Include="..\StubLib\StubLib.csproj" /> <ProjectReference Include="..\StubLib\StubLib.csproj" />

@ -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/<ValuesController>
[HttpGet]
public async Task<IActionResult> Get()
{
IEnumerable<ChampionDto> dtos = (await _manager.ChampionsMgr.GetItems(0, await _manager.ChampionsMgr.GetNbItems()))
.Select(x => x.ToDto());
return Ok(dtos);
}
// GET api/<ValuesController>/5
[HttpGet("{name}")]
public async Task<IActionResult> 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/<ValuesController>
[HttpPost]
public async Task<IActionResult> Post([FromBody] ChampionDto champion)
{
return CreatedAtAction(nameof(Get),
(await _manager.ChampionsMgr.AddItem(champion.ToModel())).ToDto());
}
// PUT api/<ValuesController>/5
[HttpPut("{name}")]
public async Task<IActionResult> 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/<ValuesController>/5
[HttpDelete("{name}")]
public async Task<IActionResult> 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()));
}
}
}

@ -0,0 +1,181 @@
using ApiMapping;
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 RunePagesController : ControllerBase
{
private readonly IDataManager _manager;
private readonly ILogger<RunePagesController> _logger;
public RunePagesController(IDataManager dataManager, ILogger<RunePagesController> logger)
{
_manager = dataManager;
_logger = logger;
}
// GET: api/<RunePagesController>
[HttpGet]
public async Task<IActionResult> Get([FromQuery] PageRequest pageRequest)
{
_logger.LogInformation("Executing {Action} - RUNEPAGE with parameters: {Parameters}", nameof(Get), pageRequest);
try
{
int nbTotal = await _manager.RunePagesMgr.GetNbItems();
if (pageRequest.count == 0)
{
pageRequest = new PageRequest() { index = 0, count = nbTotal, orderingPropertyName = pageRequest.orderingPropertyName, descending = pageRequest.descending, name = pageRequest.name };
}
else if (pageRequest.count * pageRequest.index >= nbTotal || pageRequest.count > nbTotal)
{
_logger.LogWarning("too many, maximum {number}", nbTotal);
return BadRequest($"RunePage limit exceed, max {nbTotal}");
}
IEnumerable<RunePageDto> dtos;
if (pageRequest.name == null)
{
dtos = (await _manager.RunePagesMgr.GetItems(pageRequest.index, pageRequest.count, pageRequest.orderingPropertyName, pageRequest.descending))
.Select(x => x.ToDto());
}
else
{
dtos = (await _manager.RunePagesMgr.GetItemsByName(pageRequest.name, pageRequest.index, pageRequest.count, pageRequest.orderingPropertyName, pageRequest.descending))
.Select(x => x.ToDto());
}
return Ok(new PageResponse<RunePageDto> { Data = dtos, index = pageRequest.index, count = pageRequest.count, total = nbTotal });
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// GET api/<RunePagesController>/5
[HttpGet("{name}")]
public async Task<IActionResult> Get(string name)
{
_logger.LogInformation("method {Action} - RUNEPAGE call with {name}", nameof(Get), name);
try
{
var dtos = (await _manager.RunePagesMgr.GetItemByName(name, 0, await _manager.RunePagesMgr.GetNbItems()))
.Select(x => x.ToDto());
if (dtos.IsNullOrEmpty())
{
_logger.LogWarning($"{name} was not found");
return NotFound($"{name} was not found");
}
return Ok(dtos.First());
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// POST api/<RunePagesController>
[HttpPost]
public async Task<IActionResult> Post([FromBody] RunePageDto runePage)
{
_logger.LogInformation("method {Action} - RUNEPAGE call with {item}", nameof(Post), runePage);
try
{
if (await _manager.RunePagesMgr.GetNbItemsByName(runePage.Name) == 0)
{
return CreatedAtAction(nameof(Get),
(await _manager.RunePagesMgr.AddItem(runePage.ToModel())).ToDto());
}
_logger.LogWarning($"Name : {runePage.Name} is already exist");
return BadRequest($"Name : {runePage.Name} is already exist");
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// PUT api/<RunePagesController>/5
[HttpPut("{name}")]
public async Task<IActionResult> Put(string name, [FromBody] RunePageDto runePage)
{
_logger.LogInformation("method {Action} - RUNEPAGE call with {name} and {item}", nameof(Put), name, runePage);
try
{
var dtos = (await _manager.RunePagesMgr.GetItemByName(name, 0, await _manager.RunePagesMgr.GetNbItems()));
if (dtos.IsNullOrEmpty())
{
return NotFound($"Name {name} not exist");
}
// Checks if the new name exists
if (name != runePage.Name)
{
var dtos2 = (await _manager.RunesMgr.GetItemByName(runePage.Name, 0, await _manager.RunePagesMgr.GetNbItems()));
if (!dtos2.IsNullOrEmpty() || dtos2.Count() > 0)
{
return BadRequest($"New Name {runePage.Name} is already exist");
}
}
return Ok((await _manager.RunePagesMgr.UpdateItem(dtos.First(), runePage.ToModel())).ToDto());
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// DELETE api/<RunePagesController>/5
[HttpDelete("{name}")]
public async Task<IActionResult> Delete(string name)
{
_logger.LogInformation("method {Action} - RUNEPAGE call with {name}", nameof(Delete), name);
try
{
var dtos = (await _manager.RunePagesMgr.GetItemByName(name, 0, await _manager.RunePagesMgr.GetNbItems()));
if (dtos.IsNullOrEmpty())
{
_logger.LogWarning("{name} was not found", name);
return NotFound($"{name} was not found");
}
await _manager.RunePagesMgr.DeleteItem(dtos.First());
return NoContent();
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
[HttpGet("/countRunePages")]
public async Task<ActionResult> GetCountRunePages()
{
_logger.LogInformation("method {Action} - RUNEPAGE call", nameof(GetCountRunePages));
try
{
return Ok(await _manager.RunePagesMgr.GetNbItems());
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
}
}

@ -0,0 +1,179 @@
using ApiMapping;
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 RunesController : ControllerBase
{
private readonly IDataManager _manager;
private readonly ILogger<RunesController> _logger;
public RunesController(IDataManager dataManager, ILogger<RunesController> logger)
{
_logger = logger;
this._manager = dataManager;
}
// GET: api/<RunesController>
[HttpGet]
public async Task<IActionResult> Get([FromQuery] PageRequest pageRequest)
{
_logger.LogInformation("Executing {Action} - RUNE with parameters: {Parameters}", nameof(Get), pageRequest);
try
{
int nbTotal = await _manager.RunesMgr.GetNbItems();
if (pageRequest.count == 0)
{
pageRequest = new PageRequest() { index = 0, count = nbTotal, orderingPropertyName = pageRequest.orderingPropertyName, descending = pageRequest.descending, name = pageRequest.name };
}
else if (pageRequest.count * pageRequest.index >= nbTotal || pageRequest.count > nbTotal)
{
_logger.LogWarning("too many, maximum {number}", nbTotal);
return BadRequest($"Rune limit exceed, max {nbTotal}");
}
IEnumerable<RuneDto> dtos;
if (pageRequest.name == null)
{
dtos = (await _manager.RunesMgr.GetItems(pageRequest.index, pageRequest.count, pageRequest.orderingPropertyName, pageRequest.descending))
.Select(x => x.ToDto());
}
else
{
dtos = (await _manager.RunesMgr.GetItemsByName(pageRequest.name, pageRequest.index, pageRequest.count, pageRequest.orderingPropertyName, pageRequest.descending))
.Select(x => x.ToDto());
}
return Ok(new PageResponse<RuneDto> { Data = dtos, index = pageRequest.index, count = pageRequest.count, total = nbTotal });
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// GET api/<RunesController>/5
[HttpGet("{name}")]
public async Task<IActionResult> Get(string name)
{
_logger.LogInformation("method {Action} - RUNE call with {name}", nameof(Get), name);
try
{
var dtos = (await _manager.RunesMgr.GetItemByName(name, 0, await _manager.RunesMgr.GetNbItems()))
.Select(x => x.ToDto());
if (dtos.IsNullOrEmpty())
{
_logger.LogWarning($"{name} was not found");
return NotFound($"{name} was not found");
}
return Ok(dtos.First());
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// POST api/<RunesController>
[HttpPost]
public async Task<IActionResult> Post([FromBody] RuneDto rune)
{
_logger.LogInformation("method {Action} - RUNE call with {item}", nameof(Post), rune);
try
{
if (await _manager.RunesMgr.GetNbItemsByName(rune.Name) == 0)
{
return CreatedAtAction(nameof(Get),
(await _manager.RunesMgr.AddItem(rune.ToModel())).ToDto());
}
_logger.LogWarning($"Name : {rune.Name} is already exist");
return BadRequest($"Name : {rune.Name} is already exist");
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// PUT api/<RunesController>/5
[HttpPut("{name}")]
public async Task<IActionResult> Put(string name, [FromBody] RuneDto rune)
{
_logger.LogInformation("method {Action} - RUNE call with {name} and {item}", nameof(Put), name, rune);
try
{
var dtos = (await _manager.RunesMgr.GetItemByName(name, 0, await _manager.RunesMgr.GetNbItems()));
if (dtos.IsNullOrEmpty())
{
return NotFound($"Name {name} not exist");
}
// Checks if the new name exists
if (name != rune.Name)
{
var dtos2 = (await _manager.RunesMgr.GetItemByName(rune.Name, 0, await _manager.RunesMgr.GetNbItems()));
if (!dtos2.IsNullOrEmpty() || dtos2.Count() > 0)
{
return BadRequest($"New Name {rune.Name} is already exist");
}
}
return Ok((await _manager.RunesMgr.UpdateItem(dtos.First(), rune.ToModel())).ToDto());
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// DELETE api/<RunesController>/5
[HttpDelete("{name}")]
public async Task<IActionResult> Delete(string name)
{
_logger.LogInformation("method {Action} - RUNE call with {name}", nameof(Delete), name);
try
{
var dtos = (await _manager.RunesMgr.GetItemByName(name, 0, await _manager.RunesMgr.GetNbItems()));
if (dtos.IsNullOrEmpty())
{
_logger.LogWarning("{name} was not found", name);
return NotFound($"{name} was not found");
}
await _manager.RunesMgr.DeleteItem(dtos.First());
return NoContent();
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
[HttpGet("/countRunes")]
public async Task<ActionResult> GetCountRunes()
{
_logger.LogInformation("method {Action} - RUNE call", nameof(GetCountRunes));
try
{
return Ok(await _manager.RunesMgr.GetNbItems());
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
}
}

@ -0,0 +1,223 @@
using ApiMapping;
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 SkinsController : ControllerBase
{
private readonly IDataManager _manager;
private readonly ILogger<SkinsController> _logger;
public SkinsController(IDataManager dataManager, ILogger<SkinsController> logger)
{
_logger = logger;
this._manager = dataManager;
}
// GET: api/<SkinsController>
[HttpGet]
public async Task<IActionResult> Get([FromQuery] PageRequest pageRequest)
{
_logger.LogInformation("Executing {Action} - SKIN with parameters: {Parameters}", nameof(Get), pageRequest);
try
{
int nbTotal = await _manager.SkinsMgr.GetNbItems();
if (pageRequest.count == 0)
{
pageRequest = new PageRequest() { index = 0, count = nbTotal, orderingPropertyName = pageRequest.orderingPropertyName, descending = pageRequest.descending, name = pageRequest.name };
}
else if (pageRequest.count * pageRequest.index >= nbTotal || pageRequest.count > nbTotal)
{
_logger.LogWarning("too many, maximum {number}", nbTotal);
return BadRequest($"Skin limit exceed, max {nbTotal}");
}
IEnumerable<SkinDtoC> dtos;
if (pageRequest.name == null)
{
dtos = (await _manager.SkinsMgr.GetItems(pageRequest.index, pageRequest.count, pageRequest.orderingPropertyName, pageRequest.descending))
.Select(x => x.ToDtoC());
}
else
{
dtos = (await _manager.SkinsMgr.GetItemsByName(pageRequest.name, pageRequest.index, pageRequest.count, pageRequest.orderingPropertyName, pageRequest.descending))
.Select(x => x.ToDtoC());
}
return Ok(new PageResponse<SkinDtoC> { Data = dtos, index = pageRequest.index, count = pageRequest.count, total = nbTotal });
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// GET api/<SkinsController>/5
[HttpGet("{name}")]
public async Task<IActionResult> Get(string name)
{
_logger.LogInformation("method {Action} - SKIN call with {name}", nameof(Get), name);
try
{
var dtos = (await _manager.SkinsMgr.GetItemByName(name, 0, await _manager.SkinsMgr.GetNbItems()))
.Select(x => x.ToDtoC());
if (dtos.IsNullOrEmpty())
{
_logger.LogWarning($"{name} was not found");
return NotFound($"{name} was not found");
}
return Ok(dtos.First());
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// POST api/<SkinsController>
[HttpPost]
public async Task<IActionResult> Post([FromBody] SkinDtoC skin)
{
_logger.LogInformation("method {Action} - SKIN call with {item}", nameof(Post), skin);
try
{
if (skin.ChampionName != null)
{
var dtos = (await _manager.ChampionsMgr.GetItemByName(skin.ChampionName, 0, await _manager.ChampionsMgr.GetNbItems()));
if (dtos.IsNullOrEmpty())
{
_logger.LogWarning($"Champion Name : {skin.ChampionName} not exist");
return BadRequest($"Champion Name : {skin.ChampionName} not exist");
}
else
{
if (await _manager.SkinsMgr.GetNbItemsByName(skin.Name) == 0)
{
return CreatedAtAction(nameof(Get),
(await _manager.SkinsMgr.AddItem(skin.ToModelC(dtos.First()))).ToDtoC());
}
_logger.LogWarning($"Name : {skin.Name} is already exist");
return BadRequest($"Name : {skin.Name} is already exist");
}
}
else
{
return BadRequest("Please fill in the name of the champion");
}
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// PUT api/<SkinsController>/5
[HttpPut("{name}")]
public async Task<IActionResult> Put(string name, [FromBody] SkinDtoC skin)
{
_logger.LogInformation("method {Action} - SKIN call with {name} and {item}", nameof(Put), name, skin);
try
{
var dtos = (await _manager.SkinsMgr.GetItemByName(name, 0, await _manager.SkinsMgr.GetNbItems()));
if (dtos.IsNullOrEmpty())
{
return NotFound($"Name {name} not exist");
}
// Checks if the new name exists
if (name != skin.Name)
{
var dtos2 = (await _manager.SkinsMgr.GetItemByName(skin.Name, 0, await _manager.SkinsMgr.GetNbItems()));
if (!dtos2.IsNullOrEmpty() || dtos2.Count() > 0)
{
return BadRequest($"New Name {skin.Name} is already exist");
}
}
var dtosChampion = (await _manager.ChampionsMgr.GetItemByName(skin.ChampionName, 0, await _manager.ChampionsMgr.GetNbItems()));
if (dtosChampion.IsNullOrEmpty())
{
_logger.LogWarning($"Champion Name : {skin.ChampionName} not exist");
return BadRequest($"Champion Name : {skin.ChampionName} not exist");
}
return Ok((await _manager.SkinsMgr.UpdateItem(dtos.First(), skin.ToModelC(dtosChampion.First()))).ToDtoC());
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
[HttpGet("/{name}/champion")]
public async Task<ActionResult<ChampionDto>> GetChampionBySkinName(string name)
{
_logger.LogInformation("method {Action} - Skin call with {name}", nameof(GetChampionBySkinName), name);
try
{
var dtos = (await _manager.SkinsMgr.GetItemByName(name, 0, await _manager.ChampionsMgr.GetNbItems()))
.Select(x => x.ToDtoC());
if (dtos.IsNullOrEmpty())
{
_logger.LogWarning($"{name} was not found");
return NotFound($"{name} was not found");
}
var champion = (await _manager.ChampionsMgr.GetItemByName(dtos.First().ChampionName, 0, 1));
return Ok(champion.First().ToDto());
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
[HttpGet("/countSkins")]
public async Task<ActionResult> GetCountSkins()
{
_logger.LogInformation("method {Action} - SKIN call", nameof(GetCountSkins));
try
{
return Ok(await _manager.SkinsMgr.GetNbItems());
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// DELETE api/<SkinsController>/5
[HttpDelete("{name}")]
public async Task<IActionResult> Delete(string name)
{
_logger.LogInformation("method {Action} - SKIN call with {name}", nameof(Delete), name);
try
{
var dtos = (await _manager.SkinsMgr.GetItemByName(name, 0, await _manager.SkinsMgr.GetNbItems()));
if (dtos.IsNullOrEmpty())
{
_logger.LogWarning("{name} was not found", name);
return NotFound($"{name} was not found");
}
await _manager.SkinsMgr.DeleteItem(dtos.First());
return NoContent();
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
}
}

@ -1,6 +1,4 @@
using ApiLol.Mapper;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Model;
namespace ApiLol.Controllers namespace ApiLol.Controllers
{ {
@ -23,13 +21,6 @@ namespace ApiLol.Controllers
[HttpGet(Name = "GetWeatherForecast")] [HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get() public IEnumerable<WeatherForecast> Get()
{ {
/*
var champion = new Champion("");
//var dtop = ChampionMapper.ToDto(champion);
var dto = champion.ToDto();*/
return Enumerable.Range(1, 5).Select(index => new WeatherForecast return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{ {
Date = DateTime.Now.AddDays(index), Date = DateTime.Now.AddDays(index),

@ -0,0 +1,97 @@
using ApiMapping;
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<ChampionsController> _logger;
public ChampionsController(IDataManager dataManager, ILogger<ChampionsController> logger)
{
_logger = logger;
this._manager = dataManager;
}
// GET: api/<ValuesController>
[HttpGet]
public async Task<IActionResult> Get([FromQuery] PageRequest pageRequest)
{
_logger.LogInformation("Executing {Action} - CHAMPION - V1.0 with parameters: {Parameters}", nameof(Get), pageRequest);
IEnumerable<ChampionDto> dtos = (await _manager.ChampionsMgr.GetItems(pageRequest.index, pageRequest.count, pageRequest.orderingPropertyName, pageRequest.descending))
.Select(x => x.ToDto());
return Ok(dtos);
}
// GET api/<ValuesController>/5
[HttpGet("{name}")]
public async Task<IActionResult> Get(string name)
{
_logger.LogInformation("method {Action} - CHAMPION - V1.0 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/<ValuesController>
[HttpPost]
public async Task<IActionResult> Post([FromBody] ChampionDto champion)
{
_logger.LogInformation("method {Action} - CHAMPION - V1.0 call with {item}", nameof(Post), champion);
return CreatedAtAction(nameof(Get),
(await _manager.ChampionsMgr.AddItem(champion.ToModel())).ToDto());
}
// PUT api/<ValuesController>/5
[HttpPut("{name}")]
public async Task<IActionResult> Put(string name, [FromBody] ChampionDto champion)
{
_logger.LogInformation("method {Action} - CHAMPION - V1.0 call with {name} and {item}", nameof(Put), name, champion);
var champs = (await _manager.ChampionsMgr.GetItemByName(name, 0, await _manager.ChampionsMgr.GetNbItems()));
return Ok((await _manager.ChampionsMgr.UpdateItem(champs.First(), champion.ToModel())).ToDto());
}
[HttpGet("/countChampions")]
public async Task<ActionResult> GetCountChampions()
{
try
{
return Ok(await _manager.ChampionsMgr.GetNbItems());
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// DELETE api/<ValuesController>/5
[HttpDelete("{name}")]
public async Task<IActionResult> Delete(string name)
{
_logger.LogInformation("method {Action} - CHAMPION - V1.0 call with {name}", nameof(Delete), name);
var champs = (await _manager.ChampionsMgr.GetItemByName(name, 0, await _manager.ChampionsMgr.GetNbItems()));
await _manager.ChampionsMgr.DeleteItem(champs.First());
return NoContent();
}
}
}

@ -0,0 +1,243 @@
using ApiMapping;
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<ChampionsController> _logger;
public ChampionsController(IDataManager dataManager, ILogger<ChampionsController> logger)
{
_logger = logger;
this._manager = dataManager;
}
// GET: api/<ValuesController>
[HttpGet]
public async Task<IActionResult> Get([FromQuery] PageRequest pageRequest)
{
_logger.LogInformation("Executing {Action} - CHAMPION - V2.0 with parameters: {Parameters}", nameof(Get), pageRequest);
try
{
int nbTotal = await _manager.ChampionsMgr.GetNbItems();
if (pageRequest.count == 0)
{
pageRequest = new PageRequest() { index = 0, count = nbTotal, orderingPropertyName = pageRequest.orderingPropertyName, descending = pageRequest.descending, name = pageRequest.name };
}
else if (pageRequest.count * pageRequest.index >= nbTotal)
{
_logger.LogWarning($"too many, maximum {nbTotal}");
return BadRequest($"Champion limit exceed, max {nbTotal}");
}
IEnumerable<ChampionDto> dtos = (await _manager.ChampionsMgr.GetItems(pageRequest.index, pageRequest.count, pageRequest.orderingPropertyName, pageRequest.descending))
.Select(x => x.ToDto());
return Ok(dtos);
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// GET: api/<ValuesController>
[HttpGet, MapToApiVersion("3.0")]
public async Task<IActionResult> GetV3([FromQuery] PageRequest pageRequest)
{
_logger.LogInformation("Executing {Action} - CHAMPION - V3.0 with parameters: {Parameters}", nameof(Get), pageRequest);
try
{
int nbTotal = await _manager.ChampionsMgr.GetNbItems();
if (pageRequest.count == 0)
{
pageRequest = new PageRequest() { index = 0, count = nbTotal, orderingPropertyName = pageRequest.orderingPropertyName, descending = pageRequest.descending, name = pageRequest.name };
}
else if (pageRequest.count * pageRequest.index >= nbTotal || pageRequest.count > nbTotal)
{
_logger.LogWarning("too many, maximum {number}", nbTotal);
return BadRequest($"Champion limit exceed, max {nbTotal}");
}
IEnumerable<ChampionDto> dtos;
if (pageRequest.name == null)
{
dtos = (await _manager.ChampionsMgr.GetItems(pageRequest.index, pageRequest.count, pageRequest.orderingPropertyName, pageRequest.descending))
.Select(x => x.ToDto());
}
else
{
dtos = (await _manager.ChampionsMgr.GetItemsByName(pageRequest.name, pageRequest.index, pageRequest.count, pageRequest.orderingPropertyName, pageRequest.descending))
.Select(x => x.ToDto());
}
return Ok(new PageResponse<ChampionDto> { Data = dtos, index = pageRequest.index, count = pageRequest.count, total = nbTotal });
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// GET api/<ValuesController>/5
[HttpGet("{name}")]
public async Task<IActionResult> Get(string name)
{
_logger.LogInformation("method {Action} - CHAMPION - V2.0 call with {name}", nameof(Get), name);
try
{
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($"{name} was not found");
}
return Ok(dtos.First());
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// POST api/<ValuesController>
[HttpPost]
public async Task<IActionResult> Post([FromBody] ChampionDto champion)
{
_logger.LogInformation("method {Action} - CHAMPION - V2.0 call with {item}", nameof(Post), champion);
try
{
if (await _manager.ChampionsMgr.GetNbItemsByName(champion.Name) == 0)
{
return CreatedAtAction(nameof(Get),
(await _manager.ChampionsMgr.AddItem(champion.ToModel())).ToDto());
}
_logger.LogWarning($"Name : {champion.Name} is already exist");
return BadRequest($"Name : {champion.Name} is already exist");
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// PUT api/<ValuesController>/5
[HttpPut("{name}")]
public async Task<IActionResult> Put(string name, [FromBody] ChampionDto champion)
{
_logger.LogInformation("method {Action} - CHAMPION - V2.0 call with {name} and {item}", nameof(Put), name, champion);
try
{
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($"New Name {champion.Name} is already exist");
}
}
return Ok((await _manager.ChampionsMgr.UpdateItem(dtos.First(), champion.ToModel())).ToDto());
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
[HttpGet("/{name}/skins")]
public async Task<ActionResult<SkinDto>> GetChampionsSkins(string name)
{
_logger.LogInformation("method {Action} - CHAMPION - V2.0 call with {name}", nameof(GetChampionsSkins), name);
try
{
var champions = await _manager.ChampionsMgr.GetItemByName(name, 0, await _manager.ChampionsMgr.GetNbItems());
//skinsDTO
IEnumerable<SkinDto> res = champions.First().Skins.Select(e => e.ToDto());
return Ok(res);
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
[HttpGet("/{name}/skills")]
public async Task<ActionResult<SkillDto>> GetChampionsSkills(string name)
{
try
{
_logger.LogInformation("method {Action} - CHAMPION - V2.0 call with {name}", nameof(GetChampionsSkills), name);
var champions = await _manager.ChampionsMgr.GetItemByName(name, 0, await _manager.ChampionsMgr.GetNbItems());
//SkillDTO
IEnumerable<SkillDto> res = champions.First().Skills.Select(e => e.ToDto());
return Ok(res);
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
[HttpGet("/countChampions")]
public async Task<ActionResult> GetCountChampions()
{
_logger.LogInformation("method {Action} - CHAMPION - V2.0 call", nameof(GetCountChampions));
try
{
return Ok(await _manager.ChampionsMgr.GetNbItems());
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
// DELETE api/<ValuesController>/5
[HttpDelete("{name}")]
public async Task<IActionResult> Delete(string name)
{
_logger.LogInformation("method {Action} - CHAMPION - V2.0 call with {name}", nameof(Delete), name);
try
{
var dtos = (await _manager.ChampionsMgr.GetItemByName(name, 0, await _manager.ChampionsMgr.GetNbItems()));
if (dtos.IsNullOrEmpty())
{
_logger.LogWarning("{name} was not found", name);
return NotFound($"{name} was not found");
}
await _manager.ChampionsMgr.DeleteItem(dtos.First());
return NoContent();
}
catch (Exception error)
{
_logger.LogError(error.Message);
return BadRequest(error.Message);
}
}
}
}

@ -1,26 +0,0 @@
using DTO;
using Model;
namespace ApiLol.Mapper
{
public static class ChampionMapper
{
public static ChampionDto ToDto(this Champion champion)
{
return new ChampionDto()
{
Name = champion.Name,
Bio = champion.Bio,
Class = champion.Class.ToDto(),
Icon = champion.Icon,
Image = champion.Image.Base64
};
}
public static Champion ToModel(this ChampionDto championDto)
{
return new Champion(championDto.Name, championDto.Class.ToModel(), championDto.Icon, championDto.Image,championDto.Bio);
}
}
}

@ -1,25 +0,0 @@
using DTO;
using Model;
namespace ApiLol.Mapper
{
public static class RuneMapper
{
public static RuneDto ToDto(this Rune rune)
{
return new RuneDto()
{
Name = rune.Name,
Description = rune.Description,
};
}
/* public static Rune ToModel(this RuneDto rune)
{
return new Rune(rune.Name)
{
Description = rune.Description,
};
}*/
}
}

@ -1,25 +0,0 @@
using DTO;
using Model;
namespace ApiLol.Mapper
{
public static class SkillMapper
{
public static SkillDto ToDto(this Skill skill)
{
return new SkillDto()
{
Name = skill.Name,
Description = skill.Description,
};
}
/* public static Skill ToModel(this SkillDto skill)
{
return new Skill(skill.Name)
{
Description = skill.Description
};
}*/
}
}

@ -1,18 +0,0 @@
using DTO;
namespace ApiLol.Mapper
{
public static class SkinMapper
{
public static SkinDto ToDto(this SkinDto skin)
{
return new SkinDto()
{
Name = skin.Name,
Description = skin.Description,
Icon = skin.Icon,
Price = skin.Price
};
}
}
}

@ -1,80 +0,0 @@
using DTO;
using Model;
namespace ApiLol.Mapper
{
public static class ChampionClassMapper
{
public static ChampionClassDto ToDto(this ChampionClass championClass)
{
if (championClass == ChampionClass.Unknown)
{
return ChampionClassDto.Unknown;
}
if (championClass == ChampionClass.Assassin)
{
return ChampionClassDto.Assassin;
}
if (championClass == ChampionClass.Fighter)
{
return ChampionClassDto.Fighter;
}
if (championClass == ChampionClass.Mage)
{
return ChampionClassDto.Mage;
}
if (championClass == ChampionClass.Marksman)
{
return ChampionClassDto.Marksman;
}
if (championClass == ChampionClass.Support)
{
return ChampionClassDto.Support;
}
if (championClass == ChampionClass.Tank)
{
return ChampionClassDto.Tank;
}
else
{
return ChampionClassDto.Unknown;
}
}
public static ChampionClass ToModel(this ChampionClassDto championClass)
{
if (championClass == ChampionClassDto.Unknown)
{
return ChampionClass.Unknown;
}
if (championClass == ChampionClassDto.Assassin)
{
return ChampionClass.Assassin;
}
if (championClass == ChampionClassDto.Fighter)
{
return ChampionClass.Fighter;
}
if (championClass == ChampionClassDto.Mage)
{
return ChampionClass.Mage;
}
if (championClass == ChampionClassDto.Marksman)
{
return ChampionClass.Marksman;
}
if (championClass == ChampionClassDto.Support)
{
return ChampionClass.Support;
}
if (championClass == ChampionClassDto.Tank)
{
return ChampionClass.Tank;
}
else
{
return ChampionClass.Unknown;
}
}
}
}

@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.OpenApi.Models;
using Model; using Model;
using StubLib;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@ -8,17 +10,46 @@ var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(); builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); builder.Services.AddSwaggerGen(options =>
builder.Services.AddSingleton<IDataManager, StubData>(); {
options.SwaggerDoc("v1", new OpenApiInfo { Title = "My API v1", Version = "v1" });
options.SwaggerDoc("v2", new OpenApiInfo { Title = "My API v2", Version = "v2" });
});
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<IDataManager, DbLib.DbManager>();
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) var apiVersionDescriptionProvider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
// Configure the HTTP request pipeline.
app.UseSwagger();
app.UseSwaggerUI(options =>
{ {
app.UseSwagger(); foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions)
app.UseSwaggerUI(); {
} options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json",
description.GroupName.ToUpperInvariant());
}
});
app.UseHttpsRedirection(); app.UseHttpsRedirection();

@ -1,15 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ApiLol\ApiLol.csproj" />
<ProjectReference Include="..\DTO\DTO.csproj" /> <ProjectReference Include="..\DTO\DTO.csproj" />
<ProjectReference Include="..\Model\Model.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -0,0 +1,42 @@
using ApiMapping;
using ApiMapping.enums;
using DTO;
using Model;
namespace ApiMapping
{
public static class ChampionMapper
{
public static ChampionDto ToDto(this Champion champion)
=> new()
{
Name = champion.Name,
Bio = champion.Bio,
Class = champion.Class.ToDto(),
Icon = champion.Icon,
Image = champion.Image.ToDto(),
Skins = champion.Skins.Select(e => e.ToDto()),
Skills = champion.Skills.Select(e => e.ToDto()),
Characteristics = champion.Characteristics.ToDictionary(c => c.Key, c => c.Value)
};
public static Champion ToModel(this ChampionDto championDto)
{
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));
}
foreach (var skill in championDto.Skills)
{
champ.AddSkill(skill.ToModel());
}
if (championDto.Characteristics != null)
champ.AddCharacteristics(championDto.Characteristics.Select(c => Tuple.Create(c.Key, c.Value)).ToArray());
return champ;
}
}
}

@ -0,0 +1,14 @@
using DTO;
using Model;
namespace ApiMapping
{
public static class LargeImageMapper
{
public static LargeImageDto ToDto(this LargeImage largeImage)
=> new() { Base64 = largeImage.Base64 };
public static LargeImage ToModel(this LargeImageDto largeImageDto) => new(largeImageDto.Base64);
}
}

@ -0,0 +1,23 @@
using ApiMapping;
using ApiMapping.enums;
using DTO;
using Model;
namespace ApiMapping
{
public static class RuneMapper
{
public static RuneDto ToDto(this Rune rune)
=> new()
{
Name = rune.Name,
Description = rune.Description,
Family = rune.Family.ToDto(),
Icon = rune.Icon,
Image = rune.Image.ToDto()
};
public static Rune ToModel(this RuneDto rune) => new(rune.Name, rune.Family.ToModel(), rune.Icon, rune.Image.Base64, rune.Description);
}
}

@ -0,0 +1,40 @@
using DTO;
using Model;
using static Model.RunePage;
namespace ApiMapping
{
public static class RunePageMapper
{
public static RunePageDto ToDto(this RunePage runePage)
{
return new RunePageDto()
{
Name = runePage.Name,
Runes = runePage.Runes.ToDictionary(c => c.Key.ToString(), r => r.Value.ToDto())
};
}
public static RunePage ToModel(this RunePageDto runePageDto)
{
Category category;
Dictionary<Category, Rune> runDico = runePageDto.Runes.ToDictionary(
r => (RunePage.Category)Enum.Parse(typeof(RunePage.Category), r.Key),
r => r.Value.ToModel()
);
var runePage = new RunePage(runePageDto.Name);
foreach (var rune in runePageDto.Runes)
{
if (!Enum.TryParse<Category>(rune.Key, true, out category))
{
continue;
}
runePage[category] = rune.Value.ToModel();
}
return runePage;
}
}
}

@ -0,0 +1,21 @@
using ApiMapping;
using ApiMapping.enums;
using DTO;
using Model;
namespace ApiMapping
{
public static class SkillMapper
{
public static SkillDto ToDto(this Skill skill)
=> new()
{
Name = skill.Name,
Description = skill.Description,
Type = skill.Type.ToDto()
};
public static Skill ToModel(this SkillDto skillDto) => new(skillDto.Name, skillDto.Type.ToModel(), skillDto.Description);
}
}

@ -0,0 +1,35 @@
using ApiMapping;
using DTO;
using Model;
namespace ApiMapping
{
public static class SkinMapper
{
public static SkinDto ToDto(this Skin skin)
=> new()
{
Name = skin.Name,
Description = skin.Description,
Icon = skin.Icon,
Image = skin.Image.ToDto(),
Price = skin.Price
};
public static SkinDtoC ToDtoC(this Skin skin)
=> new()
{
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) => new(skinDto.Name, champ, skinDto.Price, skinDto.Icon, skinDto.Image.Base64, skinDto.Description);
public static Skin ToModelC(this SkinDtoC skinDto, Champion champ) => new(skinDto.Name, champ, skinDto.Price, skinDto.Icon, skinDto.Image.Base64, skinDto.Description);
}
}

@ -0,0 +1,50 @@
using DTO;
using Model;
namespace ApiMapping.enums
{
public static class CategoryMapper
{
public static RunePageDto.CategoryDto ToDto(this RunePage.Category category)
{
switch (category)
{
case RunePage.Category.Major:
return RunePageDto.CategoryDto.Major;
case RunePage.Category.Minor1:
return RunePageDto.CategoryDto.Minor1;
case RunePage.Category.Minor2:
return RunePageDto.CategoryDto.Minor2;
case RunePage.Category.Minor3:
return RunePageDto.CategoryDto.Minor3;
case RunePage.Category.OtherMinor1:
return RunePageDto.CategoryDto.OtherMinor1;
case RunePage.Category.OtherMinor2:
return RunePageDto.CategoryDto.OtherMinor2;
default:
return RunePageDto.CategoryDto.Major;
}
}
public static RunePage.Category ToModel(this RunePageDto.CategoryDto category)
{
switch (category)
{
case RunePageDto.CategoryDto.Major:
return RunePage.Category.Major;
case RunePageDto.CategoryDto.Minor1:
return RunePage.Category.Minor1;
case RunePageDto.CategoryDto.Minor2:
return RunePage.Category.Minor2;
case RunePageDto.CategoryDto.Minor3:
return RunePage.Category.Minor3;
case RunePageDto.CategoryDto.OtherMinor1:
return RunePage.Category.OtherMinor1;
case RunePageDto.CategoryDto.OtherMinor2:
return RunePage.Category.OtherMinor2;
default:
return RunePage.Category.Major;
}
}
}
}

@ -0,0 +1,53 @@
using DTO;
using Model;
namespace ApiMapping.enums
{
public static class ChampionClassMapper
{
public static ChampionClassDto ToDto(this ChampionClass championClass)
{
switch (championClass)
{
case ChampionClass.Unknown:
return ChampionClassDto.Unknown;
case ChampionClass.Assassin:
return ChampionClassDto.Assassin;
case ChampionClass.Fighter:
return ChampionClassDto.Fighter;
case ChampionClass.Mage:
return ChampionClassDto.Mage;
case ChampionClass.Marksman:
return ChampionClassDto.Marksman;
case ChampionClass.Support:
return ChampionClassDto.Support;
case ChampionClass.Tank:
return ChampionClassDto.Tank;
default:
return ChampionClassDto.Unknown;
}
}
public static ChampionClass ToModel(this ChampionClassDto championClass)
{
switch (championClass)
{
case ChampionClassDto.Unknown:
return ChampionClass.Unknown;
case ChampionClassDto.Assassin:
return ChampionClass.Assassin;
case ChampionClassDto.Fighter:
return ChampionClass.Fighter;
case ChampionClassDto.Mage:
return ChampionClass.Mage;
case ChampionClassDto.Marksman:
return ChampionClass.Marksman;
case ChampionClassDto.Support:
return ChampionClass.Support;
case ChampionClassDto.Tank:
return ChampionClass.Tank;
default:
return ChampionClass.Unknown;
}
}
}
}

@ -0,0 +1,39 @@
using DTO;
using DTO.enums;
using Model;
namespace ApiMapping.enums
{
public static class RuneFamilyMapper
{
public static RuneFamilyDto ToDto(this RuneFamily runeFamily)
{
switch (runeFamily)
{
case RuneFamily.Unknown:
return RuneFamilyDto.Unknown;
case RuneFamily.Precision:
return RuneFamilyDto.Precision;
case RuneFamily.Domination:
return RuneFamilyDto.Domination;
default:
return RuneFamilyDto.Unknown;
}
}
public static RuneFamily ToModel(this RuneFamilyDto runeFamily)
{
switch (runeFamily)
{
case RuneFamilyDto.Unknown:
return RuneFamily.Unknown;
case RuneFamilyDto.Precision:
return RuneFamily.Precision;
case RuneFamilyDto.Domination:
return RuneFamily.Domination;
default:
return RuneFamily.Unknown;
}
}
}
}

@ -0,0 +1,43 @@
using DTO;
using Model;
namespace ApiMapping.enums
{
public static class SkillTypeMapper
{
public static SkillTypeDto ToDto(this SkillType skillType)
{
switch (skillType)
{
case SkillType.Unknown:
return SkillTypeDto.Unknown;
case SkillType.Basic:
return SkillTypeDto.Basic;
case SkillType.Passive:
return SkillTypeDto.Passive;
case SkillType.Ultimate:
return SkillTypeDto.Ultimate;
default:
return SkillTypeDto.Unknown;
}
}
public static SkillType ToModel(this SkillTypeDto skillTypeDto)
{
switch (skillTypeDto)
{
case SkillTypeDto.Unknown:
return SkillType.Unknown;
case SkillTypeDto.Basic:
return SkillType.Basic;
case SkillTypeDto.Passive:
return SkillType.Passive;
case SkillTypeDto.Ultimate:
return SkillType.Ultimate;
default:
return SkillType.Unknown;
}
}
}
}

@ -0,0 +1,184 @@
using ApiMapping;
using DTO;
using Model;
using System.Net.Http.Json;
namespace ApiManager
{
public partial class ApiManagerData
{
public class ChampionsManager : HttpClientManager, IChampionsManager
{
private const string UrlApiChampions = "/api/v2/champions";
public ChampionsManager(HttpClient httpClient) : base(httpClient) { }
public async Task<Champion?> AddItem(Champion? item)
{
try
{
var resp = await _httpClient.PostAsJsonAsync($"{UrlApiChampions}", item.ToDto());
if (resp.IsSuccessStatusCode)
{
var createdItem = await resp.Content.ReadFromJsonAsync<ChampionDto>();
return createdItem?.ToModel();
}
else
{
return null;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error adding champion: {ex.Message}");
return null;
}
}
public async Task<bool> DeleteItem(Champion? item)
{
try
{
var resp = await _httpClient.DeleteAsync($"{UrlApiChampions}/{item?.Name}");
return resp.IsSuccessStatusCode;
}
catch (Exception ex)
{
Console.WriteLine($"Error deleting champion: {ex.Message}");
return false;
}
}
private Func<Champion, string, bool> filterByNameContains = (champ, substring) => champ.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase);
private Func<Champion, string, bool> filterByName = (champ, substring) => champ.Name.Equals(substring, StringComparison.InvariantCultureIgnoreCase);
public async Task<IEnumerable<Champion?>> GetItemByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var response = await _httpClient.GetFromJsonAsync<IEnumerable<ChampionDto>>(UrlApiChampions);
return response.Select(c => c.ToModel()).GetItemsWithFilterAndOrdering(champ => filterByName(champ, substring), index, count, orderingPropertyName, descending);
}
public async Task<IEnumerable<Champion?>> GetItems(int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var url = $"{UrlApiChampions}?index={index}&count={count}&orderingPropertyName={orderingPropertyName}&descending={descending}";
var response = await _httpClient.GetFromJsonAsync<IEnumerable<ChampionDto>>(url);
return response.Select(c => c.ToModel());
}
private Func<Champion, string, bool> filterByCharacteristic = (champ, charName) => champ.Characteristics.Keys.Any(k => k.Contains(charName, StringComparison.InvariantCultureIgnoreCase));
public async Task<IEnumerable<Champion?>> GetItemsByCharacteristic(string charName, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var response = await _httpClient.GetFromJsonAsync<IEnumerable<ChampionDto>>(UrlApiChampions);
return response.Select(c => c.ToModel()).GetItemsWithFilterAndOrdering(
champ => filterByCharacteristic(champ, charName),
index, count, orderingPropertyName, descending);
}
private Func<Champion, ChampionClass, bool> filterByClass = (champ, championClass) => champ.Class == championClass;
public async Task<IEnumerable<Champion?>> GetItemsByClass(ChampionClass championClass, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var response = await _httpClient.GetFromJsonAsync<IEnumerable<ChampionDto>>(UrlApiChampions);
return response.Select(c => c.ToModel()).GetItemsWithFilterAndOrdering(
champ => filterByClass(champ, championClass),
index, count, orderingPropertyName, descending);
}
public async Task<IEnumerable<Champion?>> GetItemsByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var response = await _httpClient.GetFromJsonAsync<IEnumerable<ChampionDto>>(UrlApiChampions);
return response.Select(c => c.ToModel()).GetItemsWithFilterAndOrdering(champ => filterByNameContains(champ, substring), index, count, orderingPropertyName, descending);
}
public async Task<IEnumerable<Champion?>> GetItemsByRunePage(RunePage? runePage, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
throw new NotImplementedException();
}
private Func<Champion, Skill?, bool> filterBySkill = (champ, skill) => skill != null && champ.Skills.Contains(skill!);
private static Func<Champion, string, bool> filterBySkillSubstring = (champ, skill) => champ.Skills.Any(s => s.Name.Contains(skill, StringComparison.InvariantCultureIgnoreCase));
public async Task<IEnumerable<Champion?>> GetItemsBySkill(Skill? skill, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var response = await _httpClient.GetFromJsonAsync<IEnumerable<ChampionDto>>(UrlApiChampions);
return response.Select(c => c.ToModel()).GetItemsWithFilterAndOrdering(champ => filterBySkill(champ, skill), index, count, orderingPropertyName, descending);
}
public async Task<IEnumerable<Champion?>> GetItemsBySkill(string skillSubstring, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var response = await _httpClient.GetFromJsonAsync<IEnumerable<ChampionDto>>(UrlApiChampions);
return response.Select(c => c.ToModel()).GetItemsWithFilterAndOrdering(champ => filterBySkillSubstring(champ, skillSubstring), index, count, orderingPropertyName, descending);
}
public async Task<int> GetNbItems()
{
var response = await _httpClient.GetAsync("/countChampions");
var content = await response.Content.ReadAsStringAsync();
return int.Parse(content);
}
public async Task<int> GetNbItemsByCharacteristic(string charName)
{
var response = await _httpClient.GetFromJsonAsync<IEnumerable<ChampionDto>>(UrlApiChampions);
return await response.GetNbItemsWithFilter(champ => filterByCharacteristic(champ.ToModel(), charName));
}
public async Task<int> GetNbItemsByClass(ChampionClass championClass)
{
var response = await _httpClient.GetFromJsonAsync<IEnumerable<ChampionDto>>(UrlApiChampions);
return response.Where(c => c.Class.Equals(championClass))
.Count();
}
public async Task<int> GetNbItemsByName(string substring)
{
var response = await _httpClient.GetFromJsonAsync<IEnumerable<ChampionDto>>(UrlApiChampions);
return response.Where(c => c.Name.Equals(substring))
.Count();
}
public Task<int> GetNbItemsByRunePage(RunePage? runePage)
{
throw new NotImplementedException();
}
public async Task<int> GetNbItemsBySkill(Skill? skill)
{
var response = await _httpClient.GetFromJsonAsync<IEnumerable<ChampionDto>>(UrlApiChampions);
return await response.GetNbItemsWithFilter(champ => filterBySkill(champ.ToModel(), skill));
}
public async Task<int> GetNbItemsBySkill(string skill)
{
var response = await _httpClient.GetFromJsonAsync<IEnumerable<ChampionDto>>(UrlApiChampions);
return await response.GetNbItemsWithFilter(champ => filterBySkillSubstring(champ.ToModel(), skill));
}
public async Task<Champion?> UpdateItem(Champion? oldItem, Champion? newItem)
{
try
{
var resp = await _httpClient.PutAsJsonAsync($"{UrlApiChampions}/{oldItem?.Name}", newItem.ToDto());
if (resp.IsSuccessStatusCode)
{
var updatedItem = await resp.Content.ReadFromJsonAsync<ChampionDto>();
return updatedItem?.ToModel();
}
else
{
return null;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error updating champion: {ex.Message}");
return null;
}
}
}
}
}

@ -0,0 +1,145 @@
using DTO;
using Model;
using System.Net.Http.Json;
using ApiMapping;
using System.Data.SqlTypes;
namespace ApiManager
{
public partial class ApiManagerData
{
public class RunePagesManager : HttpClientManager, IRunePagesManager
{
private const string UrlApiRunePages = "/api/RunePages";
public RunePagesManager(HttpClient httpClient) : base(httpClient) { }
public async Task<RunePage?> AddItem(RunePage? item)
{
try
{
var resp = await _httpClient.PostAsJsonAsync($"{UrlApiRunePages}", item.ToDto());
if (resp.IsSuccessStatusCode)
{
var createdItem = await resp.Content.ReadFromJsonAsync<RunePageDto>();
return createdItem?.ToModel();
}
else
{
return null;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error adding runePage: {ex.Message}");
return null;
}
}
public async Task<bool> DeleteItem(RunePage? item)
{
try
{
var resp = await _httpClient.DeleteAsync($"{UrlApiRunePages}/{item?.Name}");
return resp.IsSuccessStatusCode;
}
catch (Exception ex)
{
Console.WriteLine($"Error deleting runePage: {ex.Message}");
return false;
}
}
private static Func<RunePage, string, bool> filterByName
= (rp, substring) => rp.Name.Equals(substring, StringComparison.InvariantCultureIgnoreCase);
private static Func<RunePage, string, bool> filterByNameContains
= (rp, substring) => rp.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase);
private static Func<RunePage, Rune?, bool> filterByRune
= (rp, rune) => rune != null && rp.Runes.Values.Contains(rune!);
public async Task<IEnumerable<RunePage?>> GetItemByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var runePages = await _httpClient.GetFromJsonAsync<PageResponse<RunePageDto>>(UrlApiRunePages);
return runePages.Data.Select(r => r.ToModel()).GetItemsWithFilterAndOrdering(
rp => filterByName(rp, substring),
index, count, orderingPropertyName, descending);
}
public async Task<IEnumerable<RunePage?>> GetItems(int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var runePages = await _httpClient.GetFromJsonAsync<PageResponse<RunePageDto>>($"{UrlApiRunePages}?&index={index}&count={count}&descending={descending}");
return runePages.Data.Select(c => c.ToModel());
}
public async Task<IEnumerable<RunePage?>> GetItemsByChampion(Champion? champion, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
throw new NotImplementedException();
}
public async Task<IEnumerable<RunePage?>> GetItemsByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var runePages = await _httpClient.GetFromJsonAsync<PageResponse<RunePageDto>>(UrlApiRunePages);
return runePages.Data.Select(r => r.ToModel()).GetItemsWithFilterAndOrdering(
rp => filterByNameContains(rp, substring),
index, count, orderingPropertyName, descending);
}
public async Task<IEnumerable<RunePage?>> GetItemsByRune(Rune? rune, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var runePages = await _httpClient.GetFromJsonAsync<PageResponse<RunePageDto>>(UrlApiRunePages);
return runePages.Data.Select(r => r.ToModel()).GetItemsWithFilterAndOrdering(
rp => filterByRune(rp, rune),
index, count, orderingPropertyName, descending);
}
public async Task<int> GetNbItems()
{
var response = await _httpClient.GetAsync("/countRunePages");
var content = await response.Content.ReadAsStringAsync();
return int.Parse(content);
}
public Task<int> GetNbItemsByChampion(Champion? champion)
{
throw new NotImplementedException();
}
public async Task<int> GetNbItemsByName(string substring)
{
var runePages = await _httpClient.GetFromJsonAsync<PageResponse<RunePageDto>>(UrlApiRunePages);
return await runePages.Data.Select(r => r.ToModel()).GetNbItemsWithFilter(
rp => filterByName(rp, substring));
}
public async Task<int> GetNbItemsByRune(Rune? rune)
{
var runePages = await _httpClient.GetFromJsonAsync<PageResponse<RunePageDto>>(UrlApiRunePages);
return await runePages.Data.Select(r => r.ToModel()).GetNbItemsWithFilter(
rp => filterByRune(rp, rune));
}
public async Task<RunePage?> UpdateItem(RunePage? oldItem, RunePage? newItem)
{
try
{
var resp = await _httpClient.PutAsJsonAsync($"{UrlApiRunePages}/{oldItem?.Name}", newItem.ToDto());
if (resp.IsSuccessStatusCode)
{
var updatedItem = await resp.Content.ReadFromJsonAsync<RunePageDto>();
return updatedItem?.ToModel();
}
else
{
return null;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error updating runePage: {ex.Message}");
return null;
}
}
}
}
}

@ -0,0 +1,138 @@
using DTO;
using Model;
using System.Net.Http.Json;
using ApiMapping;
using System;
using System.Data.SqlTypes;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace ApiManager
{
public partial class ApiManagerData
{
public class RunesManager : HttpClientManager, IRunesManager
{
private const string UrlApiRunes = "/api/runes";
public RunesManager(HttpClient httpClient) : base(httpClient) { }
public async Task<Rune?> AddItem(Rune? item)
{
try
{
var resp = await _httpClient.PostAsJsonAsync($"{UrlApiRunes}", item.ToDto());
if (resp.IsSuccessStatusCode)
{
var createdItem = await resp.Content.ReadFromJsonAsync<RuneDto>();
return createdItem?.ToModel();
}
else
{
return null;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error adding rune: {ex.Message}");
return null;
}
}
public async Task<bool> DeleteItem(Rune? item)
{
try
{
var resp = await _httpClient.DeleteAsync($"{UrlApiRunes}/{item?.Name}");
return resp.IsSuccessStatusCode;
}
catch (Exception ex)
{
Console.WriteLine($"Error deleting rune: {ex.Message}");
return false;
}
}
private static Func<Rune, RuneFamily, bool> filterByRuneFamily
= (rune, family) => rune.Family == family;
private static Func<Rune, string, bool> filterByName
= (rune, substring) => rune.Name.Equals(substring, StringComparison.InvariantCultureIgnoreCase);
private static Func<Rune, string, bool> filterByNameContains
= (rune, substring) => rune.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase);
public async Task<IEnumerable<Rune?>> GetItemByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var runes = await _httpClient.GetFromJsonAsync<PageResponse<RuneDto>>(UrlApiRunes);
return runes.Data.Select(r => r.ToModel()).GetItemsWithFilterAndOrdering(
rune => filterByName(rune, substring),
index, count, orderingPropertyName, descending);
}
public async Task<IEnumerable<Rune?>> GetItems(int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var runes = await _httpClient.GetFromJsonAsync<PageResponse<RuneDto>>($"{UrlApiRunes}?&index={index}&count={count}&descending={descending}");
return runes.Data.Select(c => c.ToModel());
}
public async Task<IEnumerable<Rune?>> GetItemsByFamily(RuneFamily family, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var runes = await _httpClient.GetFromJsonAsync<PageResponse<RuneDto>>(UrlApiRunes);
return runes.Data.Select(r => r.ToModel()).GetItemsWithFilterAndOrdering(
rune => filterByRuneFamily(rune, family),
index, count, orderingPropertyName, descending);
}
public async Task<IEnumerable<Rune?>> GetItemsByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var runes = await _httpClient.GetFromJsonAsync<PageResponse<RuneDto>>(UrlApiRunes);
return runes.Data.Select(r => r.ToModel()).GetItemsWithFilterAndOrdering(
rune => filterByNameContains(rune, substring),
index, count, orderingPropertyName, descending);
}
public async Task<int> GetNbItems()
{
var response = await _httpClient.GetAsync("/countRunes");
var content = await response.Content.ReadAsStringAsync();
return int.Parse(content);
}
public async Task<int> GetNbItemsByFamily(RuneFamily family)
{
var runes = await _httpClient.GetFromJsonAsync<PageResponse<RuneDto>>(UrlApiRunes);
return await runes.Data.Select(r => r.ToModel()).GetNbItemsWithFilter(
rune => filterByRuneFamily(rune, family));
}
public async Task<int> GetNbItemsByName(string substring)
{
var runes = await _httpClient.GetFromJsonAsync<PageResponse<RuneDto>>(UrlApiRunes);
return await runes.Data.Select(r => r.ToModel()).GetNbItemsWithFilter(
rune => filterByName(rune, substring));
}
public async Task<Rune?> UpdateItem(Rune? oldItem, Rune? newItem)
{
try
{
var resp = await _httpClient.PutAsJsonAsync($"{UrlApiRunes}/{oldItem?.Name}", newItem.ToDto());
if (resp.IsSuccessStatusCode)
{
var updatedItem = await resp.Content.ReadFromJsonAsync<RuneDto>();
return updatedItem?.ToModel();
}
else
{
return null;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error updating rune: {ex.Message}");
return null;
}
}
}
}
}

@ -0,0 +1,178 @@
using DTO;
using Model;
using System.Net.Http.Json;
using ApiMapping;
using System.Data.SqlTypes;
namespace ApiManager
{
public partial class ApiManagerData
{
public class SkinsManager : HttpClientManager, ISkinsManager
{
private const string UrlApiSkins = "/api/Skins";
public SkinsManager(HttpClient httpClient) : base(httpClient) { }
public async Task<Skin?> AddItem(Skin? item)
{
try
{
var resp = await _httpClient.PostAsJsonAsync($"{UrlApiSkins}", item.ToDtoC());
if (resp.IsSuccessStatusCode)
{
var createdItem = await resp.Content.ReadFromJsonAsync<SkinDtoC>();
var championManager = new ChampionsManager(_httpClient);
var champ = await championManager.GetItemByName(createdItem.ChampionName, 0, 1);
return createdItem?.ToModelC(champ.First());
}
else
{
return null;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error adding skin: {ex.Message}");
return null;
}
}
public async Task<bool> DeleteItem(Skin? item)
{
try
{
var resp = await _httpClient.DeleteAsync($"{UrlApiSkins}/{item?.Name}");
return resp.IsSuccessStatusCode;
}
catch (Exception ex)
{
Console.WriteLine($"Error deleting skin: {ex.Message}");
return false;
}
}
public async Task<IEnumerable<Skin?>> GetItemByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var dtoSkins = await _httpClient.GetFromJsonAsync<PageResponse<SkinDtoC>>(UrlApiSkins);
var skins = new List<Skin>();
var championManager = new ChampionsManager(_httpClient);
foreach (var skin in dtoSkins.Data)
{
var champ = await championManager.GetItemByName(skin.ChampionName, 0, 1);
skins.Add(skin.ToModelC(champ.First()));
}
return skins.GetItemsWithFilterAndOrdering(
skin => filterByName(skin, substring),
index, count, orderingPropertyName, descending);
}
public async Task<IEnumerable<Skin?>> GetItems(int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var dtoSkins = await _httpClient.GetFromJsonAsync<PageResponse<SkinDtoC>>($"{UrlApiSkins}?&index={index}&count={count}&descending={descending}");
var skins = new List<Skin>();
var championManager = new ChampionsManager(_httpClient);
foreach (var skin in dtoSkins.Data)
{
var champ = await championManager.GetItemByName(skin.ChampionName, 0, 1);
skins.Add(skin.ToModelC(champ.First()));
}
return skins;
}
public async Task<IEnumerable<Skin?>> GetItemsByChampion(Champion? champion, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var dtoSkins = await _httpClient.GetFromJsonAsync<PageResponse<SkinDtoC>>(UrlApiSkins);
var skins = new List<Skin>();
var championManager = new ChampionsManager(_httpClient);
foreach (var skin in dtoSkins.Data)
{
var champ = await championManager.GetItemByName(skin.ChampionName, 0, 1);
skins.Add(skin.ToModelC(champ.First()));
}
return skins.GetItemsWithFilterAndOrdering(
skin => filterByChampion(skin, champion),
index, count, orderingPropertyName, descending);
}
private static Func<Skin, Champion?, bool> filterByChampion = (skin, champion) => champion != null && skin.Champion.Equals(champion!);
private static Func<Skin, string, bool> filterByName = (skin, substring) => skin.Name.Equals(substring, StringComparison.InvariantCultureIgnoreCase);
private static Func<Skin, string, bool> filterByNameContains = (skin, substring) => skin.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase);
public async Task<IEnumerable<Skin?>> GetItemsByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
var dtoSkins = await _httpClient.GetFromJsonAsync<PageResponse<SkinDtoC>>(UrlApiSkins);
var skins = new List<Skin>();
var championManager = new ChampionsManager(_httpClient);
foreach (var skin in dtoSkins.Data)
{
var champ = await championManager.GetItemByName(skin.ChampionName,0,1);
skins.Add(skin.ToModelC(champ.First()));
}
return skins.GetItemsWithFilterAndOrdering(
skin => filterByNameContains(skin, substring),
index, count, orderingPropertyName, descending);
}
public async Task<int> GetNbItems()
{
var response = await _httpClient.GetAsync("/countSkins");
var content = await response.Content.ReadAsStringAsync();
return int.Parse(content);
}
public async Task<int> GetNbItemsByChampion(Champion? champion)
{
var dtoSkins = await _httpClient.GetFromJsonAsync<PageResponse<SkinDtoC>>(UrlApiSkins);
var skins = new List<Skin>();
var championManager = new ChampionsManager(_httpClient);
foreach (var skin in dtoSkins.Data)
{
var champ = await championManager.GetItemByName(skin.ChampionName, 0, 1);
skins.Add(skin.ToModelC(champ.First()));
}
return await skins.GetNbItemsWithFilter(
skin => filterByChampion(skin, champion));
}
public async Task<int> GetNbItemsByName(string substring)
{
var dtoSkins = await _httpClient.GetFromJsonAsync<PageResponse<SkinDtoC>>(UrlApiSkins);
var skins = new List<Skin>();
var championManager = new ChampionsManager(_httpClient);
foreach (var skin in dtoSkins.Data)
{
var champ = await championManager.GetItemByName(skin.ChampionName, 0, 1);
skins.Add(skin.ToModelC(champ.First()));
}
return await skins.GetNbItemsWithFilter(
skin => filterByName(skin, substring));
}
public async Task<Skin?> UpdateItem(Skin? oldItem, Skin? newItem)
{
try
{
var resp = await _httpClient.PutAsJsonAsync($"{UrlApiSkins}/{oldItem?.Name}", newItem.ToDto());
if (resp.IsSuccessStatusCode)
{
var updatedItem = await resp.Content.ReadFromJsonAsync<SkinDtoC>();
var championManager = new ChampionsManager(_httpClient);
var champ = await championManager.GetItemByName(updatedItem.ChampionName, 0, 1);
return updatedItem?.ToModelC(champ.First());
}
else
{
return null;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error updating skin: {ex.Message}");
return null;
}
}
}
}
}

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ApiMapping\ApiMapping.csproj" />
<ProjectReference Include="..\DTO\DTO.csproj" />
<ProjectReference Include="..\Model\Model.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,24 @@
using Model;
namespace ApiManager
{
public partial class ApiManagerData : IDataManager
{
public ApiManagerData(HttpClient httpClient)
{
ChampionsMgr = new ChampionsManager(httpClient);
SkinsMgr = new SkinsManager(httpClient);
RunesMgr = new RunesManager(httpClient);
RunePagesMgr = new RunePagesManager(httpClient);
}
public IChampionsManager ChampionsMgr { get; set; }
public ISkinsManager SkinsMgr { get; set; }
public IRunesManager RunesMgr { get; set; }
public IRunePagesManager RunePagesMgr { get; set; }
}
}

@ -1,42 +0,0 @@
using DTO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Json;
using System.Text;
using System.Threading.Tasks;
namespace Client
{
public class ChampionHttpClient
{
private const string ApiChampions = "api/champions";
private readonly HttpClient _httpClient;
public ChampionHttpClient(HttpClient httpClient)
{
_httpClient = httpClient;
httpClient.BaseAddress = new Uri("https://localhost:7252;http://localhost:5252");
}
public async Task<IEnumerable<ChampionDto>> GetChampion()
{
var champions = await _httpClient.GetFromJsonAsync<IEnumerable<ChampionDto>>(ApiChampions);
return champions;
}
public async void Add(ChampionDto champion)
{
await _httpClient.PostAsJsonAsync<ChampionDto>(ApiChampions, champion);
}
/* public async void Delete(ChampionDto champion)
{
await _httpClient.DeleteAsync(champion);
}
public async void Update(ChampionDto champion)
{
await _httpClient.PutAsJsonAsync<ChampionDto>(ApiChampions, champion);
}*/
}
}

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ApiManager
{
static class Extensions
{
internal static IEnumerable<T?> GetItemsWithFilterAndOrdering<T>(this IEnumerable<T> collection,
Func<T, bool> filter, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
IEnumerable<T> temp = collection;
temp = temp.Where(item => filter(item));
if (orderingPropertyName != null)
{
var prop = typeof(T).GetProperty(orderingPropertyName!);
if (prop != null)
{
temp = descending ? temp.OrderByDescending(item => prop.GetValue(item))
: temp.OrderBy(item => prop.GetValue(item));
}
}
return temp.Skip(index * count).Take(count);
}
internal static Task<int> GetNbItemsWithFilter<T>(this IEnumerable<T> collection, Func<T, bool> filter)
{
return Task.FromResult(collection.Count(item => filter(item)));
}
}
}

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ApiManager
{
public class HttpClientManager
{
protected readonly HttpClient _httpClient;
public HttpClientManager(HttpClient httpClient)
{
_httpClient = httpClient;
_httpClient.BaseAddress = new Uri("https://localhost:7252");
}
}
}

@ -1,2 +1,38 @@
// See https://aka.ms/new-console-template for more information /*// See https://aka.ms/new-console-template for more information
using static ApiManager.ApiManagerData;
Console.WriteLine("Hello, World!"); Console.WriteLine("Hello, World!");
var championClient = new ChampionsManager(new HttpClient());
// Get all champions
var champions = await championClient.GetItems(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.");
}
*//*
Console.ReadLine();*/

@ -6,8 +6,10 @@
public string Bio { get; set; } public string Bio { get; set; }
public ChampionClassDto Class { get; set; } public ChampionClassDto Class { get; set; }
public string Icon { get; set; } public string Icon { get; set; }
public string Image { get; set; } public LargeImageDto Image { get; set; }
public IEnumerable<SkinDto> Skins { get; set; } public IEnumerable<SkinDto>? Skins { get; set; }
public IEnumerable<SkillDto>? Skills { get; set; }
public Dictionary<string, int>? Characteristics { get; set; }
} }
} }

@ -0,0 +1,7 @@
namespace DTO
{
public class LargeImageDto
{
public string Base64 { get; set; }
}
}

@ -0,0 +1,11 @@
namespace DTO
{
public class PageRequest
{
public int index { get; set; } = 0;
public int count { get; set; } = 0;
public string? name { get; set; } = null;
public string? orderingPropertyName { get; set; } = "Name";
public bool descending { get; set; } = false;
}
}

@ -0,0 +1,11 @@
namespace DTO
{
public class PageResponse<T>
{
public IEnumerable<T> Data { get; set; }
public int index { get; set; }
public int count { get; set; }
public int total { get; set; }
}
}

@ -1,8 +1,4 @@
using System; using DTO.enums;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DTO namespace DTO
{ {
@ -10,6 +6,9 @@ namespace DTO
{ {
public string Name { get; set; } public string Name { get; set; }
public string Description { get; set; } public string Description { get; set; }
public RuneFamilyDto Family { get; set; }
public string Icon { get; set; }
public LargeImageDto Image { get; set; }
} }
} }

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DTO
{
public partial class RunePageDto
{
public string Name { get; set; }
public Dictionary<string, RuneDto> Runes { get; set; }
}
}

@ -1,14 +1,9 @@
using System; namespace DTO
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DTO
{ {
public class SkillDto public class SkillDto
{ {
public string Name { get; set; } public string Name { get; set; }
public string Description { get; set; } public string Description { get; set; }
public SkillTypeDto Type { get; set; }
} }
} }

@ -1,16 +1,11 @@
using System; namespace DTO
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DTO
{ {
public class SkinDto public class SkinDto
{ {
public string Name { get; set; } public string Name { get; set; }
public string Description { get; set; } public string Description { get; set; }
public string Icon { get; set; } public string Icon { get; set; }
public LargeImageDto Image { get; set; }
public float Price { get; set; } public float Price { get; set; }
} }

@ -0,0 +1,14 @@
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; }
}
}

@ -1,10 +1,4 @@
using System; namespace DTO
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DTO
{ {
public enum ChampionClassDto public enum ChampionClassDto
{ {

@ -0,0 +1,9 @@
namespace DTO.enums
{
public enum RuneFamilyDto
{
Unknown,
Precision,
Domination
}
}

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DTO
{
public partial class RunePageDto
{
public enum CategoryDto
{
Major,
Minor1,
Minor2,
Minor3,
OtherMinor1,
OtherMinor2
}
}
}

@ -0,0 +1,10 @@
namespace DTO
{
public enum SkillTypeDto
{
Unknown,
Basic,
Passive,
Ultimate
}
}

@ -0,0 +1,129 @@
using DbManager.Mapper;
using Microsoft.EntityFrameworkCore;
using Model;
namespace DbLib
{
public partial class DbManager
{
public class ChampionsManager : IChampionsManager
{
private readonly DbManager parent;
public ChampionsManager(DbManager parent)
=> this.parent = parent;
public async Task<Champion?> AddItem(Champion? item)
{
var champion = await parent.DbContext.Champions.AddAsync(item.ToEntity(parent.DbContext));
parent.DbContext.SaveChanges();
return champion.Entity.ToModel();
}
public async Task<bool> DeleteItem(Champion? item)
{
var toDelete = parent.DbContext.Champions.Where(c => c.Name == item.Name).First();
if (toDelete != null)
{
parent.DbContext.Champions.Remove(toDelete);
parent.DbContext.SaveChanges();
return true;
}
return false;
}
private Func<Champion, string, bool> filterByNameContains = (champ, substring) => champ.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase);
private Func<Champion, string, bool> filterByName = (champ, substring) => champ.Name.Equals(substring, StringComparison.InvariantCultureIgnoreCase);
public async Task<IEnumerable<Champion?>> GetItemByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.Champions.Include(c => c.Skills).Include(c => c.Characteristics).Include(c => c.Skins).Include(c => c.Image).GetItemsWithFilterAndOrdering(champ => filterByName(champ.ToModel(), substring), index, count, orderingPropertyName, descending)
.Select(c => c.ToModel());
public async Task<IEnumerable<Champion?>> GetItems(int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.Champions.Include(c => c.Skills).Include(c => c.Characteristics).Include(c => c.Skins).Include(c => c.Image).GetItemsWithFilterAndOrdering(
c => true,
index, count,
orderingPropertyName, descending).Select(c => c.ToModel());
private Func<Champion, string, bool> filterByCharacteristic = (champ, charName) => champ.Characteristics.Keys.Any(k => k.Contains(charName, StringComparison.InvariantCultureIgnoreCase));
public async Task<IEnumerable<Champion?>> GetItemsByCharacteristic(string charName, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.Champions.Include(c => c.Skills).Include(c => c.Characteristics).Include(c => c.Skins).Include(c => c.Image).GetItemsWithFilterAndOrdering(
champ => filterByCharacteristic(champ.ToModel(), charName),
index, count, orderingPropertyName, descending).Select(c => c.ToModel());
private Func<Champion, ChampionClass, bool> filterByClass = (champ, championClass) => champ.Class == championClass;
public async Task<IEnumerable<Champion?>> GetItemsByClass(ChampionClass championClass, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.Champions.Include(c => c.Skills).Include(c => c.Characteristics).Include(c => c.Skins).Include(c => c.Image).GetItemsWithFilterAndOrdering(
champ => filterByClass(champ.ToModel(), championClass),
index, count, orderingPropertyName, descending).Select(c => c.ToModel());
public async Task<IEnumerable<Champion?>> GetItemsByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.Champions.Include(c => c.Skills).Include(c => c.Characteristics).Include(c => c.Skins).Include(c => c.Image).GetItemsWithFilterAndOrdering(champ => filterByNameContains(champ.ToModel(), substring), index, count, orderingPropertyName, descending)
.Select(c => c.ToModel());
public async Task<IEnumerable<Champion?>> GetItemsByRunePage(RunePage? runePage, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.Champions.Include(c => c.Skills).Include(c => c.Characteristics).Include(c => c.Skins).Include(c => c.Image).GetItemsWithFilterAndOrdering(
c => c.RunePages.Any(rp => rp.Equals(runePage.ToEntity(parent.DbContext))),
index, count,
orderingPropertyName, descending).Select(c => c.ToModel());
public async Task<IEnumerable<Champion?>> GetItemsBySkill(Skill? skill, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.Champions.Include(c => c.Skills).Include(c => c.Characteristics).Include(c => c.Skins).Include(c => c.Image).GetItemsWithFilterAndOrdering(champ => filterBySkill(champ.ToModel(), skill), index, count, orderingPropertyName, descending)
.Select(c => c.ToModel());
public async Task<IEnumerable<Champion?>> GetItemsBySkill(string skill, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.Champions.Include(c => c.Skills).Include(c => c.Characteristics).Include(c => c.Skins).Include(c => c.Image).GetItemsWithFilterAndOrdering(champ => filterBySkillSubstring(champ.ToModel(), skill), index, count, orderingPropertyName, descending)
.Select(c => c.ToModel());
public async Task<int> GetNbItems()
=> parent.DbContext.Champions.Count();
public Task<int> GetNbItemsByCharacteristic(string charName)
=> parent.DbContext.Champions.GetNbItemsWithFilter(champ => filterByCharacteristic(champ.ToModel(), charName));
public async Task<int> GetNbItemsByClass(ChampionClass championClass)
=> parent.DbContext.Champions.Where(c => c.Class.Equals(championClass))
.Count();
public async Task<int> GetNbItemsByName(string substring)
=> parent.DbContext.Champions.Where(c => c.Name.Equals(substring))
.Count();
public async Task<int> GetNbItemsByRunePage(RunePage? runePage)
=> parent.DbContext.Champions.Where(c => c.RunePages.Any(rp => rp.Equals(runePage.ToEntity(parent.DbContext))))
.Count();
private Func<Champion, Skill?, bool> filterBySkill = (champ, skill) => skill != null && champ.Skills.Contains(skill!);
public Task<int> GetNbItemsBySkill(Skill? skill)
=> parent.DbContext.Champions.GetNbItemsWithFilter(champ => filterBySkill(champ.ToModel(), skill));
private static Func<Champion, string, bool> filterBySkillSubstring = (champ, skill) => champ.Skills.Any(s => s.Name.Contains(skill, StringComparison.InvariantCultureIgnoreCase));
public Task<int> GetNbItemsBySkill(string skill)
=> parent.DbContext.Champions.GetNbItemsWithFilter(champ => filterBySkillSubstring(champ.ToModel(), skill));
public async Task<Champion?> UpdateItem(Champion? oldItem, Champion? newItem)
{
var toUpdate = parent.DbContext.Champions.FirstOrDefault(champ => champ.Name == oldItem.Name);
var newEntity = newItem.ToEntity(parent.DbContext);
toUpdate.Bio = newEntity.Bio;
toUpdate.Class = newEntity.Class;
toUpdate.Icon = newEntity.Icon;
toUpdate.Image = newEntity.Image;
toUpdate.Skins = newEntity.Skins;
toUpdate.Skills = newEntity.Skills;
toUpdate.Characteristics = newEntity.Characteristics;
parent.DbContext.SaveChanges();
return toUpdate?.ToModel();
}
}
}
}

@ -0,0 +1,96 @@
using DbManager.Mapper;
using Microsoft.EntityFrameworkCore;
using Model;
namespace DbLib
{
public partial class DbManager
{
public class RunePagesManager : IRunePagesManager
{
private readonly DbManager parent;
public RunePagesManager(DbManager parent)
=> this.parent = parent;
public async Task<RunePage?> AddItem(RunePage? item)
{
var RunePage = await parent.DbContext.RunePages.AddAsync(item.ToEntity(parent.DbContext));
parent.DbContext.SaveChanges();
return RunePage.Entity.ToModel(parent.DbContext);
}
public async Task<bool> DeleteItem(RunePage? item)
{
var toDelete = parent.DbContext.RunePages.Find(item.Name);
if (toDelete != null)
{
parent.DbContext.RunePages.Remove(toDelete);
parent.DbContext.SaveChanges();
return true;
}
return false;
}
private static Func<RunePage, string, bool> filterByName
= (rp, substring) => rp.Name.Equals(substring, StringComparison.InvariantCultureIgnoreCase);
private static Func<RunePage, string, bool> filterByNameContains
= (rp, substring) => rp.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase);
private static Func<RunePage, Rune?, bool> filterByRune
= (rp, rune) => rune != null && rp.Runes.Values.Contains(rune!);
public async Task<IEnumerable<RunePage?>> GetItemByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.RunePages.Include(rp => rp.Champions).Include(rp => rp.DictionaryCategoryRunes).GetItemsWithFilterAndOrdering(
rp => filterByName(rp.ToModel(parent.DbContext), substring),
index, count, orderingPropertyName, descending).Select(c => c.ToModel(parent.DbContext));
public async Task<IEnumerable<RunePage?>> GetItems(int index, int count, string? orderingPropertyName = null, bool descending = false)
{
parent.DbContext.Runes.Include(r => r.Image);
return parent.DbContext.RunePages.Include(rp => rp.Champions).Include(rp => rp.DictionaryCategoryRunes).Include(rp => rp.DictionaryCategoryRunes).GetItemsWithFilterAndOrdering(
rp => true,
index, count, orderingPropertyName, descending).Select(c => c.ToModel(parent.DbContext));
}
public async Task<IEnumerable<RunePage?>> GetItemsByChampion(Champion? champion, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.RunePages.Include(rp => rp.Champions).Include(rp => rp.DictionaryCategoryRunes).GetItemsWithFilterAndOrdering(
rp => rp.Champions.Any(c => c.Name.Equals(champion.Name)),
index, count,
orderingPropertyName, descending).Select(rp => rp.ToModel(parent.DbContext));
public async Task<IEnumerable<RunePage?>> GetItemsByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.RunePages.Include(rp => rp.Champions).Include(rp => rp.DictionaryCategoryRunes).GetItemsWithFilterAndOrdering(
rp => filterByNameContains(rp.ToModel(parent.DbContext), substring),
index, count, orderingPropertyName, descending).Select(c => c.ToModel(parent.DbContext));
public async Task<IEnumerable<RunePage?>> GetItemsByRune(Rune? rune, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.RunePages.Include(rp => rp.Champions).Include(rp => rp.DictionaryCategoryRunes).GetItemsWithFilterAndOrdering(
rp => filterByRune(rp.ToModel(parent.DbContext), rune),
index, count, orderingPropertyName, descending).Select(c => c.ToModel(parent.DbContext));
public Task<int> GetNbItems()
=> parent.DbContext.RunePages.GetNbItemsWithFilter(
rp => true);
public async Task<int> GetNbItemsByChampion(Champion? champion)
=> parent.DbContext.RunePages.Where(rp => rp.Champions.Any(c => c.Name.Equals(champion.Name))).Count();
public async Task<int> GetNbItemsByName(string substring)
=> parent.DbContext.RunePages.Where(rp => rp.Name.Contains(substring)).Count();
public async Task<int> GetNbItemsByRune(Rune? rune)
=> parent.DbContext.RunePages.Where(rp => rp.DictionaryCategoryRunes.Any(r => r.RuneName.Equals(rune.Name))).Count();
public async Task<RunePage?> UpdateItem(RunePage? oldItem, RunePage? newItem)
{
var toUpdate = parent.DbContext.RunePages.Find(oldItem.Name);
toUpdate.DictionaryCategoryRunes = newItem.ToEntity(parent.DbContext).DictionaryCategoryRunes;
parent.DbContext.SaveChanges();
return toUpdate.ToModel(parent.DbContext);
}
}
}
}

@ -0,0 +1,90 @@
using DbManager.Mapper;
using Microsoft.EntityFrameworkCore;
using Model;
namespace DbLib
{
public partial class DbManager
{
public class RunesManager : IRunesManager
{
private readonly DbManager parent;
public RunesManager(DbManager parent)
=> this.parent = parent;
public async Task<Rune?> AddItem(Rune? item)
{
var rune = await parent.DbContext.Runes.AddAsync(item.ToEntity());
parent.DbContext.SaveChanges();
return rune.Entity.ToModel();
}
public async Task<bool> DeleteItem(Rune? item)
{
var toDelete = parent.DbContext.Runes.Find(item.Name);
if (toDelete != null)
{
parent.DbContext.Runes.Remove(toDelete);
parent.DbContext.SaveChanges();
return true;
}
return false;
}
private static Func<Rune, RuneFamily, bool> filterByRuneFamily
= (rune, family) => rune.Family == family;
private static Func<Rune, string, bool> filterByName
= (rune, substring) => rune.Name.Equals(substring, StringComparison.InvariantCultureIgnoreCase);
private static Func<Rune, string, bool> filterByNameContains
= (rune, substring) => rune.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase);
public async Task<IEnumerable<Rune?>> GetItemByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.Runes.Include(r => r.Image).GetItemsWithFilterAndOrdering(
rune => filterByName(rune.ToModel(), substring),
index, count, orderingPropertyName, descending).Select(c => c.ToModel());
public async Task<IEnumerable<Rune?>> GetItems(int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.Runes.Include(r => r.Image).GetItemsWithFilterAndOrdering(
r => true,
index, count, orderingPropertyName, descending).Select(c => c.ToModel());
public async Task<IEnumerable<Rune?>> GetItemsByFamily(RuneFamily family, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.Runes.Include(r => r.Image).GetItemsWithFilterAndOrdering(
rune => filterByRuneFamily(rune.ToModel(), family),
index, count, orderingPropertyName, descending).Select(c => c.ToModel());
public async Task<IEnumerable<Rune?>> GetItemsByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.Runes.Include(r => r.Image).GetItemsWithFilterAndOrdering(
rune => filterByNameContains(rune.ToModel(), substring),
index, count, orderingPropertyName, descending).Select(c => c.ToModel());
public Task<int> GetNbItems()
=> parent.DbContext.Runes.GetNbItemsWithFilter(
rune => true);
public Task<int> GetNbItemsByFamily(RuneFamily family)
=> parent.DbContext.Runes.GetNbItemsWithFilter(
rune => filterByRuneFamily(rune.ToModel(), family));
public Task<int> GetNbItemsByName(string substring)
=> parent.DbContext.Runes.GetNbItemsWithFilter(
rune => filterByName(rune.ToModel(), substring));
public async Task<Rune?> UpdateItem(Rune? oldItem, Rune? newItem)
{
var toUpdate = parent.DbContext.Runes.Find(oldItem.Name);
var newEntity = newItem.ToEntity();
toUpdate.Description = newEntity.Description;
toUpdate.Icon = newEntity.Icon;
toUpdate.Family = newEntity.Family;
toUpdate.Image = newEntity.Image;
parent.DbContext.SaveChanges();
return toUpdate.ToModel();
}
}
}
}

@ -0,0 +1,93 @@
using DbManager.Mapper;
using Microsoft.EntityFrameworkCore;
using Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DbLib
{
public partial class DbManager
{
public class SkinsManager : ISkinsManager
{
private readonly DbManager parent;
public SkinsManager(DbManager parent)
=> this.parent = parent;
public async Task<Skin?> AddItem(Skin? item)
{
var skin = await parent.DbContext.Skins.AddAsync(item.ToEntity(parent.DbContext));
parent.DbContext.SaveChanges();
return skin.Entity.ToModel();
}
public async Task<bool> DeleteItem(Skin? item)
{
var toDelete = parent.DbContext.Skins.Find(item.Name);
if (toDelete != null)
{
parent.DbContext.Skins.Remove(toDelete);
parent.DbContext.SaveChanges();
return true;
}
return false;
}
private static Func<Skin, Champion?, bool> filterByChampion = (skin, champion) => champion != null && skin.Champion.Equals(champion!);
private static Func<Skin, string, bool> filterByName = (skin, substring) => skin.Name.Equals(substring, StringComparison.InvariantCultureIgnoreCase);
private static Func<Skin, string, bool> filterByNameContains = (skin, substring) => skin.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase);
public async Task<IEnumerable<Skin?>> GetItemByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.Skins.Include(s => s.Champion).Include(s => s.Image).GetItemsWithFilterAndOrdering(
skin => filterByName(skin.ToModel(), substring),
index, count, orderingPropertyName, descending).Select(c => c.ToModel());
public async Task<IEnumerable<Skin?>> GetItems(int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.Skins.Include(s => s.Champion).Include(s => s.Image).GetItemsWithFilterAndOrdering(
skin => true,
index, count, orderingPropertyName, descending).Select(c => c.ToModel());
public async Task<IEnumerable<Skin?>> GetItemsByChampion(Champion? champion, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.Skins.Include(s => s.Champion).Include(s => s.Image).GetItemsWithFilterAndOrdering(
skin => filterByChampion(skin.ToModel(), champion),
index, count, orderingPropertyName, descending).Select(c => c.ToModel());
public async Task<IEnumerable<Skin?>> GetItemsByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false)
=> parent.DbContext.Skins.Include(s => s.Champion).Include(s => s.Image).GetItemsWithFilterAndOrdering(
skin => filterByNameContains(skin.ToModel(), substring),
index, count, orderingPropertyName, descending).Select(c => c.ToModel());
public Task<int> GetNbItems()
=> parent.DbContext.Skins.GetNbItemsWithFilter(
c => true);
public Task<int> GetNbItemsByChampion(Champion? champion)
=> parent.DbContext.Skins.GetNbItemsWithFilter(
skin => filterByChampion(skin.ToModel(), champion));
public Task<int> GetNbItemsByName(string substring)
=> parent.DbContext.Skins.GetNbItemsWithFilter(
skin => filterByName(skin.ToModel(), substring));
public async Task<Skin?> UpdateItem(Skin? oldItem, Skin? newItem)
{
var toUpdate = parent.DbContext.Skins.Find(oldItem.Name);
var newEntity = newItem.ToEntity(parent.DbContext);
toUpdate.Description = newEntity.Description;
toUpdate.Icon = newEntity.Icon;
toUpdate.Price = newEntity.Price;
toUpdate.Champion = newEntity.Champion;
toUpdate.Image = newEntity.Image;
parent.DbContext.SaveChanges();
return toUpdate.ToModel();
}
}
}
}

@ -0,0 +1,36 @@
using Model;
using MyFlib;
namespace DbLib
{
public partial class DbManager : IDataManager
{
protected LolDbContext DbContext { get; set; }
public DbManager()
{
DbContext = new LolDbContext();
ChampionsMgr = new ChampionsManager(this);
SkinsMgr = new SkinsManager(this);
RunesMgr = new RunesManager(this);
RunePagesMgr = new RunePagesManager(this);
}
public DbManager(LolDbContext dbContext)
{
DbContext = dbContext;
ChampionsMgr = new ChampionsManager(this);
SkinsMgr = new SkinsManager(this);
RunesMgr = new RunesManager(this);
RunePagesMgr = new RunePagesManager(this);
}
public IChampionsManager ChampionsMgr { get; set; }
public ISkinsManager SkinsMgr { get; set; }
public IRunesManager RunesMgr { get; set; }
public IRunePagesManager RunePagesMgr { get; set; }
}
}

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Model\Model.csproj" />
<ProjectReference Include="..\MyFlib\MyFlib.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DbLib
{
static class Extensions
{
internal static IEnumerable<T?> GetItemsWithFilterAndOrdering<T>(this IEnumerable<T> collection,
Func<T, bool> filter, int index, int count, string? orderingPropertyName = null, bool descending = false)
{
IEnumerable<T> temp = collection;
temp = temp.Where(item => filter(item));
if (orderingPropertyName != null)
{
var prop = typeof(T).GetProperty(orderingPropertyName!);
if (prop != null)
{
temp = descending ? temp.OrderByDescending(item => prop.GetValue(item))
: temp.OrderBy(item => prop.GetValue(item));
}
}
return temp.Skip(index * count).Take(count);
}
internal static Task<int> GetNbItemsWithFilter<T>(this IEnumerable<T> collection, Func<T, bool> filter)
{
return Task.FromResult(collection.Count(item => filter(item)));
}
}
}

@ -0,0 +1,51 @@
using DbManager.Mapper.enums;
using Model;
using MyFlib;
namespace DbManager.Mapper
{
public static class ChampionMapper
{
public static Champion ToModel(this ChampionEntity championEntity)
{
Champion champion = new (championEntity.Name, championEntity.Class.ToModel(), championEntity.Icon, championEntity.Image.Base64, championEntity.Bio);
foreach (var skill in championEntity.Skills)
{
champion.AddSkill(skill.ToModel());
}
foreach (var skin in championEntity.Skins)
{
champion.AddSkin(new Skin(skin.Name, champion, skin.Price, skin.Icon, "", skin.Description));
}
if (championEntity.Characteristics != null)
{
foreach (var c in championEntity.Characteristics)
{
champion.AddCharacteristics(c.ToModel());
}
}
return champion;
}
public static ChampionEntity ToEntity(this Champion champion, LolDbContext context)
{
var champ = new ChampionEntity()
{
Name = champion.Name,
Icon = champion.Icon,
Bio = champion.Bio,
Image = champion.Image.ToEntity(),
};
foreach (var skill in champion.Skills)
{
champ.Skills.Add(skill.ToEntity(champ, context));
}
foreach (var skin in champion.Skins)
{
champ.Skins.Add(skin.ToEntity(context));
}
champ.Characteristics = champion.Characteristics.Select(x => x.ToEntity(champ, context)).ToList();
return champ;
}
}
}

@ -0,0 +1,27 @@
using MyFlib.Entities;
using MyFlib;
namespace DbManager.Mapper
{
public static class CharacteristicMapper
{
public static CharacteristicEntity ToEntity(this KeyValuePair<string, int> item, ChampionEntity champion, LolDbContext context)
{
var characteristicEntity = context.Characteristic.Find(item.Key, champion.Id);
if (characteristicEntity == null)
{
return new()
{
Name = item.Key,
Value = item.Value,
ChampionForeignKey = champion.Id
};
}
return characteristicEntity;
}
public static Tuple<string, int> ToModel(this CharacteristicEntity entity)
=> new(entity.Name, entity.Value);
}
}

@ -0,0 +1,13 @@
using Model;
using MyFlib;
namespace DbManager.Mapper
{
public static class LargeImageMapper
{
public static LargeImage ToModel(this LargeImageEntity largeImage) => new(largeImage.Base64);
public static LargeImageEntity ToEntity(this LargeImage largeImage) => new() { Base64 = largeImage.Base64 };
}
}

@ -0,0 +1,21 @@
using DbManager.Mapper.enums;
using Model;
using MyFlib;
namespace DbManager.Mapper
{
public static class RuneMapper
{
public static Rune ToModel(this RuneEntity rune) => new(rune.Name, rune.Family.ToModel(), rune.Icon, "", rune.Description);
public static RuneEntity ToEntity(this Rune rune)
=> new()
{
Name = rune.Name,
Description = rune.Description,
Family = rune.Family.ToEntity(),
Icon = rune.Icon,
Image = rune.Image.ToEntity()
};
}
}

@ -0,0 +1,49 @@
using DbManager.Mapper.enums;
using Microsoft.EntityFrameworkCore;
using Model;
using MyFlib;
using MyFlib.Entities;
namespace DbManager.Mapper
{
public static class RunePageMapper
{
public static RunePage ToModel(this RunePageEntity runePageEntity, LolDbContext context)
{
RunePage runePage = new(runePageEntity.Name);
foreach (var d in runePageEntity.DictionaryCategoryRunes)
{
var rune = context.Runes.Find(d.RuneName);
if (rune!=null)
{
runePage[d.category.ToModel()] = rune.ToModel();
}
}
return runePage;
}
public static RunePageEntity ToEntity(this RunePage runePage, LolDbContext context)
{
RunePageEntity? runePageEntity = context.RunePages.Find(runePage.Name);
if (runePageEntity == null)
{
runePageEntity = new()
{
Name = runePage.Name,
};
runePageEntity.DictionaryCategoryRunes = new List<DictionaryCategoryRune>();
foreach (var r in runePage.Runes)
{
runePageEntity.DictionaryCategoryRunes.Add(new DictionaryCategoryRune()
{
category = r.Key.ToEntity(),
rune = r.Value.ToEntity(),
});
}
}
return runePageEntity;
}
}
}

@ -0,0 +1,28 @@
using DbManager.Mapper.enums;
using Model;
using MyFlib;
namespace DbManager.Mapper
{
public static class SkillMapper
{
public static Skill ToModel(this SkillEntity skillEntity) => new(skillEntity.Name, skillEntity.Type.ToModel(), skillEntity.Description);
public static SkillEntity ToEntity(this Skill skill, ChampionEntity championEntity, LolDbContext context)
{
var skillSearch = context.Skills.Find(skill.Name);
if (skillSearch == null)
{
return new()
{
Name = skill.Name,
Description = skill.Description,
Type = skill.Type.ToEntity(),
Champion = championEntity
};
}
throw new Exception("Skill was already exist");
}
}
}

@ -0,0 +1,29 @@
using Model;
using MyFlib;
namespace DbManager.Mapper
{
public static class SkinMapper
{
public static Skin ToModel(this SkinEntity skinEntity)
=> new(skinEntity.Name, skinEntity.Champion.ToModel(), skinEntity.Price, skinEntity.Icon, skinEntity.Image.Base64, skinEntity.Description);
public static SkinEntity ToEntity(this Skin skin, LolDbContext context)
{
var skinSearch = context.Skins.Find(skin.Name);
if(skinSearch == null)
{
return new()
{
Name = skin.Name,
Description = skin.Description,
Icon = skin.Icon,
Price = skin.Price,
Champion = context.Champions.FirstOrDefault(c => c.Name == skin.Champion.Name),
Image = skin.Image.ToEntity()
};
}
throw new Exception("Skin was already exist");
}
}
}

@ -0,0 +1,50 @@
using Model;
using MyFlib.Entities.enums;
namespace DbManager.Mapper.enums
{
public static class CategoryMapper
{
public static RunePage.Category ToModel(this CategoryEntity category)
{
switch (category)
{
case CategoryEntity.Major:
return RunePage.Category.Major;
case CategoryEntity.Minor1:
return RunePage.Category.Minor1;
case CategoryEntity.Minor2:
return RunePage.Category.Minor2;
case CategoryEntity.Minor3:
return RunePage.Category.Minor3;
case CategoryEntity.OtherMinor1:
return RunePage.Category.OtherMinor1;
case CategoryEntity.OtherMinor2:
return RunePage.Category.OtherMinor2;
default:
return RunePage.Category.Major;
}
}
public static CategoryEntity ToEntity(this RunePage.Category category)
{
switch (category)
{
case RunePage.Category.Major:
return CategoryEntity.Major;
case RunePage.Category.Minor1:
return CategoryEntity.Minor1;
case RunePage.Category.Minor2:
return CategoryEntity.Minor2;
case RunePage.Category.Minor3:
return CategoryEntity.Minor3;
case RunePage.Category.OtherMinor1:
return CategoryEntity.OtherMinor1;
case RunePage.Category.OtherMinor2:
return CategoryEntity.OtherMinor2;
default:
return CategoryEntity.Major;
}
}
}
}

@ -0,0 +1,55 @@
using Model;
using MyFlib;
namespace DbManager.Mapper.enums
{
public static class ChampionClassMapper
{
public static ChampionClass ToModel(this ChampionClassEntity championClass)
{
switch (championClass)
{
case ChampionClassEntity.Unknown:
return ChampionClass.Unknown;
case ChampionClassEntity.Assassin:
return ChampionClass.Assassin;
case ChampionClassEntity.Fighter:
return ChampionClass.Fighter;
case ChampionClassEntity.Mage:
return ChampionClass.Mage;
case ChampionClassEntity.Marksman:
return ChampionClass.Marksman;
case ChampionClassEntity.Support:
return ChampionClass.Support;
case ChampionClassEntity.Tank:
return ChampionClass.Tank;
default:
return ChampionClass.Unknown;
}
}
public static ChampionClassEntity ToEntity(this ChampionClass championClass)
{
switch (championClass)
{
case ChampionClass.Unknown:
return ChampionClassEntity.Unknown;
case ChampionClass.Assassin:
return ChampionClassEntity.Assassin;
case ChampionClass.Fighter:
return ChampionClassEntity.Fighter;
case ChampionClass.Mage:
return ChampionClassEntity.Mage;
case ChampionClass.Marksman:
return ChampionClassEntity.Marksman;
case ChampionClass.Support:
return ChampionClassEntity.Support;
case ChampionClass.Tank:
return ChampionClassEntity.Tank;
default:
return ChampionClassEntity.Unknown;
}
}
}
}

@ -0,0 +1,43 @@
using Model;
using MyFlib.Entities.enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DbManager.Mapper.enums
{
public static class RuneFamilyMapper
{
public static RuneFamily ToModel(this RuneFamilyEntity runeFamily)
{
switch (runeFamily)
{
case RuneFamilyEntity.Unknown:
return RuneFamily.Unknown;
case RuneFamilyEntity.Precision:
return RuneFamily.Precision;
case RuneFamilyEntity.Domination:
return RuneFamily.Domination;
default:
return RuneFamily.Unknown;
}
}
public static RuneFamilyEntity ToEntity(this RuneFamily runeFamily)
{
switch (runeFamily)
{
case RuneFamily.Unknown:
return RuneFamilyEntity.Unknown;
case RuneFamily.Precision:
return RuneFamilyEntity.Precision;
case RuneFamily.Domination:
return RuneFamilyEntity.Domination;
default:
return RuneFamilyEntity.Unknown;
}
}
}
}

@ -0,0 +1,42 @@
using Model;
using MyFlib;
namespace DbManager.Mapper.enums
{
public static class SkillTypeMapper
{
public static SkillType ToModel(this SkillTypeEntity skillTypeEntity)
{
switch (skillTypeEntity)
{
case SkillTypeEntity.Unknown:
return SkillType.Unknown;
case SkillTypeEntity.Basic:
return SkillType.Basic;
case SkillTypeEntity.Passive:
return SkillType.Passive;
case SkillTypeEntity.Ultimate:
return SkillType.Ultimate;
default:
return SkillType.Unknown;
}
}
public static SkillTypeEntity ToEntity(this SkillType skillType)
{
switch (skillType)
{
case SkillType.Unknown:
return SkillTypeEntity.Unknown;
case SkillType.Basic:
return SkillTypeEntity.Basic;
case SkillType.Passive:
return SkillTypeEntity.Passive;
case SkillType.Ultimate:
return SkillTypeEntity.Ultimate;
default:
return SkillTypeEntity.Unknown;
}
}
}
}

@ -19,13 +19,21 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiLol", "ApiLol\ApiLol.csp
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DTO", "DTO\DTO.csproj", "{3919E408-EB12-4422-989B-C6ED4816D465}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DTO", "DTO\DTO.csproj", "{3919E408-EB12-4422-989B-C6ED4816D465}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApiTests", "Tests\ApiTests\ApiTests.csproj", "{1779D8A4-2E12-47F3-BDA2-2E7F04B758EB}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiTests", "Tests\ApiTests\ApiTests.csproj", "{1779D8A4-2E12-47F3-BDA2-2E7F04B758EB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiManager", "Client\ApiManager.csproj", "{464DAB04-BE65-429D-9A39-3E1BB43C521A}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{464DAB04-BE65-429D-9A39-3E1BB43C521A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyFlib", "MyFlib\MyFlib.csproj", "{2142AB69-B483-4B0A-96DC-CFA87DEB11A5}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyFlib", "MyFlib\MyFlib.csproj", "{2142AB69-B483-4B0A-96DC-CFA87DEB11A5}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UT_EF", "Tests\UT_EF\UT_EF.csproj", "{F896C30B-A6FE-42B2-BAC8-08360BC48DC6}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UT_EF", "Tests\UT_EF\UT_EF.csproj", "{F896C30B-A6FE-42B2-BAC8-08360BC48DC6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DbManager", "DbManager\DbManager.csproj", "{71DDCC06-D993-47B0-92E3-5DD6E295FB8B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LolApp", "LolApp\LolApp.csproj", "{0C898A04-092A-49AA-BE65-8AE818A2AF50}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ViewModels", "ViewModels\ViewModels.csproj", "{65135247-E1AB-4EE4-9473-DFDE6AFCC250}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApiMapping", "ApiMapping\ApiMapping.csproj", "{F8D44A32-788B-4E63-B379-278B90216E28}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -73,6 +81,24 @@ Global
{F896C30B-A6FE-42B2-BAC8-08360BC48DC6}.Debug|Any CPU.Build.0 = Debug|Any CPU {F896C30B-A6FE-42B2-BAC8-08360BC48DC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F896C30B-A6FE-42B2-BAC8-08360BC48DC6}.Release|Any CPU.ActiveCfg = Release|Any CPU {F896C30B-A6FE-42B2-BAC8-08360BC48DC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F896C30B-A6FE-42B2-BAC8-08360BC48DC6}.Release|Any CPU.Build.0 = Release|Any CPU {F896C30B-A6FE-42B2-BAC8-08360BC48DC6}.Release|Any CPU.Build.0 = Release|Any CPU
{71DDCC06-D993-47B0-92E3-5DD6E295FB8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71DDCC06-D993-47B0-92E3-5DD6E295FB8B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71DDCC06-D993-47B0-92E3-5DD6E295FB8B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71DDCC06-D993-47B0-92E3-5DD6E295FB8B}.Release|Any CPU.Build.0 = Release|Any CPU
{0C898A04-092A-49AA-BE65-8AE818A2AF50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0C898A04-092A-49AA-BE65-8AE818A2AF50}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0C898A04-092A-49AA-BE65-8AE818A2AF50}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{0C898A04-092A-49AA-BE65-8AE818A2AF50}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0C898A04-092A-49AA-BE65-8AE818A2AF50}.Release|Any CPU.Build.0 = Release|Any CPU
{0C898A04-092A-49AA-BE65-8AE818A2AF50}.Release|Any CPU.Deploy.0 = Release|Any CPU
{65135247-E1AB-4EE4-9473-DFDE6AFCC250}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{65135247-E1AB-4EE4-9473-DFDE6AFCC250}.Debug|Any CPU.Build.0 = Debug|Any CPU
{65135247-E1AB-4EE4-9473-DFDE6AFCC250}.Release|Any CPU.ActiveCfg = Release|Any CPU
{65135247-E1AB-4EE4-9473-DFDE6AFCC250}.Release|Any CPU.Build.0 = Release|Any CPU
{F8D44A32-788B-4E63-B379-278B90216E28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F8D44A32-788B-4E63-B379-278B90216E28}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F8D44A32-788B-4E63-B379-278B90216E28}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F8D44A32-788B-4E63-B379-278B90216E28}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

@ -0,0 +1,95 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32616.157
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "Model\Model.csproj", "{2960F9BA-49DE-494D-92E3-CE5A794BA1A9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{C76D0C23-1FFA-4963-93CD-E12BD643F030}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTests", "Tests\ConsoleTests\ConsoleTests.csproj", "{1889FA6E-B7C6-416E-8628-9449FB9070B9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "Shared\Shared.csproj", "{3B720C0C-53FE-4642-A2DB-87FD8634CD74}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Stub", "Stub", "{2C607793-B163-4731-A4D1-AFE8A7C4C170}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StubLib", "StubLib\StubLib.csproj", "{B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiLol", "ApiLol\ApiLol.csproj", "{D59C9C7B-9BC2-4601-959D-BFA97E46D017}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DTO", "DTO\DTO.csproj", "{3919E408-EB12-4422-989B-C6ED4816D465}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiTests", "Tests\ApiTests\ApiTests.csproj", "{1779D8A4-2E12-47F3-BDA2-2E7F04B758EB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyFlib", "MyFlib\MyFlib.csproj", "{2142AB69-B483-4B0A-96DC-CFA87DEB11A5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UT_EF", "Tests\UT_EF\UT_EF.csproj", "{F896C30B-A6FE-42B2-BAC8-08360BC48DC6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DbManager", "DbManager\DbManager.csproj", "{71DDCC06-D993-47B0-92E3-5DD6E295FB8B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiManager", "Client\ApiManager.csproj", "{1903905B-13B3-4DA2-94E2-917F39588032}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2960F9BA-49DE-494D-92E3-CE5A794BA1A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2960F9BA-49DE-494D-92E3-CE5A794BA1A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2960F9BA-49DE-494D-92E3-CE5A794BA1A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2960F9BA-49DE-494D-92E3-CE5A794BA1A9}.Release|Any CPU.Build.0 = Release|Any CPU
{1889FA6E-B7C6-416E-8628-9449FB9070B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1889FA6E-B7C6-416E-8628-9449FB9070B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1889FA6E-B7C6-416E-8628-9449FB9070B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1889FA6E-B7C6-416E-8628-9449FB9070B9}.Release|Any CPU.Build.0 = Release|Any CPU
{3B720C0C-53FE-4642-A2DB-87FD8634CD74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B720C0C-53FE-4642-A2DB-87FD8634CD74}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B720C0C-53FE-4642-A2DB-87FD8634CD74}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B720C0C-53FE-4642-A2DB-87FD8634CD74}.Release|Any CPU.Build.0 = Release|Any CPU
{B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}.Release|Any CPU.Build.0 = Release|Any CPU
{D59C9C7B-9BC2-4601-959D-BFA97E46D017}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D59C9C7B-9BC2-4601-959D-BFA97E46D017}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D59C9C7B-9BC2-4601-959D-BFA97E46D017}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D59C9C7B-9BC2-4601-959D-BFA97E46D017}.Release|Any CPU.Build.0 = Release|Any CPU
{3919E408-EB12-4422-989B-C6ED4816D465}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3919E408-EB12-4422-989B-C6ED4816D465}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3919E408-EB12-4422-989B-C6ED4816D465}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3919E408-EB12-4422-989B-C6ED4816D465}.Release|Any CPU.Build.0 = Release|Any CPU
{1779D8A4-2E12-47F3-BDA2-2E7F04B758EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1779D8A4-2E12-47F3-BDA2-2E7F04B758EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1779D8A4-2E12-47F3-BDA2-2E7F04B758EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1779D8A4-2E12-47F3-BDA2-2E7F04B758EB}.Release|Any CPU.Build.0 = Release|Any CPU
{2142AB69-B483-4B0A-96DC-CFA87DEB11A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2142AB69-B483-4B0A-96DC-CFA87DEB11A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2142AB69-B483-4B0A-96DC-CFA87DEB11A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2142AB69-B483-4B0A-96DC-CFA87DEB11A5}.Release|Any CPU.Build.0 = Release|Any CPU
{F896C30B-A6FE-42B2-BAC8-08360BC48DC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F896C30B-A6FE-42B2-BAC8-08360BC48DC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F896C30B-A6FE-42B2-BAC8-08360BC48DC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F896C30B-A6FE-42B2-BAC8-08360BC48DC6}.Release|Any CPU.Build.0 = Release|Any CPU
{71DDCC06-D993-47B0-92E3-5DD6E295FB8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71DDCC06-D993-47B0-92E3-5DD6E295FB8B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71DDCC06-D993-47B0-92E3-5DD6E295FB8B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71DDCC06-D993-47B0-92E3-5DD6E295FB8B}.Release|Any CPU.Build.0 = Release|Any CPU
{1903905B-13B3-4DA2-94E2-917F39588032}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1903905B-13B3-4DA2-94E2-917F39588032}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1903905B-13B3-4DA2-94E2-917F39588032}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1903905B-13B3-4DA2-94E2-917F39588032}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{1889FA6E-B7C6-416E-8628-9449FB9070B9} = {C76D0C23-1FFA-4963-93CD-E12BD643F030}
{B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB} = {2C607793-B163-4731-A4D1-AFE8A7C4C170}
{1779D8A4-2E12-47F3-BDA2-2E7F04B758EB} = {C76D0C23-1FFA-4963-93CD-E12BD643F030}
{F896C30B-A6FE-42B2-BAC8-08360BC48DC6} = {C76D0C23-1FFA-4963-93CD-E12BD643F030}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {92F3083D-793F-4552-8A9A-0AD6534159C9}
EndGlobalSection
EndGlobal

@ -0,0 +1,142 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:LolApp.ViewModels"
xmlns:myviews="clr-namespace:LolApp.ContentViews"
xmlns:appvm="clr-namespace:LolApp.ViewModels"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Class="LolApp.AddChampionPage"
Title="AddChampionPage"
x:Name="root">
<Grid RowDefinitions="Auto,*, Auto" BackgroundColor="{StaticResource Black}">
<VerticalStackLayout>
<Label Text="Nouveau Champion" IsVisible="{Binding IsNew}"
Style="{StaticResource title}"/>
<Label Text="Modifier le Champion" IsVisible="{Binding IsNew, Converter={StaticResource invertedBoolConverter}}"
Style="{StaticResource title}"/>
<Grid><Line Stroke="{StaticResource Primary}"
X1="0" Y1="0" X2="200" Y2="0"
HorizontalOptions="Center"/>
</Grid>
</VerticalStackLayout>
<ScrollView Grid.Row="1">
<Grid ColumnDefinitions="*, 3*" RowDefinitions="Auto, Auto, Auto, 162, 162, Auto, Auto, Auto, Auto">
<Label Text="Nom :"
Style="{StaticResource labelForEntry}"/>
<Entry Grid.Column="1" Placeholder="Nom du champion" Text="{Binding Champion.Name}"
Style="{StaticResource defaultEntry}"
IsEnabled="{Binding IsNew}"/>
<Label Text="Icone :" Grid.Row="1" Style="{StaticResource labelForEntry}"/>
<ImageButton Grid.Row="1" Grid.Column="1" HeightRequest="42" WidthRequest="42"
Source="{Binding Champion.IconBase64, TargetNullValue='lol.png',
Converter={StaticResource base64ToImageSourceConverter}}"
BackgroundColor="{StaticResource Secondary}"
HorizontalOptions="Start"
Margin="6"
Command="{Binding PickIconCommand}"/>
<Label Text="Image :" Grid.Row="2" Style="{StaticResource labelForEntry}"/>
<Grid Grid.Row="2" Grid.Column="1" x:Name="largeImageGrid" Margin="0, 0, 12, 0">
<ImageButton WidthRequest="{Binding Width, Source={x:Reference largeImageGrid}}"
HeightRequest="150"
Source="{Binding Champion.LargeImageBase64, TargetNullValue='lollogo.jpg',
Converter={StaticResource base64ToImageSourceConverter}}"
BackgroundColor="{StaticResource Secondary}"
HorizontalOptions="Start"
Margin="6"
Command="{Binding PickLargeImageCommand}"/>
</Grid>
<Label Text="Bio :" Grid.Row="3"
Style="{StaticResource labelForEntry}"/>
<Editor Grid.Column="1" Grid.Row="3"
Text="{Binding Champion.Bio}" Style="{StaticResource defaultEditor}"/>
<Label Text="Classe :" Grid.Row="4"
Style="{StaticResource labelForEntry}"/>
<myviews:ChampionClassSelector Grid.Row="4" Grid.Column="1" MaximumWidthRequest="{OnPlatform WinUI=400}"
CheckedColor="{StaticResource Primary}"
UncheckedColor="{StaticResource Secondary}"
SelectedValue="{Binding Champion.ChampionClass, Mode=TwoWay}"/>
<Label Text="Caractéristiques :" Grid.Row="5" Grid.RowSpan="2"
Style="{StaticResource labelForEntry}" VerticalOptions="Start"/>
<Border Stroke="{StaticResource Secondary}" Grid.Row="5" Grid.Column="1" VerticalOptions="FillAndExpand"> <ListView ItemsSource="{Binding Champion.Characteristics}"
Margin="6" HeightRequest="100" HorizontalOptions="Fill" VerticalOptions="Fill"
BackgroundColor="{StaticResource Black}" SeparatorColor="{StaticResource Secondary}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem Command="{Binding Source={x:Reference root}, Path=BindingContext.RemoveCharacteristicCommand}"
CommandParameter="{Binding .}"
IsDestructive="True" Text="Delete"/>
</ViewCell.ContextActions>
<Border Margin="0,4" BackgroundColor="{StaticResource Secondary}">
<Border.StrokeShape>
<RoundRectangle CornerRadius="10, 10, 0, 10"/>
</Border.StrokeShape>
<Grid ColumnDefinitions="*, Auto">
<Label Text="{Binding Key}" TextColor="{StaticResource Black}"
HorizontalOptions="Start" VerticalOptions="Center" Margin="4, 0, 0, 0"/>
<Label Text="{Binding Value}" Grid.Column="1" TextColor="{StaticResource Black}"
HorizontalOptions="End" VerticalOptions="Center" Margin="0, 0, 4, 0"/>
</Grid>
</Border>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Border>
<Grid Grid.Column="1" Grid.Row="6" ColumnDefinitions="*, 58, Auto">
<Entry Style="{StaticResource defaultEntry}" Placeholder="Caractéristique" Text="{Binding NewCharacteristicDescription}"/>
<Entry Style="{StaticResource defaultEntry}" Placeholder="Valeur" Grid.Column="1" Text="{Binding NewCharacteristicValue}" Keyboard="Numeric">
<Entry.Behaviors>
<toolkit:NumericValidationBehavior Flags="ValidateOnValueChanged"
MinimumValue="0"
MaximumValue="9999999"
MaximumDecimalPlaces="0"
InvalidStyle="{StaticResource InvalidEntryStyle}"
ValidStyle="{StaticResource defaultEntry}"/>
</Entry.Behaviors>
</Entry>
<Button Grid.Column="2" Margin="4,8" CornerRadius="22"
Text="{StaticResource plus}"
Command="{Binding AddCharacteristicCommand}"/>
</Grid>
<Label Style="{StaticResource labelForEntry}" Text="Compétences :" Grid.Row="7" VerticalOptions="Start"/>
<Grid Grid.Row="7" Grid.Column="1" ColumnDefinitions="*, Auto">
<ListView ItemsSource="{Binding Champion.Skills}" Margin="6"
HeightRequest="100" HorizontalOptions="Fill" VerticalOptions="Fill" HasUnevenRows="True"
BackgroundColor="{StaticResource Black}" SeparatorColor="{StaticResource Secondary}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid RowDefinitions="Auto, Auto, *">
<Grid.Resources>
<Style TargetType="Label">
<Setter Property="TextColor" Value="{StaticResource Primary}"/>
</Style>
</Grid.Resources>
<Label Text="{Binding Name}" FontSize="Medium" FontAttributes="Bold" VerticalOptions="Center"/>
<Label Text="{Binding Type}" Grid.Row="1" VerticalOptions="Center" FontAttributes="Italic" FontSize="Micro"/>
<Label Text="{Binding Description}" FontSize="Micro" FontAttributes="Italic"
Grid.Row="2"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Grid.Column="1" Margin="4,8" CornerRadius="22"
Text="{StaticResource plus}" VerticalOptions="Start"
Command="{Binding AddSkillCommand}"/>
</Grid>
</Grid>
</ScrollView>
<HorizontalStackLayout Grid.Row="2" HorizontalOptions="Center" Spacing="40" Margin="0, 10, 0, 20">
<Button Text="Ajouter" Command="{Binding AddChampionCommand}" IsVisible="{Binding IsNew}"/>
<Button Text="Modifier" Command="{Binding EditChampionCommand}" IsVisible="{Binding IsNew, Converter={StaticResource invertedBoolConverter}}"/>
<Button Text="Annuler" Command="{Binding CancelCommand}"/>
</HorizontalStackLayout>
</Grid>
</ContentPage>

@ -0,0 +1,13 @@
using LolApp.ViewModels;
using ViewModels;
namespace LolApp;
public partial class AddChampionPage : ContentPage
{
public AddChampionPage(ChampionsMgrVM championsMgrVM, ChampionVM champion = null)
{
InitializeComponent();
BindingContext = new AddChampionPageVM(championsMgrVM, champion);
}
}

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="LolApp.AddOrEditSkinPage"
Title="AddOrEditSkinPage"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit">
<Grid RowDefinitions="Auto,*, Auto" BackgroundColor="{StaticResource Black}">
<VerticalStackLayout>
<Label Text="Nouveau Skin" IsVisible="{Binding IsNew}"
Style="{StaticResource title}"/>
<Label Text="Modifier le Skin" IsVisible="{Binding IsNew, Converter={StaticResource invertedBoolConverter}}"
Style="{StaticResource title}"/>
<Grid><Line Stroke="{StaticResource Primary}"
X1="0" Y1="0" X2="200" Y2="0"
HorizontalOptions="Center"/>
</Grid>
</VerticalStackLayout>
<ScrollView Grid.Row="1">
<Grid ColumnDefinitions="*, 3*" RowDefinitions="Auto, Auto, Auto, Auto, 162">
<Label Text="Nom :"
Style="{StaticResource labelForEntry}"/>
<Entry Grid.Column="1" Placeholder="Nom du skin" Text="{Binding Skin.Name}"
Style="{StaticResource defaultEntry}"
IsEnabled="{Binding IsNew}"/>
<Label Text="Icone :" Grid.Row="1" Style="{StaticResource labelForEntry}"/>
<ImageButton Grid.Row="1" Grid.Column="1" HeightRequest="42" WidthRequest="42"
Source="{Binding Skin.IconBase64, TargetNullValue='lol.png',
Converter={StaticResource base64ToImageSourceConverter}}"
BackgroundColor="{StaticResource Secondary}"
HorizontalOptions="Start"
Margin="6"
Command="{Binding PickIconCommand}"/>
<Label Text="Image :" Grid.Row="2" Style="{StaticResource labelForEntry}"/>
<Grid Grid.Row="2" Grid.Column="1" x:Name="largeImageGrid" Margin="0, 0, 12, 0">
<ImageButton WidthRequest="{Binding Width, Source={x:Reference largeImageGrid}}"
HeightRequest="150"
Source="{Binding Skin.LargeImageBase64, TargetNullValue='lollogo.jpg',
Converter={StaticResource base64ToImageSourceConverter}}"
BackgroundColor="{StaticResource Secondary}"
HorizontalOptions="Start"
Margin="6"
Command="{Binding PickLargeImageCommand}"/>
</Grid>
<Label Text="Prix :" Grid.Row="3"
Style="{StaticResource labelForEntry}"/>
<HorizontalStackLayout Grid.Column="1" Grid.Row="3" Margin="6">
<Image Source="rp.png" HeightRequest="16" WidthRequest="16"/>
<Entry Grid.Column="1" Placeholder="Nom du skin" Text="{Binding Skin.Price}"
Style="{StaticResource defaultEntry}" Margin="4, 0, 0, 0" HorizontalTextAlignment="Start">
<Entry.Behaviors>
<toolkit:NumericValidationBehavior Flags="ValidateOnValueChanged"
MinimumValue="0"
MaximumValue="9999999"
MaximumDecimalPlaces="0"
InvalidStyle="{StaticResource InvalidEntryStyle}"
ValidStyle="{StaticResource defaultEntry}"/>
</Entry.Behaviors>
</Entry>
</HorizontalStackLayout>
<Label Text="Description :" Grid.Row="4"
Style="{StaticResource labelForEntry}"/>
<Editor Grid.Column="1" Grid.Row="4"
Text="{Binding Skin.Description}" Style="{StaticResource defaultEditor}"/>
</Grid>
</ScrollView>
<HorizontalStackLayout Grid.Row="2" HorizontalOptions="Center" Spacing="40" Margin="0, 10, 0, 20">
<Button Text="Ajouter" Command="{Binding AddSkinCommand}" IsVisible="{Binding IsNew}"/>
<Button Text="Modifier" Command="{Binding EditSkinCommand}" IsVisible="{Binding IsNew, Converter={StaticResource invertedBoolConverter}}"/>
<Button Text="Annuler" Command="{Binding CancelCommand}"/>
</HorizontalStackLayout>
</Grid>
</ContentPage>

@ -0,0 +1,24 @@
using LolApp.ViewModels;
using ViewModels;
namespace LolApp;
public partial class AddOrEditSkinPage : ContentPage
{
AddOrEditSkinPage()
{
InitializeComponent();
}
public AddOrEditSkinPage(SkinsMgrVM skinsMgrVM, SkinVM skin)
:this()
{
BindingContext = new AddOrEditSkinPageVM(skinsMgrVM, skin);
}
public AddOrEditSkinPage(SkinsMgrVM skinsMgrVM, ChampionVM champion)
:this()
{
BindingContext = new AddOrEditSkinPageVM(skinsMgrVM, champion);
}
}

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="LolApp.AddSkill"
xmlns:appvm="clr-namespace:LolApp.ViewModels"
Title="AddSkill">
<Grid RowDefinitions="Auto,*, Auto" BackgroundColor="{StaticResource Gray900}">
<VerticalStackLayout>
<Label Text="Nouvelle Compétence"
Style="{StaticResource title}"/>
<Grid>
<Line Stroke="{StaticResource Primary}"
X1="0" Y1="0" X2="200" Y2="0"
HorizontalOptions="Center"/>
</Grid>
</VerticalStackLayout>
<ScrollView Grid.Row="1">
<Grid ColumnDefinitions="*, 3*" RowDefinitions="Auto, Auto, *">
<Label Text="Nom :" Style="{StaticResource labelForEntry}"/>
<Entry Text="{Binding Name}" Style="{StaticResource defaultEntry}"
Grid.Column="1"/>
<Label Text="Type :" Style="{StaticResource labelForEntry}" Grid.Row="1"/>
<Picker ItemsSource="{Binding AllSkills}" SelectedItem="{Binding SkillType}"
Grid.Row="1" Grid.Column="1"
Style="{StaticResource defaultPicker}"/>
<Label Text="Description :" Style="{StaticResource labelForEntry}" Grid.Row="2"/>
<Editor Grid.Row="2" Grid.Column="1" Text="{Binding Description}"
Style="{StaticResource defaultEditor}"/>
</Grid>
</ScrollView>
<HorizontalStackLayout Grid.Row="2" HorizontalOptions="Center" Spacing="40" Margin="0, 10, 0, 20">
<Button Text="Ajouter" Command="{Binding AddSkillToChampionCommand}"/>
<Button Text="Annuler" Command="{Binding CancelCommand}"/>
</HorizontalStackLayout>
</Grid>
</ContentPage>

@ -0,0 +1,13 @@
using LolApp.ViewModels;
using ViewModels;
namespace LolApp;
public partial class AddSkill : ContentPage
{
public AddSkill(EditableChampionVM champion)
{
InitializeComponent();
BindingContext = new AddSkillVM(champion);
}
}

@ -0,0 +1,18 @@
<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:LolApp"
x:Class="LolApp.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/FontAwesomeGlyphs.xaml" x:Name="Colors" />
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
<ResourceDictionary Source="Resources/Styles/MyStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

@ -0,0 +1,12 @@
namespace LolApp;
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new AppShell();
}
}

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="LolApp.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:LolApp"
Shell.FlyoutBehavior="Disabled">
<TabBar>
<ShellContent
Title="Home"
ContentTemplate="{DataTemplate local:MainPage}"
Route="MainPage"
Icon="{OnPlatform 'lol.png'}" />
<ShellContent
Title="Champions"
ContentTemplate="{DataTemplate local:ChampionsPage}"
Route="Championspage"
Icon="{OnPlatform 'sword.png'}" />
</TabBar>
</Shell>

@ -0,0 +1,10 @@
namespace LolApp;
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
}
}

@ -0,0 +1,185 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Class="LolApp.ChampionPage"
Title="ChampionPage"
x:Name="root"
BackgroundColor="Black">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Modifier" Command="{Binding AppVM.NavigateToEditChampionPageCommand, Source={x:Reference root}}"
CommandParameter="{Binding}"/>
</ContentPage.ToolbarItems>
<ScrollView>
<VerticalStackLayout>
<AbsoluteLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
MaximumHeightRequest="{OnPlatform WinUI=300}"
HeightRequest="{Binding Width,
Source={RelativeSource AncestorType={x:Type ContentPage}},
Converter={StaticResource imageRatioConverter},
ConverterParameter={StaticResource imageRatio}}">
<Image Source="{Binding Image, Converter={StaticResource base64ToImageSourceConverter}}"
Aspect="AspectFit"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
MaximumHeightRequest="{OnPlatform WinUI=300}"/>
</AbsoluteLayout>
<Grid Padding="10" BackgroundColor="{StaticResource Black}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}" FontAttributes="Bold" TextColor="{StaticResource Primary}"
FontSize="Title"/>
<VerticalStackLayout Grid.Column="1" HorizontalOptions="Center">
<Image Source="{Binding Class, Converter={StaticResource championClassToIconConverter}}"
HeightRequest="26" WidthRequest="26" x:Name="imgClass" PropertyChanged="imgClass_PropertyChanged">
<Image.Behaviors>
<toolkit:IconTintColorBehavior TintColor="{StaticResource Primary}"
x:Name="tintColor"/>
</Image.Behaviors>
</Image>
<Label Text="{Binding Class}" TextColor="{StaticResource Primary}"
FontSize="Micro"/>
</VerticalStackLayout>
</Grid>
<ScrollView VerticalScrollBarVisibility="Always" BackgroundColor="Black" >
<Label Text="{Binding Bio}" TextColor="{StaticResource Primary}" Padding="10" FontAttributes="Italic"/>
</ScrollView>
<Label Padding="10" Text="Caractéristiques" FontSize="Title" TextColor="{StaticResource Primary}"
BackgroundColor="Black"/>
<Grid MaximumHeightRequest="240">
<Grid.Resources>
<x:Double x:Key="gridHeight">120</x:Double>
<x:Int32 x:Key="nbCellsPerLine">3</x:Int32>
</Grid.Resources>
<Grid.HeightRequest>
<MultiBinding Converter="{StaticResource multiMathExpressionConverter}"
ConverterParameter="ceiling(x1/x2)*x0">
<Binding Source="{StaticResource gridHeight}"/>
<Binding Path="Characteristics.Count"/>
<Binding Source="{StaticResource nbCellsPerLine}"/>
</MultiBinding>
</Grid.HeightRequest>
<CollectionView ItemsSource="{Binding Characteristics}"
ItemsLayout="VerticalGrid, 3"
VerticalScrollBarVisibility="Always">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10" HeightRequest="120">
<Border Stroke="{StaticResource PrimaryBrush}" StrokeThickness="2" BackgroundColor="{StaticResource Black}">
<Grid RowDefinitions="4*,3*">
<Label Text="{Binding Key}" HorizontalOptions="Center" TextColor="{StaticResource Primary}"
FontSize="Small" FontAttributes="Bold" VerticalOptions="End" HorizontalTextAlignment="Center"
Margin="0, 0, 0, 5"/>
<Label Grid.Row="1" Text="{Binding Value}" HorizontalOptions="Center" TextColor="{StaticResource Primary}"
VerticalOptions="Start" HorizontalTextAlignment="Center"
FontSize="Small"/>
</Grid>
</Border>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
<Label Padding="10" Text="Compétences" FontSize="Title" TextColor="{StaticResource Primary}"
BackgroundColor="Black"/>
<ListView ItemsSource="{Binding Skills}" Margin="10"
BackgroundColor="Black" HasUnevenRows="True" VerticalScrollBarVisibility="Always"
MaximumHeightRequest="400">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.Resources>
<Style TargetType="Label">
<Setter Property="TextColor" Value="{StaticResource Primary}"/>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Text="{Binding Name}" FontSize="Medium" FontAttributes="Bold" VerticalOptions="Center"/>
<Label Text="{Binding Type}" Grid.Column="1" VerticalOptions="Center" FontAttributes="Italic" FontSize="Micro"/>
<Label Text="{Binding Description}" FontSize="Micro" FontAttributes="Italic"
Grid.ColumnSpan="2" Grid.Row="1"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid ColumnDefinitions="*, Auto">
<Label Padding="10" Text="Skins" FontSize="Title" TextColor="{StaticResource Primary}"
BackgroundColor="Black"/>
<Button Grid.Column="1" Text="{StaticResource plus}" CornerRadius="22" BackgroundColor="{StaticResource Primary}"
TextColor="{StaticResource Black}" FontSize="Header"
Command="{Binding AppVM.NavigateToAddNewSkinPageCommand, Source={x:Reference root}}"
CommandParameter="{Binding}"
VerticalOptions="Center" HorizontalOptions="Center"
Margin="6"/>
</Grid>
<ListView BindingContext="{Binding AppVM, Source={x:Reference root}}"
ItemsSource="{Binding SkinsMgrVM.Skins}" HasUnevenRows="True"
BackgroundColor="{StaticResource Black}"
x:Name="listSkins">
<ListView.Behaviors>
<toolkit:EventToCommandBehavior
EventName="ItemSelected"
Command="{Binding NavigateToSkinDetailsPageCommand}"
EventArgsConverter="{StaticResource SelectedItemEventArgsConverter}"
/>
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem IsDestructive="True"
Text="Supprimer"
Command="{Binding BindingContext.SkinsMgrVM.DeleteSkinCommand, Source={x:Reference listSkins}}"
CommandParameter="{Binding .}"/>
<MenuItem Text="Modifier"
Command="{Binding Source={x:Reference listSkins}, Path=BindingContext.NavigateToEditSkinPageCommand}"
CommandParameter="{Binding .}"/>
</ViewCell.ContextActions>
<Border Stroke="{StaticResource Primary}" Padding="8,4" HeightRequest="60" Margin="4"
StrokeThickness="3" BackgroundColor="{StaticResource Black}">
<Border.StrokeShape>
<RoundRectangle CornerRadius="0, 10, 10, 10"/>
</Border.StrokeShape>
<HorizontalStackLayout VerticalOptions="Center">
<Image Source="{Binding Icon, Converter={StaticResource base64ToImageSourceConverter}}"
HeightRequest="46" WidthRequest="46"/>
<Label Text="{Binding Name}" TextColor="{StaticResource Primary}" FontSize="Small"
VerticalOptions="Center" Margin="10,4"/>
</HorizontalStackLayout>
</Border>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</VerticalStackLayout>
</ScrollView>
</ContentPage>

@ -0,0 +1,31 @@
using CommunityToolkit.Maui.Behaviors;
using LolApp.ViewModels;
using ViewModels;
namespace LolApp;
public partial class ChampionPage : ContentPage
{
public ApplicationVM AppVM { get; set; }
public ChampionVM Champion { get; }
public ChampionPage(ChampionVM cvm, ApplicationVM appVM)
{
AppVM = appVM;
BindingContext = Champion = cvm;
InitializeComponent();
}
void imgClass_PropertyChanged(System.Object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
Image img = sender as Image;
if(e.PropertyName == "Source" && img != null && img.Behaviors.Any(b => b is IconTintColorBehavior))
{
var beh = (img.Behaviors.First(b => b is IconTintColorBehavior) as IconTintColorBehavior);
var color = beh.TintColor;
img.Behaviors.Remove(beh);
img.Behaviors.Add(new IconTintColorBehavior() { TintColor = color});
}
}
}

@ -0,0 +1,196 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:myviews="clr-namespace:LolApp.ContentViews"
xmlns:vm="clr-namespace:ViewModels;assembly=ViewModels"
xmlns:appvm="clr-namespace:LolApp.ViewModels"
x:Class="LolApp.ChampionsPage"
Title="Champions"
x:Name="root">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Ajouter" Command="{Binding AppVM.NavigateToAddNewChampionPageCommand}" />
</ContentPage.ToolbarItems>
<ContentPage.Resources>
<ControlTemplate x:Key="searchByStringControl">
<Grid Margin="20,4" HeightRequest="{OnPlatform 30, Android=40}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Entry Placeholder="{TemplateBinding PlaceHolder}"
Text="{TemplateBinding Text, Mode=TwoWay}"/>
<Button Text="{StaticResource magnifying-glass}"
FontFamily="FASolid"
Grid.Column="1" Margin="4, 0, 0, 0"
Command="{TemplateBinding Command}"
CommandParameter="{TemplateBinding CommandParameter}"/>
</Grid>
</ControlTemplate>
</ContentPage.Resources>
<ContentPage.Behaviors>
<toolkit:EventToCommandBehavior
EventName = "Loaded"
Command="{Binding AppVM.ChampionsMgrVM.LoadChampionsCommand}"/>
</ContentPage.Behaviors>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<toolkit:Expander Grid.Row="1">
<toolkit:Expander.Header>
<HorizontalStackLayout>
<Label Text="Chercher par "
HorizontalOptions="Center"/>
<Label Text="nom" IsVisible="{Binding VM.SearchedName, Converter={StaticResource isStringNotNullOrWhiteSpaceConverter}}"/>
<Label Text="compétence" IsVisible="{Binding VM.SearchedSkill, Converter={StaticResource isStringNotNullOrWhiteSpaceConverter}}"/>
<Label Text=" "/>
<Label Text="{Binding IsExpanded,
Source={RelativeSource AncestorType={x:Type toolkit:Expander}},
Converter={StaticResource isExpandedToCaretConverter}}"
FontFamily="FASolid"
VerticalOptions="Center"/>
</HorizontalStackLayout>
</toolkit:Expander.Header>
<VerticalStackLayout HorizontalOptions="Fill" BackgroundColor="WhiteSmoke">
<myviews:SearchByStringView ControlTemplate="{StaticResource searchByStringControl}"
PlaceHolder="Entrez un nom"
Text="{Binding VM.SearchedName, Mode=TwoWay}"
Command="{Binding AppVM.ChampionsMgrVM.LoadChampionsByNameCommand}"
CommandParameter="{Binding VM.SearchedName}"/>
<myviews:SearchByStringView ControlTemplate="{StaticResource searchByStringControl}"
PlaceHolder="Entrez une compétence"
Text="{Binding VM.SearchedSkill, Mode=TwoWay}"
Command="{Binding AppVM.ChampionsMgrVM.LoadChampionsBySkillCommand}"
CommandParameter="{Binding VM.SearchedSkill}"/>
<myviews:SearchByStringView ControlTemplate="{StaticResource searchByStringControl}"
PlaceHolder="Entrez une caractéristique"
Text="{Binding VM.SearchedCharacteristic, Mode=TwoWay}"
Command="{Binding AppVM.ChampionsMgrVM.LoadChampionsByCharacteristicCommand}"
CommandParameter="{Binding VM.SearchedCharacteristic}"/>
<Label Text="Filtrer par classe :" Margin="20, 4, 0, 0"
FontSize="Micro"/>
<CollectionView ItemsSource="{x:Static appvm:ChampionClassVM.Classes}" ItemsLayout="VerticalGrid, 3"
SelectionMode="Single" HeightRequest="110" x:Name="classesView"
SelectionChangedCommand="{Binding AppVM.ChampionsMgrVM.LoadChampionsByClassCommand}"
SelectionChangedCommandParameter="{Binding VM.SelectedItem, Source={RelativeSource Self}}"
SelectedItem="{Binding VM.SelectedClass, Source={x:Reference root}, Mode=TwoWay}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid HorizontalOptions="Center" RowDefinitions="*, *" WidthRequest="100" Padding="10, 10, 10, 0"
BackgroundColor="{Binding IsSelected, Converter={StaticResource isSelectedToColorConverter}}">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={x:Reference root}, Path=BindingContext.VM.SelectedChampionClassChangedCommand}"
CommandParameter="{Binding}" />
</Grid.GestureRecognizers>
<Image Source="{Binding Model, Converter={StaticResource championClassToIconConverter}}" HeightRequest="26" WidthRequest="26"
/>
<Label Text="{Binding Model}" TextColor="{StaticResource Black}"
HorizontalOptions="Center" Grid.Row="1"
FontSize="Micro">
</Label>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</toolkit:Expander>
<ListView Grid.Row="2" CachingStrategy="RecycleElementAndDataTemplate"
ItemsSource="{Binding AppVM.ChampionsMgrVM.Champions}"
RowHeight="50"
SelectedItem="{Binding AppVM.ChampionsMgrVM.SelectedChampion, Mode=TwoWay}">
<ListView.Behaviors>
<toolkit:EventToCommandBehavior
EventName="ItemSelected"
Command="{Binding AppVM.NavigateToChampionDetailsPageCommand}"
EventArgsConverter="{StaticResource SelectedItemEventArgsConverter}"
/>
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem Command="{Binding Source={x:Reference root}, Path=BindingContext.AppVM.ChampionsMgrVM.DeleteChampionCommand}"
CommandParameter="{Binding .}"
IsDestructive="True" Text="Supprimer"/>
<MenuItem Command="{Binding Source={x:Reference root}, Path=BindingContext.AppVM.NavigateToEditChampionPageCommand}"
CommandParameter="{Binding .}"
IsDestructive="False" Text="Modifier"/>
</ViewCell.ContextActions>
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Image Source="{Binding Icon, Converter={StaticResource base64ToImageSourceConverter}}"
HeightRequest="40"
WidthRequest="40"
Grid.RowSpan="2"
VerticalOptions="Center"
Margin="0, 0, 10, 0"/>
<Label Text="{Binding Name}" Grid.Column="1"
FontAttributes="Bold"
FontSize="{OnPlatform Header, WinUI=Small}"
VerticalOptions="Center"/>
<Label Text="{Binding Class}" Grid.Row="1" Grid.Column="1"
FontAttributes="Italic"
FontSize="Caption"
VerticalOptions="Center"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid Grid.Row="3" HorizontalOptions="Center" HeightRequest="45"
IsVisible="{Binding AppVM.ChampionsMgrVM.NbChampions, Converter={StaticResource intToBoolConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Style="{StaticResource iconButton}"
Command="{Binding AppVM.ChampionsMgrVM.PreviousPageCommand}">
<Button.ImageSource>
<FontImageSource Glyph="{StaticResource angle-left}"
FontFamily="FASolid"
Size="Title"/>
</Button.ImageSource>
</Button>
<StackLayout Orientation="Horizontal" Grid.Column="1"
HorizontalOptions="Center" VerticalOptions="Center">
<StackLayout.Resources>
<Style BasedOn="{StaticResource defaultLabel}" TargetType="Label">
<Setter Property="Margin" Value="2"/>
</Style>
</StackLayout.Resources>
<Label Text="{Binding AppVM.ChampionsMgrVM.Index, Converter={StaticResource plusOneConverter}}" HorizontalOptions="End"/>
<Label Text="/"/>
<Label Text="{Binding AppVM.ChampionsMgrVM.NbPages}" HorizontalOptions="Start"/>
</StackLayout>
<Button Grid.Column="2" Style="{StaticResource iconButton}"
Command="{Binding AppVM.ChampionsMgrVM.NextPageCommand}">
<Button.ImageSource>
<FontImageSource Glyph="{StaticResource angle-right}" FontFamily="FASolid"
Size="Title"/>
</Button.ImageSource>
</Button>
</Grid>
</Grid>
</ContentPage>

@ -0,0 +1,17 @@
using LolApp.ViewModels;
using ViewModels;
namespace LolApp;
public partial class ChampionsPage : ContentPage
{
public ApplicationVM AppVM { get; }
public ChampionsPageVM VM { get; }
public ChampionsPage(ApplicationVM appVM)
{
InitializeComponent();
AppVM = appVM;
VM = new ChampionsPageVM(AppVM.ChampionsMgrVM);
BindingContext = this;
}
}

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:model="clr-namespace:Model;assembly=Model"
x:Class="LolApp.ContentViews.ChampionClassSelector"
x:Name="root">
<ContentView.Resources>
<model:ChampionClass x:Key="assassin">Assassin</model:ChampionClass>
<model:ChampionClass x:Key="fighter">Fighter</model:ChampionClass>
<model:ChampionClass x:Key="mage">Mage</model:ChampionClass>
<model:ChampionClass x:Key="marksman">Marksman</model:ChampionClass>
<model:ChampionClass x:Key="support">Support</model:ChampionClass>
<model:ChampionClass x:Key="tank">Tank</model:ChampionClass>
<ControlTemplate x:Key="RadioButtonTemplate">
<Border Stroke="{StaticResource Transparent}"
BackgroundColor="{StaticResource Transparent}"
HorizontalOptions="Fill"
VerticalOptions="Fill"
Padding="0">
<Border.StrokeShape>
<RoundRectangle CornerRadius="40, 40, 0, 40"/>
</Border.StrokeShape>
<VisualStateManager.VisualStateGroups>
<VisualStateGroupList>
<VisualStateGroup x:Name="CheckedStates">
<VisualState x:Name="Checked">
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="{Binding CheckedColor, Source={x:Reference root}}" />
<Setter Property="Stroke"
Value="{Binding CheckedColor, Source={x:Reference root}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Unchecked">
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="{Binding UncheckedColor, Source={x:Reference root}}" />
<Setter Property="Stroke"
Value="{Binding UncheckedColor, Source={x:Reference root}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</VisualStateManager.VisualStateGroups>
<Grid>
<ContentPresenter VerticalOptions="Center" HorizontalOptions="Center" />
</Grid>
</Border>
</ControlTemplate>
<Style TargetType="RadioButton">
<Setter Property="ControlTemplate"
Value="{StaticResource RadioButtonTemplate}" />
</Style>
</ContentView.Resources>
<Grid ColumnDefinitions="*, *, *" RowDefinitions="*, *"
Margin="6" ColumnSpacing="6" RowSpacing="6"
RadioButtonGroup.GroupName="championClasses"
RadioButtonGroup.SelectedValue="{Binding SelectedValue, Source={x:Reference root}, Mode=TwoWay}">
<Grid.Resources>
<Style TargetType="Label">
<Setter Property="FontSize" Value="{OnPlatform Micro, WinUI=12}"/>
</Style>
</Grid.Resources>
<RadioButton Value="{Binding Source={StaticResource assassin}}">
<RadioButton.Content>
<VerticalStackLayout>
<Image Source="{Binding Source={StaticResource assassin}, Converter={StaticResource championClassToIconConverter}}"
WidthRequest="26" HeightRequest="26" HorizontalOptions="Center"/>
<Label Text="Assassin" HorizontalOptions="Center"/>
</VerticalStackLayout>
</RadioButton.Content>
</RadioButton>
<RadioButton Grid.Column="1"
Value="{Binding Source={StaticResource fighter}}">
<RadioButton.Content>
<VerticalStackLayout>
<Image Source="{Binding Source={StaticResource fighter}, Converter={StaticResource championClassToIconConverter}}"
WidthRequest="26" HeightRequest="26" HorizontalOptions="Center"/>
<Label Text="Fighter" HorizontalOptions="Center"/>
</VerticalStackLayout>
</RadioButton.Content>
</RadioButton>
<RadioButton Grid.Column="2"
Value="{Binding Source={StaticResource mage}}">
<RadioButton.Content>
<VerticalStackLayout>
<Image Source="{Binding Source={StaticResource mage}, Converter={StaticResource championClassToIconConverter}}"
WidthRequest="26" HeightRequest="26" HorizontalOptions="Center"/>
<Label Text="Mage" HorizontalOptions="Center"/>
</VerticalStackLayout>
</RadioButton.Content>
</RadioButton>
<RadioButton Grid.Row="1" Grid.Column="0"
Value="{Binding Source={StaticResource marksman}}">
<RadioButton.Content>
<VerticalStackLayout>
<Image Source="{Binding Source={StaticResource marksman}, Converter={StaticResource championClassToIconConverter}}"
WidthRequest="26" HeightRequest="26" HorizontalOptions="Center"/>
<Label Text="Marksman" HorizontalOptions="Center"/>
</VerticalStackLayout>
</RadioButton.Content>
</RadioButton>
<RadioButton Grid.Row="1" Grid.Column="1"
Value="{Binding Source={StaticResource support}}">
<RadioButton.Content>
<VerticalStackLayout>
<Image Source="{Binding Source={StaticResource support}, Converter={StaticResource championClassToIconConverter}}"
WidthRequest="26" HeightRequest="26" HorizontalOptions="Center"/>
<Label Text="Support" HorizontalOptions="Center"/>
</VerticalStackLayout>
</RadioButton.Content>
</RadioButton>
<RadioButton Grid.Row="1" Grid.Column="2"
Value="{Binding Source={StaticResource tank}}">
<RadioButton.Content>
<VerticalStackLayout>
<Image Source="{Binding Source={StaticResource tank}, Converter={StaticResource championClassToIconConverter}}"
WidthRequest="26" HeightRequest="26" HorizontalOptions="Center"/>
<Label Text="Tank" HorizontalOptions="Center"/>
</VerticalStackLayout>
</RadioButton.Content>
</RadioButton>
</Grid>
</ContentView>

@ -0,0 +1,34 @@
using Model;
namespace LolApp.ContentViews;
public partial class ChampionClassSelector : ContentView
{
public ChampionClassSelector()
{
InitializeComponent();
}
public static readonly BindableProperty SelectedValueProperty = BindableProperty.Create(nameof(SelectedValue), typeof(ChampionClass), typeof(ChampionClassSelector), ChampionClass.Unknown, BindingMode.TwoWay);
public ChampionClass SelectedValue
{
get => (ChampionClass)GetValue(SelectedValueProperty);
set => SetValue(SelectedValueProperty, value);
}
public static readonly BindableProperty CheckedColorProperty = BindableProperty.Create(nameof(CheckedColor), typeof(Color), typeof(ChampionClassSelector), Colors.DarkSalmon);
public Color CheckedColor
{
get => (Color)GetValue(CheckedColorProperty);
set => SetValue(CheckedColorProperty, value);
}
public static readonly BindableProperty UncheckedColorProperty = BindableProperty.Create(nameof(UncheckedColor), typeof(Color), typeof(ChampionClassSelector), Colors.DarkSalmon);
public Color UncheckedColor
{
get => (Color)GetValue(UncheckedColorProperty);
set => SetValue(UncheckedColorProperty, value);
}
}

@ -0,0 +1,38 @@
using System.Windows.Input;
namespace LolApp.ContentViews;
public class SearchByStringView : ContentView
{
public static readonly BindableProperty PlaceHolderProperty = BindableProperty.Create(nameof(PlaceHolder), typeof(string), typeof(SearchByStringView), string.Empty);
public string PlaceHolder
{
get => (string)GetValue(PlaceHolderProperty);
set => SetValue(PlaceHolderProperty, value);
}
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(SearchByStringView), string.Empty);
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(SearchByStringView), null);
public ICommand Command
{
get => (ICommand)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(SearchByStringView), null);
public object CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
}

@ -0,0 +1,100 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0-android;net7.0-ios;net7.0-maccatalyst</TargetFrameworks>
<OutputType>Exe</OutputType>
<RootNamespace>LolApp</RootNamespace>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<!-- Display name -->
<ApplicationTitle>LolApp</ApplicationTitle>
<!-- App Identifier -->
<ApplicationId>fr.uca.iut.lolapp</ApplicationId>
<ApplicationIdGuid>d3cd18a9-c614-4933-bd36-3008e72004d5</ApplicationIdGuid>
<!-- Versions -->
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">14.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
<ProjectGuid>{0C898A04-092A-49AA-BE65-8AE818A2AF50}</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net7.0-ios|AnyCPU'">
<CreatePackage>false</CreatePackage>
<CodesignProvision>appleIUT_TP2022</CodesignProvision>
<CodesignKey>iPhone Developer: Cedric BOUHOURS (M2E3ZQNZ3K)</CodesignKey>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net7.0-maccatalyst|AnyCPU'">
<CreatePackage>false</CreatePackage>
<CodesignKey>Developer ID Application</CodesignKey>
<PackageSigningKey>3rd Party Mac Developer Installer</PackageSigningKey>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net7.0-ios|AnyCPU'">
<CreatePackage>false</CreatePackage>
</PropertyGroup>
<PropertyGroup>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net7.0-windows10.0.22621.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<!-- App Icon -->
<MauiFont Include="Resources\Fonts\*" />
<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
<MauiIcon Include="Resources\AppIcon\appicon.png" />
</ItemGroup>
<ItemGroup>
<None Remove="Resources\Images\fighter.svg" />
<None Remove="Resources\Images\lollogo.jpg" />
<None Remove="Resources\Images\sword.png" />
<None Remove="Resources\Images\lol.png" />
<None Remove="Resources\Fonts\Font Awesome 6 Free-Solid-900.otf" />
<None Remove="Resources\Images\support.svg" />
<None Remove="Resources\Images\tank.svg" />
<None Remove="Resources\Images\marksman.svg" />
<None Remove="Resources\Images\assassin.svg" />
<None Remove="Resources\Images\mage.svg" />
<None Remove="Resources\AppIcon\appicon.png" />
<None Remove="Resources\Splash\tank.svg" />
<None Remove="Resources\Splash\splash.png" />
<None Remove="Resources\Images\rp.png" />
</ItemGroup>
<ItemGroup>
<MauiImage Include="Resources\Images\lollogo.jpg" />
<MauiImage Include="Resources\Images\sword.png" />
<MauiImage Include="Resources\Images\lol.png" />
<MauiImage Include="Resources\Images\tank.svg" />
<MauiImage Include="Resources\Images\support.svg" />
<MauiImage Include="Resources\Images\marksman.svg" />
<MauiImage Include="Resources\Images\mage.svg" />
<MauiImage Include="Resources\Images\fighter.svg" />
<MauiImage Include="Resources\Images\assassin.svg" />
<MauiImage Include="Resources\Images\rp.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Client\ApiManager.csproj" />
<ProjectReference Include="..\Model\Model.csproj" />
<ProjectReference Include="..\Shared\Shared.csproj" />
<ProjectReference Include="..\StubLib\StubLib.csproj" />
<ProjectReference Include="..\ViewModels\ViewModels.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Maui" Version="5.0.0" />
<PackageReference Include="CommunityToolkit.Maui.Markup" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
<PackageReference Include="Microsoft.Maui.Graphics.Skia" Version="7.0.59" />
</ItemGroup>
<ItemGroup>
<MauiSplashScreen Include="Resources\Splash\splash.png" />
</ItemGroup>
</Project>

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="LolApp.MainPage">
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<Image
Source="lollogo.jpg"
SemanticProperties.Description="Cute dot net bot waving hi to you!"
HeightRequest="200"
HorizontalOptions="Center" />
<Label
Text="League of Legends Data"
SemanticProperties.HeadingLevel="Level1"
FontSize="32"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"/>
<Label
Text="Find information about champions, skins and runes"
SemanticProperties.HeadingLevel="Level2"
SemanticProperties.Description="Welcome to dot net Multi platform App U I"
FontSize="18"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"/>
</VerticalStackLayout>
</ScrollView>
</ContentPage>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save