You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
Go to file
Simon GUILLOT 6a39f92060
continuous-integration/drone/push Build is passing Details
Merge pull request 'fin 2' (#70) from rendu into main
3 months ago
DbContextLib fin 3 months ago
Dtos extensions tests 3 months ago
Entities fin 3 months ago
Entities2Dtos fin 3 months ago
Shared fin 3 months ago
Stubs fin 3 months ago
Tests fin 3 months ago
WebApi dockerfile issue 3 months ago
images fin 2 3 months ago
.dockerignore dockerfile issue 3 months ago
.drone.yml fin 2 3 months ago
.gitignore 🎨 reformated file and improved file orgnisation to improve consistency 4 months ago
LICENSE Initial commit 5 months ago
README.md fin 2 3 months ago
gazet-api.sln fin 3 months ago
j1.md fin 3 months ago

README.md

gazet-api

Nous avons déplacé la partie du J1 dans le fichier j1.md

Built with

  • Language: C# 12
  • ORM: Entity Framework Core 8
  • API: ASP.NET Core 8
  • Unit testing: xUnit
  • Database: PostgresSQL, SQLite (not recommended for production)

MLD

classDiagram
    user <|-- source
    user <|-- tag
    user <|-- collection

    collection <|-- belongs_to
    
    tag <|-- has_tag

    content <|-- belongs_to
    content <|-- has_tag

    content_type <|-- content

    class belongs_to {
        long CollectionId
        long ContentId
        date CreatedAt
        bool IsFavorite
        bool IsVisible
    }

    class collection {
        long Id
        long TeacherId
        string name
        date CreatedAt
    }

    class content {
        long Id
        string title
        string content_type
        string? url
        string source
        string? comment
    }

    class content_type {
        long Id
        string type
    }

    class has_tag {
        long Id
        long ContentId
    }

    class tag {
        long Id
        long TeacherId
        string name
    }

    class user {
        long Id
        string Username
        string Email
        string Password
        date CreatedAt
        string Role
    }

    class source {
        long Id
        long UserId
        string name
        string url
    }

How to tun migrations

cd StubbedContextLib
dotnet ef migrations add MigrationName
dotnet ef database update MigrationName

How to run the tests

dotnet test

How to run console tests for WebApi

Its possible when you run consoles tests for WebApi that you have an error of port and authorization. Its certainly due to the base address of the client in the firsts lines. Just change the port after localhost in the string to correspond to the correct port where WebApi is launched. Also change if you use http or https at the beginning of the address.

Project structure

All tests (Unit and Console) are located in the Tests folder under the name of the project followed by .UnitTests or .ConsoleTests.

We have 2 main parts in the project:

  1. WebApi: a RESTful API using ASP.NET 8
  2. DbContext: an Entity Framework Core 8 context AS ORM

The other projects are serve the 2 mentioned before and are used to separate concerns and make the code more maintainable.

Data

The way we transfer data between the API and the client are through DTOs which are located in the DTO project.

The way we store data in the database and the ORM is working with is through Entity which are located in the Entity project.

Then to make the link between the DTO and the Entity, we are using extensions (ToDto/ToDtos and ToEntity/ToEntities) methods to transform Entity to DTO and vice versa.

Handling the data

We have multiple interfaces that represent what we can do with the data. For example, we have IUserManager that represents the operations we can do with the User entity and DTO.

Our interface is generic, meaning we can use the same interface for Entities and DTOs. This is useful to avoid code duplication and to make sure that the same operations are available for both entities and DTOs.

We implement these interfaces in their respective project as mentioned below.

On the ORM side

We have TypeDbManagers that implement the interfaces mentioned above to manipulate entities across the ORM, this usually include CRUD operations and more specific methods such GetByName. These are located in the DbManagers folders in the DbContext project.

On the API side

We also have TypeDtoManagers that implement the interfaces mentioned above to be called from the controllers to call the DbManager in the ORM to perform the actual operation. At this point we estimate that the controllers have done their work to check if the given data are valid and won't be checked later. Those are located in the Managers folders in the WebApi project.

Sample data et utilisation

Pagination : Order By, Page, PageSize peuvent être laissés vide et prendront par défaut les 25 premiers éléments non triés. PageSize ne peut quant à lui pas dépasser 50.

Voici quelques champs préremplit pour tester les tags : Pour récupérer/delete/update par id : id de 1 à 9 disponibles par nom : vela ou course sont disponibles

Name : littérature, Réseaux UserId : users de 1 à 4 disponibles

Conception

Diagramme de paquets de notre application

La partie Entity Framework (ORM) contient les projets suivants

  • Entities
  • DbContextLib
  • StubbedContext
  • DbContextLib.ConsoleTests
  • DbContextLib.UnitTests

La partie ASP.NET (API) contient les projets suivants

  • Dtos
  • WebApi
  • StubbedStoLib
  • WebApi.ConsoleTests
  • WebApi.UnitTests

Le projet Shared contient les ressources partagées entre la partie EF et API Le projet Entities2Dtos est responsable de la transformation d'entités vers leur DTO associés et inversement

Diagrammes de classes

EF

Voici les diagrammes de classes, respectivement de la partie EF puis API. Nous avons volontairement omis certaines classes redondantes par soucis de lisibilité, la compréhension ne devrait pas être altérée

classDiagram
    Entity <|-- UserEntity
    Entity <|-- CollectionEntity
    Entity <|-- ContentEntity
    DBManager <|-- IDbManager
    CollectionEntity "" <-- UserEntity
    ContentEntity "" <-- CollectionEntity
    UnitOfWork <-- DBManager
    GazetDbContext <-- DBManager
    ContentEntity "" <-- GazetDbContext
    CollectionEntity "" <-- GazetDbContext
    UserEntity "*" <-- GazetDbContext
    GenericRepository "Un par entité" <-- UnitOfWork
    class Entity{
        
    }
    class ContentEntity{
        +long Id
        +long? ContentTypeId
        +ContentTypeEntity ContentType
        +long? SourceId
        +SourceEntity Source
        +string Title
        +string? Url
        +string? Comment
        +DateTime PublishedAt
    }
    class UserEntity{
        +long Id 
        +string Username
        +string Email
        +string Password
        +DateTime CreatedAt
        +Role Role
    }
    class CollectionEntity{
        +long Id
        +string Name
        +long UserId
        +UserEntity User
        +DateTime CreatedAt
    }
    class GenericRepository{
        +Create()
        +Delete()
        +Update()
        +GetById()
        +Get()
    }
    class UnitOfWork{
        
    }
    class DBManager{
        
    }
    class GazetDbContext{
        
    }
    class IDbManager {
        <<Interface>>
    }

API

classDiagram
    IDbManager <|-- DtoManager
    DtoManager <-- Controller
    ContentDto <.. DtoManager
    CollectionDto <.. DtoManager
    UserDto <.. DtoManager
    Program <-- Controller
    class IDbManager {
        <<Interface>>
    }
    class DtoManager {
        +Get()
        +Getby...()
        ...()
    }
    class Controller{
        +Get()
        +Getby...()
        ...()
    }
    class ContentDto{
        +long Id
        +string Title
        +string Url
        +string? Comment
        +DateTime PublishedAt
    }
    class CollectionDto{
        +long Id
        +long UserId
        +string Name
        +DateTime CreatedAt
    }
    class UserDto{
        +long Id
        +string Username
        +string Email
        +string Password
        +DateTime CreatedAt
        +Role Role
    }
    class Program{

    }

Évolution par rapport au J1

Pour le J2, nous avons donc déployé l'api sur CodeFirst à l'adresse suivante : https://codefirst.iut.uca.fr/containers/simonguillot2-gazet-api-container

De plus, nous avons implémenté les Controllers, Dto manager et EntityManager qu'il nous manquait. À savoir pour les éléments suivants : collection, content et source. De plus, nous avons mis le patron Unit Of Work afin de factoriser certaines actions. Enfin, nous avons fait une grande partie des tests pour s'assurer du bon fonctionnement de l'API.

Extra J2 (couverture de tests, tests de charge et de performances)

Pour la partie extra en J2, par manque de temps et le fait que nous avions déjà commencé à le faire pour une autre SAE, nous avons décidé d'augmenter la couverture de tests ainsi que d'effectuer des tests de charge et de performances.

Couverture de tests

Selon Sonar, nous avons 75% de code coverage avec ce que l'on estime être de "bons" tests et côté IDE, il nous indique 84% avec la répartition suivante

Coverage

Tests de charge

Pour mesurer et monitorer lutilisation de la mémoire, nous avons utilisé dotMemory, un outil de profiling de JetBrains. Pour cela, nous avons dabord compilé le projet en mode Release, puis configuré le profiler pour lancer lexécutable généré et analyser celui-ci. Voici les résultats pour 100 requêtes par seconde.

i5-3570k (GNU/Linux)

test_charge_pc

Mac

test_charge_mac

Comme on peut le constater sur les deux graphiques ci-dessus, on constate que se fait en dents de scie, montrant bien les allocations de mémoire nécessaire à toutes nos requêtes, puis lappel du garbage collector qui supprime les objets plus utilisés.

Tests de performance

Dans un second temps, nous avons voulu nous assurer que notre API était performante afin que le temps de réponse pour nos clients soient les meilleurs possibles, pour ça nous avons utilisé loutil de mesure de performance de Postman. Voici les résultats

i5-3570k (GNU/Linux)

test_perfs_pc

Mac

test_perfs_mac

Dans les deux cas, on peut constater que les performances sont excellentes et très stables. Ainsi, nous pouvons consommer notre API sans craindre dimpacts de performance même avec un trafic 5 fois plus élevé que la capacité maximum prévue.

Piste d'amélioration si nous avions eu plus de temps

Si nous avions eu plus de temps, nous aurions

  • Utiliser nos UserEntity comme IdentityUser et réécrire le controller d'authentification
  • Injecter un fournisseur de base de données côté API