Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// <copyright file="UpdateTournamentPictureCommand.cs" company="LeadOn's Corp'">
// Copyright (c) LeadOn's Corp'. All rights reserved.
// </copyright>

namespace GameOn.Application.FIFA.Tournaments.Commands.UpdateTournamentPicture
{
using GameOn.Common.DTOs;
using GameOn.Domain;
using MediatR;
using Microsoft.AspNetCore.Http;

/// <summary>
/// UpdateTournamentPictureCommand class.
/// </summary>
public class UpdateTournamentPictureCommand : IRequest<bool>
{
/// <summary>
/// Gets or sets Tournament ID.
/// </summary>
public int TournamentId { get; set; }

/// <summary>
/// Gets or sets File.
/// </summary>
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
public IFormFile File { get; set; }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// <copyright file="UpdateTournamentPictureCommandHandler.cs" company="LeadOn's Corp'">
// Copyright (c) LeadOn's Corp'. All rights reserved.
// </copyright>

using GameOn.Application.FIFA.Tournaments.Queries.GetTournamentById;

namespace GameOn.Application.FIFA.Tournaments.Commands.UpdateTournamentPicture
{
using GameOn.Application.Common.Players.Queries.GetPlayerById;
using GameOn.Common.Exceptions;
using GameOn.Common.Interfaces;
using GameOn.Domain;
using GameOn.External.NetworkStorage.Interfaces;
using MediatR;
using Microsoft.EntityFrameworkCore;

/// <summary>
/// UpdateTournamentPictureCommandHandler class.
/// </summary>
public class UpdateTournamentPictureCommandHandler : IRequestHandler<UpdateTournamentPictureCommand, bool>
{
private readonly IMediator mediator;
private readonly IApplicationDbContext context;
private readonly INetworkStorageService nsService;
private readonly string bucketName = Environment.GetEnvironmentVariable("S3_BUCKET_NAME") ?? throw new MissingEnvironmentVariableException("S3_BUCKET_NAME");
private readonly string tpBasePath = Environment.GetEnvironmentVariable("S3_TP_BASE_PATH") ?? throw new MissingEnvironmentVariableException("S3_TP_BASE_PATH");

/// <summary>
/// Initializes a new instance of the <see cref="UpdateTournamentPictureCommandHandler"/> class.
/// </summary>
/// <param name="context">DbContext, injected.</param>
/// <param name="nsService">NetworkStorageService, injected.</param>
/// <param name="mediator">Mediator, injected.</param>
public UpdateTournamentPictureCommandHandler(IApplicationDbContext context, INetworkStorageService nsService, IMediator mediator)
{
this.context = context;
this.nsService = nsService;
this.mediator = mediator;
}

/// <inheritdoc />
public async Task<bool> Handle(UpdateTournamentPictureCommand request, CancellationToken cancellationToken)
{
try
{
await this.nsService.UploadFile(this.bucketName, this.tpBasePath + "/" + request.TournamentId + Path.GetExtension(request.File.FileName), request.File);

// now that file is uploaded, updating user
var tournamentInDb = await this.context.Tournaments.FirstOrDefaultAsync(x => x.Id == request.TournamentId, cancellationToken);

if (tournamentInDb is not null)
{
tournamentInDb.LogoUrl = request.TournamentId + Path.GetExtension(request.File.FileName);
this.context.Tournaments.Update(tournamentInDb);
await this.context.SaveChangesAsync(cancellationToken);
}

return true;
}
catch
{
return false;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// <copyright file="GetTournamentLogoQuery.cs" company="LeadOn's Corp'">
// Copyright (c) LeadOn's Corp'. All rights reserved.
// </copyright>

namespace GameOn.Application.FIFA.Tournaments.Queries.GetTournamentLogo
{
using GameOn.Common.DTOs;
using MediatR;

/// <summary>
/// GetTournamentLogoQuery class.
/// </summary>
public class GetTournamentLogoQuery : IRequest<TournamentLogoDto>
{
/// <summary>
/// Gets or sets Tournament ID.
/// </summary>
public int TournamentId { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// <copyright file="GetTournamentLogoQueryHandler.cs" company="LeadOn's Corp'">
// Copyright (c) LeadOn's Corp'. All rights reserved.
// </copyright>

using GameOn.Application.FIFA.Tournaments.Queries.GetTournamentById;

namespace GameOn.Application.FIFA.Tournaments.Queries.GetTournamentLogo
{
using GameOn.Application.Common.Players.Queries.GetPlayerById;
using GameOn.Common.DTOs;
using GameOn.Common.Exceptions;
using GameOn.External.NetworkStorage.Interfaces;
using MediatR;

/// <summary>
/// GetTournamentLogoQueryHandler class.
/// </summary>
public class GetTournamentLogoQueryHandler : IRequestHandler<GetTournamentLogoQuery, TournamentLogoDto>
{
private readonly IMediator mediator;
private readonly INetworkStorageService nsService;
private readonly string bucketName = Environment.GetEnvironmentVariable("S3_BUCKET_NAME") ?? throw new MissingEnvironmentVariableException("S3_BUCKET_NAME");
private readonly string tpBasePath = Environment.GetEnvironmentVariable("S3_TP_BASE_PATH") ?? throw new MissingEnvironmentVariableException("S3_TP_BASE_PATH");

/// <summary>
/// Initializes a new instance of the <see cref="GetTournamentLogoQueryHandler"/> class.
/// </summary>
/// <param name="nsService">NetworkStorageService, injected.</param>
/// <param name="mediator">Mediator, injected.</param>
public GetTournamentLogoQueryHandler(INetworkStorageService nsService, IMediator mediator)
{
this.nsService = nsService;
this.mediator = mediator;
}

/// <inheritdoc />
public async Task<TournamentLogoDto> Handle(GetTournamentLogoQuery request, CancellationToken cancellationToken)
{
var tpDto = new TournamentLogoDto();

// First, getting tournament
var tournamentInDb = await this.mediator.Send(new GetTournamentByIdQuery { TournamentId = request.TournamentId }, cancellationToken);

if (tournamentInDb is null)
{
return tpDto;
}

if (tournamentInDb.LogoUrl is null || tournamentInDb.LogoUrl == string.Empty)
{
tpDto.FileName = Environment.GetEnvironmentVariable("DEFAULT_PROFILE_PIC") ?? throw new MissingEnvironmentVariableException("DEFAULT_PROFILE_PIC");
}
else
{
tpDto.FileName = tournamentInDb.LogoUrl;
}

// Getting profile picture
tpDto.FileStream = await this.nsService.GetFile(this.bucketName, this.tpBasePath + "/" + tpDto.FileName);
return tpDto;
}
}
}
3 changes: 2 additions & 1 deletion GameOn.Application/GameOn.Application.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
<Title>Game On! Application</Title>
<Authors>LeadOn</Authors>
<Company>LeadOn's Corp</Company>
<Description>Application layer of the Game On! project.</Description>
<Description>Application layer of the GameOn! app.</Description>
<Copyright>LeadOn's Corp</Copyright>
<PackageProjectUrl>https://www.valentinvirot.fr</PackageProjectUrl>
<PackageIcon>gameon-icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/LeadOn/GameOn-API</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Version>6.0.0</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
22 changes: 22 additions & 0 deletions GameOn.Common/DTOs/TournamentLogoDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// <copyright file="TournamentLogoDto.cs" company="LeadOn's Corp'">
// Copyright (c) LeadOn's Corp'. All rights reserved.
// </copyright>

namespace GameOn.Common.DTOs
{
/// <summary>
/// TournamentLogoDto class.
/// </summary>
public class TournamentLogoDto
{
/// <summary>
/// Gets or sets File Stream.
/// </summary>
public Stream? FileStream { get; set; }

/// <summary>
/// Gets or sets File name.
/// </summary>
public string FileName { get; set; } = string.Empty;
}
}
3 changes: 2 additions & 1 deletion GameOn.Common/GameOn.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Title>GameOn! Common</Title>
<Authors>LeadOn's Corp</Authors>
<Description>Common utilities used in the GameOn! project.</Description>
<Description>Common utilities used in the GameOn! app.</Description>
<Copyright>LeadOn's Corp</Copyright>
<PackageProjectUrl>https://www.valentinvirot.fr</PackageProjectUrl>
<Company>LeadOn's Corp</Company>
Expand All @@ -16,6 +16,7 @@
<RepositoryUrl>https://github.com/LeadOn/GameOn-API</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<ApplicationIcon>gameon-icon.ico</ApplicationIcon>
<Version>6.0.0</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
3 changes: 2 additions & 1 deletion GameOn.Domain/GameOn.Domain.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
<Title>GameOn! Domain</Title>
<Authors>LeadOn</Authors>
<Company>LeadOn's Corp</Company>
<Description>Domain layer of the GameOn! project.</Description>
<Description>Domain layer of the GameOn! app.</Description>
<Copyright>LeadOn's Corp</Copyright>
<PackageProjectUrl>https://www.valentinvirot.fr</PackageProjectUrl>
<PackageIcon>gameon-icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/LeadOn/GameOn-API</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Version>6.0.0</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
3 changes: 2 additions & 1 deletion GameOn.External/GameOn.External.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
<Title>GameOn! External</Title>
<Authors>LeadOn</Authors>
<Company>LeadOn's Corp</Company>
<Description>External layer of the GameOn! project.</Description>
<Description>External layer of the GameOn! app.</Description>
<Copyright>LeadOn's Corp</Copyright>
<PackageProjectUrl>https://www.valentinvirot.fr</PackageProjectUrl>
<PackageIcon>gameon-icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/LeadOn/GameOn-API</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Version>6.0.0</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
3 changes: 2 additions & 1 deletion GameOn.Persistence/GameOn.Persistence.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
<Title>GameOn! Persistence</Title>
<Authors>LeadOn</Authors>
<Company>LeadOn's Corp</Company>
<Description>Persistance layer of the GameOn! Project</Description>
<Description>Persistance layer of the GameOn! app.</Description>
<Copyright>LeadOn's Corp</Copyright>
<PackageProjectUrl>https://www.valentinvirot.fr</PackageProjectUrl>
<PackageIcon>gameon-icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/LeadOn/GameOn-API</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Version>6.0.0</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
68 changes: 67 additions & 1 deletion GameOn.Presentation/Controllers/FIFA/TournamentController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ namespace GameOn.Presentation.Controllers.FIFA
using GameOn.Application.FIFA.Tournaments.Commands.SavePhase1Score;
using GameOn.Application.FIFA.Tournaments.Commands.SubscribeTournament;
using GameOn.Application.FIFA.Tournaments.Commands.UpdateTournament;
using GameOn.Application.FIFA.Tournaments.Commands.UpdateTournamentPicture;
using GameOn.Application.FIFA.Tournaments.Queries.CheckTournamentSubscription;
using GameOn.Application.FIFA.Tournaments.Queries.GetAllTournaments;
using GameOn.Application.FIFA.Tournaments.Queries.GetFeaturedTournaments;
using GameOn.Application.FIFA.Tournaments.Queries.GetTournamentById;
using GameOn.Application.FIFA.Tournaments.Queries.GetTournamentLogo;
using GameOn.Common.DTOs;
using GameOn.Domain;
using GameOn.External.NetworkStorage.Interfaces;
using GameOn.Presentation.Classes;
using MediatR;
using Microsoft.AspNetCore.Authorization;
Expand All @@ -33,14 +36,17 @@ namespace GameOn.Presentation.Controllers.FIFA
public class TournamentController : ControllerBase
{
private readonly ISender mediator;
private readonly INetworkStorageService nsService;

/// <summary>
/// Initializes a new instance of the <see cref="TournamentController"/> class.
/// </summary>
/// <param name="mediator">MediatR interface, injected.</param>
public TournamentController(ISender mediator)
/// <param name="nsService">NetworkStorage interface, injected.</param>
public TournamentController(ISender mediator, INetworkStorageService nsService)
{
this.mediator = mediator;
this.nsService = nsService;
}

/// <summary>
Expand Down Expand Up @@ -331,5 +337,65 @@ public async Task<IActionResult> Delete(int id)
return this.NoContent();
}
}

/// <summary>
/// Upload tournament picture.
/// </summary>
/// <param name="tournamentId">Tournament ID.</param>
/// <param name="tournamentPicture">Tournament picture file.</param>
/// <returns>IActionResult.</returns>
[HttpPost]
[Authorize(Roles = "gameon_admin")]
[Route("{tournamentId}/logo")]
[SwaggerOperation(Summary = "Updates tournament picture.")]
[SwaggerResponse(201, "Tournament's picture created on the server.")]
[SwaggerResponse(401, "Unauthorized.")]
[SwaggerResponse(406, "Wrong file format.")]
[SwaggerResponse(404, "No profile picture found.")]
[SwaggerResponse(500, "Unknown error happened.")]
public async Task<IActionResult> UpdateTournamentPicture(int tournamentId, IFormFile tournamentPicture)
{
if (!CheckIfPictureFormatIsAuthorized(tournamentPicture.ContentType))
{
return this.StatusCode(406);
}

var currentPlayer = await this.mediator.Send(new UpdateTournamentPictureCommand { TournamentId = tournamentId, File = tournamentPicture });

return currentPlayer ? this.Created() : this.Problem();
}

/// <summary>
/// Gets tournament logo from network attached storage.
/// </summary>
/// <param name="tournamentId">Tournament ID.</param>
/// <returns>File.</returns>
[HttpGet]
[Route("{tournamentId}/logo")]
[SwaggerOperation(Summary = "Get tournament logo from server.")]
[SwaggerResponse(200, "Tournament logo from server.", typeof(FileStreamResult))]
[SwaggerResponse(404, "No tournament logo found.")]
[SwaggerResponse(500, "Unknown error happened.")]
public async Task<IActionResult> GetTournamentLogo(int tournamentId)
{
var tpDto = await this.mediator.Send(new GetTournamentLogoQuery { TournamentId = tournamentId });

if (tpDto.FileStream is null)
{
return this.NotFound();
}

return this.File(tpDto.FileStream, this.nsService.GetContentType(tpDto.FileName));
}

/// <summary>
/// Checks if file type is valid.
/// </summary>
/// <param name="contentType">File content type.</param>
/// <returns>True if valid, false if not.</returns>
private static bool CheckIfPictureFormatIsAuthorized(string contentType)
{
return contentType is "image/jpeg" or "image/png" or "image/gif" or "image/webp";
}
}
}
Loading