diff --git a/.drone.yml b/.drone.yml index 51019cc..643d17f 100644 --- a/.drone.yml +++ b/.drone.yml @@ -3,9 +3,6 @@ type: docker name: HeartTrack-API trigger: - branch: - - WORK-CD - - WORK-EF_WebAPI event: - push @@ -18,6 +15,14 @@ steps: - dotnet build HeartTrack.sln -c Release --no-restore - dotnet publish HeartTrack.sln -c Release --no-restore -o CI_PROJECT_DIR/build/release + - name: tests + image: mcr.microsoft.com/dotnet/sdk:8.0 + commands: + - cd src/ + - dotnet restore HeartTrack.sln + - dotnet test HeartTrack.sln --no-restore + depends_on: [build] + - name: code-analysis image: hub.codefirst.iut.uca.fr/marc.chevaldonne/codefirst-dronesonarplugin-dotnet8 secrets: [ SECRET_SONAR_LOGIN ] @@ -36,8 +41,55 @@ steps: - reportgenerator -reports:"**/coverage.cobertura.xml" -reporttypes:SonarQube -targetdir:"coveragereport" - dotnet publish HeartTrack.sln -c Release --no-restore -o $CI_PROJECT_DIR/build/release - dotnet sonarscanner end /d:sonar.login=$${PLUGIN_SONAR_TOKEN} - depends_on: [ build ] + depends_on: [ tests ] + - name: swagger + image: mcr.microsoft.com/dotnet/sdk:8.0 + failure: ignore + volumes: + - name: docs + path: /docs + environment: + CODEFIRST_CLIENTDRONE_ENV_DOTNET_ROLL_FORWARD: LatestMajor + CODEFIRST_CLIENTDRONE_ENV_DOTNET_ROLL_FORWARD_TO_PRERELEASE: 1 + commands: + - cd src/ + - dotnet restore HeartTrack.sln + - cd HeartTrackAPI + - dotnet new tool-manifest + - dotnet tool install -g --version 6.5.0 Swashbuckle.AspNetCore.Cli + - cd ../ + - dotnet build HeartTrack.sln -c Release --no-restore + - dotnet publish HeartTrack.sln -c Release --no-restore -o CI_PROJECT_DIR/build/release + - export PATH="$PATH:/root/.dotnet/tools" + - swagger tofile --output /docs/swagger.json HeartTrackAPI/bin/Release/net8.0/HeartTrackAPI.dll v1 + depends_on: [build,tests] + + - name: generate-and-deploy-docs + image: hub.codefirst.iut.uca.fr/maxime.batista/codefirst-docdeployer + failure: ignore + commands: + - /entrypoint.sh -l docs/doxygen -t doxygen + when: + event: + - push + depends_on: [ build ] + +volumes: +- name: docs + temp: {} + +--- + +kind: pipeline +type: docker +name: HeartTrack-API-CD + +trigger: + event: + - push +steps: + - name: docker-build-and-push image: plugins/docker settings: @@ -49,25 +101,70 @@ steps: from_secret: SECRET_REGISTRY_USERNAME password: from_secret: SECRET_REGISTRY_PASSWORD - depends_on: [ build ] + + # database container stub + - name: deploy-container-stub + image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest + environment: + CODEFIRST_CLIENTDRONE_ENV_TYPE: STUB + IMAGENAME: hub.codefirst.iut.uca.fr/david.d_almeida/api:latest + CONTAINERNAME: heart_stub + COMMAND: create + ADMINS: davidd_almeida,kevinmonteiro,antoineperederii,paullevrault,antoinepinagot,nicolas.raymond + OVERWRITE: true + depends_on: [ docker-build-and-push ] - - name: deploy-container + # - name: deploy-container + # image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest + # environment: + # IMAGENAME: hub.codefirst.iut.uca.fr/david.d_almeida/api:latest + # CONTAINERNAME: heart_api + # CODEFIRST_CLIENTDRONE_ENV_TYPE: API + # CODEFIRST_CLIENTDRONE_ENV_PORT: 8080 + # ADMINS: davidd_almeida,kevinmonteiro,antoineperederii,paullevrault,antoinepinagot,nicolas.raymond + # COMMAND: create + # OVERWRITE: true + # depends_on: [ docker-build-and-push, deploy-container-stub ] + + + + # database container deployment + - name: deploy-container-mysql image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest environment: + IMAGENAME: mariadb:10 + CONTAINERNAME: mysql + COMMAND: create + OVERWRITE: true + PRIVATE: false + CODEFIRST_CLIENTDRONE_ENV_MARIADB_ROOT_PASSWORD: + from_secret: db_root_password + CODEFIRST_CLIENTDRONE_ENV_MARIADB_DATABASE: + from_secret: db_database + CODEFIRST_CLIENTDRONE_ENV_MARIADB_USER: + from_secret: db_user + CODEFIRST_CLIENTDRONE_ENV_MARIADB_PASSWORD: + from_secret: db_password + ADMINS: davidd_almeida,kevinmonteiro,antoineperederii,paullevrault,antoinepinagot,nicolas.raymond + + + # database container bdd + - name: deploy-container-bdd + image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest + environment: + CODEFIRST_CLIENTDRONE_ENV_TYPE: BDD + CODEFIRST_CLIENTDRONE_ENV_HOST: HeartDev-mysql + CODEFIRST_CLIENTDRONE_ENV_PORTDB: 3306 + CODEFIRST_CLIENTDRONE_ENV_DATABASE: + from_secret: db_database + CODEFIRST_CLIENTDRONE_ENV_USERNAME: + from_secret: db_user + CODEFIRST_CLIENTDRONE_ENV_PASSWORD: + from_secret: db_password IMAGENAME: hub.codefirst.iut.uca.fr/david.d_almeida/api:latest CONTAINERNAME: api CODEFIRST_CLIENTDRONE_ENV_PORT: 8080 ADMINS: davidd_almeida,kevinmonteiro,antoineperederii,paullevrault,antoinepinagot,nicolas.raymond COMMAND: create OVERWRITE: true - depends_on: [ docker-build-and-push ] - - - name: generate-and-deploy-docs - image: hub.codefirst.iut.uca.fr/maxime.batista/codefirst-docdeployer - failure: ignore - commands: - - /entrypoint.sh -l docs/doxygen -t doxygen - when: - event: - - push - depends_on: [ build ] \ No newline at end of file + depends_on: [deploy-container-mysql, docker-build-and-push, deploy-container-stub] \ No newline at end of file diff --git a/.gitignore b/.gitignore index a7bcc21..70b6c0a 100644 --- a/.gitignore +++ b/.gitignore @@ -565,4 +565,7 @@ xcuserdata/ /dataSources.local.xml .ideaMigration/ Migration/ +Migrations/ + + *.db \ No newline at end of file diff --git a/README.md b/README.md index e00e2f0..97126b3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,16 @@ # EF_WebAPI -This repository make a meeting of EF and WebAPI parts. \ No newline at end of file +This repository make a meeting of EF and WebAPI parts. + +FROM /src dir + +do + +```bash +dotnet ef migrations add --project StubbedContextLib/StubbedContextLib.csproj --startup-project HeartTrackAPI/HeartTrackAPI.csproj --context StubbedContextLib.TrainingStubbedContext --configuration Debug Initial --output-dir Migrations +``` +then + +```bash +dotnet ef database update --project StubbedContextLib/StubbedContextLib.csproj --startup-project HeartTrackAPI/HeartTrackAPI.csproj --context StubbedContextLib.TrainingStubbedContext --configuration Debug +``` diff --git a/src/APIMappers/APIMappers.csproj b/src/APIMappers/APIMappers.csproj new file mode 100644 index 0000000..3bb04a5 --- /dev/null +++ b/src/APIMappers/APIMappers.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/src/APIMappers/ActivityMapper.cs b/src/APIMappers/ActivityMapper.cs new file mode 100644 index 0000000..6013e57 --- /dev/null +++ b/src/APIMappers/ActivityMapper.cs @@ -0,0 +1,53 @@ +using Dto; +using Model; +using Shared; + +namespace APIMappers; + +public static class ActivityMapper +{ + private static GenericMapper _mapper = new GenericMapper(); + + public static ActivityDto ToDto(this Activity activity) + { + return activity.ToU(_mapper, activityDto => new ActivityDto + { + Id = activity.Id, + Type = activity.Type, + Date = activity.Date, + StartTime = activity.StartTime, + EndTime = activity.EndTime, + EffortFelt = activity.Effort, + Variability = activity.Variability, + Variance = activity.Variance, + StandardDeviation = activity.StandardDeviation, + Average = activity.Average, + Maximum = activity.Maximum, + Minimum = activity.Minimum, + AverageTemperature = activity.AverageTemperature, + HasAutoPause = activity.HasAutoPause + }); + } + + public static Activity ToModel(this ActivityDto activityDto) + { + return activityDto.ToT(_mapper, activity => new Activity + { + Id = activityDto.Id, + Type = activityDto.Type, + Date = activityDto.Date, + StartTime = activityDto.StartTime, + EndTime = activityDto.EndTime, + Effort = activityDto.EffortFelt, + Variability = activityDto.Variability, + Variance = activityDto.Variance, + StandardDeviation = activityDto.StandardDeviation, + Average = activityDto.Average, + Maximum = activityDto.Maximum, + Minimum = activityDto.Minimum, + AverageTemperature = activityDto.AverageTemperature, + HasAutoPause = activityDto.HasAutoPause + }); + } + +} \ No newline at end of file diff --git a/src/APIMappers/UserMappeur.cs b/src/APIMappers/UserMappeur.cs new file mode 100644 index 0000000..48a8cda --- /dev/null +++ b/src/APIMappers/UserMappeur.cs @@ -0,0 +1,51 @@ +using Dto; +using Model; +using Shared; + +namespace APIMappers; + +public static class UserMappeur +{ + private static GenericMapper _mapper = new GenericMapper(); + + public static UserDto ToDto(this User user) + { + return user.ToU(_mapper, userDto => new UserDto + { + Id = user.Id, + Username = user.Username, + ProfilePicture = user.ProfilePicture, + LastName = user.LastName, + FirstName = user.FirstName, + Email = user.Email, + Password = user.MotDePasse, + Sexe = user.Sexe, + Lenght = user.Lenght, + Weight = user.Weight, + DateOfBirth = user.DateOfBirth, + IsCoach = user.Role is Coach + }); + + } + + + public static User ToModel(this UserDto userDto) + { + return userDto.ToT(_mapper, user => new User + { + Username = userDto.Username, + ProfilePicture = userDto.ProfilePicture, + LastName = userDto.LastName, + FirstName = userDto.FirstName, + Email = userDto.Email, + MotDePasse = userDto.Password, + Sexe = userDto.Sexe, + Lenght = userDto.Lenght, + Weight = userDto.Weight, + DateOfBirth = userDto.DateOfBirth, + Role = userDto.IsCoach ? new Coach() : new Athlete() + + }); + } + +} \ No newline at end of file diff --git a/src/DbContextLib/DbContextLib.csproj b/src/DbContextLib/DbContextLib.csproj index 928ae7a..cd47dbc 100644 --- a/src/DbContextLib/DbContextLib.csproj +++ b/src/DbContextLib/DbContextLib.csproj @@ -6,13 +6,15 @@ enable - - - + + + + + - - + + diff --git a/src/DbContextLib/HeartTrackContext.cs b/src/DbContextLib/HeartTrackContext.cs index 5dbb584..1ce7564 100644 --- a/src/DbContextLib/HeartTrackContext.cs +++ b/src/DbContextLib/HeartTrackContext.cs @@ -55,13 +55,31 @@ namespace DbContextLib /// /// Initializes a new instance of the class. /// - public HeartTrackContext() : base() { } + public HeartTrackContext() : base() + { } /// /// Initializes a new instance of the class with the specified options. /// /// The options for the context. - public HeartTrackContext(DbContextOptions options) : base(options) { } + public HeartTrackContext(DbContextOptions options) : base(options) + { } + + public HeartTrackContext(string dbPlatformPath) + : this(InitPlaformDb(dbPlatformPath)) + { + } + + private static DbContextOptions InitPlaformDb(string dbPlatformPath) + { + var options = new DbContextOptionsBuilder() + .UseMySql($"{dbPlatformPath}", new MySqlServerVersion(new Version(10, 11, 1))) + .Options; + return options; + } + + + /// /// Configures the database options if they are not already configured. diff --git a/src/DbContextLib/Identity/AuthDbContext.cs b/src/DbContextLib/Identity/AuthDbContext.cs new file mode 100644 index 0000000..89924a7 --- /dev/null +++ b/src/DbContextLib/Identity/AuthDbContext.cs @@ -0,0 +1,25 @@ +using Entities; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; + +namespace DbContextLib.Identity; + +public class AuthDbContext: IdentityDbContext +{ + + public AuthDbContext(DbContextOptions options) : base(options) { } + public AuthDbContext() { } + /* + /// + /// Configures the database options if they are not already configured. + /// + /// The options builder instance. + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + if (!optionsBuilder.IsConfigured) + { + optionsBuilder.UseSqlite($"Data Source=uca.HeartTrack.db"); + } + }*/ +} \ No newline at end of file diff --git a/src/Dto/ActivityDto.cs b/src/Dto/ActivityDto.cs index d4bf5f1..fcb108c 100644 --- a/src/Dto/ActivityDto.cs +++ b/src/Dto/ActivityDto.cs @@ -3,17 +3,17 @@ namespace Dto; public class ActivityDto { public int Id { get; set; } - public string Name { get; set; } - public string Type { get; set; } + public string? Type { get; set; } public DateTime Date { get; set; } - public TimeSpan Duration { get; set; } - public float Distance { get; set; } - public float Elevation { get; set; } - public float AverageSpeed { get; set; } - public int AverageHeartRate { get; set; } - public int Calories { get; set; } - public string Description { get; set; } - public string? Gpx { get; set; } - public string? Image { get; set; } - public int AthleteId { get; set; } + public DateTime StartTime { get; set; } + public DateTime EndTime { get; set; } + public int EffortFelt { get; set; } + public float Variability { get; set; } + public float Variance { get; set; } + public float StandardDeviation { get; set; } + public float Average { get; set; } + public int Maximum { get; set; } + public int Minimum { get; set; } + public float AverageTemperature { get; set; } + public bool HasAutoPause { get; set; } } \ No newline at end of file diff --git a/src/Dto/ActivityFitFileDto.cs b/src/Dto/ActivityFitFileDto.cs new file mode 100644 index 0000000..f68fbd1 --- /dev/null +++ b/src/Dto/ActivityFitFileDto.cs @@ -0,0 +1,16 @@ +namespace Dto; + +public class ActivityFitFileDto +{ + public ActivityFitFileDto(string activityName, string activityType, int effortFeel) + { + ActivityName = activityName; + ActivityType = activityType; + EffortFeel = effortFeel; + } + + public string ActivityName { get; set; } + public string ActivityType { get; set; } + public int EffortFeel { get; set; } + //public IFormFile +} \ No newline at end of file diff --git a/src/EFMappers/AthleteMappeur.cs b/src/EFMappers/AthleteMappeur.cs index 219305d..48dea10 100644 --- a/src/EFMappers/AthleteMappeur.cs +++ b/src/EFMappers/AthleteMappeur.cs @@ -1,10 +1,13 @@ -using Dto; +using System.Buffers; +using Dto; using Model; namespace ApiMappeur; -// anotine public static class UserMappeur { + private static readonly ArrayPool UserDtoPool = ArrayPool.Create(); + private static readonly Dictionary userToDtoMap = new Dictionary(); + private static readonly Dictionary dtoToUserMap = new Dictionary(); public static UserDto ToDto(this User user) { return new UserDto @@ -26,8 +29,71 @@ public static class UserMappeur public static User ToModel(this UserDto userDto) { - if (userDto.IsCoach) + return new User { + Username = userDto.Username, + ProfilePicture = userDto.ProfilePicture, + LastName = userDto.LastName, + FirstName = userDto.FirstName, + Email = userDto.Email, + MotDePasse = userDto.Password, + Sexe = userDto.Sexe, + Lenght = userDto.Lenght, + Weight = userDto.Weight, + DateOfBirth = userDto.DateOfBirth, + Role = userDto.IsCoach ? new Coach() : new Athlete() + + }; + } +} +/* +using Dto; +using Model; +using System.Buffers; + +namespace ApiMappeur +{ + // anotine + public static class UserMappeur + { + private static readonly ArrayPool UserDtoPool = ArrayPool.Create(); + + public static UserDto ToDto(this User user) + { + UserDto userDto = UserDtoPool.Rent(); + userDto.Id = user.Id; + userDto.Username = user.Username; + userDto.ProfilePicture = user.ProfilePicture; + userDto.LastName = user.LastName; + userDto.FirstName = user.FirstName; + userDto.Email = user.Email; + userDto.Password = user.MotDePasse; + userDto.Sexe = user.Sexe; + userDto.Lenght = user.Lenght; + userDto.Weight = user.Weight; + userDto.DateOfBirth = user.DateOfBirth; + userDto.IsCoach = user.Role is Coach; + return userDto; + } + + public static User ToModel(this UserDto userDto) + { + if (userDto.IsCoach) + { + return new User( + userDto.Username, + userDto.ProfilePicture, + userDto.LastName, + userDto.FirstName, + userDto.Email, + userDto.Password, + userDto.Sexe, + userDto.Lenght, + userDto.Weight, + userDto.DateOfBirth, + new Coach() + ); + } return new User( userDto.Username, userDto.ProfilePicture, @@ -39,20 +105,13 @@ public static class UserMappeur userDto.Lenght, userDto.Weight, userDto.DateOfBirth, - new Coach() - ); + new Athlete()); + } + + public static void ReturnToPool(this UserDto userDto) + { + UserDtoPool.Return(userDto); } - return new User( - userDto.Username, - userDto.ProfilePicture, - userDto.LastName, - userDto.FirstName, - userDto.Email, - userDto.Password, - userDto.Sexe, - userDto.Lenght, - userDto.Weight, - userDto.DateOfBirth, - new Athlete()); } -} \ No newline at end of file +} +*/ \ No newline at end of file diff --git a/src/Entities/ActivityEntity.cs b/src/Entities/ActivityEntity.cs index ab26369..5dc7761 100644 --- a/src/Entities/ActivityEntity.cs +++ b/src/Entities/ActivityEntity.cs @@ -29,7 +29,7 @@ namespace Entities /// [Required] [MaxLength(100)] - public string Type { get; set; } = null!; + public string? Type { get; set; } = null!; /// /// Gets or sets the date of the activity. diff --git a/src/Entities/AthleteEntity.cs b/src/Entities/AthleteEntity.cs index cc883d6..6d6f351 100644 --- a/src/Entities/AthleteEntity.cs +++ b/src/Entities/AthleteEntity.cs @@ -15,8 +15,10 @@ namespace Entities /// Represents an athlete entity in the database. /// [Table("Athlete")] - public class AthleteEntity + public class AthleteEntity { + public AthleteEntity() : base() { } + /// /// Gets or sets the unique identifier of the athlete. /// @@ -28,35 +30,35 @@ namespace Entities /// Gets or sets the username of the athlete. /// [MaxLength(100)] - [Required(ErrorMessage = "Athlete Username is required")] + [Required(ErrorMessage = "Athlete Username is ")] public required string Username { get; set; } /// /// Gets or sets the last name of the athlete. /// [MaxLength(100)] - [Required(ErrorMessage = "Athlete Last Name is required")] + [Required(ErrorMessage = "Athlete Last Name is ")] public required string LastName { get; set; } /// /// Gets or sets the first name of the athlete. /// [MaxLength(150)] - [Required(ErrorMessage = "Athlete First Name is required")] + [Required(ErrorMessage = "Athlete First Name is ")] public required string FirstName { get; set; } /// /// Gets or sets the email of the athlete. /// [MaxLength(100)] - [Required(ErrorMessage = "Athlete Email is required")] + [Required(ErrorMessage = "Athlete Email is ")] public required string Email { get; set; } /// /// Gets or sets the gender of the athlete. /// [MaxLength(1)] - [Required(ErrorMessage = "Athlete Sexe is required")] + [Required(ErrorMessage = "Athlete Sexe is ")] public required string Sexe { get; set; } /// @@ -72,13 +74,13 @@ namespace Entities /// /// Gets or sets the password of the athlete. /// - [Required(ErrorMessage = "Athlete Password is required")] + [Required(ErrorMessage = "Athlete Password is ")] public required string Password { get; set; } /// /// Gets or sets the date of birth of the athlete. /// - [Required(ErrorMessage = "Athlete Date of Birth is required")] + [Required(ErrorMessage = "Athlete Date of Birth is ")] [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)] public DateOnly DateOfBirth { get; set; } @@ -87,7 +89,7 @@ namespace Entities /// public bool IsCoach { get; set; } - public required byte[] ProfilPicture { get; set; } + public byte[]? ProfilPicture { get; set; } diff --git a/src/Entities/Entities.csproj b/src/Entities/Entities.csproj index bb23fb7..b007a4d 100644 --- a/src/Entities/Entities.csproj +++ b/src/Entities/Entities.csproj @@ -5,5 +5,4 @@ enable enable - diff --git a/src/HeartTrack.sln b/src/HeartTrack.sln index 57167ac..8947e4c 100644 --- a/src/HeartTrack.sln +++ b/src/HeartTrack.sln @@ -3,52 +3,49 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DbContextLib", "DbContextLib\DbContextLib.csproj", "{330A5DA0-0B4E-48D4-9AF3-6DCB39EE9622}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DbContextLib", "DbContextLib\DbContextLib.csproj", "{330A5DA0-0B4E-48D4-9AF3-6DCB39EE9622}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Entities", "Entities\Entities.csproj", "{A07F86B3-555B-4B35-8BB1-25E844825A38}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Entities", "Entities\Entities.csproj", "{A07F86B3-555B-4B35-8BB1-25E844825A38}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StubbedContextLib", "StubbedContextLib\StubbedContextLib.csproj", "{2F44DE6E-EFFC-42FE-AFF6-79CDC762E6D8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StubbedContextLib", "StubbedContextLib\StubbedContextLib.csproj", "{2F44DE6E-EFFC-42FE-AFF6-79CDC762E6D8}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleTestEntities", "Tests\ConsoleTestEntities\ConsoleTestEntities.csproj", "{477D2129-A6C9-4FF8-8BE9-5E9E8E5282F8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTestEntities", "Tests\ConsoleTestEntities\ConsoleTestEntities.csproj", "{477D2129-A6C9-4FF8-8BE9-5E9E8E5282F8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleTestRelationships", "Tests\ConsoleTestRelationships\ConsoleTestRelationships.csproj", "{2D166FAD-4934-474B-96A8-6C0635156EC2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTestRelationships", "Tests\ConsoleTestRelationships\ConsoleTestRelationships.csproj", "{2D166FAD-4934-474B-96A8-6C0635156EC2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dto", "Dto\Dto.csproj", "{562019BC-0F61-41B0-9BAE-259B92C6BFBA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dto", "Dto\Dto.csproj", "{562019BC-0F61-41B0-9BAE-259B92C6BFBA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeartTrackAPI", "HeartTrackAPI\HeartTrackAPI.csproj", "{C1C2EAC3-3347-466B-BFB6-2A9F11A3AE12}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HeartTrackAPI", "HeartTrackAPI\HeartTrackAPI.csproj", "{C1C2EAC3-3347-466B-BFB6-2A9F11A3AE12}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "Shared\Shared.csproj", "{F80C60E1-1E06-46C2-96DE-42B1C7DE65BC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "Shared\Shared.csproj", "{F80C60E1-1E06-46C2-96DE-42B1C7DE65BC}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestsAPI", "TestsAPI", "{30FC2BE9-7397-445A-84AD-043CE70F4281}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientTests", "Tests\TestsAPI\ClientTests\ClientTests.csproj", "{9E4D3AC5-E6CA-4753-BD96-BF5EE793931A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClientTests", "Tests\TestsAPI\ClientTests\ClientTests.csproj", "{9E4D3AC5-E6CA-4753-BD96-BF5EE793931A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestsXUnit", "Tests\TestsAPI\TestsXUnit\TestsXUnit.csproj", "{44C367DC-5FE0-4CF2-9E76-A0282E931853}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "Model\Model.csproj", "{30AB7FAA-6072-40B6-A15E-9188B59144F9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "Model\Model.csproj", "{30AB7FAA-6072-40B6-A15E-9188B59144F9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTestApi", "Tests\TestsAPI\UnitTestApi\UnitTestApi.csproj", "{E515C8B6-6282-4D8B-8523-7B3A13E4AF58}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestApi", "Tests\TestsAPI\UnitTestApi\UnitTestApi.csproj", "{E515C8B6-6282-4D8B-8523-7B3A13E4AF58}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTestsEntities", "Tests\UnitTestsEntities\UnitTestsEntities.csproj", "{31FA8E5E-D642-4C43-A2B2-02B9832B2CEC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestsEntities", "Tests\UnitTestsEntities\UnitTestsEntities.csproj", "{31FA8E5E-D642-4C43-A2B2-02B9832B2CEC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model2Entities", "Model2Entities\Model2Entities.csproj", "{FA329DEF-4756-4A8B-84E9-5A625FF94CBF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model2Entities", "Model2Entities\Model2Entities.csproj", "{FA329DEF-4756-4A8B-84E9-5A625FF94CBF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StubAPI", "StubAPI\StubAPI.csproj", "{C9BD0310-DC18-4356-B8A7-2B6959AF7813}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StubAPI", "StubAPI\StubAPI.csproj", "{C9BD0310-DC18-4356-B8A7-2B6959AF7813}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTestEFMapper", "Tests\ConsoleTestEFMapper\ConsoleTestEFMapper.csproj", "{73EA27F2-9F0C-443F-A5EE-2960C983A422}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleTestEFMapper", "Tests\ConsoleTestEFMapper\ConsoleTestEFMapper.csproj", "{73EA27F2-9F0C-443F-A5EE-2960C983A422}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFMappers", "EFMappers\EFMappers.csproj", "{9397795D-F482-44C4-8443-A20AC26671AA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFMappers", "EFMappers\EFMappers.csproj", "{9397795D-F482-44C4-8443-A20AC26671AA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "APIMappers", "APIMappers\APIMappers.csproj", "{41D18203-1688-43BD-A3AC-FD0C2BD81909}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {330A5DA0-0B4E-48D4-9AF3-6DCB39EE9622}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {330A5DA0-0B4E-48D4-9AF3-6DCB39EE9622}.Debug|Any CPU.Build.0 = Debug|Any CPU @@ -86,22 +83,10 @@ Global {9E4D3AC5-E6CA-4753-BD96-BF5EE793931A}.Debug|Any CPU.Build.0 = Debug|Any CPU {9E4D3AC5-E6CA-4753-BD96-BF5EE793931A}.Release|Any CPU.ActiveCfg = Release|Any CPU {9E4D3AC5-E6CA-4753-BD96-BF5EE793931A}.Release|Any CPU.Build.0 = Release|Any CPU - {44C367DC-5FE0-4CF2-9E76-A0282E931853}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {44C367DC-5FE0-4CF2-9E76-A0282E931853}.Debug|Any CPU.Build.0 = Debug|Any CPU - {44C367DC-5FE0-4CF2-9E76-A0282E931853}.Release|Any CPU.ActiveCfg = Release|Any CPU - {44C367DC-5FE0-4CF2-9E76-A0282E931853}.Release|Any CPU.Build.0 = Release|Any CPU {30AB7FAA-6072-40B6-A15E-9188B59144F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {30AB7FAA-6072-40B6-A15E-9188B59144F9}.Debug|Any CPU.Build.0 = Debug|Any CPU {30AB7FAA-6072-40B6-A15E-9188B59144F9}.Release|Any CPU.ActiveCfg = Release|Any CPU {30AB7FAA-6072-40B6-A15E-9188B59144F9}.Release|Any CPU.Build.0 = Release|Any CPU - {CB142F6B-0FF1-45B3-AB46-6F8DCD096C20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CB142F6B-0FF1-45B3-AB46-6F8DCD096C20}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CB142F6B-0FF1-45B3-AB46-6F8DCD096C20}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CB142F6B-0FF1-45B3-AB46-6F8DCD096C20}.Release|Any CPU.Build.0 = Release|Any CPU - {B9679DCA-F4C8-45BE-A849-44E2BA814083}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B9679DCA-F4C8-45BE-A849-44E2BA814083}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B9679DCA-F4C8-45BE-A849-44E2BA814083}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B9679DCA-F4C8-45BE-A849-44E2BA814083}.Release|Any CPU.Build.0 = Release|Any CPU {E515C8B6-6282-4D8B-8523-7B3A13E4AF58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E515C8B6-6282-4D8B-8523-7B3A13E4AF58}.Debug|Any CPU.Build.0 = Debug|Any CPU {E515C8B6-6282-4D8B-8523-7B3A13E4AF58}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -118,31 +103,32 @@ Global {C9BD0310-DC18-4356-B8A7-2B6959AF7813}.Debug|Any CPU.Build.0 = Debug|Any CPU {C9BD0310-DC18-4356-B8A7-2B6959AF7813}.Release|Any CPU.ActiveCfg = Release|Any CPU {C9BD0310-DC18-4356-B8A7-2B6959AF7813}.Release|Any CPU.Build.0 = Release|Any CPU - {06DBE9E4-6AA5-4D09-8544-D3ED91E2D980}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {06DBE9E4-6AA5-4D09-8544-D3ED91E2D980}.Debug|Any CPU.Build.0 = Debug|Any CPU - {06DBE9E4-6AA5-4D09-8544-D3ED91E2D980}.Release|Any CPU.ActiveCfg = Release|Any CPU - {06DBE9E4-6AA5-4D09-8544-D3ED91E2D980}.Release|Any CPU.Build.0 = Release|Any CPU {73EA27F2-9F0C-443F-A5EE-2960C983A422}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {73EA27F2-9F0C-443F-A5EE-2960C983A422}.Debug|Any CPU.Build.0 = Debug|Any CPU {73EA27F2-9F0C-443F-A5EE-2960C983A422}.Release|Any CPU.ActiveCfg = Release|Any CPU {73EA27F2-9F0C-443F-A5EE-2960C983A422}.Release|Any CPU.Build.0 = Release|Any CPU - {C9C9F2A5-9132-4067-B240-B299D2FCF4E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C9C9F2A5-9132-4067-B240-B299D2FCF4E9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C9C9F2A5-9132-4067-B240-B299D2FCF4E9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C9C9F2A5-9132-4067-B240-B299D2FCF4E9}.Release|Any CPU.Build.0 = Release|Any CPU {9397795D-F482-44C4-8443-A20AC26671AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9397795D-F482-44C4-8443-A20AC26671AA}.Debug|Any CPU.Build.0 = Debug|Any CPU {9397795D-F482-44C4-8443-A20AC26671AA}.Release|Any CPU.ActiveCfg = Release|Any CPU {9397795D-F482-44C4-8443-A20AC26671AA}.Release|Any CPU.Build.0 = Release|Any CPU + {41D18203-1688-43BD-A3AC-FD0C2BD81909}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {41D18203-1688-43BD-A3AC-FD0C2BD81909}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41D18203-1688-43BD-A3AC-FD0C2BD81909}.Release|Any CPU.ActiveCfg = Release|Any CPU + {41D18203-1688-43BD-A3AC-FD0C2BD81909}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {477D2129-A6C9-4FF8-8BE9-5E9E8E5282F8} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18} {2D166FAD-4934-474B-96A8-6C0635156EC2} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18} {30FC2BE9-7397-445A-84AD-043CE70F4281} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18} {9E4D3AC5-E6CA-4753-BD96-BF5EE793931A} = {30FC2BE9-7397-445A-84AD-043CE70F4281} - {44C367DC-5FE0-4CF2-9E76-A0282E931853} = {30FC2BE9-7397-445A-84AD-043CE70F4281} {E515C8B6-6282-4D8B-8523-7B3A13E4AF58} = {30FC2BE9-7397-445A-84AD-043CE70F4281} {31FA8E5E-D642-4C43-A2B2-02B9832B2CEC} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18} {73EA27F2-9F0C-443F-A5EE-2960C983A422} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0F3487F4-66CA-4034-AC66-1BC899C9B523} + EndGlobalSection EndGlobal diff --git a/src/HeartTrackAPI/AppBootstrap.cs b/src/HeartTrackAPI/AppBootstrap.cs new file mode 100644 index 0000000..fdbe6a0 --- /dev/null +++ b/src/HeartTrackAPI/AppBootstrap.cs @@ -0,0 +1,224 @@ +using System.Reflection; +using DbContextLib; +using DbContextLib.Identity; +using Entities; +using HeartTrackAPI.Utils; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.Versioning; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Microsoft.OpenApi.Models; +using Model.Manager; +using Model2Entities; +using StubAPI; +using Swashbuckle.AspNetCore.SwaggerGen; + + +namespace HeartTrackAPI; + +public class AppBootstrap(IConfiguration configuration) +{ + private IConfiguration Configuration { get; } = configuration; + + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + services.AddEndpointsApiExplorer(); + AddSwagger(services); + // include Xml comment + // addsecurityRequiment + // securityDef + AddHeartTrackContextServices(services); + AddModelService(services); + AddIdentityServices(services); + AddApiVersioning(services); + + services.AddHealthChecks(); + + } + + private void AddHeartTrackContextServices(IServiceCollection services) + { + string connectionString; + + switch (Environment.GetEnvironmentVariable("TYPE")) + { + case "BDD": + var HOST = System.Environment.GetEnvironmentVariable("HOST"); + var PORT = System.Environment.GetEnvironmentVariable("PORTDB"); + var DATABASE = System.Environment.GetEnvironmentVariable("DATABASE"); + var USERNAME = System.Environment.GetEnvironmentVariable("USERNAME"); + var PASSWORD = System.Environment.GetEnvironmentVariable("PASSWORD"); + + connectionString = $"Server={HOST};port={PORT};database={DATABASE};user={USERNAME};password={PASSWORD}"; + Console.WriteLine(connectionString); + Console.WriteLine("======================"); + Console.WriteLine($"server={HOST};port={PORT};database={DATABASE};user={USERNAME};password={PASSWORD}"); + Console.WriteLine(connectionString); + services.AddDbContext(options => + options.UseMySql($"{connectionString}", new MySqlServerVersion(new Version(10, 11, 1))) + , ServiceLifetime.Singleton); + break; + default: + connectionString = Configuration.GetConnectionString("HeartTrackAuthConnection"); + if (!string.IsNullOrWhiteSpace(connectionString)) + { + services.AddDbContext(options => options.UseInMemoryDatabase("AuthDb")); + Console.WriteLine(connectionString); + Console.WriteLine("======================"); + //options => options.UseSqlite(connectionString) + //services.AddDbContext(); + services.AddDbContext(options => + options.UseSqlite(connectionString), ServiceLifetime.Singleton); + } + else + { + services.AddDbContext(options => options.UseInMemoryDatabase("AuthDb")); + services.AddDbContext(options => options.UseInMemoryDatabase("HeartTrackDb")); + } + break; + + } + +/* + services.AddSingleton>(provider => + { + var connection = new SqliteConnection("DataSource=:memory:"); + connection.Open(); + + var options = new DbContextOptionsBuilder() + .UseSqlite(connection) + .Options; + + return options; + });*/ + } + + private void AddModelService(IServiceCollection services) + { + //services.AddSingleton(provider => new DbDataManager(provider.GetService())); + //services.AddSingleton(); + services.AddSingleton(provider => new DbDataManager(provider.GetRequiredService())); + + //services.AddTransient(); + } + + private void AddIdentityServices(IServiceCollection services) + { +// services.AddTransient, EmailSender>(); + services.AddAuthorization(); + + services.AddIdentityApiEndpoints() + .AddEntityFrameworkStores(); + //services.AddIdentity() + // .AddEntityFrameworkStores().AddDefaultTokenProviders(); + } + + private void AddApiVersioning(IServiceCollection services) + { + + services.AddApiVersioning(opt => + { + opt.ReportApiVersions = true; + opt.AssumeDefaultVersionWhenUnspecified = true; + opt.DefaultApiVersion = new Microsoft.AspNetCore.Mvc.ApiVersion(1, 0); +// options.ApiVersionReader = new HeaderApiVersionReader("api-version"); + + opt.ApiVersionReader = ApiVersionReader.Combine(new UrlSegmentApiVersionReader(), + new HeaderApiVersionReader("x-api-version"), + new MediaTypeApiVersionReader("x-api-version")); + }); + + } + private void AddSwagger(IServiceCollection services) + { + services.AddSwaggerGen(options => + { + options.OperationFilter(); + + var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + options.IncludeXmlComments(xmlPath); + + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = + "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.ApiKey + }); + var scheme = new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + }, + Scheme = "oauth2", + Name = "Bearer", + In = ParameterLocation.Header, + }, + new List() + } + }; + options.AddSecurityRequirement(scheme); + }); + services.AddTransient, SwaggerOptions>(); + services.AddSwaggerGen(options => + { + options.OperationFilter(); + }); + /* services.AddSwaggerGen(options => + { + options.SwaggerDoc("v1", new OpenApiInfo { Title = "HeartTrackAPI", Version = "v1" }); + options.SwaggerDoc("v2", new OpenApiInfo { Title = "HeartTrackAPI", Version = "v2" }); + });*/ + + services.AddVersionedApiExplorer(setup => + { + setup.GroupNameFormat = "'v'VVV"; + setup.SubstituteApiVersionInUrl = true; + }); + + } + + public void Configure(WebApplication app, IWebHostEnvironment env) + { + app.UseHttpsRedirection(); + app.MapIdentityApi(); + + app.MapControllers(); + app.UseAuthorization(); + + app.MapHealthChecks("/health"); + + // Configure the HTTP request pipeline. + if (true) + { + var apiVersionDescriptionProvider = app.Services.GetRequiredService(); + app.UseSwagger(); + app.UseSwaggerUI(); + app.MapSwagger(); + app.UseSwaggerUI(options => + { + foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions) + + //foreach (var description in apiVersionDescriptionProvider) + { + options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", + description.GroupName.ToUpperInvariant()); + } + }); + + } + + + } +} diff --git a/src/HeartTrackAPI/Controllers/ActivityController.cs b/src/HeartTrackAPI/Controllers/ActivityController.cs index 5aaf722..d20e167 100644 --- a/src/HeartTrackAPI/Controllers/ActivityController.cs +++ b/src/HeartTrackAPI/Controllers/ActivityController.cs @@ -1,22 +1,25 @@ +using APIMappers; using Dto; using HeartTrackAPI.Request; using HeartTrackAPI.Responce; using Microsoft.AspNetCore.Mvc; -using Shared; using Model; +using Shared; +using Model.Manager; using Model.Repository; namespace HeartTrackAPI.Controllers; [ApiController] -[Route("api/activities")] +[ApiVersion("1.0")] +[Route("api/v{version:apiVersion}/[controller]")] public class ActivityController : Controller { private readonly IActivityRepository _activityService; private readonly ILogger _logger; - public ActivityController(IActivityRepository activityService, ILogger logger) + public ActivityController(IDataManager dataManager, ILogger logger) { - _activityService = activityService; + _activityService = dataManager.ActivityRepo; _logger = logger; } @@ -36,9 +39,12 @@ public class ActivityController : Controller } _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(GetActivities), pageRequest); var activities = await _activityService.GetActivities(pageRequest.Index, pageRequest.Count, ActivityOrderCriteria.None, pageRequest.Descending ?? false); - // var pageResponse = new PageResponse(pageRequest.Index, pageRequest.Count, totalCount, activities.Select(a => a.ToDto())); - // return Ok(pageResponse); - return Ok(); + if(activities == null) + { + return NotFound("No activities found"); + } + var pageResponse = new PageResponse(pageRequest.Index, pageRequest.Count, totalCount, activities.Select(a => a.ToDto())); + return Ok(pageResponse); } catch (Exception e) { @@ -46,7 +52,64 @@ public class ActivityController : Controller return StatusCode(500); } } -/* + /* + [HttpPost] + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status415UnsupportedMediaType)] + [MultipartFormData] + [DisableFormValueModelBinding] + public async Task PostFitFile( Stream file, string contentType) // [FromForm] + { + if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) + { + ModelState.AddModelError("File", + $"The request couldn't be processed (Error 1)."); + // Log error + + return BadRequest(ModelState); + } + if (file == null) + { + return BadRequest("No file was provided"); + } + //var fileUploadSummary = await _fileService.UploadFileAsync(HttpContext.Request.Body, Request.ContentType); +// var activity = await _activityManager.AddActivityFromFitFile(file); + var activity = new Activity + { + Id = 1, + Type = "Running", + Date = new DateTime(2021, 10, 10), + StartTime = new DateTime(2021, 10, 10, 10, 0, 0), + EndTime = new DateTime(2021, 10, 10, 11, 0, 0), + Effort = 3, + Variability = 0.5f, + Variance = 0.5f, + StandardDeviation = 0.5f, + Average = 5.0f, + Maximum = 10, + Minimum = 0, + AverageTemperature = 20.0f, + HasAutoPause = false, + Users = + { + new User + { + Id = 3, Username = "Athlete3", + ProfilePicture = + "https://plus.unsplash.com/premium_photo-1705091981693-6006f8a20479?q=80&w=1974&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + FirstName = "First3", LastName = "Last3", + Sexe = "M", Lenght = 190, Weight = 80, DateOfBirth = new DateTime(1994, 3, 3), Email = "ath@ex.fr", + Role = new Athlete() + } + } + }; + if (activity == null) + { + return BadRequest("The file provided is not a valid fit file"); + } + return CreatedAtAction(nameof(GetActivity), new { id = activity.Id }, activity.ToDto()); + }*/ + [HttpGet("{id}")] public async Task> GetActivity(int id) { @@ -58,18 +121,6 @@ public class ActivityController : Controller return Ok(activity.ToDto()); } - [HttpPost] - public async Task> PostActivity(ActivityDto activityDto) - { - var activity = activityDto.ToModel(); - var result = await _activityService.AddActivity(activity); - if (result == null) - { - return BadRequest(); - } - return CreatedAtAction(nameof(GetActivity), new { id = result.Id }, result.ToDto()); - } - [HttpPut("{id}")] public async Task PutActivity(int id, ActivityDto activityDto) { @@ -95,5 +146,35 @@ public class ActivityController : Controller return NotFound(); } return NoContent(); + } + +/* + public async Task UploadFileAsync(Stream fileStream, string contentType) + { + var fileCount = 0; + long totalSizeInBytes = 0; + var boundary = GetBoundary(MediaTypeHeaderValue.Parse(contentType)); + var multipartReader = new MultipartReader(boundary, fileStream); + var section = await multipartReader.ReadNextSectionAsync(); + var filePaths = new List(); + var notUploadedFiles = new List(); + + while (section != null) + { + var fileSection = section.AsFileSection(); + if (fileSection != null) + { + totalSizeInBytes += await SaveFileAsync(fileSection, filePaths, notUploadedFiles); + fileCount++; + } + section = await multipartReader.ReadNextSectionAsync(); + } + return new FileUploadSummary + { + TotalFilesUploaded = fileCount, + TotalSizeUploaded = ConvertSizeToString(totalSizeInBytes), + FilePaths = filePaths, + NotUploadedFiles = notUploadedFiles + }; }*/ } \ No newline at end of file diff --git a/src/HeartTrackAPI/Controllers/UsersController.cs b/src/HeartTrackAPI/Controllers/UsersController.cs index a8d0a09..69d2bc9 100644 --- a/src/HeartTrackAPI/Controllers/UsersController.cs +++ b/src/HeartTrackAPI/Controllers/UsersController.cs @@ -1,26 +1,42 @@ +using System.ComponentModel.DataAnnotations; +using APIMappers; using Dto; using HeartTrackAPI.Request; using HeartTrackAPI.Responce; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; -using Model; using Model.Manager; using Model.Repository; using Shared; namespace HeartTrackAPI.Controllers; - +/// +/// Contrôle les actions liées aux utilisateurs dans l'application HeartTrack. +/// Gère les opérations CRUD sur les utilisateurs, leurs amis, et leurs activités. +/// [ApiController] -[Route("api/users")] +[ApiVersion("1.0")] +[Route("api/v{version:apiVersion}/[controller]")] public class UsersController : Controller { private readonly ILogger _logger; + private readonly IActivityRepository _activityService; private readonly IUserRepository _userService; public UsersController(ILogger logger, IDataManager dataManager) { _logger = logger; _userService = dataManager.UserRepo; + _activityService = dataManager.ActivityRepo; } - + + /// + /// Récupère une page d'utilisateurs en fonction des critères de pagination et de tri fournis. + /// + /// Les critères de pagination et de tri pour les utilisateurs. + /// Une page de données utilisateur selon les critères spécifiés. + /// Retourne la page demandée d'utilisateurs. + /// La demande de pagination est invalide. + /// Erreur interne du serveur. [HttpGet] [ProducesResponseType(typeof(PageResponse), 200)] [ProducesResponseType(400)] @@ -39,22 +55,29 @@ public class UsersController : Controller _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(Get), null); var athletes = await _userService.GetUsers(request.Index, request.Count, Enum.TryParse(request.OrderingPropertyName, out AthleteOrderCriteria result) ? result : AthleteOrderCriteria.None, request.Descending ?? false); - // var pageResponse = new PageResponse(request.Index, request.Count, totalCount, athletes.Select(a => a.ToDto())); - // return Ok(pageResponse); - return Ok(); + var pageResponse = new PageResponse(request.Index, request.Count, totalCount, athletes!.Select(a => a.ToDto())); + return Ok(pageResponse); } catch (Exception e) { _logger.LogError(e, "Error while getting all athletes"); - return StatusCode(500); + return Problem(); } } + /// + /// Récupère un utilisateur spécifique par son identifiant. + /// + /// L'identifiant de l'utilisateur à récupérer. + /// L'utilisateur correspondant à l'identifiant spécifié. + /// Retourne l'utilisateur demandé. + /// Aucun utilisateur trouvé pour l'identifiant spécifié. + /// Erreur interne du serveur. [HttpGet("{id}")] [ProducesResponseType(typeof(UserDto), 200)] [ProducesResponseType(404)] [ProducesResponseType(500)] - public async Task> GetById(int id) + public async Task> GetById([Range(0,int.MaxValue)]int id) { try { @@ -65,17 +88,21 @@ public class UsersController : Controller _logger.LogError("Athlete with id {id} not found", id); return NotFound($"Athlete with id {id} not found"); } - // return Ok(athlete.ToDto()); - return Ok(); + return Ok(athlete.ToDto()); } catch (Exception e) { _logger.LogError(e, "Error while getting athlete by id {id}", id); - return StatusCode(500); + return Problem(); } } - + /// + /// Obtient le nombre total d'utilisateurs. + /// + /// Le nombre total d'utilisateurs. + /// Retourne le nombre total d'utilisateurs. + /// Erreur interne du serveur. [HttpGet("count")] [ProducesResponseType(typeof(int), 200)] [ProducesResponseType(500)] @@ -90,10 +117,19 @@ public class UsersController : Controller catch (Exception e) { _logger.LogError(e, "Error while getting the number of users"); - return StatusCode(500); + return Problem(); } } + /// + /// Met à jour les informations d'un utilisateur spécifique. + /// + /// L'identifiant de l'utilisateur à mettre à jour. + /// Les données de l'utilisateur pour la mise à jour. + /// L'utilisateur mis à jour. + /// Retourne l'utilisateur mis à jour. + /// Utilisateur non trouvé. + /// Erreur interne du serveur. [HttpPut("{id}")] [ProducesResponseType(typeof(UserDto), 200)] [ProducesResponseType(404)] @@ -109,23 +145,30 @@ public class UsersController : Controller _logger.LogError("Athlete with id {id} not found", id); return NotFound($"Athlete with id {id} not found"); } - // var updatedAthlete = await _userService.UpdateItem(id, user.ToModel()); - // if(updatedAthlete == null) - // { - // _logger.LogError("Error while updating athlete with id {id}", id); - // return StatusCode(500); - // } - // return Ok(updatedAthlete.ToDto()); - return Ok(); + var updatedAthlete = await _userService.UpdateItem(id, user.ToModel()); + if(updatedAthlete == null) + { + _logger.LogError("Error while updating athlete with id {id}", id); + return Problem(); + } + return Ok(updatedAthlete.ToDto()); } catch (Exception e) { _logger.LogError(e, "Error while getting the number of users"); - return StatusCode(500); + return Problem(); } } + /// + /// Supprime un utilisateur spécifique. + /// + /// L'identifiant de l'utilisateur à supprimer. + /// Action result. + /// Utilisateur supprimé avec succès. + /// Utilisateur non trouvé. + /// Erreur interne du serveur. [HttpDelete("{id}")] [ProducesResponseType(200)] [ProducesResponseType(404)] @@ -147,16 +190,256 @@ public class UsersController : Controller if(!isDeleted) { _logger.LogError("Error while deleting athlete with id {id}", id); - return StatusCode(500); + return Problem(); } return Ok(); } catch (Exception e) { _logger.LogError(e, "Error while getting the number of users"); - return StatusCode(500); + return Problem(); } } - + /// + /// Obtient la liste des amis d'un utilisateur spécifique. + /// + /// L'identifiant de l'utilisateur. + /// Les critères de pagination et de tri. + /// La liste paginée des amis. + /// Retourne la liste paginée des amis de l'utilisateur. + /// Utilisateur non trouvé. + /// Erreur interne du serveur. + [HttpGet("{id}/friends")] + [ProducesResponseType(typeof(PageResponse), 200)] + [ProducesResponseType(404)] + [ProducesResponseType(500)] + public async Task>> GetFriends(int id, [FromQuery] PageRequest request) + { + try + { + _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(GetFriends), null,id); + var athlete = await _userService.GetItemById(id); + if (athlete == null) + { + _logger.LogError("Athlete with id {id} not found", id); + return NotFound($"Athlete with id {id} not found"); + } + var totalCount = await _userService.GetNbFriends(athlete); + if (request.Count * request.Index >= totalCount) + { + _logger.LogError("To many object is asked the max is {totalCount} but the request is superior of ", totalCount); + return BadRequest("To many object is asked the max is : " + totalCount); + } + var friends = await _userService.GetFriends(athlete, request.Index, request.Count, Enum.TryParse(request.OrderingPropertyName, out AthleteOrderCriteria result) ? result : AthleteOrderCriteria.None, request.Descending ?? false); + if (friends == null) return NotFound(); + var pageResponse = new PageResponse(request.Index, request.Count, totalCount, friends.Select(a => a.ToDto())); + return Ok(pageResponse); + } + catch (Exception e) + { + _logger.LogError(e, "Error while getting the number of users"); + return Problem(); + } + } + + /// + /// Ajoute un ami à un utilisateur spécifique. + /// + /// L'identifiant de l'utilisateur. + /// L'identifiant de l'ami à ajouter. + /// Action result. + /// Ami ajouté avec succès. + /// Utilisateur ou ami non trouvé. + /// Erreur interne du serveur. + [HttpPost("{id}/friend/{friendId}")] + [ProducesResponseType(200)] + [ProducesResponseType(404)] + [ProducesResponseType(500)] + public async Task AddFriend(int id, int friendId) + { + try + { + _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(AddFriend), friendId,id); + var athlete = await _userService.GetItemById(id); + if (athlete == null) + { + _logger.LogError("Athlete with id {id} not found", id); + return NotFound($"Athlete with id {id} not found"); + } + var friend = await _userService.GetItemById(friendId); + if (friend == null) + { + _logger.LogError("Athlete with id {id} not found", friendId); + return NotFound($"Athlete with id {friendId} not found"); + } + var isAdded = await _userService.AddFriend(athlete, friend); + if(!isAdded) + { + _logger.LogError("Error while adding friend with id {friendId} to athlete with id {id}", friendId, id); + return Problem(); + } + return Ok(); + } + catch (Exception e) + { + _logger.LogError(e, "Error while getting the number of users"); + return Problem(); + } + } + + + /// + /// Supprime un ami d'un utilisateur spécifique. + /// + /// L'identifiant de l'utilisateur. + /// L'identifiant de l'ami à supprimer. + /// Action result. + /// Ami supprimé avec succès. + /// Utilisateur ou ami non trouvé. + /// Erreur interne du serveur. + [HttpDelete("{id}/friend/{friendId}")] + [ProducesResponseType(200)] + [ProducesResponseType(404)] + [ProducesResponseType(500)] + public async Task RemoveFriend(int id, int friendId) + { + try + { + _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(RemoveFriend), friendId,id); + var athlete = await _userService.GetItemById(id); + if (athlete == null) + { + _logger.LogError("Athlete with id {id} not found", id); + return NotFound($"Athlete with id {id} not found"); + } + var friend = await _userService.GetItemById(friendId); + if (friend == null) + { + _logger.LogError("Athlete with id {id} not found", friendId); + return NotFound($"Athlete with id {friendId} not found"); + } + var isRemoved = await _userService.RemoveFriend(athlete, friend); + if(!isRemoved) + { + _logger.LogError("Error while removing friend with id {friendId} to athlete with id {id}", friendId, id); + return Problem(); + } + return Ok(); + } + catch (Exception e) + { + _logger.LogError(e, "Error while getting the number of users"); + return Problem(); + } + } + + + // #[TODO] [Dave] ou faire un get qui si le role est coach resend les athletes et si le role est athlete resend les coach + /// + /// Obtient la liste des athlètes d'un coach spécifique. + /// + /// L'identifiant du coach. + /// Les critères de pagination et de tri. + /// La liste paginée des athlètes. + /// Retourne la liste paginée des athlètes du coach. + /// Coach non trouvé. + /// Erreur interne du serveur. + [HttpGet("{coachId}/athletes")] + [ProducesResponseType(typeof(PageResponse), 200)] + [ProducesResponseType(404)] + [ProducesResponseType(500)] + public async Task>> GetAthletes(int coachId, [FromQuery] PageRequest request) + { + try + { + _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(GetAthletes), null,coachId); + var coach = await _userService.GetItemById(coachId); + if (coach == null) + { + _logger.LogError("Athlete with id {id} not found", coachId); + return NotFound($"Athlete with id {coachId} not found"); + } + var totalCount = await _userService.GetNbFriends(coach); + if (request.Count * request.Index >= totalCount) + { + _logger.LogError("To many object is asked the max is {totalCount} but the request is superior of ", totalCount); + return BadRequest("To many object is asked the max is : " + totalCount); + } + var athletes = await _userService.GetFriends(coach, request.Index, request.Count, Enum.TryParse(request.OrderingPropertyName, out AthleteOrderCriteria result) ? result : AthleteOrderCriteria.None, request.Descending ?? false); + if (athletes == null) return NotFound(); + var pageResponse = new PageResponse(request.Index, request.Count, totalCount, athletes.Select(a => a.ToDto())); + return Ok(pageResponse); + } + catch (Exception e) + { + _logger.LogError(e, "Error while getting the number of users"); + return Problem(); + } + } + + /// + /// Obtient la liste des activités d'un utilisateur spécifique. + /// + /// L'identifiant de l'utilisateur. + /// Les critères de pagination et de tri. + /// La liste paginée des activités de l'utilisateur. + /// Retourne la liste paginée des activités. + /// Aucune activité trouvée. + /// Erreur interne du serveur. + [HttpGet("{userId}/activities")] + // should be tiny DTOActivity returned with only the necessary information (will be used in the list of activities of a user) + public async Task>> GetActivitiesByUser(int userId, [FromQuery] PageRequest pageRequest) + { + try + { + var totalCount = await _activityService.GetNbActivitiesByUser(userId); + if (pageRequest.Count * pageRequest.Index >= totalCount) + { + _logger.LogError("To many object is asked the max is {totalCount} but the request is superior of ", totalCount); + return BadRequest("To many object is asked the max is : " + totalCount); + } + _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(GetActivitiesByUser), pageRequest); + var activities = await _activityService.GetActivitiesByUser(userId, pageRequest.Index, pageRequest.Count, ActivityOrderCriteria.None, pageRequest.Descending ?? false); + if(activities == null) + { + return NotFound("No activities found"); + } + var pageResponse = new PageResponse(pageRequest.Index, pageRequest.Count, totalCount, activities.Select(a => a.ToDto())); + return Ok(pageResponse); + } + catch (Exception e) + { + _logger.LogError(e, "Error while getting all activities"); + return Problem(); + } + } + /* [TODO] [Dave] + [HttpGet("{userId}/trainings")] + [ProducesResponseType(typeof(PageResponse), 200)] + [ProducesResponseType(404)] + [ProducesResponseType(500)] + public async Task> GetTrainings(int userId, [FromQuery] PageRequest request) + */ + + /// + /// Déconnecte l'utilisateur actuel. + /// + /// Le gestionnaire de connexion. + /// Paramètre vide utilisé pour s'assurer que la requête provient bien d'un client. + /// Action result. + /// Déconnexion réussie. + /// Déconnexion non autorisée. + /// Erreur interne du serveur. + [HttpPost("logout")] + [ProducesResponseType(200)] + [ProducesResponseType(401)] + [ProducesResponseType(500)] + public async Task Logout(SignInManager signInManager, [FromBody] object? empty) + { + if (empty == null) return Unauthorized(); + await signInManager.SignOutAsync(); + return Ok(); + } + } \ No newline at end of file diff --git a/src/HeartTrackAPI/Dockerfile b/src/HeartTrackAPI/Dockerfile index c8f3264..a00c03b 100644 --- a/src/HeartTrackAPI/Dockerfile +++ b/src/HeartTrackAPI/Dockerfile @@ -12,25 +12,34 @@ COPY ["StubbedContextLib/StubbedContextLib.csproj", "StubbedContextLib/"] COPY ["Shared/Shared.csproj", "Shared/"] COPY ["Entities/Entities.csproj", "Entities/"] COPY ["Dto/Dto.csproj", "Dto/"] -COPY ["ApiMappeur/ApiMappeur.csproj", "ApiMappeur/"] +COPY ["APIMappers/APIMappers.csproj", "APIMappers/"] +COPY ["EFMappers/EFMappers.csproj", "EFMappers/"] COPY ["DbContextLib/DbContextLib.csproj", "DbContextLib/"] COPY ["Model/Model.csproj", "Model/"] COPY ["Model2Entities/Model2Entities.csproj", "Model2Entities/"] COPY ["StubAPI/StubAPI.csproj", "StubAPI/"] COPY ["StubbedContextLib/StubbedContextLib.csproj", "StubbedContextLib/"] - RUN dotnet restore "HeartTrackAPI/HeartTrackAPI.csproj" COPY . . +RUN dotnet tool install --global dotnet-ef --version 8.0 + +ENV PATH="${PATH}:/root/.dotnet/tools" +# Add the migrations +RUN dotnet-ef migrations add --project StubbedContextLib/StubbedContextLib.csproj --startup-project HeartTrackAPI/HeartTrackAPI.csproj --context StubbedContextLib.TrainingStubbedContext --configuration Debug Initial +# Update the database +RUN dotnet-ef database update --project StubbedContextLib/StubbedContextLib.csproj --startup-project HeartTrackAPI/HeartTrackAPI.csproj --context StubbedContextLib.TrainingStubbedContext --configuration Debug +RUN chmod 777 HeartTrackAPI/uca.HeartTrack.db + WORKDIR "/src/HeartTrackAPI" -RUN ls RUN dotnet build "HeartTrackAPI.csproj" -c $BUILD_CONFIGURATION -o /app/build FROM build AS publish ARG BUILD_CONFIGURATION=Release RUN dotnet publish "HeartTrackAPI.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false - FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -RUN ls +COPY --from=publish /src/HeartTrackAPI/uca.HeartTrack.db . +RUN ls -l uca.HeartTrack.db + ENTRYPOINT ["dotnet", "HeartTrackAPI.dll"] \ No newline at end of file diff --git a/src/HeartTrackAPI/FileUploadSummary.cs b/src/HeartTrackAPI/FileUploadSummary.cs new file mode 100644 index 0000000..1721ae7 --- /dev/null +++ b/src/HeartTrackAPI/FileUploadSummary.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.ModelBinding; + +namespace HeartTrackAPI; + +public class FileUploadSummary +{ + public int TotalFilesUploaded { get; set; } + public string TotalSizeUploaded { get; set; } + public IList FilePaths { get; set; } = new List(); + public IList NotUploadedFiles { get; set; } = new List(); +} +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] +public class MultipartFormDataAttribute : ActionFilterAttribute +{ + public override void OnActionExecuting(ActionExecutingContext context) + { + var request = context.HttpContext.Request; + + if (request.HasFormContentType + && request.ContentType.StartsWith("multipart/form-data", StringComparison.OrdinalIgnoreCase)) + { + return; + } + + context.Result = new StatusCodeResult(StatusCodes.Status415UnsupportedMediaType); + } +} +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] +public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter +{ + public void OnResourceExecuting(ResourceExecutingContext context) + { + var factories = context.ValueProviderFactories; + factories.RemoveType(); + factories.RemoveType(); + factories.RemoveType(); + } + + public void OnResourceExecuted(ResourceExecutedContext context) + { + } +} \ No newline at end of file diff --git a/src/HeartTrackAPI/HeartTrackAPI.csproj b/src/HeartTrackAPI/HeartTrackAPI.csproj index 2c5e31d..d8fac4c 100644 --- a/src/HeartTrackAPI/HeartTrackAPI.csproj +++ b/src/HeartTrackAPI/HeartTrackAPI.csproj @@ -5,24 +5,31 @@ enable enable true + true + $(NoWarn);1591 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + - - - - ..\..\..\..\..\.nuget\packages\newtonsoft.json\13.0.1\lib\netstandard2.0\Newtonsoft.Json.dll - - - + diff --git a/src/HeartTrackAPI/HeartTrackAPI.http b/src/HeartTrackAPI/HeartTrackAPI.http deleted file mode 100644 index 97c9a67..0000000 --- a/src/HeartTrackAPI/HeartTrackAPI.http +++ /dev/null @@ -1,6 +0,0 @@ -@HeartTrackAPI_HostAddress = http://localhost:5030 - -GET {{HeartTrackAPI_HostAddress}}/weatherforecast/ -Accept: application/json - -### diff --git a/src/HeartTrackAPI/Program.cs b/src/HeartTrackAPI/Program.cs index b43dfbe..04e64af 100644 --- a/src/HeartTrackAPI/Program.cs +++ b/src/HeartTrackAPI/Program.cs @@ -1,25 +1,22 @@ -using Model; -using Model.Manager; -using Model.Repository; -using StubAPI; +using DbContextLib; +using HeartTrackAPI; var builder = WebApplication.CreateBuilder(args); -// Add services to the container. -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); -builder.Services.AddControllers(); -builder.Services.AddSingleton(); -var app = builder.Build(); +builder.Logging.AddConsole(); +builder.WebHost.ConfigureKestrel(serverOptions => +{ + serverOptions.Limits.MaxRequestBodySize = long.MaxValue; +}); + +var init = new AppBootstrap(builder.Configuration); -// Configure the HTTP request pipeline. +init.ConfigureServices(builder.Services); - app.UseSwagger(); - app.UseSwaggerUI(); +var app = builder.Build(); +init.Configure(app, app.Environment); -app.UseHttpsRedirection(); +app.Services.GetService()!.Database.EnsureCreated(); -app.MapControllers(); app.Run(); \ No newline at end of file diff --git a/src/HeartTrackAPI/Request/PageRequest.cs b/src/HeartTrackAPI/Request/PageRequest.cs index 9fad40d..a4399f4 100644 --- a/src/HeartTrackAPI/Request/PageRequest.cs +++ b/src/HeartTrackAPI/Request/PageRequest.cs @@ -1,10 +1,16 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc.ModelBinding; + namespace HeartTrackAPI.Request; public class PageRequest { - - public string? OrderingPropertyName { get; set; } = null;// need to be map on the dto OrderCriteria + public string? OrderingPropertyName { get; set; } = null; public bool? Descending { get; set; } = false; + +// [Range(0, int.MaxValue, ErrorMessage = "Count must be greater than 0")] public int Index { get; set; } = 0; - public int Count { get; set; } = 5; + +// [Range(0, int.MaxValue, ErrorMessage = "Count must be greater than 0")] + public int Count { get; set; } = 1; } diff --git a/src/HeartTrackAPI/Utils/SwaggerDefaultValues.cs b/src/HeartTrackAPI/Utils/SwaggerDefaultValues.cs new file mode 100644 index 0000000..dd8671c --- /dev/null +++ b/src/HeartTrackAPI/Utils/SwaggerDefaultValues.cs @@ -0,0 +1,53 @@ +using System.Text.Json; +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace HeartTrackAPI.Utils; + +public class SwaggerDefaultValues : IOperationFilter +{ + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + var apiDescription = context.ApiDescription; + operation.Deprecated |= apiDescription.IsDeprecated(); + + foreach (var responseType in context.ApiDescription.SupportedResponseTypes) + { + var responseKey = responseType.IsDefaultResponse ? "default" : responseType.StatusCode.ToString(); + var response = operation.Responses[responseKey]; + + foreach (var contentType in response.Content.Keys) + { + if (responseType.ApiResponseFormats.All(x => x.MediaType != contentType)) + { + response.Content.Remove(contentType); + } + } + } + + if (operation.Parameters == null) + { + return; + } + + foreach (var parameter in operation.Parameters) + { + var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); + + parameter.Description ??= description.ModelMetadata?.Description; + + if (parameter.Schema.Default == null && + description.DefaultValue != null && + description.DefaultValue is not DBNull && + description.ModelMetadata is ModelMetadata modelMetadata) + { + var json = JsonSerializer.Serialize(description.DefaultValue, modelMetadata.ModelType); + parameter.Schema.Default = OpenApiAnyFactory.CreateFromJson(json); + } + + parameter.Required |= description.IsRequired; + } + } +} \ No newline at end of file diff --git a/src/HeartTrackAPI/Utils/SwaggerOptions.cs b/src/HeartTrackAPI/Utils/SwaggerOptions.cs new file mode 100644 index 0000000..02c92d8 --- /dev/null +++ b/src/HeartTrackAPI/Utils/SwaggerOptions.cs @@ -0,0 +1,68 @@ +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.Extensions.Options; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +namespace HeartTrackAPI.Utils; + +public class SwaggerOptions: IConfigureNamedOptions + + { + private readonly IApiVersionDescriptionProvider _provider; + + public SwaggerOptions( + IApiVersionDescriptionProvider provider) + { + _provider = provider; + } + + /// + /// Configure each API discovered for Swagger Documentation + /// + /// + public void Configure(SwaggerGenOptions options) + { + // add swagger document for every API version discovered + foreach (var description in _provider.ApiVersionDescriptions) + { + options.SwaggerDoc( + description.GroupName, + CreateVersionInfo(description)); + } + } + + /// + /// Configure Swagger Options. Inherited from the Interface + /// + /// + /// + public void Configure(string? name, SwaggerGenOptions options) + { + Configure(options); + } + + /// + /// Create information about the version of the API + /// + /// + /// Information about the API + private OpenApiInfo CreateVersionInfo( + ApiVersionDescription desc) + { + var info = new OpenApiInfo() + { + Title = "Web API For HeartTrack .NET 8", + Version = desc.ApiVersion.ToString(), + Description = "The HeartTrack project API, aims to provide an Open Source solution for heart rate data analysis.", + Contact = new OpenApiContact { Name = "HeartTrackDev", Email = "toto@toto.fr" }, + License = new OpenApiLicense { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") } + + }; + + if (desc.IsDeprecated) + { + info.Description += " This API version has been deprecated. Please use one of the new APIs available from the explorer."; + } + + return info; + } + } diff --git a/src/HeartTrackAPI/appsettings.Development.json b/src/HeartTrackAPI/appsettings.Development.json index ff66ba6..c603414 100644 --- a/src/HeartTrackAPI/appsettings.Development.json +++ b/src/HeartTrackAPI/appsettings.Development.json @@ -4,5 +4,8 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } + }, + "ConnectionStrings": { + "HeartTrackAuthConnection": "Data Source=uca.HeartTrack.db" } } diff --git a/src/HeartTrackAPI/appsettings.json b/src/HeartTrackAPI/appsettings.json index 4d56694..952a46c 100644 --- a/src/HeartTrackAPI/appsettings.json +++ b/src/HeartTrackAPI/appsettings.json @@ -5,5 +5,8 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "ConnectionStrings": { + "HeartTrackAuthConnection": "Data Source=uca.HeartTrack.db" + } } diff --git a/src/Model/Activity.cs b/src/Model/Activity.cs index 06801bf..cef25b7 100644 --- a/src/Model/Activity.cs +++ b/src/Model/Activity.cs @@ -4,7 +4,7 @@ namespace Model; public class Activity { public int Id { get; set; } - public string Type { get; set; } + public string? Type { get; set; } public DateTime Date { get; set; } public DateTime StartTime { get; set; } public DateTime EndTime { get; set; } @@ -31,8 +31,9 @@ public class Activity public int Minimum { get; set; } public float AverageTemperature { get; set; } public bool HasAutoPause { get; set; } - - public Activity(int idActivity ,string type, DateTime date, DateTime startTime, DateTime endTime, + + public HashSet Users { get; private set; } = new HashSet(); + public Activity(int idActivity ,string? type, DateTime date, DateTime startTime, DateTime endTime, int effort, float variability, float variance, float standardDeviation, float average, int maximum, int minimum, float averageTemperature, bool hasAutoPause) { diff --git a/src/Model/Manager/ActivityManager.cs b/src/Model/Manager/ActivityManager.cs new file mode 100644 index 0000000..6cd1ef1 --- /dev/null +++ b/src/Model/Manager/ActivityManager.cs @@ -0,0 +1,9 @@ +namespace Model.Manager; + +public class ActivityManager : IActivityManager +{ + public void AddActivityFromFitFile(byte filePath) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/Model/Manager/Contract/IActivityManager.cs b/src/Model/Manager/Contract/IActivityManager.cs new file mode 100644 index 0000000..2be28cc --- /dev/null +++ b/src/Model/Manager/Contract/IActivityManager.cs @@ -0,0 +1,6 @@ +namespace Model.Manager; + +public interface IActivityManager +{ + public void AddActivityFromFitFile(byte filePath); +} \ No newline at end of file diff --git a/src/Model/Manager/UserManager.cs b/src/Model/Manager/UserManager.cs new file mode 100644 index 0000000..d1b697a --- /dev/null +++ b/src/Model/Manager/UserManager.cs @@ -0,0 +1,6 @@ +namespace Model.Manager; + +public class UserManager +{ + +} \ No newline at end of file diff --git a/src/Model/Model.csproj b/src/Model/Model.csproj index 18de4eb..ead9f5a 100644 --- a/src/Model/Model.csproj +++ b/src/Model/Model.csproj @@ -7,7 +7,12 @@ - + + + + + + diff --git a/src/Model/Repository/IActivityRepository.cs b/src/Model/Repository/IActivityRepository.cs index 258bbd9..eab97da 100644 --- a/src/Model/Repository/IActivityRepository.cs +++ b/src/Model/Repository/IActivityRepository.cs @@ -4,10 +4,12 @@ namespace Model.Repository; public interface IActivityRepository { - public Task> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false); + public Task?> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false); public Task GetActivityByIdAsync(int id); public Task AddActivity(Activity activity); public Task UpdateActivity(int id, Activity activity); public Task DeleteActivity(int id); public Task GetNbItems(); + public Task?> GetActivitiesByUser(int userId, int index, int count, ActivityOrderCriteria orderCriteria, bool descending= false); + public Task GetNbActivitiesByUser(int userId); } \ No newline at end of file diff --git a/src/Model/Repository/IUserRepository.cs b/src/Model/Repository/IUserRepository.cs index d6d528c..6ba01b9 100644 --- a/src/Model/Repository/IUserRepository.cs +++ b/src/Model/Repository/IUserRepository.cs @@ -4,5 +4,14 @@ namespace Model.Repository; public interface IUserRepository : IGenericRepository { - public Task> GetUsers(int index, int count, AthleteOrderCriteria? criteria , bool descending = false); + public Task?> GetUsers(int index, int count, AthleteOrderCriteria? criteria , bool descending = false); + + public Task AddFriend(User user, User friend); + + public Task RemoveFriend(User user, User friend); + + // should be removed cause i just have to call the GetItem then get the friends + public Task?> GetFriends(User user, int index, int count, AthleteOrderCriteria? criteria, bool descending = false); + + public Task GetNbFriends(User user); } \ No newline at end of file diff --git a/src/Model/User.cs b/src/Model/User.cs index 6efffa8..0991ca5 100644 --- a/src/Model/User.cs +++ b/src/Model/User.cs @@ -8,7 +8,7 @@ public class User public string LastName { get; set; } public string FirstName { get; set; } public string Email { get; set; } - public string MotDePasse { get; set; } + public string? MotDePasse { get; set; } public string Sexe { get; set; } public float Lenght { get; set; } public float Weight { get; set; } @@ -17,6 +17,8 @@ public class User protected List Notifications { get; set; } = new List(); + public List Users { get; set; } = new List(); + public User( string username, string profilePicture, string nom, string prenom, string email, string motDePasse, string sexe, float taille, float poids, DateTime dateNaissance, Role role) { Username = username; diff --git a/src/Model2Entities/ActivityRepository.cs b/src/Model2Entities/ActivityRepository.cs index facb34c..845d6bb 100644 --- a/src/Model2Entities/ActivityRepository.cs +++ b/src/Model2Entities/ActivityRepository.cs @@ -38,7 +38,7 @@ public partial class DbDataManager : IDataManager { _logger.LogInformation($"GetActivityByIdAsync with id {id}", id); // ! By None don't pass the filter - var activity = _dataManager.DbContext.ActivitiesSet.GetItemsWithFilterAndOrdering(b => b.IdActivity == id, 0, 1, ActivityOrderCriteria.ByType, false).First().ToModel(); + var activity = _dataManager.DbContext.ActivitiesSet.GetItemsWithFilterAndOrdering(b => b.IdActivity == id, 0, 1, ActivityOrderCriteria.None, false).First().ToModel(); if (activity != null) _logger.LogInformation($"Retrieved activity with ID {id}"); @@ -121,5 +121,15 @@ public partial class DbDataManager : IDataManager throw; } } + + public Task> GetActivitiesByUser(int userId, int index, int count, ActivityOrderCriteria criteria, bool descending = false) + { + throw new NotImplementedException(); + } + + public Task GetNbActivitiesByUser(int userId) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/src/Model2Entities/DbDataManager.cs b/src/Model2Entities/DbDataManager.cs index 52f441f..6384b43 100644 --- a/src/Model2Entities/DbDataManager.cs +++ b/src/Model2Entities/DbDataManager.cs @@ -10,22 +10,30 @@ public partial class DbDataManager: IDataManager { public IActivityRepository ActivityRepo { get; } public IUserRepository UserRepo { get; } - protected HeartTrackContext DbContext { get; } // mettre si pb lors d'une requete si rollback ou pas public DbDataManager(HeartTrackContext dbContext) { DbContext = dbContext; + Console.WriteLine("Contexttttttttt"); + Console.WriteLine($"Database created Context: {DbContext.Database.EnsureCreated()}"); ActivityRepo = new ActivityRepository(this); UserRepo = new UserRepository(this); ActivityMapper.Reset(); // Faire pour les autres reset() des autres mappers } + + public DbDataManager(string dbPlatformPath) + : this(new HeartTrackContext(dbPlatformPath)) + { + Console.WriteLine($"Database created String: {DbContext.Database.EnsureCreated()}"); } + public DbDataManager() { DbContext = new HeartTrackContext(); + Console.WriteLine($"Database created None: {DbContext.Database.EnsureCreated()}"); ActivityRepo = new ActivityRepository(this); UserRepo= new UserRepository(this); } diff --git a/src/Model2Entities/Extension.cs b/src/Model2Entities/Extension.cs index 24c1e2b..ea02563 100644 --- a/src/Model2Entities/Extension.cs +++ b/src/Model2Entities/Extension.cs @@ -7,28 +7,25 @@ namespace Model2Entities; public static class Extensions { - internal static Task AddItem(this HeartTrackContext context, T? item) where T :class + internal static async Task AddItem(this HeartTrackContext context, T? item) where T :class { if(item == null || context.Set().Contains(item)) { - return Task.FromResult(default(T)); + return await Task.FromResult(null); } - context.Set().Add(item); - context.SaveChangesAsync(); + var entry = context.Set().Add(item); + await context.SaveChangesAsync(); - return Task.FromResult(item); + return await Task.FromResult(entry.Entity); } - internal static Task DeleteItem(this HeartTrackContext context, int? id) where T:class + internal static async Task DeleteItem(this HeartTrackContext context, int? id) where T:class { - var item = context.Set().Find(id); - if(item == null) - { - return Task.FromResult(false); - } + var item = await context.Set().FindAsync(id); + if(item == null) return await Task.FromResult(false); context.Set().Remove(item); - context.SaveChangesAsync(); - return Task.FromResult(true); + await context.SaveChangesAsync(); + return await Task.FromResult(true); } internal static async Task UpdateItem(this IList collection, T? oldItem, T? newItem) where T : class @@ -50,10 +47,11 @@ public static class Extensions { var filteredList = list.Where(filter); - if(orderCriterium != null) + if(orderCriterium != null && orderCriterium.ToString() != "None") { filteredList = filteredList.OrderByCriteria(orderCriterium, descending); - } + } + return filteredList .Skip(index * count) .Take(count); diff --git a/src/Model2Entities/Model2Entities.csproj b/src/Model2Entities/Model2Entities.csproj index bb068f4..d298015 100644 --- a/src/Model2Entities/Model2Entities.csproj +++ b/src/Model2Entities/Model2Entities.csproj @@ -10,6 +10,14 @@ + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/src/Model2Entities/UserRepository.cs b/src/Model2Entities/UserRepository.cs index e9716f1..7791636 100644 --- a/src/Model2Entities/UserRepository.cs +++ b/src/Model2Entities/UserRepository.cs @@ -49,5 +49,25 @@ public partial class DbDataManager { throw new NotImplementedException(); } + + public async Task AddFriend(User user, User friend) + { + throw new NotImplementedException(); + } + + public async Task RemoveFriend(User user, User friend) + { + throw new NotImplementedException(); + } + + public Task?>? GetFriends(User user, int index, int count, AthleteOrderCriteria? criteria, bool descending = false) + { + throw new NotImplementedException(); + } + + public Task GetNbFriends(User user) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/src/Shared/Extension.cs b/src/Shared/Extension.cs index 121352c..6301ad7 100644 --- a/src/Shared/Extension.cs +++ b/src/Shared/Extension.cs @@ -2,7 +2,7 @@ namespace Shared; public static class Extensions { - public static U? ToU(this T t, GenericMapper mapper, Func func) where U :class where T :class + public static U ToU(this T t, GenericMapper mapper, Func func) where U :class where T :class { var u = mapper.GetU(t); if (u != null) { @@ -14,7 +14,7 @@ public static class Extensions return u; } // , Action action - public static T? ToT(this U u, GenericMapper mapper, Func func) where U :class where T :class + public static T ToT(this U u, GenericMapper mapper, Func func) where U :class where T :class { var t = mapper.GetT(u); if (t != null) { diff --git a/src/StubAPI/ActivityService.cs b/src/StubAPI/ActivityService.cs index 0e87004..2b274ae 100644 --- a/src/StubAPI/ActivityService.cs +++ b/src/StubAPI/ActivityService.cs @@ -6,33 +6,73 @@ namespace StubAPI; public class ActivityService: IActivityRepository { - public async Task> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false) - { - throw new NotImplementedException(); - } + private List _activities = new List( + new Activity[] + { + new Activity + { + Id = 1, + Type = "Running", + Date = new DateTime(2021, 10, 10), + StartTime = new DateTime(2021, 10, 10, 10, 0, 0), + EndTime = new DateTime(2021, 10, 10, 11, 0, 0), + Effort = 3, + Variability = 0.5f, + Variance = 0.5f, + StandardDeviation = 0.5f, + Average = 5.0f, + Maximum = 10, + Minimum = 0, + AverageTemperature = 20.0f, + HasAutoPause = false, + Users = {new User + { + Id = 3, Username = "Athlete3", ProfilePicture = "https://plus.unsplash.com/premium_photo-1705091981693-6006f8a20479?q=80&w=1974&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", FirstName = "First3", LastName = "Last3", + Sexe = "M", Lenght = 190, Weight = 80, DateOfBirth = new DateTime(1994, 3, 3), Email = "ath@ex.fr", + Role = new Athlete() + }} + }, + } + ); + + public async Task?> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false) + => await Task.FromResult(_activities.GetItemsWithFilterAndOrdering(c=>true,index, count,criteria != ActivityOrderCriteria.None ? criteria: null , descending)); - public async Task GetActivityByIdAsync(int id) + public Task GetActivityByIdAsync(int id) { - throw new NotImplementedException(); + return Task.FromResult(_activities.FirstOrDefault(s => s.Id == id)); } - public async Task AddActivity(Activity activity) + public Task AddActivity(Activity activity) + => _activities.AddItem(activity); + + + public async Task UpdateActivity(int id, Activity activity) { - throw new NotImplementedException(); + var oldActivity = _activities.FirstOrDefault(s => s.Id == id); + if (oldActivity == null) return null; + return await _activities.UpdateItem(oldActivity, activity); } - public async Task UpdateActivity(int id, Activity activity) + public Task DeleteActivity(int id) { - throw new NotImplementedException(); + var activity = _activities.FirstOrDefault(s => s.Id == id); + if (activity == null) return Task.FromResult(false); + return _activities.DeleteItem(activity); } - public async Task DeleteActivity(int id) + public Task GetNbItems() + => Task.FromResult(_activities.Count); + + public async Task?> GetActivitiesByUser(int userId, int index, int count, ActivityOrderCriteria criteria, bool descending = false) { - throw new NotImplementedException(); + var activities = _activities.GetItemsWithFilterAndOrdering(c => c.Users.Any(u => u.Id == userId), index, count, + criteria != ActivityOrderCriteria.None ? criteria : null, descending); + return await Task.FromResult(activities); } - public async Task GetNbItems() + public Task GetNbActivitiesByUser(int userId) { - throw new NotImplementedException(); + return Task.FromResult(_activities.Count(a => a.Users.Any(u => u.Id == userId))); } } \ No newline at end of file diff --git a/src/StubAPI/AthleteService.cs b/src/StubAPI/AthleteService.cs index b1b1a28..7729296 100644 --- a/src/StubAPI/AthleteService.cs +++ b/src/StubAPI/AthleteService.cs @@ -34,14 +34,53 @@ public class UserService : IUserRepository public async Task> GetUsers(int index, int count, AthleteOrderCriteria? orderingProperty = null, bool descending = false) => athletes.GetItemsWithFilterAndOrdering(c=>true,index, count,orderingProperty != AthleteOrderCriteria.None ? orderingProperty: null , descending); - public async Task> GetItems(int index, int count, string? orderingProperty = null, - bool descending = false) + public async Task AddFriend(User user, User friend) { + if (user == null || friend == null) + { + return false; + } + + if (user.Users.Contains(friend)) + { + return false; + } - return await this.GetUsers(index, count, this.ToEnum(orderingProperty), descending); + user.Users.Add(friend); + return true; } + public async Task RemoveFriend(User user, User friend) + { + if (user == null || friend == null) + { + return false; + } + + if (!user.Users.Contains(friend)) + { + return false; + } + + user.Users.Remove(friend); + + return true; + } + + public async Task?>? GetFriends(User user, int index, int count, AthleteOrderCriteria? criteria, bool descending = false) + =>await Task.FromResult(athletes.FirstOrDefault(s => s.Id == user.Id)?.Users.GetItemsWithFilterAndOrdering(c=>true,index, count,criteria, descending)); + + public Task GetNbFriends(User user) + { + return Task.FromResult(athletes.FirstOrDefault(s => s.Id == user.Id)?.Users.Count ?? 0); + } + + public async Task> GetItems(int index, int count, string? orderingProperty = null, + bool descending = false) + =>await GetUsers(index, count, this.ToEnum(orderingProperty), descending); + + public async Task GetItemById(int id) =>await Task.FromResult(athletes.FirstOrDefault(s => s.Id == id)); diff --git a/src/StubAPI/Extensions.cs b/src/StubAPI/Extensions.cs index 9917cac..af6f004 100644 --- a/src/StubAPI/Extensions.cs +++ b/src/StubAPI/Extensions.cs @@ -39,13 +39,15 @@ public static class Extensions public static IEnumerable GetItemsWithFilterAndOrdering(this IEnumerable list, Func filter, int index, int count, Enum? orderCriterium, bool descending = false ) where T : class { - var filteredList = list.Where(filter); + IEnumerable query = list; + + query = query.Where(filter); if(orderCriterium != null) { - filteredList = filteredList.OrderByCriteria(orderCriterium, descending); + query = query.OrderByCriteria(orderCriterium, descending); } - return filteredList + return query .Skip(index * count) .Take(count); } diff --git a/src/StubbedContextLib/StubbedContextLib.csproj b/src/StubbedContextLib/StubbedContextLib.csproj index ecea2c4..0c5f479 100644 --- a/src/StubbedContextLib/StubbedContextLib.csproj +++ b/src/StubbedContextLib/StubbedContextLib.csproj @@ -1,16 +1,20 @@  - - - + + + - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + diff --git a/src/Tests/TestsAPI/TestsXUnit/GlobalUsings.cs b/src/Tests/TestsAPI/TestsXUnit/GlobalUsings.cs deleted file mode 100644 index 8c927eb..0000000 --- a/src/Tests/TestsAPI/TestsXUnit/GlobalUsings.cs +++ /dev/null @@ -1 +0,0 @@ -global using Xunit; \ No newline at end of file diff --git a/src/Tests/TestsAPI/TestsXUnit/UnitTest1.cs b/src/Tests/TestsAPI/TestsXUnit/UnitTest1.cs deleted file mode 100644 index 70d745a..0000000 --- a/src/Tests/TestsAPI/TestsXUnit/UnitTest1.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace TestsXUnit; - -public class UnitTest1 -{ - [Fact] - public void Test1() - { - - } -} \ No newline at end of file diff --git a/src/Tests/TestsAPI/UnitTestApi/Controllers/UsersControllerTest.cs b/src/Tests/TestsAPI/UnitTestApi/Controllers/UsersControllerTest.cs new file mode 100644 index 0000000..1d1500c --- /dev/null +++ b/src/Tests/TestsAPI/UnitTestApi/Controllers/UsersControllerTest.cs @@ -0,0 +1,225 @@ +using ApiMappeur; +using Dto; +using HeartTrackAPI.Controllers; +using HeartTrackAPI.Request; +using HeartTrackAPI.Responce; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging.Abstractions; +using Model; +using Model.Manager; +using Model.Repository; +using Moq; +using Shared; +using StubAPI; + +namespace UnitTestApi.Controllers; + +[TestClass] +[TestSubject(typeof(UsersController))] +public class UsersControllerTest +{ + private Mock _dataManagerMock; + private IDataManager _dataManager; + private UsersController _usersController; + + private readonly List _users = + [ + new User + { + Id = 1, Username = "DoeDoe", + ProfilePicture = + "https://images.unsplash.com/photo-1682687982134-2ac563b2228b?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + FirstName = "John", LastName = "Doe", + Sexe = "M", Lenght = 180, Weight = 70, DateOfBirth = new DateTime(1990, 1, 1), + Email = "john.doe@example.com", Role = new Athlete() + }, + + new User + { + Id = 2, Username = "SmithSmith", + ProfilePicture = + "https://images.unsplash.com/photo-1709507779917-242b560288be?q=80&w=2080&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + FirstName = "Jane", LastName = "Smith", + Sexe = "F", Lenght = 170, Weight = 60, DateOfBirth = new DateTime(1992, 2, 2), + Email = "athlete2@example.com", Role = new Coach() + }, + + new User + { + Id = 3, Username = "Athlete3", + ProfilePicture = + "https://plus.unsplash.com/premium_photo-1705091981693-6006f8a20479?q=80&w=1974&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + FirstName = "First3", LastName = "Last3", + Sexe = "M", Lenght = 190, Weight = 80, DateOfBirth = new DateTime(1994, 3, 3), Email = "ath@ex.fr", + Role = new Athlete() + } + ]; + + [TestInitialize] + public void SetUp() + { + _dataManagerMock = new Mock(); + + _dataManagerMock.Setup(dm => dm.UserRepo.GetNbItems()).ReturnsAsync(_users.Count); + _dataManagerMock.Setup(dm => + dm.UserRepo.GetUsers(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny())).ReturnsAsync( + (int index, int count, AthleteOrderCriteria criteria, bool descending) => + _users.GetItemsWithFilterAndOrdering(c => true, index, count, + criteria != AthleteOrderCriteria.None ? criteria : null, descending) + ); + + + + _usersController = new UsersController(new NullLogger(), _dataManagerMock.Object); + } +/* + [TestInitialize] + public void SetUp() + { + _dataManager = new StubData(); + _usersController = new UsersController(new NullLogger(), _dataManager); + }*/ + + + [TestMethod] + public async Task Get_ReturnsPageResponse_WhenRequestIsValid() + { + // Arrange + var request = new PageRequest + { + Index = 0, + Count = 3, + OrderingPropertyName = "Id", + Descending = false + }; + + // Act + var result = await _usersController.Get(request); + Assert.IsInstanceOfType(result.Result, typeof(OkObjectResult)); + var okResult = result.Result as OkObjectResult; + // Assert + Assert.IsNotNull(okResult); + Assert.IsInstanceOfType(okResult.Value, typeof(PageResponse)); + var pageResponse = okResult.Value as PageResponse; + Assert.IsNotNull(pageResponse); + Assert.AreEqual(3, pageResponse.Items.Count()); + Assert.AreEqual(3, pageResponse.Total); + Assert.AreEqual(0, pageResponse.Index); + Assert.AreEqual(3, pageResponse.Count); + Assert.AreEqual(3, pageResponse.Count); + } + + [DataTestMethod] + [DataRow(0, 2, "Id", false, 2)] + [DataRow(1, 1, "Id", false, 1)] + [DataRow(0, 3, "Id", true, 3)] + public async Task Get_ReturnsCorrectPaginationAndOrdering(int index, int count, string orderingProperty, + bool descending, int expectedItemCount) + { + // Arrange + var request = new PageRequest + { + Index = index, + Count = count, + OrderingPropertyName = orderingProperty, + Descending = descending + }; + // Act + var result = await _usersController.Get(request); + Assert.IsInstanceOfType(result.Result, typeof(OkObjectResult)); + var okResult = result.Result as OkObjectResult; + // Assert + Assert.IsNotNull(okResult); + Assert.IsInstanceOfType(okResult.Value, typeof(PageResponse)); + var pageResponse = okResult.Value as PageResponse; + Assert.IsNotNull(pageResponse); + Assert.AreEqual(expectedItemCount, pageResponse.Items.Count()); + } + + [TestMethod] + public async Task Get_ReturnsInternalServerError_OnException() + { + _dataManagerMock.Setup(dm => + dm.UserRepo.GetUsers(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny())) + .ThrowsAsync(new Exception("Simulated database failure.")); + + var request = new PageRequest { Index = 0, Count = 3 }; + + var result = await _usersController.Get(request); + + Assert.IsInstanceOfType(result.Result, typeof(ObjectResult)); + var objectResult = result.Result as ObjectResult; + Assert.AreEqual(500, objectResult.StatusCode); + } + + + + [TestMethod] + public async Task GetById_ReturnsUserDto_WhenRequestIsValid() + { + // Arrange + var id = 1; + _dataManagerMock.Setup(dm => dm.UserRepo.GetItemById(id)).ReturnsAsync(_users.First(x => x.Id == id)); + + // Act + var result = await _usersController.GetById(id) ; + Assert.IsInstanceOfType(result.Result, typeof(OkObjectResult)); + var okResult = result.Result as OkObjectResult; + + // Assert + Assert.IsNotNull(okResult); + var resultObject = result.Result as OkObjectResult; + Assert.IsNotNull(resultObject); + Assert.IsInstanceOfType(resultObject.Value, typeof(UserDto)); + var user = resultObject.Value as UserDto; + Assert.IsNotNull(user); + var tmp = _users.First(x => x.Id == id).ToDto(); + Assert.AreEqual(tmp.Id, user.Id); + } + + [TestMethod] + public async Task GetById_ReturnsUserDto_WhenRequestUserDoesNotExist() + { + // Arrange + var id = 0; + _dataManagerMock.Setup(dm => dm.UserRepo.GetItemById(id)).ReturnsAsync((User)null!); + + // Act + var result = await _usersController.GetById(id) ; + + // Assert + Assert.IsInstanceOfType(result.Result, typeof(NotFoundObjectResult)); + } + + + [TestMethod] + public async Task GetById_Returns404_WhenIdIsInvalid() + { + // Arrange + var id = -2; + + // Act + var result = await _usersController.GetById(id); + + // Assert + Assert.IsInstanceOfType(result.Result, typeof(NotFoundObjectResult)); + } + + + [TestMethod] + public async Task Count_ReturnsInt_WhenRequestIsValid() + { + // Act + var result = await _usersController.Count(); + Assert.IsNotNull(result); + result = result.Result as OkObjectResult; + + // Assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result.Value, typeof(int)); + } + +} \ No newline at end of file diff --git a/src/Tests/TestsAPI/UnitTestApi/UnitTestApi.csproj b/src/Tests/TestsAPI/UnitTestApi/UnitTestApi.csproj index 45b5c8f..9931d8b 100644 --- a/src/Tests/TestsAPI/UnitTestApi/UnitTestApi.csproj +++ b/src/Tests/TestsAPI/UnitTestApi/UnitTestApi.csproj @@ -10,7 +10,9 @@ + + diff --git a/src/Tests/TestsAPI/UnitTestApi/UserControllerTest.cs b/src/Tests/TestsAPI/UnitTestApi/UserControllerTest.cs deleted file mode 100644 index dd511e7..0000000 --- a/src/Tests/TestsAPI/UnitTestApi/UserControllerTest.cs +++ /dev/null @@ -1,124 +0,0 @@ -using Dto; -using HeartTrackAPI.Controllers; -using HeartTrackAPI.Request; -using HeartTrackAPI.Responce; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging.Abstractions; -using Model.Manager; -using Model.Repository; -using StubAPI; - -namespace UnitTestApi; - -[TestClass] -public class UserControllerTest -{ - private readonly IDataManager StubDataManager; - private readonly UsersController _usersController; - - public UserControllerTest() - { - StubDataManager = new StubData(); - _usersController = new UsersController(new NullLogger(), StubDataManager); - - } - - [TestMethod] - public void Get_ReturnsPageResponse_WhenRequestIsValid() - { - // Arrange - var request = new PageRequest - { - Index = 0, - Count = 10, - OrderingPropertyName = "Id", - Descending = false - }; - - // Act - //var result = _usersController.Get(request).Result as OkObjectResult; - - // Assert - // Assert.IsNotNull(result); - //Assert.IsInstanceOfType(result.Value, typeof(PageResponse)); - } - /* - [TestMethod] - public void GetById_ReturnsUserDto_WhenRequestIsValid() - { - // Arrange - var id = 1; - - // Act - var result = _usersController.GetById(id).Result as OkObjectResult; - - // Assert - Assert.IsNotNull(result); - Assert.IsInstanceOfType(result.Value, typeof(UserDto)); - } - - [TestMethod] - public void GetById_Returns404_WhenIdIsInvalid() - { - // Arrange - var id = 0; - - // Act - var result = _usersController.GetById(id).Result as NotFoundResult; - - // Assert - Assert.IsNotNull(result); - } - - [TestMethod] - public void GetById_Returns500_WheExceptionIsThrown() - { - // Arrange - var id = 0; - - // Act - var result = _usersController.GetById(id).Result as StatusCodeResult; - - // Assert - Assert.IsNotNull(result); - Assert.AreEqual(500, result.StatusCode); - } - - [TestMethod] - public void Count_ReturnsInt_WhenRequestIsValid() - { - // Act - var result = _usersController.Count().Result as OkObjectResult; - - // Assert - Assert.IsNotNull(result); - Assert.IsInstanceOfType(result.Value, typeof(int)); - } - - [TestMethod] - public void Count_Returns500_WheExceptionIsThrown() - { - // Act - var result = _usersController.Count().Result as StatusCodeResult; - - // Assert - Assert.IsNotNull(result); - Assert.AreEqual(500, result.StatusCode); - } - - [TestMethod] - public void Update_ReturnsUserDto_WhenRequestIsValid() - { - // Arrange - var id = 1; - var user = new UserDto - { - Id = 1, - FirstName = "John", - LastName = "Doe", - Email = "toto@eoeo.fr", - }; - - }*/ - -} \ No newline at end of file diff --git a/src/Tests/UnitTestsEntities/ActivityEntityTests.cs b/src/Tests/UnitTestsEntities/ActivityEntityTests.cs new file mode 100644 index 0000000..62caaae --- /dev/null +++ b/src/Tests/UnitTestsEntities/ActivityEntityTests.cs @@ -0,0 +1,140 @@ +namespace UnitTestsEntities; +using Xunit; +using System.Linq; +using Entities; +using Microsoft.EntityFrameworkCore; + +public class ActivityEntityTests +{ + [Fact] + public void Add_Activity_Success() + { + + + var activity = new ActivityEntity + { + Type = "Running", + Date = new DateOnly(2024, 3, 15), + StartTime = new TimeOnly(9, 0), + EndTime = new TimeOnly(10, 0), + EffortFelt = 7, + Variability = 0.5f, + Variance = 0.2f, + StandardDeviation = 0.3f, + Average = 0.4f, + Maximum = 200, + Minimum = 100, + AverageTemperature = 25.5f, + HasAutoPause = true, + DataSourceId = 1, + AthleteId = 1 + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Activities.Add(activity); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedActivity = context.Activities.First(a => a.Type == "Running"); + Assert.NotNull(savedActivity); + Assert.Equal("Running", savedActivity.Type ); + + }*/ + } + + [Fact] + public void Update_Activity_Success() + { + var activity = new ActivityEntity + { + Type = "Running", + Date = new DateOnly(2024, 3, 15), + StartTime = new TimeOnly(9, 0), + EndTime = new TimeOnly(10, 0), + EffortFelt = 7, + Variability = 0.5f, + Variance = 0.2f, + StandardDeviation = 0.3f, + Average = 0.4f, + Maximum = 200, + Minimum = 100, + AverageTemperature = 25.5f, + HasAutoPause = true, + DataSourceId = 1, + AthleteId = 1 + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Activities.Add(activity); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedActivity = context.Activities.First(a => a.Type == "Running"); + savedActivity.Type = "Walking"; + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var updatedActivity = context.Activities.First(a => a.Type == "Walking"); + Assert.NotNull(updatedActivity); + Assert.Equal("Walking", updatedActivity.Type ); + Assert.Equal(7, updatedActivity.EffortFelt ); + }*/ + } + + [Fact] + public void Delete_Activity_Success() + { + var activity = new ActivityEntity + { + Type = "Running", + Date = new DateOnly(2024, 3, 15), + StartTime = new TimeOnly(9, 0), + EndTime = new TimeOnly(10, 0), + EffortFelt = 7, + Variability = 0.5f, + Variance = 0.2f, + StandardDeviation = 0.3f, + Average = 0.4f, + Maximum = 200, + Minimum = 100, + AverageTemperature = 25.5f, + HasAutoPause = true, + DataSourceId = 1, + AthleteId = 1 + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Activities.Add(activity); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedActivity = context.Activities.First(a => a.Type == "Running"); + context.Activities.Remove(savedActivity); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var deletedActivity = context.Activities.FirstOrDefault(a => a.Type == "Running"); + Assert.Null(deletedActivity); + }*/ + } +} + diff --git a/src/Tests/UnitTestsEntities/AthleteEntityTests.cs b/src/Tests/UnitTestsEntities/AthleteEntityTests.cs new file mode 100644 index 0000000..878d7d8 --- /dev/null +++ b/src/Tests/UnitTestsEntities/AthleteEntityTests.cs @@ -0,0 +1,122 @@ +namespace UnitTestsEntities; +using Xunit; +using System.Linq; +using Entities; +using Microsoft.EntityFrameworkCore; + +public class AthleteEntityTests +{ + [Fact] + public void Add_Athlete_Success() + { + var athlete = new AthleteEntity + { + Username = "john_doe", + LastName = "Doe", + FirstName = "John", + Email = "john.doe@example.com", + Sexe = "M", + Length = 180.0, + Weight = 75.5f, + Password = "password", + DateOfBirth = new DateOnly(1990, 1, 1), + IsCoach = false + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Athletes.Add(athlete); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedAthlete = context.Athletes.First(a => a.Username == "john_doe"); + Assert.NotNull(savedAthlete); + Assert.Equal("john_doe", savedAthlete.Username); + }*/ + } + + [Fact] + public void Update_Athlete_Success() + { + var athlete = new AthleteEntity + { + Username = "jane_smith", + LastName = "Smith", + FirstName = "Jane", + Email = "jane.smith@example.com", + Sexe = "F", + Length = 165.0, + Weight = 60.0f, + Password = "password123", + DateOfBirth = new DateOnly(1995, 5, 10), + IsCoach = false + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Athletes.Add(athlete); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedAthlete = context.Athletes.First(a => a.Username == "jane_smith"); + savedAthlete.Username = "jane_doe"; + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var updatedAthlete = context.Athletes.First(a => a.Username == "jane_doe"); + Assert.NotNull(updatedAthlete); + Assert.Equal("jane_doe", updatedAthlete.Username); + Assert.Equal("Smith", updatedAthlete.LastName); + }*/ + } + + [Fact] + public void Delete_Athlete_Success() + { + var athlete = new AthleteEntity + { + Username = "test_user", + LastName = "Test", + FirstName = "User", + Email = "test.user@example.com", + Sexe = "M", + Length = 170.0, + Weight = 70.0f, + Password = "testpassword", + DateOfBirth = new DateOnly(1985, 10, 20), + IsCoach = false + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Athletes.Add(athlete); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedAthlete = context.Athletes.First(a => a.Username == "test_user"); + context.Athletes.Remove(savedAthlete); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var deletedAthlete = context.Athletes.FirstOrDefault(a => a.Username == "test_user"); + Assert.Null(deletedAthlete); + }*/ + } +} + diff --git a/src/Tests/UnitTestsEntities/DataSourceEntityTests.cs b/src/Tests/UnitTestsEntities/DataSourceEntityTests.cs new file mode 100644 index 0000000..6f4c1db --- /dev/null +++ b/src/Tests/UnitTestsEntities/DataSourceEntityTests.cs @@ -0,0 +1,102 @@ +namespace UnitTestsEntities; +using Xunit; +using System.Linq; +using Entities; +using Microsoft.EntityFrameworkCore; + + +public class DataSourceEntityTests +{ + [Fact] + public void Add_DataSource_Success() + { + var dataSource = new DataSourceEntity + { + Type = "GPS", + Model = "Garmin Forerunner 945", + Precision = 0.1f + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.DataSources.Add(dataSource); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedDataSource = context.DataSources.First(d => d.Type == "GPS"); + Assert.NotNull(savedDataSource); + Assert.Equal("Garmin Forerunner 945", savedDataSource.Model); + }*/ + } + + [Fact] + public void Update_DataSource_Success() + { + var dataSource = new DataSourceEntity + { + Type = "Heart Rate Monitor", + Model = "Polar H10", + Precision = 0.2f + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.DataSources.Add(dataSource); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedDataSource = context.DataSources.First(d => d.Type == "Heart Rate Monitor"); + savedDataSource.Model = "Polar H9"; + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var updatedDataSource = context.DataSources.First(d => d.Model == "Polar H9"); + Assert.NotNull(updatedDataSource); + Assert.Equal("Heart Rate Monitor", updatedDataSource.Type); + Assert.Equal(0.2f, updatedDataSource.Precision); + }*/ + } + + [Fact] + public void Delete_DataSource_Success() + { + var dataSource = new DataSourceEntity + { + Type = "Smartwatch", + Model = "Apple Watch Series 6", + Precision = 0.05f + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.DataSources.Add(dataSource); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedDataSource = context.DataSources.First(d => d.Type == "Smartwatch"); + context.DataSources.Remove(savedDataSource); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var deletedDataSource = context.DataSources.FirstOrDefault(d => d.Type == "Smartwatch"); + Assert.Null(deletedDataSource); + }*/ + } +} + diff --git a/src/Tests/UnitTestsEntities/FriendshipEntityTests.cs b/src/Tests/UnitTestsEntities/FriendshipEntityTests.cs new file mode 100644 index 0000000..492506c --- /dev/null +++ b/src/Tests/UnitTestsEntities/FriendshipEntityTests.cs @@ -0,0 +1,206 @@ +namespace UnitTestsEntities; +using Xunit; +using System.Linq; +using Entities; +using Microsoft.EntityFrameworkCore; + +public class FriendshipEntityTests +{ + [Fact] + public void Add_Friendship_Success() + { + var follower = new AthleteEntity + { + Username = "follower_user", + LastName = "Follower", + FirstName = "User", + Email = "follower.user@example.com", + Sexe = "M", + Length = 170.0, + Weight = 70.0f, + Password = "followerpassword", + DateOfBirth = new DateOnly(1990, 1, 1), + IsCoach = false + }; + + var following = new AthleteEntity + { + Username = "following_user", + LastName = "Following", + FirstName = "User", + Email = "following.user@example.com", + Sexe = "F", + Length = 165.0, + Weight = 60.0f, + Password = "followingpassword", + DateOfBirth = new DateOnly(1995, 5, 10), + IsCoach = false + }; + + var friendship = new FriendshipEntity + { + Follower = follower, + Following = following, + StartDate = DateTime.Now + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Athletes.Add(follower); + context.Athletes.Add(following); + context.Friendships.Add(friendship); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedFriendship = context.Friendships.FirstOrDefault(); + Assert.NotNull(savedFriendship); + Assert.Equal(follower.IdAthlete, savedFriendship.FollowerId); + Assert.Equal(following.IdAthlete, savedFriendship.FollowingId); + }*/ + } + + [Fact] + public void Update_Friendship_Success() + { + var follower = new AthleteEntity + { + Username = "follower_user", + LastName = "Follower", + FirstName = "User", + Email = "follower.user@example.com", + Sexe = "M", + Length = 170.0, + Weight = 70.0f, + Password = "followerpassword", + DateOfBirth = new DateOnly(1990, 1, 1), + IsCoach = false + }; + + var following = new AthleteEntity + { + Username = "following_user", + LastName = "Following", + FirstName = "User", + Email = "following.user@example.com", + Sexe = "F", + Length = 165.0, + Weight = 60.0f, + Password = "followingpassword", + DateOfBirth = new DateOnly(1995, 5, 10), + IsCoach = false + }; + + var thirdAthlete = new AthleteEntity + { + Username = "third_user", + LastName = "Third", + FirstName = "User", + Email = "third.user@example.com", + Sexe = "M", + Length = 180.0, + Weight = 75.0f, + Password = "thirdpassword", + DateOfBirth = new DateOnly(1988, 3, 15), + IsCoach = false + }; + + var friendship = new FriendshipEntity + { + Follower = follower, + Following = following, + StartDate = DateTime.Now + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Athletes.Add(follower); + context.Athletes.Add(following); + context.Athletes.Add(thirdAthlete); + context.Friendships.Add(friendship); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedFriendship = context.Friendships.FirstOrDefault(); + savedFriendship.Follower = thirdAthlete; // Update the follower + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var updatedFriendship = context.Friendships.FirstOrDefault(); + Assert.NotNull(updatedFriendship); + Assert.Equal(thirdAthlete.IdAthlete, updatedFriendship.FollowerId); + }*/ + } + + [Fact] + public void Delete_Friendship_Success() + { + var follower = new AthleteEntity + { + Username = "follower_user", + LastName = "Follower", + FirstName = "User", + Email = "follower.user@example.com", + Sexe = "M", + Length = 170.0, + Weight = 70.0f, + Password = "followerpassword", + DateOfBirth = new DateOnly(1990, 1, 1), + IsCoach = false + }; + + var following = new AthleteEntity + { + Username = "following_user", + LastName = "Following", + FirstName = "User", + Email = "following.user@example.com", + Sexe = "F", + Length = 165.0, + Weight = 60.0f, + Password = "followingpassword", + DateOfBirth = new DateOnly(1995, 5, 10), + IsCoach = false + }; + + var friendship = new FriendshipEntity + { + Follower = follower, + Following = following, + StartDate = DateTime.Now + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Athletes.Add(follower); + context.Athletes.Add(following); + context.Friendships.Add(friendship); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedFriendship = context.Friendships.FirstOrDefault(); + context.Friendships.Remove(savedFriendship); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var deletedFriendship = context.Friendships.FirstOrDefault(); + Assert.Null(deletedFriendship); + }*/ + } +} + diff --git a/src/Tests/UnitTestsEntities/HeartRateEntityTests.cs b/src/Tests/UnitTestsEntities/HeartRateEntityTests.cs new file mode 100644 index 0000000..c3bc36f --- /dev/null +++ b/src/Tests/UnitTestsEntities/HeartRateEntityTests.cs @@ -0,0 +1,172 @@ +namespace UnitTestsEntities; +using Xunit; +using System.Linq; +using Entities; +using Microsoft.EntityFrameworkCore; + +public class HeartRateEntityTests +{ + [Fact] + public void Add_HeartRate_Success() + { + var activity = new ActivityEntity + { + Type = "Running", + Date = new DateOnly(2024, 3, 15), + StartTime = new TimeOnly(9, 0), + EndTime = new TimeOnly(10, 0), + EffortFelt = 7, + Variability = 0.5f, + Variance = 0.2f, + StandardDeviation = 0.3f, + Average = 0.4f, + Maximum = 200, + Minimum = 100, + AverageTemperature = 25.5f, + HasAutoPause = true, + DataSourceId = 1, + AthleteId = 1 + }; + + var heartRateEntry = new HeartRateEntity + { + Altitude = 100.0, + Time = new TimeOnly(9, 30), + Temperature = 20.0f, + Bpm = 150, + Longitude = 45.12345f, + Latitude = 35.6789f, + Activity = activity + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Activities.Add(activity); + context.HeartRates.Add(heartRateEntry); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedHeartRate = context.HeartRates.First(); + Assert.NotNull(savedHeartRate); + Assert.Equal(150, savedHeartRate.Bpm); + }*/ + } + + [Fact] + public void Update_HeartRate_Success() + { + var activity = new ActivityEntity + { + Type = "Running", + Date = new DateOnly(2024, 3, 15), + StartTime = new TimeOnly(9, 0), + EndTime = new TimeOnly(10, 0), + EffortFelt = 7, + Variability = 0.5f, + Variance = 0.2f, + StandardDeviation = 0.3f, + Average = 0.4f, + Maximum = 200, + Minimum = 100, + AverageTemperature = 25.5f, + HasAutoPause = true, + DataSourceId = 1, + AthleteId = 1 + }; + + var heartRateEntry = new HeartRateEntity + { + Altitude = 100.0, + Time = new TimeOnly(9, 30), + Temperature = 20.0f, + Bpm = 150, + Longitude = 45.12345f, + Latitude = 35.6789f, + Activity = activity + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Activities.Add(activity); + context.HeartRates.Add(heartRateEntry); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedHeartRate = context.HeartRates.First(); + savedHeartRate.Bpm = 160; + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var updatedHeartRate = context.HeartRates.First(); + Assert.NotNull(updatedHeartRate); + Assert.Equal(160, updatedHeartRate.Bpm); + }*/ + } + + [Fact] + public void Delete_HeartRate_Success() + { + var activity = new ActivityEntity + { + Type = "Running", + Date = new DateOnly(2024, 3, 15), + StartTime = new TimeOnly(9, 0), + EndTime = new TimeOnly(10, 0), + EffortFelt = 7, + Variability = 0.5f, + Variance = 0.2f, + StandardDeviation = 0.3f, + Average = 0.4f, + Maximum = 200, + Minimum = 100, + AverageTemperature = 25.5f, + HasAutoPause = true, + DataSourceId = 1, + AthleteId = 1 + }; + + var heartRateEntry = new HeartRateEntity + { + Altitude = 100.0, + Time = new TimeOnly(9, 30), + Temperature = 20.0f, + Bpm = 150, + Longitude = 45.12345f, + Latitude = 35.6789f, + Activity = activity + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Activities.Add(activity); + context.HeartRates.Add(heartRateEntry); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedHeartRate = context.HeartRates.First(); + context.HeartRates.Remove(savedHeartRate); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var deletedHeartRate = context.HeartRates.FirstOrDefault(); + Assert.Null(deletedHeartRate); + }*/ + } +} + diff --git a/src/Tests/UnitTestsEntities/NotificationEntityTests.cs b/src/Tests/UnitTestsEntities/NotificationEntityTests.cs new file mode 100644 index 0000000..6d656a4 --- /dev/null +++ b/src/Tests/UnitTestsEntities/NotificationEntityTests.cs @@ -0,0 +1,151 @@ +namespace UnitTestsEntities; +using Xunit; +using System.Linq; +using Entities; +using Microsoft.EntityFrameworkCore; + +public class NotificationEntityTests +{ + [Fact] + public void Add_Notification_Success() + { + var sender = new AthleteEntity + { + Username = "sender_user", + LastName = "Sender", + FirstName = "User", + Email = "sender.user@example.com", + Sexe = "M", + Length = 170.0, + Weight = 70.0f, + Password = "senderpassword", + DateOfBirth = new DateOnly(1990, 1, 1), + IsCoach = false + }; + + var notification = new NotificationEntity + { + Message = "Test notification", + Date = DateTime.Now, + Statut = false, + Urgence = "High", + Sender = sender + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Athletes.Add(sender); + context.Notifications.Add(notification); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedNotification = context.Notifications.First(); + Assert.NotNull(savedNotification); + Assert.Equal("Test notification", savedNotification.Message); + }*/ + } + + [Fact] + public void Update_Notification_Success() + { + var sender = new AthleteEntity + { + Username = "sender_user", + LastName = "Sender", + FirstName = "User", + Email = "sender.user@example.com", + Sexe = "M", + Length = 170.0, + Weight = 70.0f, + Password = "senderpassword", + DateOfBirth = new DateOnly(1990, 1, 1), + IsCoach = false + }; + + var notification = new NotificationEntity + { + Message = "Test notification", + Date = DateTime.Now, + Statut = false, + Urgence = "High", + Sender = sender + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Athletes.Add(sender); + context.Notifications.Add(notification); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedNotification = context.Notifications.First(); + savedNotification.Message = "Updated message"; + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var updatedNotification = context.Notifications.First(); + Assert.NotNull(updatedNotification); + Assert.Equal("Updated message", updatedNotification.Message); + }*/ + } + + [Fact] + public void Delete_Notification_Success() + { + var sender = new AthleteEntity + { + Username = "sender_user", + LastName = "Sender", + FirstName = "User", + Email = "sender.user@example.com", + Sexe = "M", + Length = 170.0, + Weight = 70.0f, + Password = "senderpassword", + DateOfBirth = new DateOnly(1990, 1, 1), + IsCoach = false + }; + + var notification = new NotificationEntity + { + Message = "Test notification", + Date = DateTime.Now, + Statut = false, + Urgence = "High", + Sender = sender + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Athletes.Add(sender); + context.Notifications.Add(notification); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedNotification = context.Notifications.First(); + context.Notifications.Remove(savedNotification); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var deletedNotification = context.Notifications.FirstOrDefault(); + Assert.Null(deletedNotification); + }*/ + } +} + diff --git a/src/Tests/UnitTestsEntities/StatisticEntityTests.cs b/src/Tests/UnitTestsEntities/StatisticEntityTests.cs new file mode 100644 index 0000000..c91b2ab --- /dev/null +++ b/src/Tests/UnitTestsEntities/StatisticEntityTests.cs @@ -0,0 +1,155 @@ +namespace UnitTestsEntities; +using Xunit; +using System.Linq; +using Entities; +using Microsoft.EntityFrameworkCore; + + +public class StatisticEntityTests +{ + [Fact] + public void Add_Statistic_Success() + { + var athlete = new AthleteEntity + { + Username = "athlete_user", + LastName = "Athlete", + FirstName = "User", + Email = "athlete.user@example.com", + Sexe = "M", + Length = 170.0, + Weight = 70.0f, + Password = "athletepassword", + DateOfBirth = new DateOnly(1990, 1, 1), + IsCoach = false + }; + + var statistic = new StatisticEntity + { + Weight = 75.0f, + AverageHeartRate = 150.0, + MaximumHeartRate = 180.0, + AverageCaloriesBurned = 500.0, + Date = new DateOnly(2024, 3, 15), + Athlete = athlete + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Athletes.Add(athlete); + context.Statistics.Add(statistic); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedStatistic = context.Statistics.First(); + Assert.NotNull(savedStatistic); + Assert.Equal(75.0f, savedStatistic.Weight); + }*/ + } + + [Fact] + public void Update_Statistic_Success() + { + var athlete = new AthleteEntity + { + Username = "athlete_user", + LastName = "Athlete", + FirstName = "User", + Email = "athlete.user@example.com", + Sexe = "M", + Length = 170.0, + Weight = 70.0f, + Password = "athletepassword", + DateOfBirth = new DateOnly(1990, 1, 1), + IsCoach = false + }; + + var statistic = new StatisticEntity + { + Weight = 75.0f, + AverageHeartRate = 150.0, + MaximumHeartRate = 180.0, + AverageCaloriesBurned = 500.0, + Date = new DateOnly(2024, 3, 15), + Athlete = athlete + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Athletes.Add(athlete); + context.Statistics.Add(statistic); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedStatistic = context.Statistics.First(); + savedStatistic.Weight = 80.0f; + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var updatedStatistic = context.Statistics.First(); + Assert.NotNull(updatedStatistic); + Assert.Equal(80.0f, updatedStatistic.Weight); + }*/ + } + + [Fact] + public void Delete_Statistic_Success() + { + var athlete = new AthleteEntity + { + Username = "athlete_user", + LastName = "Athlete", + FirstName = "User", + Email = "athlete.user@example.com", + Sexe = "M", + Length = 170.0, + Weight = 70.0f, + Password = "athletepassword", + DateOfBirth = new DateOnly(1990, 1, 1), + IsCoach = false + }; + + var statistic = new StatisticEntity + { + Weight = 75.0f, + AverageHeartRate = 150.0, + MaximumHeartRate = 180.0, + AverageCaloriesBurned = 500.0, + Date = new DateOnly(2024, 3, 15), + Athlete = athlete + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Athletes.Add(athlete); + context.Statistics.Add(statistic); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedStatistic = context.Statistics.First(); + context.Statistics.Remove(savedStatistic); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var deletedStatistic = context.Statistics.FirstOrDefault(); + Assert.Null(deletedStatistic); + }*/ + } +} + diff --git a/src/Tests/UnitTestsEntities/TrainingEntityTests.cs b/src/Tests/UnitTestsEntities/TrainingEntityTests.cs new file mode 100644 index 0000000..84ec46e --- /dev/null +++ b/src/Tests/UnitTestsEntities/TrainingEntityTests.cs @@ -0,0 +1,202 @@ +namespace UnitTestsEntities; +using Xunit; +using System.Linq; +using Entities; +using Microsoft.EntityFrameworkCore; + + +public class TrainingEntityTests +{ + [Fact] + public void Add_Training_Success() + { + var coach = new AthleteEntity + { + Username = "coach_user", + LastName = "Coach", + FirstName = "User", + Email = "coach.user@example.com", + Sexe = "M", + Length = 180.0, + Weight = 75.0f, + Password = "coachpassword", + DateOfBirth = new DateOnly(1985, 5, 15), + IsCoach = true + }; + + var athlete = new AthleteEntity + { + Username = "athlete_user", + LastName = "Athlete", + FirstName = "User", + Email = "athlete.user@example.com", + Sexe = "F", + Length = 165.0, + Weight = 60.0f, + Password = "athletepassword", + DateOfBirth = new DateOnly(1990, 3, 20), + IsCoach = false + }; + + var training = new TrainingEntity + { + Date = new DateOnly(2024, 3, 15), + Description = "Training description", + Latitude = 40.12345f, + Longitude = -74.56789f, + FeedBack = "Training feedback", + Coach = coach, + Athletes = { athlete } + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Athletes.Add(coach); + context.Athletes.Add(athlete); + context.Trainings.Add(training); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedTraining = context.Trainings.First(); + Assert.NotNull(savedTraining); + Assert.Equal("Training description", savedTraining.Description); + }*/ + } + + [Fact] + public void Update_Training_Success() + { + var coach = new AthleteEntity + { + Username = "coach_user", + LastName = "Coach", + FirstName = "User", + Email = "coach.user@example.com", + Sexe = "M", + Length = 180.0, + Weight = 75.0f, + Password = "coachpassword", + DateOfBirth = new DateOnly(1985, 5, 15), + IsCoach = true + }; + + var athlete = new AthleteEntity + { + Username = "athlete_user", + LastName = "Athlete", + FirstName = "User", + Email = "athlete.user@example.com", + Sexe = "F", + Length = 165.0, + Weight = 60.0f, + Password = "athletepassword", + DateOfBirth = new DateOnly(1990, 3, 20), + IsCoach = false + }; + + var training = new TrainingEntity + { + Date = new DateOnly(2024, 3, 15), + Description = "Training description", + Latitude = 40.12345f, + Longitude = -74.56789f, + FeedBack = "Training feedback", + Coach = coach, + Athletes = { athlete } + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Athletes.Add(coach); + context.Athletes.Add(athlete); + context.Trainings.Add(training); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedTraining = context.Trainings.First(); + savedTraining.Description = "Updated training description"; + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var updatedTraining = context.Trainings.First(); + Assert.NotNull(updatedTraining); + Assert.Equal("Updated training description", updatedTraining.Description); + }*/ + } + + [Fact] + public void Delete_Training_Success() + { + var coach = new AthleteEntity + { + Username = "coach_user", + LastName = "Coach", + FirstName = "User", + Email = "coach.user@example.com", + Sexe = "M", + Length = 180.0, + Weight = 75.0f, + Password = "coachpassword", + DateOfBirth = new DateOnly(1985, 5, 15), + IsCoach = true + }; + + var athlete = new AthleteEntity + { + Username = "athlete_user", + LastName = "Athlete", + FirstName = "User", + Email = "athlete.user@example.com", + Sexe = "F", + Length = 165.0, + Weight = 60.0f, + Password = "athletepassword", + DateOfBirth = new DateOnly(1990, 3, 20), + IsCoach = false + }; + + var training = new TrainingEntity + { + Date = new DateOnly(2024, 3, 15), + Description = "Training description", + Latitude = 40.12345f, + Longitude = -74.56789f, + FeedBack = "Training feedback", + Coach = coach, + Athletes = { athlete } + }; + + /* + using (var context = new StubbedContext(options)) + { + context.Database.EnsureCreated(); + context.Athletes.Add(coach); + context.Athletes.Add(athlete); + context.Trainings.Add(training); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var savedTraining = context.Trainings.First(); + context.Trainings.Remove(savedTraining); + context.SaveChanges(); + } + + using (var context = new StubbedContext(options)) + { + var deletedTraining = context.Trainings.FirstOrDefault(); + Assert.Null(deletedTraining); + }*/ + } +} diff --git a/src/Tests/UnitTestsEntities/UnitTest1.cs b/src/Tests/UnitTestsEntities/UnitTest1.cs deleted file mode 100644 index 4494897..0000000 --- a/src/Tests/UnitTestsEntities/UnitTest1.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace UnitTestsEntities; - -public class UnitTest1 -{ - [Fact] - public void Test1() - { - - } -} \ No newline at end of file diff --git a/src/Tests/UnitTestsEntities/UnitTestsEntities.csproj b/src/Tests/UnitTestsEntities/UnitTestsEntities.csproj index 22b0134..e372137 100644 --- a/src/Tests/UnitTestsEntities/UnitTestsEntities.csproj +++ b/src/Tests/UnitTestsEntities/UnitTestsEntities.csproj @@ -10,6 +10,7 @@ + @@ -22,4 +23,8 @@ + + + + diff --git a/src/UnitTestsEntities2/UnitTest1.cs b/src/UnitTestsEntities2/UnitTest1.cs new file mode 100644 index 0000000..8b121a7 --- /dev/null +++ b/src/UnitTestsEntities2/UnitTest1.cs @@ -0,0 +1,11 @@ +namespace UnitTestsEntities2 +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } +} \ No newline at end of file diff --git a/src/Tests/TestsAPI/TestsXUnit/TestsXUnit.csproj b/src/UnitTestsEntities2/UnitTestsEntities2.csproj similarity index 53% rename from src/Tests/TestsAPI/TestsXUnit/TestsXUnit.csproj rename to src/UnitTestsEntities2/UnitTestsEntities2.csproj index 22b0134..9c5b30a 100644 --- a/src/Tests/TestsAPI/TestsXUnit/TestsXUnit.csproj +++ b/src/UnitTestsEntities2/UnitTestsEntities2.csproj @@ -1,25 +1,23 @@ - - - - net8.0 - enable - enable - - false - true - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + +