From 39193ed5acacc6ba744240d47627766059dcc59c Mon Sep 17 00:00:00 2001 From: "nathan.boileau" Date: Sun, 26 Mar 2023 23:31:45 +0200 Subject: [PATCH] :art: READ ME + Remove files --- .../DiagrammeArchitecture.png | Bin README.md | 82 ++++- Sources/TestUnitaireAPiLol/TestAPILol.cs | 37 -- Sources/TestUnitaireAPiLol/TestEFLol.cs | 49 --- .../TestUnitaireAPiLol/TestUnitaire.csproj | 34 -- Sources/TestUnitaireAPiLol/Usings.cs | 1 - .../Tests/ConsoleTests/ConsoleTests.csproj | 28 -- Sources/Tests/ConsoleTests/Program.cs | 338 ------------------ 8 files changed, 80 insertions(+), 489 deletions(-) rename Docs/{ => imageCodeFirst}/DiagrammeArchitecture.png (100%) delete mode 100644 Sources/TestUnitaireAPiLol/TestAPILol.cs delete mode 100644 Sources/TestUnitaireAPiLol/TestEFLol.cs delete mode 100644 Sources/TestUnitaireAPiLol/TestUnitaire.csproj delete mode 100644 Sources/TestUnitaireAPiLol/Usings.cs delete mode 100644 Sources/Tests/ConsoleTests/ConsoleTests.csproj delete mode 100644 Sources/Tests/ConsoleTests/Program.cs diff --git a/Docs/DiagrammeArchitecture.png b/Docs/imageCodeFirst/DiagrammeArchitecture.png similarity index 100% rename from Docs/DiagrammeArchitecture.png rename to Docs/imageCodeFirst/DiagrammeArchitecture.png diff --git a/README.md b/README.md index ef228cf..bba5945 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,85 @@ -# LOL Project +# Projet d'Entity FrameWork et Consomation et Développement de services sur League Of Legends Ce projet est un travail universitaire réalisé durant la deuxième année de BUT à Clermont-Ferrand. Il correspond au travail demandé dans le cadre du cours regroupant Entity Framework et Consommation et Developpement de Services. ![League Of Legends](./Docs/imageCodeFirst/frontImageReadMe.jpg) -Explique ce qu'on a fait et ce qu'on a pas fait et pourquoi on a priorisé ca plutot que d'autre \ No newline at end of file +Explique ce qu'on a fait et ce qu'on a pas fait et pourquoi on a priorisé ca plutot que d'autre + + +# :zap: Consomation et Développement de services + +Voici l'état des différentes tâches liées à la consommation et au développement de services : + + +> * :white_check_mark: La mise en place de toutes les opérations CRUD est terminée. +> * :white_check_mark: Une API RESTful a été mise en place en respectant les règles de routage et en utilisant les bons codes de statut. +> * :white_check_mark: La version de l'API a été gérée de manière appropriée. +> * :white_check_mark: Les logs ont été implémentés. +> * :construction: Les tests unitaires sont en cours de réalisation. +> * :construction: La création du client MAUI et sa liaison avec l'API sont en cours de réalisation. +> * :white_check_mark: La liaison avec la base de données est opérationnelle. +> * :white_check_mark: Le filtrage et la pagination des données ont été implémentés. +> * :white_check_mark: Le code est de qualité grâce à l'utilisation de SonarQube. +> * :white_check_mark: L'API a été dockerisée et hébergée sur CodeFirst. +> * :construction: Sécurité + +`Note : Le client MAUI n'a pas été réalisé par manque de temps. Les tests unitaires et la sécurité sont réalisés au fur et à mesure de l'avancement du projet.` + + +--- + + +# :zap: Entity Framework : + +Voici l'état des différentes tâches liées à Entity Framework : + +> * :white_check_mark: **Exercice 1** : Une base de données a été créée avec une table pour les champions, et des requêtes CRUD ont été implémentées, ainsi que du filtrage et de la pagination. Le client console n'a pas été réalisé pour cet exercice par manque de temps. +> * :white_check_mark: **Exercice 2** : Des tests unitaires ont été écrits et une base de données a été simulée à l'aide de SQLiteInMemory. +> * :white_check_mark: **Exercice 3** : Entity Framework a été déployé et les tests ont été effectués via Code#0. +> * :white_check_mark: **Exercice 4**: Les tables pour les runes et les skins ont été implémentées (sans les relations). +> * :white_check_mark: **Exercice 5** : Une relation OneToMany a été établie entre les champions et les skins. +> * :white_check_mark: **Exercice 6** : Une relation ManyToMany a été établie entre les champions, les rune pages et les runes. +> * :construction: **Exercice 7** : Le mapping entre le modèle et l'entité a été réalisé pour améliorer la qualité du code. +> * :construction: **Exercice 8** : La mise en place du pattern UnitOfWork n'a pas pu être implémentée par manque de temps. +--- + + +### Diagramme d'architechture : + +![Diagramme d'architechture](./Docs/imageCodeFirst/DiagrammeArchitecture.png) + + +#### Partie Client : + +La partie client qui n'a pas pu être réalisé dans notre cas, est sensée être constituée du client MAUI et du client Console, qui affichent les ressources et testent l'architecture en utilisant le HTTPDataManager pour effectuer des requêtes à l'API et récupérer des données. + +#### DataManager : + +Le DataManager utilise l'une des extensions mapper pour convertir les objets DTO en Model, et peut être remplacé par EFDataManager ou StubLib. + +#### Partie API : + +La partie API reçoit les requêtes et renvoie les objets en conséquence, en utilisant l'EFDataManager pour accéder aux données stockées en base de données. La fluent API permet de définir précisément les attributs de la base de données. + +#### Entity Framework : +L'EntityFramework est implémenté avec toutes les classes Entity dérivant du modèle, en utilisant OneToMany et ManyToMany de manière dérivée de celle prévue. Les méthodes CRUD sont implémentées grâce à l'utilisation de l'EFDataManager et le Mapper entre entity et model est requis. + +#### Déploiement : +Le projet est déployé avec le projet en conteneur, mais le pattern UnitOfWork n'a pas été abordé. + +# :tada: Comment lancer le projet + +## 1 - Cloner le dépot + +Cloner le dépôt Git en utilisant la commande suivante : + + git clone https://codefirst.iut.uca.fr/git/bastien.ollier/LOL.git + +## 2 - Configurer le démarrage du projet + +> Configurer le projet de démarrage en cliquant sur "Projet de démarrage" à gauche de la flèche verte, puis en sélectionnant l'option "apiLOL". + +## 3 - Lancement du projet + +Le projet est maintenant prêt à être lancé. Vous pouvez commencer à faire des requêtes sur la base de données via l'API. \ No newline at end of file diff --git a/Sources/TestUnitaireAPiLol/TestAPILol.cs b/Sources/TestUnitaireAPiLol/TestAPILol.cs deleted file mode 100644 index b2a1758..0000000 --- a/Sources/TestUnitaireAPiLol/TestAPILol.cs +++ /dev/null @@ -1,37 +0,0 @@ -using apiLOL; -using apiLOL.Controllers; -using Microsoft.AspNetCore.Mvc; -using StubLib; - -namespace TestUnitaire -{ - public class TestAPILol - { - [Fact] - public void Test1() - { - - } - - [Fact] - public void TestPostChampion() - { - // Arrange - var data = new StubData(); - var controller = new ControllerChampions(new StubData()); - var champDTO = new ChampionDTO("Charles", "Charles est un champion de League of Legends"); - - // Act - var result = controller.Post(champDTO); - data.ChampionsMgr.AddItem(champDTO.ToModel()); - var nbItem = data.ChampionsMgr.GetNbItems(); - Task nbItemTask = nbItem; - - // Assert - Assert.IsType(result); - // Verify that the champions is added to the stub - Assert.Equal(7, nbItemTask.Result); - - } - } -} \ No newline at end of file diff --git a/Sources/TestUnitaireAPiLol/TestEFLol.cs b/Sources/TestUnitaireAPiLol/TestEFLol.cs deleted file mode 100644 index 491f46e..0000000 --- a/Sources/TestUnitaireAPiLol/TestEFLol.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace TestUnitaire -{ - public class TestEFLol - { - public void TestAddIntoDB() - { - // Arrange - ChampionEntity Zeus = new ChampionEntity - { - Name = "Zeus", - Bio = "Zeus is the king of the gods." - }; - - // Act - using (var context = new ChampionContext()) - { - Console.WriteLine("Adding Zeus to the database..."); - context.Champions.Add(Zeus); - context.SaveChanges(); - } - - // Assert - using (var context = new ChampionContext()) - { - var champion = context.Champions.FirstOrDefault(c => c.Name == "Zeus"); - if (champion == null) - { - Assert.True(false, "Champion not found in database."); - } - Assert.NotNull(champion); - Assert.Equal("Zeus", champion.Name); - Assert.Equal("Zeus is the king of the gods.", champion.Bio); - } - } - - public void TestDeleteFromDB() - { - // Act - - } - - } -} diff --git a/Sources/TestUnitaireAPiLol/TestUnitaire.csproj b/Sources/TestUnitaireAPiLol/TestUnitaire.csproj deleted file mode 100644 index 393919a..0000000 --- a/Sources/TestUnitaireAPiLol/TestUnitaire.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - net6.0 - enable - enable - - false - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - diff --git a/Sources/TestUnitaireAPiLol/Usings.cs b/Sources/TestUnitaireAPiLol/Usings.cs deleted file mode 100644 index 8c927eb..0000000 --- a/Sources/TestUnitaireAPiLol/Usings.cs +++ /dev/null @@ -1 +0,0 @@ -global using Xunit; \ No newline at end of file diff --git a/Sources/Tests/ConsoleTests/ConsoleTests.csproj b/Sources/Tests/ConsoleTests/ConsoleTests.csproj deleted file mode 100644 index 557bdc5..0000000 --- a/Sources/Tests/ConsoleTests/ConsoleTests.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - Exe - net6.0 - enable - enable - - - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - diff --git a/Sources/Tests/ConsoleTests/Program.cs b/Sources/Tests/ConsoleTests/Program.cs deleted file mode 100644 index 93e266f..0000000 --- a/Sources/Tests/ConsoleTests/Program.cs +++ /dev/null @@ -1,338 +0,0 @@ -using System.Collections.Immutable; -using System.Diagnostics; -using Microsoft.Extensions.DependencyInjection; -using Model; -using StubLib; -using static System.Console; - -namespace ConsoleTests -{ - static class Program - { - static IDataManager dataManager = null!; - - static async Task Main(string[] args) - { - try - { - using var servicesProvider = new ServiceCollection() - .AddSingleton() - .BuildServiceProvider(); - - dataManager = servicesProvider.GetRequiredService(); - - await DisplayMainMenu(); - - Console.ReadLine(); - } - catch (Exception ex) - { - Debug.WriteLine(ex, "Stopped program because of exception"); - throw; - } - } - - public static async Task DisplayMainMenu() - { - Dictionary choices = new Dictionary() - { - [1] = "1- Manage Champions", - [2] = "2- Manage Skins", - [3] = "3- Manage Runes", - [4] = "4- Manage Rune Pages", - [99] = "99- Quit" - }; - - while(true) - { - int input = DisplayAMenu(choices); - - switch(input) - { - case 1: - await DisplayChampionsMenu(); - break; - case 2: - break; - case 3: - break; - case 4: - break; - case 99: - WriteLine("Bye bye!"); - return; - default: - break; - } - } - } - - private static int DisplayAMenu(Dictionary choices) - { - int input=-1; - while(true) - { - WriteLine("What is your choice?"); - WriteLine("--------------------"); - foreach(var choice in choices.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value)) - { - WriteLine(choice); - } - if(!int.TryParse(ReadLine(), out input) || input == -1) - { - WriteLine("I do not understand what your choice is. Please try again."); - continue; - } - break; - } - WriteLine($"You have chosen: {choices[input]}"); - WriteLine(); - return input; - } - - public static async Task DisplayChampionsMenu() - { - Dictionary choices = new Dictionary() - { - [0] = "0- Get number of champions", - [1] = "1- Get champions", - [2] = "2- Find champions by name", - [3] = "3- Find champions by characteristic", - [4] = "4- Find champions by class", - [5] = "5- Find champions by skill", - [6] = "6- Add new champion", - [7] = "7- Delete a champion", - [8] = "8- Update a champion", - }; - - int input = DisplayAMenu(choices); - - switch(input) - { - case 0: - int nb = await dataManager.ChampionsMgr.GetNbItems(); - WriteLine($"There are {nb} champions"); - WriteLine("**********************"); - break; - case 1: - { - int index = ReadAnInt("Please enter the page index"); - int count = ReadAnInt("Please enter the number of elements to display"); - WriteLine($"{count} champions of page {index+1}"); - var champions = await dataManager.ChampionsMgr.GetItems(index, count, nameof(Champion.Name)); - foreach(var champion in champions) - { - WriteLine($"\t{champion}"); - } - WriteLine("**********************"); - } - break; - case 2: - { - string substring = ReadAString("Please enter the substring to look for in the name of a champion"); - int index = ReadAnInt("Please enter the page index"); - int count = ReadAnInt("Please enter the number of elements to display"); - var champions = await dataManager.ChampionsMgr.GetItemsByName(substring, index, count, nameof(Champion.Name)); - foreach(var champion in champions) - { - WriteLine($"\t{champion}"); - } - WriteLine("**********************"); - } - break; - case 3: - { - string substring = ReadAString("Please enter the substring to look for in the characteristics of champions"); - int index = ReadAnInt("Please enter the page index"); - int count = ReadAnInt("Please enter the number of elements to display"); - var champions = await dataManager.ChampionsMgr.GetItemsByCharacteristic(substring, index, count, nameof(Champion.Name)); - foreach(var champion in champions) - { - WriteLine($"\t{champion}"); - } - WriteLine("**********************"); - } - break; - case 4: - { - ChampionClass championClass = ReadAnEnum($"Please enter the champion class (possible values are: {Enum.GetNames().Aggregate("", (name, chaine) => $"{chaine} {name}")}):"); - int index = ReadAnInt("Please enter the page index"); - int count = ReadAnInt("Please enter the number of elements to display"); - var champions = await dataManager.ChampionsMgr.GetItemsByClass(championClass, index, count, nameof(Champion.Name)); - foreach(var champion in champions) - { - WriteLine($"\t{champion}"); - } - WriteLine("**********************"); - } - break; - case 5: - { - string substring = ReadAString("Please enter the substring to look for in the skills of champions"); - int index = ReadAnInt("Please enter the page index"); - int count = ReadAnInt("Please enter the number of elements to display"); - var champions = await dataManager.ChampionsMgr.GetItemsBySkill(substring, index, count, nameof(Champion.Name)); - foreach(var champion in champions) - { - WriteLine($"\t{champion}"); - } - WriteLine("**********************"); - } - break; - case 6: - { - WriteLine("You are going to create a new champion."); - string name = ReadAString("Please enter the champion name:"); - ChampionClass championClass = ReadAnEnum($"Please enter the champion class (possible values are: {Enum.GetNames().Aggregate("", (name, chaine) => $"{chaine} {name}")}):"); - string bio = ReadAString("Please enter the champion bio:"); - Champion champion = new Champion(name, championClass, bio: bio); - DisplayCreationChampionMenu(champion); - _ = await dataManager.ChampionsMgr.AddItem(champion); - } - break; - case 7: - { - WriteLine("You are going to delete a champion."); - string name = ReadAString("Please enter the champion name:"); - var somechampions = await dataManager.ChampionsMgr.GetItemsByName(name, 0, 10, nameof(Champion.Name)); - var someChampionNames = somechampions.Select(c => c!.Name); - var someChampionNamesAsOneString = someChampionNames.Aggregate("", (name, chaine) => $"{chaine} {name}"); - string champName = ReadAStringAmongPossibleValues($"Who do you want to delete among these champions? (type \"Cancel\" to ... cancel) {someChampionNamesAsOneString}", - someChampionNames.ToArray()); - if(champName != "Cancel") - { - await dataManager.ChampionsMgr.DeleteItem(somechampions.Single(c => c!.Name == champName)); - } - } - break; - case 8: - { - WriteLine("You are going to update a champion."); - string name = ReadAString("Please enter the champion name:"); - var somechampions = await dataManager.ChampionsMgr.GetItemsByName(name, 0, 10, nameof(Champion.Name)); - var someChampionNames = somechampions.Select(c => c!.Name); - var someChampionNamesAsOneString = someChampionNames.Aggregate("", (name, chaine) => $"{chaine} {name}"); - string champName = ReadAStringAmongPossibleValues($"Who do you want to update among these champions? (type \"Cancel\" to ... cancel) {someChampionNamesAsOneString}", - someChampionNames.ToArray()); - if(champName == "Cancel") break; - ChampionClass championClass = ReadAnEnum($"Please enter the champion class (possible values are: {Enum.GetNames().Aggregate("", (name, chaine) => $"{chaine} {name}")}):"); - string bio = ReadAString("Please enter the champion bio:"); - Champion champion = new Champion(champName, championClass, bio: bio); - DisplayCreationChampionMenu(champion); - await dataManager.ChampionsMgr.UpdateItem(somechampions.Single(c => c!.Name == champName), champion); - } - break; - default: - break; - } - - } - - public static void DisplayCreationChampionMenu(Champion champion) - { - Dictionary choices = new Dictionary() - { - [1] = "1- Add a skill", - [2] = "2- Add a skin", - [3] = "3- Add a characteristic", - [99] = "99- Finish" - }; - - while(true) - { - int input = DisplayAMenu(choices); - - switch(input) - { - case 1: - string skillName = ReadAString("Please enter the skill name:"); - SkillType skillType = ReadAnEnum($"Please enter the skill type (possible values are: {Enum.GetNames().Aggregate("", (name, chaine) => $"{chaine} {name}")}):"); - string skillDescription = ReadAString("Please enter the skill description:"); - Skill skill = new Skill(skillName, skillType, skillDescription); - champion.AddSkill(skill); - break; - case 2: - string skinName = ReadAString("Please enter the skin name:"); - string skinDescription = ReadAString("Please enter the skin description:"); - float skinPrice = ReadAFloat("Please enter the price of this skin:"); - Skin skin = new Skin(skinName, champion, skinPrice, description: skinDescription); - break; - case 3: - string characteristic = ReadAString("Please enter the characteristic:"); - int value = ReadAnInt("Please enter the value associated to this characteristic:"); - champion.AddCharacteristics(Tuple.Create(characteristic, value)); - break; - case 99: - return; - default: - break; - } - } - } - - private static int ReadAnInt(string message) - { - while(true) - { - WriteLine(message); - if(!int.TryParse(ReadLine(), out int result)) - { - continue; - } - return result; - } - } - - private static float ReadAFloat(string message) - { - while(true) - { - WriteLine(message); - if(!float.TryParse(ReadLine(), out float result)) - { - continue; - } - return result; - } - } - - private static string ReadAString(string message) - { - while(true) - { - WriteLine(message); - string? line = ReadLine(); - if(line == null) - { - continue; - } - return line!; - } - } - - private static TEnum ReadAnEnum(string message) where TEnum :struct - { - while(true) - { - WriteLine(message); - if(!Enum.TryParse(ReadLine(), out TEnum result)) - { - continue; - } - return result; - } - } - - private static string ReadAStringAmongPossibleValues(string message, params string[] possibleValues) - { - while(true) - { - WriteLine(message); - string? result = ReadLine(); - if(result == null) continue; - if(result != "Cancel" && !possibleValues.Contains(result!)) continue; - return result!; - } - } - } -} \ No newline at end of file