Merge remote-tracking branch 'origin/WORK-KEV' into WORK-APE

WORK-APE
Antoine PEREDERII 1 year ago
commit de458a8f35

@ -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 ]
depends_on: [deploy-container-mysql, docker-build-and-push, deploy-container-stub]

3
.gitignore vendored

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

@ -1,3 +1,16 @@
# EF_WebAPI
This repository make a meeting of EF and WebAPI parts.
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
```

@ -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,51 @@
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
});
}
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()
});
}
}

@ -6,13 +6,15 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Entities\Entities.csproj" />
<ItemGroup>
<ProjectReference Include="..\Entities\Entities.csproj" />
</ItemGroup>
</Project>

@ -55,13 +55,31 @@ namespace DbContextLib
/// <summary>
/// Initializes a new instance of the <see cref="HeartTrackContext"/> class.
/// </summary>
public HeartTrackContext() : base() { }
public HeartTrackContext() : base()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="HeartTrackContext"/> class with the specified options.
/// </summary>
/// <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>
/// Configures the database options if they are not already configured.

@ -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<IdentityUser>
{
public AuthDbContext(DbContextOptions<AuthDbContext> options) : base(options) { }
public AuthDbContext() { }
/*
/// <summary>
/// Configures the database options if they are not already configured.
/// </summary>
/// <param name="optionsBuilder">The options builder instance.</param>
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlite($"Data Source=uca.HeartTrack.db");
}
}*/
}

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

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

@ -1,10 +1,13 @@
using Dto;
using System.Buffers;
using Dto;
using Model;
namespace ApiMappeur;
// anotine
public static class UserMappeur
{
private static readonly ArrayPool<UserDto> UserDtoPool = ArrayPool<UserDto>.Create();
private static readonly Dictionary<User, UserDto> userToDtoMap = new Dictionary<User, UserDto>();
private static readonly Dictionary<UserDto, User> dtoToUserMap = new Dictionary<UserDto, User>();
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<UserDto> UserDtoPool = ArrayPool<UserDto>.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());
}
}
}
*/

@ -29,7 +29,7 @@ namespace Entities
/// </summary>
[Required]
[MaxLength(100)]
public string Type { get; set; } = null!;
public string? Type { get; set; } = null!;
/// <summary>
/// Gets or sets the date of the activity.

@ -15,8 +15,10 @@ namespace Entities
/// Represents an athlete entity in the database.
/// </summary>
[Table("Athlete")]
public class AthleteEntity
public class AthleteEntity
{
public AthleteEntity() : base() { }
/// <summary>
/// Gets or sets the unique identifier of the athlete.
/// </summary>
@ -28,35 +30,35 @@ namespace Entities
/// Gets or sets the username of the athlete.
/// </summary>
[MaxLength(100)]
[Required(ErrorMessage = "Athlete Username is required")]
[Required(ErrorMessage = "Athlete Username is ")]
public required string Username { get; set; }
/// <summary>
/// Gets or sets the last name of the athlete.
/// </summary>
[MaxLength(100)]
[Required(ErrorMessage = "Athlete Last Name is required")]
[Required(ErrorMessage = "Athlete Last Name is ")]
public required string LastName { get; set; }
/// <summary>
/// Gets or sets the first name of the athlete.
/// </summary>
[MaxLength(150)]
[Required(ErrorMessage = "Athlete First Name is required")]
[Required(ErrorMessage = "Athlete First Name is ")]
public required string FirstName { get; set; }
/// <summary>
/// Gets or sets the email of the athlete.
/// </summary>
[MaxLength(100)]
[Required(ErrorMessage = "Athlete Email is required")]
[Required(ErrorMessage = "Athlete Email is ")]
public required string Email { get; set; }
/// <summary>
/// Gets or sets the gender of the athlete.
/// </summary>
[MaxLength(1)]
[Required(ErrorMessage = "Athlete Sexe is required")]
[Required(ErrorMessage = "Athlete Sexe is ")]
public required string Sexe { get; set; }
/// <summary>
@ -72,13 +74,13 @@ namespace Entities
/// <summary>
/// Gets or sets the password of the athlete.
/// </summary>
[Required(ErrorMessage = "Athlete Password is required")]
[Required(ErrorMessage = "Athlete Password is ")]
public required string Password { get; set; }
/// <summary>
/// Gets or sets the date of birth of the athlete.
/// </summary>
[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
/// </summary>
public bool IsCoach { get; set; }
public required byte[] ProfilPicture { get; set; }
public byte[]? ProfilPicture { get; set; }

@ -5,5 +5,4 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

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

@ -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<HeartTrackContext>(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<AuthDbContext>(options => options.UseInMemoryDatabase("AuthDb"));
Console.WriteLine(connectionString);
Console.WriteLine("======================");
//options => options.UseSqlite(connectionString)
//services.AddDbContext<HeartTrackContext>();
services.AddDbContext<HeartTrackContext>(options =>
options.UseSqlite(connectionString), ServiceLifetime.Singleton);
}
else
{
services.AddDbContext<AuthDbContext>(options => options.UseInMemoryDatabase("AuthDb"));
services.AddDbContext<HeartTrackContext>(options => options.UseInMemoryDatabase("HeartTrackDb"));
}
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)
{
//services.AddSingleton<IDataManager>(provider => new DbDataManager(provider.GetService<HeartTrackContext>()));
//services.AddSingleton<IDataManager, StubData>();
services.AddSingleton<IDataManager>(provider => new DbDataManager(provider.GetRequiredService<HeartTrackContext>()));
//services.AddTransient<IActivityManager, ActivityManager>();
}
private void AddIdentityServices(IServiceCollection services)
{
// services.AddTransient<IEmailSender<AthleteEntity>, EmailSender>();
services.AddAuthorization();
services.AddIdentityApiEndpoints<IdentityUser>()
.AddEntityFrameworkStores<AuthDbContext>();
//services.AddIdentity<AthleteEntity, IdentityRole>()
// .AddEntityFrameworkStores<AuthDbContext>().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<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("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<IdentityUser>();
app.MapControllers();
app.UseAuthorization();
app.MapHealthChecks("/health");
// Configure the HTTP request pipeline.
if (true)
{
var apiVersionDescriptionProvider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
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());
}
});
}
}
}

@ -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<ActivityController> _logger;
public ActivityController(IActivityRepository activityService, ILogger<ActivityController> logger)
public ActivityController(IDataManager dataManager, ILogger<ActivityController> 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<ActivityDto>(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<ActivityDto>(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<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)
{
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<ActionResult<ActivityDto>> GetActivity(int id)
{
@ -58,18 +121,6 @@ public class ActivityController : Controller
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}")]
public async Task<IActionResult> PutActivity(int id, ActivityDto activityDto)
{
@ -95,5 +146,35 @@ public class ActivityController : Controller
return NotFound();
}
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,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;
/// <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]
[Route("api/users")]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class UsersController : Controller
{
private readonly ILogger<UsersController> _logger;
private readonly IActivityRepository _activityService;
private readonly IUserRepository _userService;
public UsersController(ILogger<UsersController> logger, IDataManager dataManager)
{
_logger = logger;
_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]
[ProducesResponseType(typeof(PageResponse<UserDto>), 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<UserDto>(request.Index, request.Count, totalCount, athletes.Select(a => a.ToDto()));
// return Ok(pageResponse);
return Ok();
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 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}")]
[ProducesResponseType(typeof(UserDto), 200)]
[ProducesResponseType(404)]
[ProducesResponseType(500)]
public async Task<ActionResult<UserDto>> GetById(int id)
public async Task<ActionResult<UserDto>> 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();
}
}
/// <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")]
[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();
}
}
/// <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}")]
[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();
}
}
/// <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}")]
[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();
}
}
/// <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}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[ProducesResponseType(500)]
public async Task<IActionResult> 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();
}
}
/// <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")]
[ProducesResponseType(200)]
[ProducesResponseType(401)]
[ProducesResponseType(500)]
public async Task<IActionResult> Logout(SignInManager<IdentityUser> signInManager, [FromBody] object? empty)
{
if (empty == null) return Unauthorized();
await signInManager.SignOutAsync();
return Ok();
}
}

@ -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"]

@ -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,24 +5,31 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<InvariantGlobalization>true</InvariantGlobalization>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.2" />
<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.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="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\APIMappers\APIMappers.csproj" />
<ProjectReference Include="..\DbContextLib\DbContextLib.csproj" />
<ProjectReference Include="..\Dto\Dto.csproj" />
<ProjectReference Include="..\Model2Entities\Model2Entities.csproj" />
<ProjectReference Include="..\Model\Model.csproj" />
<ProjectReference Include="..\Shared\Shared.csproj" />
<ProjectReference Include="..\StubAPI\StubAPI.csproj" />
</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>

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

@ -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<IDataManager, StubData>();
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<HeartTrackContext>()!.Database.EnsureCreated();
app.MapControllers();
app.Run();

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

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

@ -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<SwaggerGenOptions>
{
private readonly IApiVersionDescriptionProvider _provider;
public SwaggerOptions(
IApiVersionDescriptionProvider provider)
{
_provider = provider;
}
/// <summary>
/// Configure each API discovered for Swagger Documentation
/// </summary>
/// <param name="options"></param>
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));
}
}
/// <summary>
/// Configure Swagger Options. Inherited from the Interface
/// </summary>
/// <param name="name"></param>
/// <param name="options"></param>
public void Configure(string? name, SwaggerGenOptions options)
{
Configure(options);
}
/// <summary>
/// Create information about the version of the API
/// </summary>
/// <param name="description"></param>
/// <returns>Information about the API</returns>
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;
}
}

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

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

@ -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<User> Users { get; private set; } = new HashSet<User>();
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)
{

@ -0,0 +1,9 @@
namespace Model.Manager;
public class ActivityManager : IActivityManager
{
public void AddActivityFromFitFile(byte filePath)
{
throw new NotImplementedException();
}
}

@ -0,0 +1,6 @@
namespace Model.Manager;
public interface IActivityManager
{
public void AddActivityFromFitFile(byte filePath);
}

@ -0,0 +1,6 @@
namespace Model.Manager;
public class UserManager
{
}

@ -7,7 +7,12 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\Shared.csproj" />
<ProjectReference Include="..\Entities\Entities.csproj" />
<ProjectReference Include="..\Shared\Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.2" />
</ItemGroup>
</Project>

@ -4,10 +4,12 @@ namespace Model.Repository;
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?> AddActivity(Activity activity);
public Task<Activity?> UpdateActivity(int id, Activity activity);
public Task<bool> DeleteActivity(int id);
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,5 +4,14 @@ namespace Model.Repository;
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> 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);
}

@ -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<Notification> Notifications { get; set; } = new List<Notification>();
public List<User> Users { get; set; } = new List<User>();
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;

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

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

@ -7,28 +7,25 @@ namespace Model2Entities;
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))
{
return Task.FromResult<T?>(default(T));
return await Task.FromResult<T?>(null);
}
context.Set<T>().Add(item);
context.SaveChangesAsync();
var entry = context.Set<T>().Add(item);
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);
if(item == null)
{
return Task.FromResult(false);
}
var item = await context.Set<T>().FindAsync(id);
if(item == null) return await Task.FromResult(false);
context.Set<T>().Remove(item);
context.SaveChangesAsync();
return Task.FromResult(true);
await context.SaveChangesAsync();
return await Task.FromResult(true);
}
internal static async Task<T?> UpdateItem<T>(this IList<T> 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);

@ -10,6 +10,14 @@
<ProjectReference Include="..\DbContextLib\DbContextLib.csproj" />
<ProjectReference Include="..\Model\Model.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>
</Project>

@ -49,5 +49,25 @@ public partial class DbDataManager
{
throw new NotImplementedException();
}
public async Task<bool> AddFriend(User user, User friend)
{
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 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);
if (u != null) {
@ -14,7 +14,7 @@ public static class Extensions
return u;
}
// , 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);
if (t != null) {

@ -6,33 +6,73 @@ namespace StubAPI;
public class ActivityService: IActivityRepository
{
public async Task<IEnumerable<Activity>> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false)
{
throw new NotImplementedException();
}
private List<Activity> _activities = new List<Activity>(
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<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)));
}
}

@ -34,14 +34,53 @@ public class UserService : IUserRepository
public async Task<IEnumerable<User>> 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<IEnumerable<User>> GetItems(int index, int count, string? orderingProperty = null,
bool descending = false)
public async Task<bool> 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<bool> 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<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)
=>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
{
var filteredList = list.Where(filter);
IEnumerable<T> 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);
}

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

@ -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>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2023.3.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.TestFramework" Version="3.0.4"/>
<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",
};
}*/
}

@ -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);
}*/
}
}

@ -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);
}*/
}
}

@ -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);
}*/
}
}

@ -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);
}*/
}
}

@ -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);
}*/
}
}

@ -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);
}*/
}
}

@ -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);
}*/
}
}

@ -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);
}*/
}
}

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

@ -10,6 +10,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.2" />
<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">
@ -22,4 +23,8 @@
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Entities\Entities.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,11 @@
namespace UnitTestsEntities2
{
public class UnitTest1
{
[Fact]
public void Test1()
{
}
}
}

@ -1,25 +1,23 @@
<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>
<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="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
</Project>
Loading…
Cancel
Save