Merge branch 'merging_EF-API' of codefirst.iut.uca.fr:HeartDev/API into merging_EF-API

merging_EF-API
Antoine PEREDERII 1 year ago
commit a5e602c533

@ -3,9 +3,6 @@ type: docker
name: HeartTrack-API name: HeartTrack-API
trigger: trigger:
branch:
- WORK-CD
- WORK-EF_WebAPI
event: event:
- push - push
@ -18,6 +15,14 @@ steps:
- dotnet build HeartTrack.sln -c Release --no-restore - dotnet build HeartTrack.sln -c Release --no-restore
- dotnet publish HeartTrack.sln -c Release --no-restore -o CI_PROJECT_DIR/build/release - 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 - name: code-analysis
image: hub.codefirst.iut.uca.fr/marc.chevaldonne/codefirst-dronesonarplugin-dotnet8 image: hub.codefirst.iut.uca.fr/marc.chevaldonne/codefirst-dronesonarplugin-dotnet8
secrets: [ SECRET_SONAR_LOGIN ] secrets: [ SECRET_SONAR_LOGIN ]
@ -36,8 +41,55 @@ steps:
- reportgenerator -reports:"**/coverage.cobertura.xml" -reporttypes:SonarQube -targetdir:"coveragereport" - reportgenerator -reports:"**/coverage.cobertura.xml" -reporttypes:SonarQube -targetdir:"coveragereport"
- dotnet publish HeartTrack.sln -c Release --no-restore -o $CI_PROJECT_DIR/build/release - dotnet publish HeartTrack.sln -c Release --no-restore -o $CI_PROJECT_DIR/build/release
- dotnet sonarscanner end /d:sonar.login=$${PLUGIN_SONAR_TOKEN} - dotnet sonarscanner end /d:sonar.login=$${PLUGIN_SONAR_TOKEN}
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 - name: docker-build-and-push
image: plugins/docker image: plugins/docker
settings: settings:
@ -49,25 +101,67 @@ steps:
from_secret: SECRET_REGISTRY_USERNAME from_secret: SECRET_REGISTRY_USERNAME
password: password:
from_secret: SECRET_REGISTRY_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
OVERWRITE: true
depends_on: [ docker-build-and-push ]
# - 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 ]
- name: deploy-container # database container deployment
- name: deploy-container-mysql
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest
environment: environment:
IMAGENAME: mariadb:10
CONTAINERNAME: mysql
COMMAND: create
# OVERWRITE: false
PRIVATE: true
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
# 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: dadalmeida-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 IMAGENAME: hub.codefirst.iut.uca.fr/david.d_almeida/api:latest
CONTAINERNAME: api CONTAINERNAME: api
CODEFIRST_CLIENTDRONE_ENV_PORT: 8080 CODEFIRST_CLIENTDRONE_ENV_PORT: 8080
ADMINS: davidd_almeida,kevinmonteiro,antoineperederii,paullevrault,antoinepinagot,nicolas.raymond ADMINS: davidd_almeida,kevinmonteiro,antoineperederii,paullevrault,antoinepinagot,nicolas.raymond
COMMAND: create COMMAND: create
OVERWRITE: true OVERWRITE: true
depends_on: [ docker-build-and-push ] depends_on: [deploy-container-mysql, docker-build-and-push, deploy-container-stub]
- 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 ]

3
.gitignore vendored

@ -565,4 +565,7 @@ xcuserdata/
/dataSources.local.xml /dataSources.local.xml
.ideaMigration/ .ideaMigration/
Migration/ Migration/
Migrations/
*.db *.db

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

@ -0,0 +1,53 @@
using Dto;
using Model;
using Shared;
namespace APIMappers;
public static class ActivityMapper
{
private static GenericMapper<Activity, ActivityDto> _mapper = new GenericMapper<Activity, ActivityDto>();
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
});
}
}

@ -0,0 +1,54 @@
using Dto;
using Model;
using Shared;
namespace APIMappers;
public static class UserMappeur
{
private static GenericMapper<User, UserDto> _mapper = new GenericMapper<User, UserDto>();
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
});
// ), (activity, entity) => activity.Id = entity.IdActivity);
}
// ochestrateur => api gateway
// corégraphie => microservice TCP
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()
});
}
}

@ -10,6 +10,7 @@
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.2" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -55,13 +55,31 @@ namespace DbContextLib
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HeartTrackContext"/> class. /// Initializes a new instance of the <see cref="HeartTrackContext"/> class.
/// </summary> /// </summary>
public HeartTrackContext() : base() { } public HeartTrackContext() : base()
{ }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HeartTrackContext"/> class with the specified options. /// Initializes a new instance of the <see cref="HeartTrackContext"/> class with the specified options.
/// </summary> /// </summary>
/// <param name="options">The options for the context.</param> /// <param name="options">The options for the context.</param>
public HeartTrackContext(DbContextOptions<HeartTrackContext> options) : base(options) { } public HeartTrackContext(DbContextOptions<HeartTrackContext> options) : base(options)
{ }
public HeartTrackContext(string dbPlatformPath)
: this(InitPlaformDb(dbPlatformPath))
{
}
private static DbContextOptions<HeartTrackContext> InitPlaformDb(string dbPlatformPath)
{
var options = new DbContextOptionsBuilder<HeartTrackContext>()
.UseMySql($"{dbPlatformPath}", new MySqlServerVersion(new Version(10, 11, 1)))
.Options;
return options;
}
/// <summary> /// <summary>
/// Configures the database options if they are not already configured. /// Configures the database options if they are not already configured.

@ -3,17 +3,17 @@ namespace Dto;
public class ActivityDto public class ActivityDto
{ {
public int Id { get; set; } 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 DateTime Date { get; set; }
public TimeSpan Duration { get; set; } public DateTime StartTime { get; set; }
public float Distance { get; set; } public DateTime EndTime { get; set; }
public float Elevation { get; set; } public int EffortFelt { get; set; }
public float AverageSpeed { get; set; } public float Variability { get; set; }
public int AverageHeartRate { get; set; } public float Variance { get; set; }
public int Calories { get; set; } public float StandardDeviation { get; set; }
public string Description { get; set; } public float Average { get; set; }
public string? Gpx { get; set; } public int Maximum { get; set; }
public string? Image { get; set; } public int Minimum { get; set; }
public int AthleteId { get; set; } public float AverageTemperature { get; set; }
public bool HasAutoPause { get; set; }
} }

@ -8,7 +8,6 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNetCore.Identity;
namespace Entities namespace Entities
{ {
@ -16,7 +15,7 @@ namespace Entities
/// Represents an athlete entity in the database. /// Represents an athlete entity in the database.
/// </summary> /// </summary>
[Table("Athlete")] [Table("Athlete")]
public class AthleteEntity : IdentityUser public class AthleteEntity
{ {
public AthleteEntity() : base() { } public AthleteEntity() : base() { }
@ -32,35 +31,35 @@ namespace Entities
/// </summary> /// </summary>
[MaxLength(100)] [MaxLength(100)]
[Required(ErrorMessage = "Athlete Username is ")] [Required(ErrorMessage = "Athlete Username is ")]
public string Username { get; set; } public required string Username { get; set; }
/// <summary> /// <summary>
/// Gets or sets the last name of the athlete. /// Gets or sets the last name of the athlete.
/// </summary> /// </summary>
[MaxLength(100)] [MaxLength(100)]
[Required(ErrorMessage = "Athlete Last Name is ")] [Required(ErrorMessage = "Athlete Last Name is ")]
public string LastName { get; set; } public required string LastName { get; set; }
/// <summary> /// <summary>
/// Gets or sets the first name of the athlete. /// Gets or sets the first name of the athlete.
/// </summary> /// </summary>
[MaxLength(150)] [MaxLength(150)]
[Required(ErrorMessage = "Athlete First Name is ")] [Required(ErrorMessage = "Athlete First Name is ")]
public string FirstName { get; set; } public required string FirstName { get; set; }
/// <summary> /// <summary>
/// Gets or sets the email of the athlete. /// Gets or sets the email of the athlete.
/// </summary> /// </summary>
[MaxLength(100)] [MaxLength(100)]
[Required(ErrorMessage = "Athlete Email is ")] [Required(ErrorMessage = "Athlete Email is ")]
public string Email { get; set; } public required string Email { get; set; }
/// <summary> /// <summary>
/// Gets or sets the gender of the athlete. /// Gets or sets the gender of the athlete.
/// </summary> /// </summary>
[MaxLength(1)] [MaxLength(1)]
[Required(ErrorMessage = "Athlete Sexe is ")] [Required(ErrorMessage = "Athlete Sexe is ")]
public string Sexe { get; set; } public required string Sexe { get; set; }
/// <summary> /// <summary>
/// Gets or sets the height of the athlete. /// Gets or sets the height of the athlete.
@ -76,7 +75,7 @@ namespace Entities
/// Gets or sets the password of the athlete. /// Gets or sets the password of the athlete.
/// </summary> /// </summary>
[Required(ErrorMessage = "Athlete Password is ")] [Required(ErrorMessage = "Athlete Password is ")]
public string Password { get; set; } public required string Password { get; set; }
/// <summary> /// <summary>
/// Gets or sets the date of birth of the athlete. /// Gets or sets the date of birth of the athlete.
@ -90,7 +89,7 @@ namespace Entities
/// </summary> /// </summary>
public bool IsCoach { get; set; } public bool IsCoach { get; set; }
public byte[] ProfilPicture { get; set; } public byte[]? ProfilPicture { get; set; }

@ -5,11 +5,4 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Extensions.Identity.Stores">
<HintPath>..\..\..\..\..\.nuget\packages\microsoft.extensions.identity.stores\8.0.2\lib\net8.0\Microsoft.Extensions.Identity.Stores.dll</HintPath>
</Reference>
</ItemGroup>
</Project> </Project>

@ -25,8 +25,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestsAPI", "TestsAPI", "{30
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientTests", "Tests\TestsAPI\ClientTests\ClientTests.csproj", "{9E4D3AC5-E6CA-4753-BD96-BF5EE793931A}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientTests", "Tests\TestsAPI\ClientTests\ClientTests.csproj", "{9E4D3AC5-E6CA-4753-BD96-BF5EE793931A}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestsXUnit", "Tests\TestsAPI\TestsXUnit\TestsXUnit.csproj", "{44C367DC-5FE0-4CF2-9E76-A0282E931853}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "Model\Model.csproj", "{30AB7FAA-6072-40B6-A15E-9188B59144F9}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "Model\Model.csproj", "{30AB7FAA-6072-40B6-A15E-9188B59144F9}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestApi", "Tests\TestsAPI\UnitTestApi\UnitTestApi.csproj", "{E515C8B6-6282-4D8B-8523-7B3A13E4AF58}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestApi", "Tests\TestsAPI\UnitTestApi\UnitTestApi.csproj", "{E515C8B6-6282-4D8B-8523-7B3A13E4AF58}"
@ -41,6 +39,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleTestEFMapper", "Test
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFMappers", "EFMappers\EFMappers.csproj", "{9397795D-F482-44C4-8443-A20AC26671AA}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFMappers", "EFMappers\EFMappers.csproj", "{9397795D-F482-44C4-8443-A20AC26671AA}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "APIMappers", "APIMappers\APIMappers.csproj", "{41D18203-1688-43BD-A3AC-FD0C2BD81909}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -86,10 +86,6 @@ Global
{9E4D3AC5-E6CA-4753-BD96-BF5EE793931A}.Debug|Any CPU.Build.0 = Debug|Any CPU {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.ActiveCfg = Release|Any CPU
{9E4D3AC5-E6CA-4753-BD96-BF5EE793931A}.Release|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
{30AB7FAA-6072-40B6-A15E-9188B59144F9}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
@ -134,13 +130,16 @@ Global
{9397795D-F482-44C4-8443-A20AC26671AA}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{9397795D-F482-44C4-8443-A20AC26671AA}.Release|Any CPU.Build.0 = 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 EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{477D2129-A6C9-4FF8-8BE9-5E9E8E5282F8} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18} {477D2129-A6C9-4FF8-8BE9-5E9E8E5282F8} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}
{2D166FAD-4934-474B-96A8-6C0635156EC2} = {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} {30FC2BE9-7397-445A-84AD-043CE70F4281} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}
{9E4D3AC5-E6CA-4753-BD96-BF5EE793931A} = {30FC2BE9-7397-445A-84AD-043CE70F4281} {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} {E515C8B6-6282-4D8B-8523-7B3A13E4AF58} = {30FC2BE9-7397-445A-84AD-043CE70F4281}
{31FA8E5E-D642-4C43-A2B2-02B9832B2CEC} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18} {31FA8E5E-D642-4C43-A2B2-02B9832B2CEC} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}
{73EA27F2-9F0C-443F-A5EE-2960C983A422} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18} {73EA27F2-9F0C-443F-A5EE-2960C983A422} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}

@ -1,19 +1,21 @@
using System.Reflection;
using DbContextLib;
using DbContextLib.Identity; using DbContextLib.Identity;
using Entities; using Entities;
using HeartTrackAPI.Utils;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Model.Manager; using Model.Manager;
using Model.Service; using Model2Entities;
using StubAPI; using StubAPI;
using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerGen;
namespace HeartTrackAPI; namespace HeartTrackAPI;
public class AppBootstrap(IConfiguration configuration) public class AppBootstrap(IConfiguration configuration)
@ -24,16 +26,14 @@ public class AppBootstrap(IConfiguration configuration)
{ {
services.AddControllers(); services.AddControllers();
services.AddEndpointsApiExplorer(); services.AddEndpointsApiExplorer();
//services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOption>(); AddSwagger(services);
// include Xml comment // include Xml comment
// addsecurityRequiment // addsecurityRequiment
// securityDef // securityDef
services.AddSwaggerGen();
AddHeartTrackContextServices(services); AddHeartTrackContextServices(services);
AddModelService(services); AddModelService(services);
AddIdentityServices(services); AddIdentityServices(services);
AddApiVersioning(services); AddApiVersioning(services);
AddSwagger(services);
services.AddHealthChecks(); services.AddHealthChecks();
@ -41,23 +41,58 @@ public class AppBootstrap(IConfiguration configuration)
private void AddHeartTrackContextServices(IServiceCollection services) private void AddHeartTrackContextServices(IServiceCollection services)
{ {
var connectionString = Configuration.GetConnectionString("HeartTrackAuthConnection"); string connectionString;
if (string.IsNullOrWhiteSpace(connectionString))
{ switch (Environment.GetEnvironmentVariable("TYPE"))
throw new InvalidOperationException("The connection string for the database is not set.");
}
else
{ {
services.AddDbContext<AuthDbContext>(options => options.UseInMemoryDatabase("AuthDb")); case "BDD":
// builder.Services.AddDbContext<HeartTrackContext>(options => options.UseSqlite(connectionString)); var HOST = System.Environment.GetEnvironmentVariable("HOST");
services.AddSingleton<IDataManager, StubData>(); 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(connectionString);
services.AddSingleton<IDataManager>(provider => new DbDataManager(connectionString));
break;
default:
connectionString = Configuration.GetConnectionString("HeartTrackAuthConnection");
if (string.IsNullOrWhiteSpace(connectionString))
{
services.AddDbContext<AuthDbContext>(options => options.UseInMemoryDatabase("AuthDb"));
//options => options.UseSqlite(connectionString)
//services.AddDbContext<HeartTrackContext>();
services.AddDbContext<HeartTrackContext>(options =>
options.UseSqlite(connectionString), ServiceLifetime.Singleton);
}
break;
} }
/*
services.AddSingleton<DbContextOptions<HeartTrackContext>>(provider =>
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
var options = new DbContextOptionsBuilder<HeartTrackContext>()
.UseSqlite(connection)
.Options;
return options;
});*/
} }
private void AddModelService(IServiceCollection services) private void AddModelService(IServiceCollection services)
{ {
//services.AddSingleton<IDataManager>(provider => new DbDataManager(provider.GetService<HeartTrackContext>()));
services.AddSingleton<IDataManager, StubData>(); services.AddSingleton<IDataManager, StubData>();
services.AddTransient<IActivityManager, ActivityManager>(); //services.AddTransient<IActivityManager, ActivityManager>();
} }
private void AddIdentityServices(IServiceCollection services) private void AddIdentityServices(IServiceCollection services)
@ -90,25 +125,66 @@ public class AppBootstrap(IConfiguration configuration)
private void AddSwagger(IServiceCollection services) private void AddSwagger(IServiceCollection services)
{ {
services.AddSwaggerGen(options => services.AddSwaggerGen(options =>
{
options.OperationFilter<SwaggerDefaultValues>();
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<string>()
}
};
options.AddSecurityRequirement(scheme);
});
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, SwaggerOptions>();
services.AddSwaggerGen(options =>
{
options.OperationFilter<SwaggerDefaultValues>();
});
/* services.AddSwaggerGen(options =>
{ {
options.SwaggerDoc("v1", new OpenApiInfo { Title = "HeartTrackAPI", Version = "v1" }); options.SwaggerDoc("v1", new OpenApiInfo { Title = "HeartTrackAPI", Version = "v1" });
options.SwaggerDoc("v2", new OpenApiInfo { Title = "HeartTrackAPI", Version = "v2" }); options.SwaggerDoc("v2", new OpenApiInfo { Title = "HeartTrackAPI", Version = "v2" });
}); });*/
services.AddVersionedApiExplorer(setup => services.AddVersionedApiExplorer(setup =>
{ {
setup.GroupNameFormat = "'v'VVV"; setup.GroupNameFormat = "'v'VVV";
setup.SubstituteApiVersionInUrl = true; setup.SubstituteApiVersionInUrl = true;
}); });
} }
public void Configure(WebApplication app, IWebHostEnvironment env) public void Configure(WebApplication app, IWebHostEnvironment env)
{ {
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.MapIdentityApi<IdentityUser>();
app.MapControllers(); app.MapControllers();
app.UseAuthorization(); app.UseAuthorization();
app.MapIdentityApi<IdentityUser>();
app.MapHealthChecks("/health"); app.MapHealthChecks("/health");

@ -1,24 +1,25 @@
using APIMappers;
using Dto; using Dto;
using HeartTrackAPI.Request; using HeartTrackAPI.Request;
using HeartTrackAPI.Responce; using HeartTrackAPI.Responce;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Shared;
using Model; using Model;
using Shared;
using Model.Manager; using Model.Manager;
using Model.Repository; using Model.Repository;
namespace HeartTrackAPI.Controllers; namespace HeartTrackAPI.Controllers;
[ApiController] [ApiController]
[Route("api/activities")] [ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class ActivityController : Controller public class ActivityController : Controller
{ {
private readonly IActivityRepository _activityService; private readonly IActivityRepository _activityService;
private readonly ILogger<ActivityController> _logger; private readonly ILogger<ActivityController> _logger;
private readonly IActivityManager _activityManager;
public ActivityController(IActivityRepository activityService, ILogger<ActivityController> logger) public ActivityController(IDataManager dataManager, ILogger<ActivityController> logger)
{ {
_activityService = activityService; _activityService = dataManager.ActivityRepo;
_logger = logger; _logger = logger;
} }
@ -38,9 +39,12 @@ public class ActivityController : Controller
} }
_logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(GetActivities), pageRequest); _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 activities = await _activityService.GetActivities(pageRequest.Index, pageRequest.Count, ActivityOrderCriteria.None, pageRequest.Descending ?? false);
// var pageResponse = new PageResponse<ActivityDto>(pageRequest.Index, pageRequest.Count, totalCount, activities.Select(a => a.ToDto())); if(activities == null)
// return Ok(pageResponse); {
return Ok(); return NotFound("No activities found");
}
var pageResponse = new PageResponse<ActivityDto>(pageRequest.Index, pageRequest.Count, totalCount, activities.Select(a => a.ToDto()));
return Ok(pageResponse);
} }
catch (Exception e) catch (Exception e)
{ {
@ -49,21 +53,63 @@ public class ActivityController : Controller
} }
} }
/* /*
public async Task<IActionResult> PostFitFile([FromForm] IFormFile file) [HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status415UnsupportedMediaType)]
[MultipartFormData]
[DisableFormValueModelBinding]
public async Task<IActionResult> 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) if (file == null)
{ {
return BadRequest("No file was provided"); return BadRequest("No file was provided");
} }
var activity = await _activityManager.AddActivityFromFitFile(file); //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) if (activity == null)
{ {
return BadRequest("The file provided is not a valid fit file"); return BadRequest("The file provided is not a valid fit file");
} }
return CreatedAtAction(nameof(GetActivities), activity.ToDto()); return CreatedAtAction(nameof(GetActivity), new { id = activity.Id }, activity.ToDto());
}*/ }*/
/*
[HttpGet("{id}")] [HttpGet("{id}")]
public async Task<ActionResult<ActivityDto>> GetActivity(int id) public async Task<ActionResult<ActivityDto>> GetActivity(int id)
{ {
@ -75,18 +121,6 @@ public class ActivityController : Controller
return Ok(activity.ToDto()); return Ok(activity.ToDto());
} }
[HttpPost]
public async Task<ActionResult<ActivityDto>> 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}")] [HttpPut("{id}")]
public async Task<IActionResult> PutActivity(int id, ActivityDto activityDto) public async Task<IActionResult> PutActivity(int id, ActivityDto activityDto)
{ {
@ -112,5 +146,35 @@ public class ActivityController : Controller
return NotFound(); return NotFound();
} }
return NoContent(); return NoContent();
}
/*
public async Task<FileUploadSummary> 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<string>();
var notUploadedFiles = new List<string>();
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
};
}*/ }*/
} }

@ -1,30 +1,42 @@
using System.ComponentModel.DataAnnotations;
using APIMappers;
using Dto; using Dto;
using Entities;
using HeartTrackAPI.Request; using HeartTrackAPI.Request;
using HeartTrackAPI.Responce; using HeartTrackAPI.Responce;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Model;
using Model.Manager; using Model.Manager;
using Model.Repository; using Model.Repository;
using Shared; using Shared;
namespace HeartTrackAPI.Controllers; namespace HeartTrackAPI.Controllers;
/// <summary>
/// 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.
/// </summary>
[ApiController] [ApiController]
[ApiVersion("1.0")] [ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/users")] [Route("api/v{version:apiVersion}/[controller]")]
public class UsersController : Controller public class UsersController : Controller
{ {
private readonly ILogger<UsersController> _logger; private readonly ILogger<UsersController> _logger;
private readonly IActivityRepository _activityService;
private readonly IUserRepository _userService; private readonly IUserRepository _userService;
// private readonly
public UsersController(ILogger<UsersController> logger, IDataManager dataManager) public UsersController(ILogger<UsersController> logger, IDataManager dataManager)
{ {
_logger = logger; _logger = logger;
_userService = dataManager.UserRepo; _userService = dataManager.UserRepo;
_activityService = dataManager.ActivityRepo;
} }
/// <summary>
/// Récupère une page d'utilisateurs en fonction des critères de pagination et de tri fournis.
/// </summary>
/// <param name="request">Les critères de pagination et de tri pour les utilisateurs.</param>
/// <returns>Une page de données utilisateur selon les critères spécifiés.</returns>
/// <response code="200">Retourne la page demandée d'utilisateurs.</response>
/// <response code="400">La demande de pagination est invalide.</response>
/// <response code="500">Erreur interne du serveur.</response>
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(PageResponse<UserDto>), 200)] [ProducesResponseType(typeof(PageResponse<UserDto>), 200)]
[ProducesResponseType(400)] [ProducesResponseType(400)]
@ -43,22 +55,29 @@ public class UsersController : Controller
_logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(Get), null); _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 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<UserDto>(request.Index, request.Count, totalCount, athletes.Select(a => a.ToDto())); var pageResponse = new PageResponse<UserDto>(request.Index, request.Count, totalCount, athletes!.Select(a => a.ToDto()));
// return Ok(pageResponse); return Ok(pageResponse);
return Ok();
} }
catch (Exception e) catch (Exception e)
{ {
_logger.LogError(e, "Error while getting all athletes"); _logger.LogError(e, "Error while getting all athletes");
return StatusCode(500); return Problem();
} }
} }
/// <summary>
/// Récupère un utilisateur spécifique par son identifiant.
/// </summary>
/// <param name="id">L'identifiant de l'utilisateur à récupérer.</param>
/// <returns>L'utilisateur correspondant à l'identifiant spécifié.</returns>
/// <response code="200">Retourne l'utilisateur demandé.</response>
/// <response code="404">Aucun utilisateur trouvé pour l'identifiant spécifié.</response>
/// <response code="500">Erreur interne du serveur.</response>
[HttpGet("{id}")] [HttpGet("{id}")]
[ProducesResponseType(typeof(UserDto), 200)] [ProducesResponseType(typeof(UserDto), 200)]
[ProducesResponseType(404)] [ProducesResponseType(404)]
[ProducesResponseType(500)] [ProducesResponseType(500)]
public async Task<ActionResult<UserDto>> GetById(int id) public async Task<ActionResult<UserDto>> GetById([Range(0,int.MaxValue)]int id)
{ {
try try
{ {
@ -69,17 +88,21 @@ public class UsersController : Controller
_logger.LogError("Athlete with id {id} not found", id); _logger.LogError("Athlete with id {id} not found", id);
return NotFound($"Athlete with id {id} not found"); return NotFound($"Athlete with id {id} not found");
} }
// return Ok(athlete.ToDto()); return Ok(athlete.ToDto());
return Ok();
} }
catch (Exception e) catch (Exception e)
{ {
_logger.LogError(e, "Error while getting athlete by id {id}", id); _logger.LogError(e, "Error while getting athlete by id {id}", id);
return StatusCode(500); return Problem();
} }
} }
/// <summary>
/// Obtient le nombre total d'utilisateurs.
/// </summary>
/// <returns>Le nombre total d'utilisateurs.</returns>
/// <response code="200">Retourne le nombre total d'utilisateurs.</response>
/// <response code="500">Erreur interne du serveur.</response>
[HttpGet("count")] [HttpGet("count")]
[ProducesResponseType(typeof(int), 200)] [ProducesResponseType(typeof(int), 200)]
[ProducesResponseType(500)] [ProducesResponseType(500)]
@ -94,10 +117,19 @@ public class UsersController : Controller
catch (Exception e) catch (Exception e)
{ {
_logger.LogError(e, "Error while getting the number of users"); _logger.LogError(e, "Error while getting the number of users");
return StatusCode(500); return Problem();
} }
} }
/// <summary>
/// Met à jour les informations d'un utilisateur spécifique.
/// </summary>
/// <param name="id">L'identifiant de l'utilisateur à mettre à jour.</param>
/// <param name="user">Les données de l'utilisateur pour la mise à jour.</param>
/// <returns>L'utilisateur mis à jour.</returns>
/// <response code="200">Retourne l'utilisateur mis à jour.</response>
/// <response code="404">Utilisateur non trouvé.</response>
/// <response code="500">Erreur interne du serveur.</response>
[HttpPut("{id}")] [HttpPut("{id}")]
[ProducesResponseType(typeof(UserDto), 200)] [ProducesResponseType(typeof(UserDto), 200)]
[ProducesResponseType(404)] [ProducesResponseType(404)]
@ -113,23 +145,30 @@ public class UsersController : Controller
_logger.LogError("Athlete with id {id} not found", id); _logger.LogError("Athlete with id {id} not found", id);
return NotFound($"Athlete with id {id} not found"); return NotFound($"Athlete with id {id} not found");
} }
// var updatedAthlete = await _userService.UpdateItem(id, user.ToModel()); var updatedAthlete = await _userService.UpdateItem(id, user.ToModel());
// if(updatedAthlete == null) if(updatedAthlete == null)
// { {
// _logger.LogError("Error while updating athlete with id {id}", id); _logger.LogError("Error while updating athlete with id {id}", id);
// return StatusCode(500); return Problem();
// } }
// return Ok(updatedAthlete.ToDto()); return Ok(updatedAthlete.ToDto());
return Ok();
} }
catch (Exception e) catch (Exception e)
{ {
_logger.LogError(e, "Error while getting the number of users"); _logger.LogError(e, "Error while getting the number of users");
return StatusCode(500); return Problem();
} }
} }
/// <summary>
/// Supprime un utilisateur spécifique.
/// </summary>
/// <param name="id">L'identifiant de l'utilisateur à supprimer.</param>
/// <returns>Action result.</returns>
/// <response code="200">Utilisateur supprimé avec succès.</response>
/// <response code="404">Utilisateur non trouvé.</response>
/// <response code="500">Erreur interne du serveur.</response>
[HttpDelete("{id}")] [HttpDelete("{id}")]
[ProducesResponseType(200)] [ProducesResponseType(200)]
[ProducesResponseType(404)] [ProducesResponseType(404)]
@ -151,18 +190,68 @@ public class UsersController : Controller
if(!isDeleted) if(!isDeleted)
{ {
_logger.LogError("Error while deleting athlete with id {id}", id); _logger.LogError("Error while deleting athlete with id {id}", id);
return StatusCode(500); return Problem();
} }
return Ok(); return Ok();
} }
catch (Exception e) catch (Exception e)
{ {
_logger.LogError(e, "Error while getting the number of users"); _logger.LogError(e, "Error while getting the number of users");
return StatusCode(500); return Problem();
} }
} }
/// <summary>
/// Obtient la liste des amis d'un utilisateur spécifique.
/// </summary>
/// <param name="id">L'identifiant de l'utilisateur.</param>
/// <param name="request">Les critères de pagination et de tri.</param>
/// <returns>La liste paginée des amis.</returns>
/// <response code="200">Retourne la liste paginée des amis de l'utilisateur.</response>
/// <response code="404">Utilisateur non trouvé.</response>
/// <response code="500">Erreur interne du serveur.</response>
[HttpGet("{id}/friends")]
[ProducesResponseType(typeof(PageResponse<UserDto>), 200)]
[ProducesResponseType(404)]
[ProducesResponseType(500)]
public async Task<ActionResult<PageResponse<UserDto>>> 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<UserDto>(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();
}
}
/// <summary>
/// Ajoute un ami à un utilisateur spécifique.
/// </summary>
/// <param name="id">L'identifiant de l'utilisateur.</param>
/// <param name="friendId">L'identifiant de l'ami à ajouter.</param>
/// <returns>Action result.</returns>
/// <response code="200">Ami ajouté avec succès.</response>
/// <response code="404">Utilisateur ou ami non trouvé.</response>
/// <response code="500">Erreur interne du serveur.</response>
[HttpPost("{id}/friend/{friendId}")] [HttpPost("{id}/friend/{friendId}")]
[ProducesResponseType(200)] [ProducesResponseType(200)]
[ProducesResponseType(404)] [ProducesResponseType(404)]
@ -188,17 +277,160 @@ public class UsersController : Controller
if(!isAdded) if(!isAdded)
{ {
_logger.LogError("Error while adding friend with id {friendId} to athlete with id {id}", friendId, id); _logger.LogError("Error while adding friend with id {friendId} to athlete with id {id}", friendId, id);
return StatusCode(500); return Problem();
} }
return Ok(); return Ok();
} }
catch (Exception e) catch (Exception e)
{ {
_logger.LogError(e, "Error while getting the number of users"); _logger.LogError(e, "Error while getting the number of users");
return StatusCode(500); return Problem();
} }
} }
/// <summary>
/// Supprime un ami d'un utilisateur spécifique.
/// </summary>
/// <param name="id">L'identifiant de l'utilisateur.</param>
/// <param name="friendId">L'identifiant de l'ami à supprimer.</param>
/// <returns>Action result.</returns>
/// <response code="200">Ami supprimé avec succès.</response>
/// <response code="404">Utilisateur ou ami non trouvé.</response>
/// <response code="500">Erreur interne du serveur.</response>
[HttpDelete("{id}/friend/{friendId}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[ProducesResponseType(500)]
public async Task<IActionResult> 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
/// <summary>
/// Obtient la liste des athlètes d'un coach spécifique.
/// </summary>
/// <param name="coachId">L'identifiant du coach.</param>
/// <param name="request">Les critères de pagination et de tri.</param>
/// <returns>La liste paginée des athlètes.</returns>
/// <response code="200">Retourne la liste paginée des athlètes du coach.</response>
/// <response code="404">Coach non trouvé.</response>
/// <response code="500">Erreur interne du serveur.</response>
[HttpGet("{coachId}/athletes")]
[ProducesResponseType(typeof(PageResponse<UserDto>), 200)]
[ProducesResponseType(404)]
[ProducesResponseType(500)]
public async Task<ActionResult<PageResponse<UserDto>>> 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<UserDto>(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();
}
}
/// <summary>
/// Obtient la liste des activités d'un utilisateur spécifique.
/// </summary>
/// <param name="userId">L'identifiant de l'utilisateur.</param>
/// <param name="pageRequest">Les critères de pagination et de tri.</param>
/// <returns>La liste paginée des activités de l'utilisateur.</returns>
/// <response code="200">Retourne la liste paginée des activités.</response>
/// <response code="404">Aucune activité trouvée.</response>
/// <response code="500">Erreur interne du serveur.</response>
[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<ActionResult<PageResponse<ActivityDto>>> 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<ActivityDto>(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<TrainingDto>), 200)]
[ProducesResponseType(404)]
[ProducesResponseType(500)]
public async Task<ActionResult<PageResponse<TrainingDto>> GetTrainings(int userId, [FromQuery] PageRequest request)
*/
/// <summary>
/// Déconnecte l'utilisateur actuel.
/// </summary>
/// <param name="signInManager">Le gestionnaire de connexion.</param>
/// <param name="empty">Paramètre vide utilisé pour s'assurer que la requête provient bien d'un client.</param>
/// <returns>Action result.</returns>
/// <response code="200">Déconnexion réussie.</response>
/// <response code="401">Déconnexion non autorisée.</response>
/// <response code="500">Erreur interne du serveur.</response>
[HttpPost("logout")] [HttpPost("logout")]
[ProducesResponseType(200)] [ProducesResponseType(200)]
[ProducesResponseType(401)] [ProducesResponseType(401)]

@ -12,15 +12,27 @@ COPY ["StubbedContextLib/StubbedContextLib.csproj", "StubbedContextLib/"]
COPY ["Shared/Shared.csproj", "Shared/"] COPY ["Shared/Shared.csproj", "Shared/"]
COPY ["Entities/Entities.csproj", "Entities/"] COPY ["Entities/Entities.csproj", "Entities/"]
COPY ["Dto/Dto.csproj", "Dto/"] 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 ["DbContextLib/DbContextLib.csproj", "DbContextLib/"]
COPY ["Model/Model.csproj", "Model/"] COPY ["Model/Model.csproj", "Model/"]
COPY ["Model2Entities/Model2Entities.csproj", "Model2Entities/"] COPY ["Model2Entities/Model2Entities.csproj", "Model2Entities/"]
COPY ["StubAPI/StubAPI.csproj", "StubAPI/"] COPY ["StubAPI/StubAPI.csproj", "StubAPI/"]
COPY ["StubbedContextLib/StubbedContextLib.csproj", "StubbedContextLib/"] COPY ["StubbedContextLib/StubbedContextLib.csproj", "StubbedContextLib/"]
RUN dotnet restore "HeartTrackAPI/HeartTrackAPI.csproj" RUN dotnet restore "HeartTrackAPI/HeartTrackAPI.csproj"
COPY . . COPY . .
RUN echo $DOTNET_ROOT
RUN echo SHOULDDOTNET_ROOT
RUN ls
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/ --startup-project HeartTrackAPI/ --context StubbedContextLib.TrainingStubbedContext --configuration Debug Initial --output-dir Migrations
# Update the database
RUN dotnet ef database update --project StubbedContextLib/ --startup-project HeartTrackAPI/ --context StubbedContextLib.TrainingStubbedContext --configuration Debug
WORKDIR "/src/HeartTrackAPI" WORKDIR "/src/HeartTrackAPI"
RUN ls RUN ls
RUN dotnet build "HeartTrackAPI.csproj" -c $BUILD_CONFIGURATION -o /app/build RUN dotnet build "HeartTrackAPI.csproj" -c $BUILD_CONFIGURATION -o /app/build

@ -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<string> FilePaths { get; set; } = new List<string>();
public IList<string> NotUploadedFiles { get; set; } = new List<string>();
}
[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<FormValueProviderFactory>();
factories.RemoveType<FormFileValueProviderFactory>();
factories.RemoveType<JQueryFormValueProviderFactory>();
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
}

@ -5,6 +5,8 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<InvariantGlobalization>true</InvariantGlobalization> <InvariantGlobalization>true</InvariantGlobalization>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -12,23 +14,22 @@
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.1.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ApiMappeur\ApiMappeur.csproj" /> <ProjectReference Include="..\APIMappers\APIMappers.csproj" />
<ProjectReference Include="..\DbContextLib\DbContextLib.csproj" /> <ProjectReference Include="..\DbContextLib\DbContextLib.csproj" />
<ProjectReference Include="..\Dto\Dto.csproj" /> <ProjectReference Include="..\Dto\Dto.csproj" />
<ProjectReference Include="..\Model2Entities\Model2Entities.csproj" />
<ProjectReference Include="..\Model\Model.csproj" /> <ProjectReference Include="..\Model\Model.csproj" />
<ProjectReference Include="..\Shared\Shared.csproj" /> <ProjectReference Include="..\Shared\Shared.csproj" />
<ProjectReference Include="..\StubAPI\StubAPI.csproj" /> <ProjectReference Include="..\StubAPI\StubAPI.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json" Version="13.0.1">
<HintPath>..\..\..\..\..\.nuget\packages\newtonsoft.json\13.0.1\lib\netstandard2.0\Newtonsoft.Json.dll</HintPath>
</Reference>
</ItemGroup>
</Project> </Project>

@ -1,6 +0,0 @@
@HeartTrackAPI_HostAddress = http://localhost:5030
GET {{HeartTrackAPI_HostAddress}}/weatherforecast/
Accept: application/json
###

@ -1,16 +1,13 @@
using DbContextLib; using DbContextLib;
using DbContextLib.Identity;
using Entities;
using HeartTrackAPI; using HeartTrackAPI;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi.Models;
using Model.Manager;
using StubAPI;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddConsole(); builder.Logging.AddConsole();
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.Limits.MaxRequestBodySize = long.MaxValue;
});
var init = new AppBootstrap(builder.Configuration); var init = new AppBootstrap(builder.Configuration);
@ -19,5 +16,7 @@ init.ConfigureServices(builder.Services);
var app = builder.Build(); var app = builder.Build();
init.Configure(app, app.Environment); init.Configure(app, app.Environment);
// app?.Services?.GetService<HeartTrackContext>()?.Database.EnsureCreated();
app.Services.GetService<HeartTrackContext>()!.Database.EnsureCreated();
app.Run(); app.Run();

@ -1,10 +1,16 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace HeartTrackAPI.Request; namespace HeartTrackAPI.Request;
public class PageRequest public class PageRequest
{ {
public string? OrderingPropertyName { get; set; } = null;
public string? OrderingPropertyName { get; set; } = null;// need to be map on the dto OrderCriteria
public bool? Descending { get; set; } = false; public bool? Descending { get; set; } = false;
[Range(1, int.MaxValue, ErrorMessage = "Count must be greater than 0")]
public int Index { get; set; } = 0; public int Index { get; set; } = 0;
public int Count { get; set; } = 5;
[Range(1, int.MaxValue, ErrorMessage = "Count must be greater than 0")]
public int Count { get; set; } = 1;
} }

@ -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;
}
}
}

@ -35,7 +35,7 @@ public class SwaggerOptions: IConfigureNamedOptions<SwaggerGenOptions>
/// </summary> /// </summary>
/// <param name="name"></param> /// <param name="name"></param>
/// <param name="options"></param> /// <param name="options"></param>
public void Configure(string name, SwaggerGenOptions options) public void Configure(string? name, SwaggerGenOptions options)
{ {
Configure(options); Configure(options);
} }
@ -50,8 +50,12 @@ public class SwaggerOptions: IConfigureNamedOptions<SwaggerGenOptions>
{ {
var info = new OpenApiInfo() var info = new OpenApiInfo()
{ {
Title = ".NET Core (.NET 6) Web API For Lol", Title = "Web API For HeartTrack .NET 8",
Version = desc.ApiVersion.ToString() 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) if (desc.IsDeprecated)

@ -4,5 +4,8 @@
"Default": "Information", "Default": "Information",
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
},
"ConnectionStrings": {
"HeartTrackAuthConnection": "Data Source=uca.HeartTrack.db"
} }
} }

@ -31,7 +31,8 @@ public class Activity
public int Minimum { get; set; } public int Minimum { get; set; }
public float AverageTemperature { get; set; } public float AverageTemperature { get; set; }
public bool HasAutoPause { get; set; } public bool HasAutoPause { get; set; }
public HashSet<User> Users { get; private set; } = new HashSet<User>();
public Activity(int idActivity ,string type, DateTime date, DateTime startTime, DateTime endTime, public Activity(int idActivity ,string type, DateTime date, DateTime startTime, DateTime endTime,
int effort, float variability, float variance, float standardDeviation, int effort, float variability, float variance, float standardDeviation,
float average, int maximum, int minimum, float averageTemperature, bool hasAutoPause) float average, int maximum, int minimum, float averageTemperature, bool hasAutoPause)

@ -1,4 +1,3 @@
using Microsoft.AspNetCore.Http;
namespace Model.Manager; namespace Model.Manager;
public interface IActivityManager public interface IActivityManager

@ -7,18 +7,12 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Entities\Entities.csproj" /> <ProjectReference Include="..\Entities\Entities.csproj" />
<ProjectReference Include="..\Shared\Shared.csproj" /> <ProjectReference Include="..\Shared\Shared.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.2" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.2" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Identity">
<HintPath>..\..\..\..\..\.dotnet\shared\Microsoft.AspNetCore.App\8.0.1\Microsoft.AspNetCore.Identity.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -4,10 +4,12 @@ namespace Model.Repository;
public interface IActivityRepository public interface IActivityRepository
{ {
public Task<IEnumerable<Activity>> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false); public Task<IEnumerable<Activity>?> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false);
public Task<Activity?> GetActivityByIdAsync(int id); public Task<Activity?> GetActivityByIdAsync(int id);
public Task<Activity?> AddActivity(Activity activity); public Task<Activity?> AddActivity(Activity activity);
public Task<Activity?> UpdateActivity(int id, Activity activity); public Task<Activity?> UpdateActivity(int id, Activity activity);
public Task<bool> DeleteActivity(int id); public Task<bool> DeleteActivity(int id);
public Task<int> GetNbItems(); public Task<int> GetNbItems();
public Task<IEnumerable<Activity>?> GetActivitiesByUser(int userId, int index, int count, ActivityOrderCriteria orderCriteria, bool descending= false);
public Task<int> GetNbActivitiesByUser(int userId);
} }

@ -4,7 +4,14 @@ namespace Model.Repository;
public interface IUserRepository : IGenericRepository<User> public interface IUserRepository : IGenericRepository<User>
{ {
public Task<IEnumerable<User>> GetUsers(int index, int count, AthleteOrderCriteria? criteria , bool descending = false); public Task<IEnumerable<User>?> GetUsers(int index, int count, AthleteOrderCriteria? criteria , bool descending = false);
public Task<bool> AddFriend(User user, User friend); public Task<bool> AddFriend(User user, User friend);
public Task<bool> RemoveFriend(User user, User friend);
// should be removed cause i just have to call the GetItem then get the friends
public Task<IEnumerable<User>?> GetFriends(User user, int index, int count, AthleteOrderCriteria? criteria, bool descending = false);
public Task<int> GetNbFriends(User user);
} }

@ -1,31 +0,0 @@
using Entities;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
namespace Model.Service;
public class EmailSender : IEmailSender<AthleteEntity>
{
private IEmailSender<AthleteEntity> _emailSenderImplementation;
public async Task SendEmailAsync(string email, string subject, string htmlMessage)
{
throw new NotImplementedException();
}
public async Task SendConfirmationLinkAsync(AthleteEntity user, string email, string confirmationLink)
{
throw new NotImplementedException();
}
public async Task SendPasswordResetLinkAsync(AthleteEntity user, string email, string resetLink)
{
throw new NotImplementedException();
}
public async Task SendPasswordResetCodeAsync(AthleteEntity user, string email, string resetCode)
{
throw new NotImplementedException();
}
}

@ -38,7 +38,7 @@ public partial class DbDataManager : IDataManager
{ {
_logger.LogInformation($"GetActivityByIdAsync with id {id}", id); _logger.LogInformation($"GetActivityByIdAsync with id {id}", id);
// ! By None don't pass the filter // ! 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) if (activity != null)
_logger.LogInformation($"Retrieved activity with ID {id}"); _logger.LogInformation($"Retrieved activity with ID {id}");
@ -136,5 +136,15 @@ public partial class DbDataManager : IDataManager
throw; throw;
} }
} }
public Task<IEnumerable<Activity>> GetActivitiesByUser(int userId, int index, int count, ActivityOrderCriteria criteria, bool descending = false)
{
throw new NotImplementedException();
}
public Task<int> GetNbActivitiesByUser(int userId)
{
throw new NotImplementedException();
}
} }
} }

@ -18,9 +18,17 @@ public partial class DbDataManager: IDataManager
DbContext = dbContext; DbContext = dbContext;
ActivityRepo = new ActivityRepository(this); ActivityRepo = new ActivityRepository(this);
UserRepo = new UserRepository(this); UserRepo = new UserRepository(this);
DbContext.Database.EnsureCreated();
ActivityMapper.Reset(); ActivityMapper.Reset();
// Faire pour les autres reset() des autres mappers // Faire pour les autres reset() des autres mappers
} }
public DbDataManager(string dbPlatformPath)
: this(new HeartTrackContext(dbPlatformPath))
{
DbContext.Database.EnsureCreated();
}
public DbDataManager() public DbDataManager()
{ {

@ -7,28 +7,25 @@ namespace Model2Entities;
public static class Extensions public static class Extensions
{ {
internal static Task<T?> AddItem<T>(this HeartTrackContext context, T? item) where T :class internal static async Task<T?> AddItem<T>(this HeartTrackContext context, T? item) where T :class
{ {
if(item == null || context.Set<T>().Contains(item)) if(item == null || context.Set<T>().Contains(item))
{ {
return Task.FromResult<T?>(default(T)); return await Task.FromResult<T?>(null);
} }
context.Set<T>().Add(item); var entry = context.Set<T>().Add(item);
context.SaveChangesAsync(); await context.SaveChangesAsync();
return Task.FromResult<T?>(item); return await Task.FromResult<T?>(entry.Entity);
} }
internal static Task<bool> DeleteItem<T>(this HeartTrackContext context, int? id) where T:class internal static async Task<bool> DeleteItem<T>(this HeartTrackContext context, int? id) where T:class
{ {
var item = context.Set<T>().Find(id); var item = await context.Set<T>().FindAsync(id);
if(item == null) if(item == null) return await Task.FromResult(false);
{
return Task.FromResult(false);
}
context.Set<T>().Remove(item); context.Set<T>().Remove(item);
context.SaveChangesAsync(); await context.SaveChangesAsync();
return Task.FromResult(true); return await Task.FromResult(true);
} }
// ! 1 // ! 1
@ -81,10 +78,11 @@ public static class Extensions
{ {
var filteredList = list.Where(filter); var filteredList = list.Where(filter);
if(orderCriterium != null) if(orderCriterium != null && orderCriterium.ToString() != "None")
{ {
filteredList = filteredList.OrderByCriteria(orderCriterium, descending); filteredList = filteredList.OrderByCriteria(orderCriterium, descending);
} }
return filteredList return filteredList
.Skip(index * count) .Skip(index * count)
.Take(count); .Take(count);

@ -10,6 +10,14 @@
<ProjectReference Include="..\DbContextLib\DbContextLib.csproj" /> <ProjectReference Include="..\DbContextLib\DbContextLib.csproj" />
<ProjectReference Include="..\Model\Model.csproj" /> <ProjectReference Include="..\Model\Model.csproj" />
<ProjectReference Include="..\EFMappers\EFMappers.csproj" /> <ProjectReference Include="..\EFMappers\EFMappers.csproj" />
<ProjectReference Include="..\StubbedContextLib\StubbedContextLib.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -54,5 +54,20 @@ public partial class DbDataManager
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public async Task<bool> RemoveFriend(User user, User friend)
{
throw new NotImplementedException();
}
public Task<IEnumerable<User>?>? GetFriends(User user, int index, int count, AthleteOrderCriteria? criteria, bool descending = false)
{
throw new NotImplementedException();
}
public Task<int> GetNbFriends(User user)
{
throw new NotImplementedException();
}
} }
} }

@ -2,7 +2,7 @@ namespace Shared;
public static class Extensions public static class Extensions
{ {
public static U? ToU<T, U>(this T t, GenericMapper<T, U> mapper, Func<T, U> func) where U :class where T :class public static U ToU<T, U>(this T t, GenericMapper<T, U> mapper, Func<T, U> func) where U :class where T :class
{ {
var u = mapper.GetU(t); var u = mapper.GetU(t);
if (u != null) { if (u != null) {
@ -14,7 +14,7 @@ public static class Extensions
return u; return u;
} }
// , Action<T, U> action // , Action<T, U> action
public static T? ToT<T,U>(this U u, GenericMapper<T, U> mapper, Func<U, T> func) where U :class where T :class public static T ToT<T,U>(this U u, GenericMapper<T, U> mapper, Func<U, T> func) where U :class where T :class
{ {
var t = mapper.GetT(u); var t = mapper.GetT(u);
if (t != null) { if (t != null) {

@ -6,33 +6,73 @@ namespace StubAPI;
public class ActivityService: IActivityRepository public class ActivityService: IActivityRepository
{ {
public async Task<IEnumerable<Activity>> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false) private List<Activity> _activities = new List<Activity>(
{ new Activity[]
throw new NotImplementedException(); {
} 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<IEnumerable<Activity>?> 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<Activity?> GetActivityByIdAsync(int id) public Task<Activity?> GetActivityByIdAsync(int id)
{ {
throw new NotImplementedException(); return Task.FromResult(_activities.FirstOrDefault(s => s.Id == id));
} }
public async Task<Activity?> AddActivity(Activity activity) public Task<Activity?> AddActivity(Activity activity)
=> _activities.AddItem(activity);
public async Task<Activity?> 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<Activity?> UpdateActivity(int id, Activity activity) public Task<bool> 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<bool> DeleteActivity(int id) public Task<int> GetNbItems()
=> Task.FromResult(_activities.Count);
public async Task<IEnumerable<Activity>?> 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<int> GetNbItems() public Task<int> GetNbActivitiesByUser(int userId)
{ {
throw new NotImplementedException(); return Task.FromResult(_activities.Count(a => a.Users.Any(u => u.Id == userId)));
} }
} }

@ -51,14 +51,36 @@ public class UserService : IUserRepository
return true; return true;
} }
public async Task<IEnumerable<User>> GetItems(int index, int count, string? orderingProperty = null, public async Task<bool> RemoveFriend(User user, User friend)
bool descending = false)
{ {
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.Remove(friend);
return true;
}
public async Task<IEnumerable<User>?>? 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<int> GetNbFriends(User user)
{
return Task.FromResult(athletes.FirstOrDefault(s => s.Id == user.Id)?.Users.Count ?? 0);
} }
public async Task<IEnumerable<User>> GetItems(int index, int count, string? orderingProperty = null,
bool descending = false)
=>await GetUsers(index, count, this.ToEnum(orderingProperty), descending);
public async Task<User?> GetItemById(int id) public async Task<User?> GetItemById(int id)
=>await Task.FromResult(athletes.FirstOrDefault(s => s.Id == id)); =>await Task.FromResult(athletes.FirstOrDefault(s => s.Id == id));

@ -39,13 +39,15 @@ public static class Extensions
public static IEnumerable<T> GetItemsWithFilterAndOrdering<T>(this IEnumerable<T> list, Func<T, bool> filter, int index, int count, Enum? orderCriterium, bool descending = false ) where T : class public static IEnumerable<T> GetItemsWithFilterAndOrdering<T>(this IEnumerable<T> list, Func<T, bool> filter, int index, int count, Enum? orderCriterium, bool descending = false ) where T : class
{ {
var filteredList = list.Where(filter); IEnumerable<T> query = list;
query = query.Where(filter);
if(orderCriterium != null) if(orderCriterium != null)
{ {
filteredList = filteredList.OrderByCriteria(orderCriterium, descending); query = query.OrderByCriteria(orderCriterium, descending);
} }
return filteredList return query
.Skip(index * count) .Skip(index * count)
.Take(count); .Take(count);
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,16 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\DbContextLib\DbContextLib.csproj" /> <ProjectReference Include="..\DbContextLib\DbContextLib.csproj" />
<ProjectReference Include="..\Entities\Entities.csproj" /> <ProjectReference Include="..\Entities\Entities.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>

@ -1,25 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>

@ -1,10 +0,0 @@
namespace TestsXUnit;
public class UnitTest1
{
[Fact]
public void Test1()
{
}
}

@ -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<IDataManager> _dataManagerMock;
private IDataManager _dataManager;
private UsersController _usersController;
private readonly List<User> _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<IDataManager>();
_dataManagerMock.Setup(dm => dm.UserRepo.GetNbItems()).ReturnsAsync(_users.Count);
_dataManagerMock.Setup(dm =>
dm.UserRepo.GetUsers(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<AthleteOrderCriteria>(),
It.IsAny<bool>())).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<UsersController>(), _dataManagerMock.Object);
}
/*
[TestInitialize]
public void SetUp()
{
_dataManager = new StubData();
_usersController = new UsersController(new NullLogger<UsersController>(), _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<UserDto>));
var pageResponse = okResult.Value as PageResponse<UserDto>;
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<UserDto>));
var pageResponse = okResult.Value as PageResponse<UserDto>;
Assert.IsNotNull(pageResponse);
Assert.AreEqual(expectedItemCount, pageResponse.Items.Count());
}
[TestMethod]
public async Task Get_ReturnsInternalServerError_OnException()
{
_dataManagerMock.Setup(dm =>
dm.UserRepo.GetUsers(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<AthleteOrderCriteria>(),
It.IsAny<bool>()))
.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));
}
}

@ -10,7 +10,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0"/> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0"/>
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="MSTest.TestAdapter" Version="3.0.4"/> <PackageReference Include="MSTest.TestAdapter" Version="3.0.4"/>
<PackageReference Include="MSTest.TestFramework" Version="3.0.4"/> <PackageReference Include="MSTest.TestFramework" Version="3.0.4"/>
<PackageReference Include="coverlet.collector" Version="6.0.0"/> <PackageReference Include="coverlet.collector" Version="6.0.0"/>

@ -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<UsersController>(), 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<UserDto>));
}
/*
[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",
};
}*/
}
Loading…
Cancel
Save