<file_sep>﻿using GamingWorld.API.Security.Domain.Models;

namespace GamingWorld.API.Business.Domain.Models
{
    public class Participant
    {
        public int Id { get; set; }

        //Relationships
        public int Points { get; set; }

        public int UserId { get; set; }
        
        public User User { get; set; }
        
        public int TournamentId { get; set; }
    }
}<file_sep>﻿using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using GamingWorld.API.Shared.Inbound.Games.Domain.Models;
using GamingWorld.API.Shared.Inbound.Games.Domain.Services;
using Microsoft.AspNetCore.Mvc;

namespace GamingWorld.API.Shared.Inbound.Games.Controllers
{
    [ApiController]
    [Route("/api/v1/[controller]")]
    public class GamesController : ControllerBase
    {
        private readonly IGamesService _gamesService;
        private readonly int DEFAULT_LIMIT = 10;

        public GamesController(IGamesService gamesService)
        {
            _gamesService = gamesService;
        }
        
        [HttpGet]
        public async Task<IEnumerable<Game>> GetRandomListAsync(int limit)
        {
            return await _gamesService.ListRandomAsync(limit);
        }

        [HttpGet("{id}")]
        public async Task<Game> GetByIdAsync(int id)
        {
            return await _gamesService.GetById(id);
        }
        
        
        [HttpGet("find")]
        public async Task<IEnumerable<Game>> GetByNameAsync(string name, int limit)
        {
            return await _gamesService.FindByName(name, limit);
        }
        
        [HttpGet("top")]
        public async Task<string> GetTopGames(int limit)
        {
            return await _gamesService.FindTopGames(limit);
        }
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using GamingWorld.API.Business.Domain.Models;


namespace GamingWorld.API.Business.Domain.Repositories
{
    public interface ITournamentRepository
    {
        Task<IEnumerable<Tournament>> ListAsync();

        Task AddAsync(Tournament tournament);

        Task<Tournament> FindByIdAsync(int id);

        Tournament FindById(int id);

        Task<Tournament> ListWithParticipantsById(int id);

        void Update(Tournament tournament);
        
        void Remove(Tournament tournament);
    }
}<file_sep>﻿using System.Collections.Generic;
using GamingWorld.API.Profiles.Domain.Models;
using GamingWorld.API.Security.Resources;

namespace GamingWorld.API.Business.Resources
{
    public class ParticipantResource
    {
        public int Id { get; set;  }
        public UserResource User { get; set;  }
        public int TournamentId { get; set; }
        public int Points { get; set; }

    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using GamingWorld.API.Business.Domain.Models;
using GamingWorld.API.Business.Domain.Repositories;
using GamingWorld.API.Shared.Persistence.Contexts;
using GamingWorld.API.Shared.Persistence.Repositories;
using Microsoft.EntityFrameworkCore;

namespace GamingWorld.API.Business.Persistence.Repositories
{
    public class TournamentRepository : BaseRepository, ITournamentRepository
    {
        public TournamentRepository(AppDbContext context) : base(context)
        {
        }

        public async Task<IEnumerable<Tournament>> ListAsync()
        {
            return await _context.Tournaments.ToListAsync();
        }
        

        public async Task AddAsync(Tournament tournament)
        {
            await _context.Tournaments.AddAsync(tournament);
        }
        
        public Tournament FindById(int id)
        {
            return _context.Tournaments.Find(id);
        }
        
        public async Task<Tournament> ListWithParticipantsById(int id)
        {
            return await _context.Tournaments.Include(t=> t.Participants).ThenInclude(u=>u.User).FirstOrDefaultAsync(t=>t.Id==id);
        }

        public async Task<Tournament> FindByIdAsync(int id)
        {
            return await _context.Tournaments.FindAsync(id);
        }

        public void Update(Tournament tournament)
        {
            _context.Tournaments.Update(tournament);
        }

        public void Remove(Tournament tournament)
        {
            _context.Tournaments.Remove(tournament);
        }
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using GamingWorld.API.Security.Domain.Models;

namespace GamingWorld.API.Security.Domain.Repositories
{
    public interface IUserRepository
    {
        Task<IEnumerable<User>> ListAsync();
        Task AddAsync(User user);
        Task<User> FindByIdAsync(int id);
        Task<User> FindByUsernameAsync(string username);
        bool ExistsByUsername(string username);
        void Update(User user);
        void Remove(User user);
        Task<User> FindByEmailAsync(string email);
    }
}<file_sep>﻿using System.ComponentModel;

namespace GamingWorld.API.Profiles.Domain.Models
{
    public enum EGamingLevel : short
    {
        [Description("Newbie")]
        Newbie = 1,
        [Description("Medium")]
        Medium = 2,
        [Description("Advanced")]
        Advanced = 3,
    }
}<file_sep>﻿using System;
using System.Linq;
using System.Net.Http;
using System.Net.Mime;
using System.Text;
using System.Threading.Tasks;
using GamingWorld.API;
using GamingWorld.API.Profiles.Resources;
using Microsoft.AspNetCore.Mvc.Testing;
using Newtonsoft.Json;
using SpecFlow.Internal.Json;
using TechTalk.SpecFlow;
using TechTalk.SpecFlow.Assist;
using Xunit;

namespace GamingWorldBackEnd.Tests.UserProfiles
{
    [Binding]
    public class PostUserProfileServiceStepsDefinition
    {
        private readonly WebApplicationFactory<Startup> _factory;
        private HttpClient _client;
        private Uri _baseUri;
        private Task<HttpResponseMessage> Response { get; set; }
        public PostUserProfileServiceStepsDefinition(WebApplicationFactory<Startup> factory)
        {
            _factory = factory;
        }
        
        [Given(@"the endpoint https://localhost:(.*)/api/v(.*)/profiles is available")]
        public void GivenTheEndpointHttpLocalhostApiVUserprofilesIsAvailable(int port, int version)
        {
            _baseUri = new Uri($"https://localhost:{port}/api/v{version}/profiles");
            _client = _factory.CreateClient(new WebApplicationFactoryClientOptions {BaseAddress = _baseUri});
        }

        [When(@"a POST Request is sent with this body")]
        public void WhenApostRequestIsSentWithThisBody(Table saveUserProfileResource)
        {
            var resource = saveUserProfileResource.CreateSet<SaveProfileResource>().First();
            var content = new StringContent(resource.ToJson(), Encoding.UTF8, MediaTypeNames.Application.Json);
            Response = _client.PostAsync(_baseUri, content);
        }
        
        
        [Then(@"a UserProfile resource is included in the response body\.")]
        public async void ThenAUserProfileResourceIsIncludedInTheResponseBody(Table expectedUserProfileResource)
        {
            var expectedResource = expectedUserProfileResource.CreateSet<ProfileResource>().First();
            var responseData = await Response.Result.Content.ReadAsStringAsync();
            var resource = JsonConvert.DeserializeObject<ProfileResource>(responseData);
            expectedResource.Id = resource.Id;
            var jsonExpectedResource = expectedResource.ToJson();
            var jsonActualResource = resource.ToJson();
            Assert.Equal(jsonExpectedResource, jsonActualResource);
        }

       
    }
}<file_sep>﻿using System;
using System.Collections.Generic;
using System.Linq;
using GamingWorld.API.Profiles.Domain.Models;
using GamingWorld.API.Publications.Domain.Models;
using GamingWorld.API.Publications.Resources;

namespace GamingWorld.API.Business.Domain.Models
{
    public class Tournament
    {
        public int Id { get; set; }

        //Relationships

        public int ParticipantLimit { get; set; }
             
        public int PrizePool { get; set; }
        
        public string TournamentDate { get; set; }
        
        public Publication Publication { get; set; }
        public string TournamentHour { get; set; }
        public IEnumerable<Participant> Participants { get; set; }
        
        public bool TournamentStatus { get; set; }
        

    }
}<file_sep>﻿using AutoMapper;
using GamingWorld.API.Security.Domain.Models;
using GamingWorld.API.Security.Domain.Services.Communication;
using GamingWorld.API.Security.Resources;

namespace GamingWorld.API.Security.Mapping
{
    public class ModelToResourceProfile : Profile
    {
        public ModelToResourceProfile()
        {
            CreateMap<User, AuthenticateResponse>();
            
            CreateMap<User, UserResource>();

        }
    }
}<file_sep>﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapper;
using GamingWorld.API.Business.Domain.Models;
using GamingWorld.API.Business.Domain.Repositories;
using GamingWorld.API.Business.Domain.Services;
using GamingWorld.API.Business.Domain.Services.Communication;
using GamingWorld.API.Business.Resources;
using GamingWorld.API.Security.Exceptions;
using GamingWorld.API.Shared.Domain.Repositories;

namespace GamingWorld.API.Business.Services
{
    public class TournamentService : ITournamentService
    {
        
        private readonly ITournamentRepository _tournamentRepository;
        private readonly IParticipantRepository _participantRepository;
        private readonly IMapper _mapper;
        private readonly IUnitOfWork _unitOfWork;

        public TournamentService(IMapper mapper, ITournamentRepository tournamentRepository, IUnitOfWork unitOfWork, IParticipantRepository participantRepository)
        {
            _mapper = mapper;
            _tournamentRepository = tournamentRepository;
            _unitOfWork = unitOfWork;
            _participantRepository = participantRepository;
        }

        public async Task<IEnumerable<Tournament>> ListAsync()
        {
            return await _tournamentRepository.ListAsync();
        }

        public async Task<TournamentResponse> SaveAsync(Tournament tournament)
        {
            //Validate
            
            try
            {
                await _tournamentRepository.AddAsync(tournament);
                await _unitOfWork.CompleteAsync();

                return new TournamentResponse(tournament);
            }
            catch (Exception e)
            {
                return new TournamentResponse($"An error occured while saving the publication: {e.Message}");
            }
        }

        public async Task<Tournament> ListWithParticipantsByIdAsync(int tournamentId)
        {
            return await _tournamentRepository.ListWithParticipantsById(tournamentId);
        }

        public async Task<ParticipantResponse> UpdateParticipantPoints(int tournamentId, int participantId, int points)
        {
            var existingTournament = await _tournamentRepository.FindByIdAsync(tournamentId);
            if (existingTournament == null)
                return new ParticipantResponse("Tournament Not Found");
            
            var existingParticipant = await _participantRepository.FindByIdAsync(participantId);
            if (existingParticipant == null)
                return new ParticipantResponse("Participant Not Found");
            
            if (points < 0)
                return new ParticipantResponse("Points must be positive.");
            
            try
            {
                existingParticipant.Points = points;
                _participantRepository.Update(existingParticipant);
                await _unitOfWork.CompleteAsync();
                return new ParticipantResponse(existingParticipant);
            }
            catch (Exception e)
            {
                return new ParticipantResponse($"An error occurred while updating the participant.");
            }
        }

        public async Task<TournamentResponse> UpdateAsync(int id, Tournament tournament)
        {
            var existingTournament = await _tournamentRepository.FindByIdAsync(id);
            if (existingTournament == null)
                return new TournamentResponse("Tournament Not Found");
            existingTournament.Id = tournament.Id;
            
            try
            {
                _tournamentRepository.Update(tournament);
                await _unitOfWork.CompleteAsync();
                return new TournamentResponse(existingTournament);
            }
            catch (Exception e)
            {
                return new TournamentResponse($"An error occurred while updating the user: {tournament}");
            }
        }

        public async Task<TournamentResponse> DeleteAsync(int id)
        {
            var existingTournament =  await GetById(id);

            if (existingTournament == null)
                return new TournamentResponse("Tournament not found");
            try
            {
                _tournamentRepository.Remove(existingTournament);
                await _unitOfWork.CompleteAsync();

                return new TournamentResponse(existingTournament);
            }
            catch (Exception e)
            {
                return new TournamentResponse($"An error occurred while deleting publication:{e.Message}");
            }
        }
        
        public async Task<Tournament> GetById(int id)
        {
            var tournament = await _tournamentRepository.FindByIdAsync(id);
            await _unitOfWork.CompleteAsync();
            if (tournament == null) throw new KeyNotFoundException("Tournament not found.");
            return tournament;
        }

        public async Task<TournamentResponse> EndTournament(int id, bool status)
        {
            var tournament = await _tournamentRepository.FindByIdAsync(id);
            await _unitOfWork.CompleteAsync();
            if (tournament == null) throw new KeyNotFoundException("Tournament not found.");
            tournament.TournamentStatus = status;
            _tournamentRepository.Update(tournament);
            await _unitOfWork.CompleteAsync();
            return new TournamentResponse(tournament);
            
        }
        
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using GamingWorld.API.Security.Domain.Models;
using GamingWorld.API.Security.Domain.Services.Communication;

namespace GamingWorld.API.Security.Domain.Services
{
    public interface IUserService
    {
        
        Task<AuthenticateResponse> Authenticate(AuthenticateRequest request);
        Task RegisterAsync(RegisterRequest request);
        Task<IEnumerable<User>> ListAsync();
        Task<User> GetByIdAsync(int userId);
        Task<User> ListByUserUsernameAsync(int username);
        Task<UserResponse> SaveAsync(User user);
        Task<UserResponse> UpdateAsync(int id, User user);
        Task<UserResponse> DeleteAsync(int id);
    }
}<file_sep>﻿using AutoMapper;
using GamingWorld.API.Publications.Domain.Models;
using GamingWorld.API.Publications.Resources;

namespace GamingWorld.API.Publications.Mapping
{
    public class ModelToResourceProfile : Profile
    {
        public ModelToResourceProfile()
        {
            CreateMap<Publication, PublicationResource>();
        }
        
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using GamingWorld.API.Publications.Domain.Models;

namespace GamingWorld.API.Publications.Domain.Repositories
{
    public interface IPublicationRepository
    {
        Task<IEnumerable<Publication>> ListAsync();
        
        Task<IEnumerable<Publication>> ListByTypeAsync();
        
        Task AddAsync(Publication publication);

        Task<Publication> FindByIdAsync(int id);

        void Update(Publication publication);
        
        void Remove(Publication publication);
    }
}<file_sep>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using GamingWorld.API.Profiles.Domain.Models;
using GamingWorld.API.Profiles.Domain.Repositories;
using GamingWorld.API.Profiles.Domain.Services;
using GamingWorld.API.Profiles.Domain.Services.Communication;
using GamingWorld.API.Security.Domain.Repositories;
using IUnitOfWork = GamingWorld.API.Shared.Domain.Repositories.IUnitOfWork;

namespace GamingWorld.API.Profiles.Services
{
    public class ProfileService : IUProfileService
    {
        private readonly IProfileRepository _profileRepository;
        private readonly IUserRepository _userRepository;
        private readonly IUnitOfWork _unitOfWork;

        public ProfileService(IProfileRepository profileRepository, IUserRepository userRepository, IUnitOfWork unitOfWork)
        {
            _profileRepository = profileRepository;
            _userRepository = userRepository;
            _unitOfWork = unitOfWork;
        }
        public async Task<IEnumerable<Profile>> ListAsync()
        {
            return await _profileRepository.ListAsync();
        }

        public async Task<Profile> ListByUserIdAsync(int userId)
        {
            return await _profileRepository.FindByUserId(userId);
        }

        public async Task<Profile> ListByIdAsync(int id)
        {
            return await _profileRepository.FindByIdAsync(id);
        }

        public async Task<ProfileResponse> SaveAsync(Profile profile)
        {
            var existingUserId = await _profileRepository.FindByUserId(profile.UserId);
            if (existingUserId != null)
                return new ProfileResponse("User already has a profile.");

            var user = await _userRepository.FindByIdAsync(profile.UserId);
            if (user == null)
                return new ProfileResponse("User not found");

            try
            {
                await _profileRepository.AddAsync(profile);
                await _unitOfWork.CompleteAsync();

                return new ProfileResponse(profile);
            }
            catch (Exception e)
            {
                return new ProfileResponse($"An error occurred while saving the profile: {e.Message}");
            }
        }

        public async Task<ProfileResponse> UpdateAsync(int id, Profile profile)
        {
            var existingProfile = await _profileRepository.FindByIdAsync(id);

            if (existingProfile == null)
                return new ProfileResponse("Profile not found.");
            
            existingProfile.GamingLevel = profile.GamingLevel;
            existingProfile.IsStreamer = profile.IsStreamer;
            existingProfile.GameExperiences = profile.GameExperiences;
            existingProfile.FavoriteGames = profile.FavoriteGames;
            existingProfile.StreamingCategories = profile.StreamingCategories;
            existingProfile.TournamentExperiences = profile.TournamentExperiences;
            existingProfile.StreamerSponsors = profile.StreamerSponsors;

            try
            {
                _profileRepository.Update(existingProfile);
                await _unitOfWork.CompleteAsync();

                return new ProfileResponse(existingProfile);
            }
            catch (Exception e)
            {
                return new ProfileResponse($"An error occurred while updating the profile: {e.Message}");
            }
        }

        public async Task<ProfileResponse> DeleteAsync(int id)
        {
            var existingProfile = await _profileRepository.FindByIdAsync(id);

            if (existingProfile == null)
                return new ProfileResponse("Profile not found. ");

            try
            {
                _profileRepository.Remove(existingProfile);
                await _unitOfWork.CompleteAsync();

                return new ProfileResponse(existingProfile);
            }
            catch (Exception e)
            {
                return new ProfileResponse($"An error occurred while deleting the profile: {e.Message}");
            }
        }
    }
}<file_sep>using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using AutoMapper;
using GamingWorld.API.Business.Domain.Repositories;
using GamingWorld.API.Business.Domain.Services;
using GamingWorld.API.Business.Persistence.Repositories;
using GamingWorld.API.Business.Services;
using GamingWorld.API.Profiles.Domain.Repositories;
using GamingWorld.API.Profiles.Domain.Services;
using GamingWorld.API.Profiles.Persistence.Repositories;
using GamingWorld.API.Profiles.Services;
using GamingWorld.API.Publications.Domain.Repositories;
using GamingWorld.API.Publications.Domain.Services;
using GamingWorld.API.Publications.Persistence.Repositories;
using GamingWorld.API.Publications.Services;
using GamingWorld.API.Security.Authorization.Handlers.Implementations;
using GamingWorld.API.Security.Authorization.Handlers.Interfaces;
using GamingWorld.API.Security.Authorization.Middleware;
using GamingWorld.API.Security.Authorization.Settings;
using GamingWorld.API.Security.Domain.Repositories;
using GamingWorld.API.Security.Domain.Services;
using GamingWorld.API.Security.Persistence.Repositories;
using GamingWorld.API.Security.Services;
using GamingWorld.API.Shared.Inbound.ExternalAPIs.Domain.Repositories;
using GamingWorld.API.Shared.Inbound.ExternalAPIs.Persistence.Repositories;
using GamingWorld.API.Shared.Inbound.Games.Domain.Services;
using GamingWorld.API.Shared.Inbound.Games.Services;
using GamingWorld.API.Shared.Inbound.News.Domain.Services;
using GamingWorld.API.Shared.Inbound.News.Services;
using GamingWorld.API.Shared.Persistence.Contexts;
using GamingWorld.API.Shared.Persistence.Repositories;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using IUnitOfWork = GamingWorld.API.Shared.Domain.Repositories.IUnitOfWork;

namespace GamingWorld.API
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddCors();
            services.AddControllers();
            services.AddRouting(options => options.LowercaseUrls = true);
            
            services.AddDbContext<AppDbContext>(options =>
            {
                //options.UseInMemoryDatabase("supermarket-api-in-memory");
                options.UseMySQL(Configuration.GetConnectionString("Default"));
            });
            
            services.AddSwaggerGen(c =>
            {
                c.DocumentFilter<SnakeCaseDocumentFilter>();
                c.OperationFilter<SnakeCaseOperationFilter>();
                c.SwaggerDoc("v1", new OpenApiInfo {Title = "GamingWorld.API", Version = "v1"});
            });

            services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
            
            services.AddScoped<IUserRepository, UserRepository>();
            services.AddScoped<IUserService, UserService>();
            
            services.AddScoped<IPublicationRepository, PublicationRepository>();
            services.AddScoped<IPublicationService, PublicationService>();
            
            services.AddScoped<ITournamentRepository, TournamentRepository>();
            services.AddScoped<ITournamentService, TournamentService>();
            
            services.AddScoped<IParticipantRepository, ParticipantRepository>();
            services.AddScoped<IParticipantService, ParticipantService>();

            services.AddScoped<IProfileRepository, ProfileRepository>();
            services.AddScoped<IUProfileService, ProfileService>();

            services.AddScoped<IExternalAPIRepository, ExternalAPIRepository>();
            
            services.AddScoped<IGamesService, GamesService>();

            services.AddScoped<INewsService, NewsService>();
            
            services.AddScoped<IUnitOfWork, UnitOfWork>();
            
            services.AddScoped<IJwtHandler, JwtHandler>();
            
            services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            // For this course purpose we allow Swagger in release mode.
            app.UseDeveloperExceptionPage();
            app.UseSwagger();
            app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "GamingWorld.API v1"));

            app.UseMiddleware<ErrorHandlerMiddleware>();

            app.UseMiddleware<JwtMiddleware>();

            app.UseCors(builder => builder
                .AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader());

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
        }
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using GamingWorld.API.Business.Domain.Models;


namespace GamingWorld.API.Business.Domain.Repositories
{
    public interface IParticipantRepository
    {
        Task<IEnumerable<Participant>> ListAsync();

        Task AddAsync(Participant participant);

        Task<Participant> FindByIdAsync(int id);

        void Update(Participant participant);
        Participant FindById(int id);
        void Remove(Participant participant);
    }
}<file_sep>﻿using System.ComponentModel.DataAnnotations;

namespace GamingWorld.API.Publications.Resources
{
    public class SavePublicationResource
    {

        [Required] public short PublicationType { get; set; }
             
        [Required] [MinLength(15)] public string Title { get; set; }
             
        [Required] [MinLength(40)] public string Content { get; set; }
             
        public int ParticipantLimit { get; set; }
             
        public int PrizePool { get; set; }

        public string TournamentDate { get; set; }
             
        public string TournamentHour { get; set; }
        
        public string UrlToImage { get; set; }
             
        [Required] public string CreatedAt { get; set; }
        
        public int GameId { get; set; }
             
       [Required] public int UserId { get; set; }

    }
}

<file_sep>#nullable enable
using System;
using System.Globalization;

namespace GamingWorld.API.Security.Exceptions
{
    public class AppException : Exception
    {
        public AppException() : base()
        {
        }

        public AppException(string? message) : base(message)
        {
        }

        public AppException(string message, params object[] args)
        : base(String.Format(CultureInfo.CurrentCulture, message, args))
        {
            
        }
    }
}<file_sep>﻿using System;
using System.ComponentModel.DataAnnotations;

namespace GamingWorld.API.Business.Resources
{
    public class SaveTournamentResource
    {
        
        //Relationships
        [Required]
        public int PublicationId { get; set; }
        
        [Required]
        public int ParticipantLimit { get; set; }
        
        [Required]
        public int PrizePool { get; set; }
        
        [Required]
        public string TournamentDate { get; set; }
        
        [Required]
        public string TournamentHour { get; set; }
        

    }
}

<file_sep>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using GamingWorld.API.Business.Domain.Models;
using GamingWorld.API.Business.Domain.Repositories;
using GamingWorld.API.Business.Domain.Services;
using GamingWorld.API.Business.Domain.Services.Communication;
using GamingWorld.API.Security.Domain.Repositories;
using GamingWorld.API.Security.Exceptions;
using GamingWorld.API.Shared.Domain.Repositories;

namespace GamingWorld.API.Business.Services
{
    public class ParticipantService : IParticipantService
    {
        
        private readonly IParticipantRepository _participantRepository;
        private readonly ITournamentRepository _tournamentRepository;
        private readonly IMapper _mapper;
        private readonly IUnitOfWork _unitOfWork;

        public ParticipantService(IMapper mapper, IParticipantRepository participantRepository, IUnitOfWork unitOfWork, ITournamentRepository tournamentRepository)
        {
            _mapper = mapper;
            _participantRepository = participantRepository;
            _unitOfWork = unitOfWork;
            _tournamentRepository = tournamentRepository;
        }

        public async Task<IEnumerable<Participant>> ListAsync()
        {
            return await _participantRepository.ListAsync();
        }

        public async Task<ParticipantResponse> SaveAsync(int tournamentId, Participant participant)
        {
            var tournament = await _tournamentRepository.ListWithParticipantsById(tournamentId);
            if(tournament==null)
                return new ParticipantResponse("Tournament Not Found");
            if (tournament.Participants != null)
            {
                if (tournament.Participants.Count() >= tournament.ParticipantLimit)
                    return new ParticipantResponse("This tournament is full.");
                if (tournament.Participants.Any(x => x.UserId == participant.UserId))
                    return new ParticipantResponse("This user already is a participant.");
            }

            try
            {
                participant.TournamentId = tournamentId;
                await _participantRepository.AddAsync(participant);
                await _unitOfWork.CompleteAsync();

                return new ParticipantResponse(participant);
            }
            catch (Exception e)
            {
                return new ParticipantResponse($"An error occured while saving the publication: {e.Message}");
            }
        }

        public async Task<bool> ValidateParticipantTournament(int tournamentId, int participantId)
        {
            var tournament = await _tournamentRepository.ListWithParticipantsById(tournamentId);
            var participant = await _participantRepository.FindByIdAsync(participantId);
            if(tournament==null)
                return true;
            if (tournament.Participants != null)
            {
                if (tournament.Participants.Count() >= tournament.ParticipantLimit)
                    return false;
                if (tournament.Participants.Any(x => x.UserId == participant.UserId))
                    return false;
            }

            return true;
        }
        public async Task<ParticipantResponse> UpdateAsync(int id, Participant tournament)
        {
            var existingParticipant = GetById(id);
            if (existingParticipant == null)
                return new ParticipantResponse("Participant Not Found");
            existingParticipant.Points = tournament.Points;
            
            try
            {
                _participantRepository.Update(tournament);
                await _unitOfWork.CompleteAsync();
                return new ParticipantResponse(existingParticipant);
            }
            catch (Exception e)
            {
                return new ParticipantResponse($"An error occurred while updating the user: {tournament}");
            }
        }

        public async Task<ParticipantResponse> DeleteAsync(int id)
        {
            var existingParticipant =  GetById(id);

            if (existingParticipant == null)
                return new ParticipantResponse("Participant not found");
            try
            {
                _participantRepository.Remove(existingParticipant);
                await _unitOfWork.CompleteAsync();

                return new ParticipantResponse(existingParticipant);
            }
            catch (Exception e)
            {
                return new ParticipantResponse($"An error occurred while deleting participant:{e.Message}");
            }
        }
        
        private Participant GetById(int id)
        {
            var participant = _participantRepository.FindById(id);
            if (participant == null) throw new KeyNotFoundException("Participant not found.");
            return participant;
        }
        
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using GamingWorld.API.Business.Domain.Models;
using GamingWorld.API.Business.Domain.Repositories;
using GamingWorld.API.Shared.Persistence.Contexts;
using GamingWorld.API.Shared.Persistence.Repositories;
using Microsoft.EntityFrameworkCore;

namespace GamingWorld.API.Business.Persistence.Repositories
{
    public class ParticipantRepository : BaseRepository, IParticipantRepository
    {
        public ParticipantRepository(AppDbContext context) : base(context)
        {
        }

        public async Task<IEnumerable<Participant>> ListAsync()
        {
            return await _context.Participants.ToListAsync();
        }

        public async Task AddAsync(Participant participant)
        {
            await _context.Participants.AddAsync(participant);
        }

        public async Task<Participant> FindByIdAsync(int id)
        {
            return await _context.Participants.FindAsync(id);
        }

        public void Update(Participant participant)
        {
            _context.Participants.Update(participant);
        }

        public Participant FindById(int id)
        {
            return _context.Participants.Find(id);
        }

        public void Remove(Participant participant)
        {
            _context.Participants.Remove(participant);
        }
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using GamingWorld.API.Business.Domain.Models;
using GamingWorld.API.Business.Domain.Services.Communication;
using GamingWorld.API.Profiles.Domain.Services.Communication;

namespace GamingWorld.API.Business.Domain.Services
{
    public interface IParticipantService
    {
        Task<IEnumerable<Participant>> ListAsync();
        Task<ParticipantResponse> SaveAsync(int tournamentId, Participant participant);
        Task<ParticipantResponse> UpdateAsync(int id, Participant participant);
        Task<ParticipantResponse> DeleteAsync(int id);
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using GamingWorld.API.Security.Domain.Models;
using GamingWorld.API.Security.Domain.Repositories;
using GamingWorld.API.Shared.Persistence.Contexts;
using GamingWorld.API.Shared.Persistence.Repositories;
using Microsoft.EntityFrameworkCore;

namespace GamingWorld.API.Security.Persistence.Repositories
{
    public class UserRepository : BaseRepository, IUserRepository
    {
        public UserRepository(AppDbContext context) : base(context)
        {
        }

        public async Task<IEnumerable<User>> ListAsync()
        {
            return await _context.Users.ToListAsync();
        }
        
        public async Task AddAsync(User user)
        {
            await _context.Users.AddAsync(user);
        }

        public async Task<User> FindByIdAsync(int id)
        {
            return await _context.Users.FirstOrDefaultAsync(p => p.Id == id);
        }

        public async Task<User> FindByUsernameAsync(string username)
        {
            return await _context.Users.FirstOrDefaultAsync(p => p.Username == username);
        }

        public bool ExistsByUsername(string username)
        {
            return _context.Users.Any(u => u.Username == username);
        }

        public async Task<User> FindByEmailAsync(string email)
        {
            return await _context.Users.FirstOrDefaultAsync(p => p.Email == email);
        }
        
        public void Update(User user)
        {
            _context.Users.Update(user);
        }

        public void Remove(User user)
        {
            _context.Users.Remove(user);
        }
    }
}<file_sep>﻿using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using GamingWorld.API.Security.Exceptions;
using GamingWorld.API.Shared.Inbound.News.Domain.Services;

namespace GamingWorld.API.Shared.Inbound.News.Services
{
    public class NewsService: INewsService
    {
        private readonly string NEWS_URL = "https://newsapi.org/v2/everything";
        
        public async Task<string> ListByTheme(string theme)
        {
            using var client = new HttpClient();
            var builder = new UriBuilder(NEWS_URL);
            builder.Port = -1;
            var query = HttpUtility.ParseQueryString(builder.Query);
            query["q"] = theme;
            query["language"] = "es";
            query["apiKey"] = "<KEY>";
                
            builder.Query = query.ToString();

            string requestURL = builder.ToString();

            var content = await client.GetStringAsync(requestURL);

            return content;
        }
    }
}<file_sep>﻿using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Mime;
using System.Text;
using System.Threading.Tasks;
using GamingWorld.API;
using GamingWorld.API.Security.Domain.Services.Communication;
using GamingWorld.API.Security.Resources;
using Microsoft.AspNetCore.Mvc.Testing;
using Newtonsoft.Json;
using SpecFlow.Internal.Json;
using TechTalk.SpecFlow;
using TechTalk.SpecFlow.Assist;
using Xunit;

namespace GamingWorldBackEnd.Tests.Users
{
    [Binding]
    public class PostUsersServiceStepsDefinition
    {
        
        private readonly WebApplicationFactory<Startup> _factory;
        private HttpClient _client;
        private Uri _baseUri;
        private Task<HttpResponseMessage> Response { get; set; }

        public PostUsersServiceStepsDefinition(WebApplicationFactory<Startup> factory)
        {
            _factory = factory;
        }
        
        [Given(@"the endpoint https://localhost:(.*)/api/v(.*)/users/auth/sign-up is available")]
        public void GivenTheEndpointHttpLocalhostApiVUsersIsAvailable(int port, int version)
        {
            _baseUri = new Uri($"https://localhost:{port}/api/v{version}/users/auth/sign-up");
            _client = _factory.CreateClient(new WebApplicationFactoryClientOptions {BaseAddress = _baseUri});
        }

        [When(@"a POST Request is sent")]
        public void WhenApostRequestIsSent(Table saveUserResource)
        {
            var resource = saveUserResource.CreateSet<RegisterRequest>().First();
            var content = new StringContent(resource.ToJson(), Encoding.UTF8, MediaTypeNames.Application.Json);
            Response = _client.PostAsync(_baseUri, content);
        }
        
        [Then(@"a Response With Status (.*) is received")]
        public void ThenAResponseIsReceivedWithStatus(int expectedStatus)
        {
            var expectedStatusCode3 = ((HttpStatusCode) expectedStatus).ToString();
            var actualStatusCode3 = Response.Result.StatusCode.ToString();
            Assert.Equal(expectedStatusCode3, actualStatusCode3);
        }

        [Then(@"a User Resource is included in the response body\.")]
        public async void ThenAUserResourceIsIncludedInTheResponseBody(Table expectedUserResource)
        {
            var expectedResource = expectedUserResource.CreateSet<UserResource>().First();
            var responseData = await Response.Result.Content.ReadAsStringAsync();
            var resource = JsonConvert.DeserializeObject<UserResource>(responseData);
            expectedResource.Id = resource.Id;
            var jsonExpectedResource = expectedResource.ToJson();
            var jsonActualResource = resource.ToJson();
            Assert.Equal(jsonExpectedResource, jsonActualResource);
        }
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapper;
using GamingWorld.API.Profiles.Domain.Models;
using GamingWorld.API.Profiles.Domain.Services;
using GamingWorld.API.Profiles.Resources;
using GamingWorld.API.Shared.Extensions;
using Microsoft.AspNetCore.Mvc;
using Profile = GamingWorld.API.Profiles.Domain.Models.Profile;

namespace GamingWorld.API.Profiles.Controllers
{
    [ApiController]
    [Route("/api/v1/[controller]")]
    public class ProfilesController : ControllerBase
    {
        private readonly IUProfileService _uProfileService;
        private readonly IMapper _mapper;

        public ProfilesController(IUProfileService uProfileService, IMapper mapper)
        {
            _uProfileService = uProfileService;
            _mapper = mapper;
        }
        
        [HttpGet]
        public async Task<IEnumerable<ProfileResource>> GetAllAsync()
        {
            var profiles = await _uProfileService.ListAsync();
            var resources = _mapper.Map<IEnumerable<Profile>, IEnumerable<ProfileResource>>(profiles);
            return resources;
        }
        
        [HttpGet("{id}")]
        public async Task<ProfileResource> GetById(int id)
        {
            var profile = await _uProfileService.ListByIdAsync(id);
            var resources = _mapper.Map<Profile, ProfileResource>(profile);
            return resources;
        }
        
        [HttpGet("user/{userId}")]
        public async Task<ProfileResource> GetByUserId(int userId)
        {
            var profile = await _uProfileService.ListByUserIdAsync(userId);
            var resources = _mapper.Map<Profile, ProfileResource>(profile);
            return resources;
        }

        [HttpPost]
        public async Task<IActionResult> PostAsync([FromBody] SaveProfileResource resource)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState.GetErrorMessages());

            var profile = _mapper.Map<SaveProfileResource, Profile>(resource);
            var result = await _uProfileService.SaveAsync(profile);

            if (!result.Success)
                return BadRequest(result.Message);

            var profileResource = _mapper.Map<Profile, ProfileResource>(result.Resource);
            return Ok(profileResource);
        }
        
        [HttpPut("{id}")]
        public async Task<IActionResult> PutAsync(int id, [FromBody] SaveProfileResource resource)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState.GetErrorMessages());

            var profile = _mapper.Map<SaveProfileResource, Profile>(resource);
            var result = await _uProfileService.UpdateAsync(id, profile);
            
            if (!result.Success)
                return BadRequest(result.Message);

            var profileResource = _mapper.Map<Profile, ProfileResource>(result.Resource);
            return Ok(profileResource);

        }
        
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteAsync(int id)
        {
            var result = await _uProfileService.DeleteAsync(id);
            
            if (!result.Success)
                return BadRequest(result.Message);

            var profileResource = _mapper.Map<Profile, ProfileResource>(result.Resource);
            return Ok(profileResource);
        }
    }

    
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using GamingWorld.API.Publications.Domain.Models;
using GamingWorld.API.Publications.Domain.Repositories;
using GamingWorld.API.Shared.Persistence.Contexts;
using GamingWorld.API.Shared.Persistence.Repositories;
using Microsoft.EntityFrameworkCore;

namespace GamingWorld.API.Publications.Persistence.Repositories
{
    public class PublicationRepository : BaseRepository, IPublicationRepository
    {
        public PublicationRepository(AppDbContext context) : base(context)
        {
            
        }

        public async Task<IEnumerable<Publication>> ListAsync()
        {
            return await _context.Publications.Include(p=>p.Tournament).ToListAsync();
        }

        public async Task<IEnumerable<Publication>> ListByTypeAsync()
        {
            return await _context.Publications.Include(p=>p.Tournament).ToListAsync();
        }

        public async Task AddAsync(Publication publication)
        {
            await _context.Publications.AddAsync(publication);
        }

        public async Task<Publication> FindByIdAsync(int id)
        {
            return await _context.Publications.Include(p=>p.Tournament).FirstOrDefaultAsync(p=>p.Id==id);
        }

        public void Update(Publication publication)
        {
            _context.Publications.Update(publication);
        }

        public void Remove(Publication publication)
        {
            _context.Publications.Remove(publication);
        }
    }
}<file_sep>﻿using GamingWorld.API.Business.Domain.Models;

namespace GamingWorld.API.Publications.Domain.Models
{
    public class Publication
    {
        public int Id { get; set; }
        
        public short PublicationType { get; set; }
             
        public string Title { get; set; }
             
        public string Content { get; set; }
             

        
        public string UrlToImage { get; set; }
        
        public int? TournamentId { get; set; }
        
        public Tournament Tournament { get; set; }

        public string CreatedAt { get; set; }
             
             
        //Relationships
        public int GameId { get; set; }
             
        public int UserId { get; set; }
             
         }
}<file_sep>﻿namespace GamingWorld.API.Profiles.Domain.Models
{
    public class FavoriteGame
    {
        public int Id { get; set; }
        public string GameName { get; set; }

        // Relations
        public int ProfileId { get; set; }
        //public Profile Profile { get; set; }
    }
}<file_sep>﻿namespace GamingWorld.API.Shared.Inbound.Games.Domain.Models
{
    public class GameCover
    {
        // NOT FOLLOWING THE NAMING CONVENTION BECAUSE WE NEED TO MAP EXACT FIELD NAMES FROM OBJECT RESPONSE FROM IGDB.
        
        public int id { get; set; }
        
        public string url { get; set; }
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using GamingWorld.API.Business.Domain.Models;
using GamingWorld.API.Business.Domain.Services.Communication;
using GamingWorld.API.Profiles.Domain.Services.Communication;

namespace GamingWorld.API.Business.Domain.Services
{
    public interface ITournamentService
    {
        Task<Tournament> GetById(int id);
        Task<IEnumerable<Tournament>> ListAsync();
        Task<Tournament> ListWithParticipantsByIdAsync(int tournamentId);
        Task<TournamentResponse> SaveAsync(Tournament tournament);
        Task<ParticipantResponse> UpdateParticipantPoints(int tournamentId, int participantId, int points);
        Task<TournamentResponse> EndTournament(int tournamentId, bool tournamentStatus);
        
    }
}<file_sep>﻿using AutoMapper;
using GamingWorld.API.Business.Domain.Models;
using GamingWorld.API.Business.Resources;
using GamingWorld.API.Publications.Domain.Models;
using GamingWorld.API.Publications.Resources;

namespace GamingWorld.API.Business.Mapping
{
    public class ResourceToModelProfile : AutoMapper.Profile
    {
        public ResourceToModelProfile()
        {
            CreateMap<SaveTournamentResource, Tournament>();
            CreateMap<SaveParticipantResource, Participant>();
        }
    }
}<file_sep>using System;
using System.Linq;
using GamingWorld.API.Security.Domain.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace GamingWorld.API.Security.Authorization.Attributes
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class AuthorizeAttribute : Attribute, IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            var allowAnonymous = context.ActionDescriptor.EndpointMetadata.OfType<AllowAnonymousAttribute>().Any();

            // When Action is decorated with [AllowAnonymous] attribute
            // Then skip authorization process
            if (allowAnonymous)
                return;
            
            // Authorization Process
            var user = (User)context.HttpContext.Items["User"];
            
            // If user is null then Unauthorized Request    
            if (user == null)
                context.Result = new JsonResult(
                        new { message = "Unauthorized" })
                    { StatusCode = StatusCodes.Status401Unauthorized };

        }
    }
}<file_sep>﻿using System.ComponentModel;
using System.Reflection;

namespace GamingWorld.API.Shared.Extensions
{
    public static class EnumExtensions
    {
        public static string ToDescriptionString<TEnum>(this TEnum sourceEnum)
        {
            FieldInfo info = sourceEnum.GetType().GetField(sourceEnum.ToString());
            var attributes = (DescriptionAttribute[]) info.GetCustomAttributes(typeof(DescriptionAttribute), false);
            return attributes?[0].Description ?? sourceEnum.ToString();
        }
    }
}<file_sep>﻿using System;
using System.Collections.Generic;
using System.Linq;
using GamingWorld.API.Shared.Extensions;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace GamingWorld.API
{
    public class SnakeCaseDocumentFilter : IDocumentFilter
    {
        public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
        {
            var paths = swaggerDoc.Paths;
            
            //	New Keys
            var newPaths = new Dictionary<string, OpenApiPathItem>();
            var removeKeys = new List<string>(); // Old keys
            foreach (var path in paths)
            {
                var newKey = path.Key.ToLower();
                if (newKey != path.Key)
                {
                    removeKeys.Add(path.Key);
                    newPaths.Add(newKey, path.Value);
                }
            }
            
            foreach (var path in newPaths)
            {
                swaggerDoc.Paths.Add(path.Key, path.Value);
            }

            //	Removing old keys
            foreach (var key in removeKeys)
            {
                swaggerDoc.Paths.Remove(key);
            }
        }
    }
    public class SnakeCaseOperationFilter : IOperationFilter {
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            for (int i = 0; i < operation.Tags.Count; ++i)
            {
                operation.Tags[i].Name = operation.Tags[i].Name.ToSnakeCase();
            }
        }
    }
}<file_sep>﻿using System;

namespace GamingWorld.API.Shared.Inbound.ExternalAPIs.Domain.Models
{
    public class ExternalAPI
    {
        public int Id { get; set; }
        
        public string Name { get; set; }
        
        public string Token { get; set; }
        
        public DateTime Expiration { get; set; }
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapper;
using GamingWorld.API.Security.Authorization.Attributes;
using GamingWorld.API.Security.Domain.Services;
using GamingWorld.API.Security.Domain.Services.Communication;
using GamingWorld.API.Security.Resources;
using GamingWorld.API.Shared.Extensions;
using Microsoft.AspNetCore.Mvc;

namespace GamingWorld.API.Security.Controllers
{
    [Authorize]
    [ApiController]
    [Route("/api/v1/[controller]")]
    public class UsersController : ControllerBase
    {
        private readonly IUserService _userService;
        private readonly IMapper _mapper;

        public UsersController(IUserService userService, IMapper mapper)
        {
            _userService = userService;
            _mapper = mapper;
        }
        
        [AllowAnonymous]
        [HttpPost("auth/sign-in")]
        public async Task<IActionResult> Authenticate( AuthenticateRequest request)
        {
            var response = await _userService.Authenticate(request);
            return Ok(response);
        }

        [AllowAnonymous]
        [HttpPost("auth/sign-up")]
        public async Task<IActionResult> Register( RegisterRequest request)
        {
            await _userService.RegisterAsync(request);
            return Ok(new {message = "Registration successful."});
        }

        [HttpGet]
        public async Task<IEnumerable<UserResource>> GetAllAsync()
        {
            var users = await _userService.ListAsync();
            var resources = _mapper.Map<IEnumerable<Domain.Models.User>, IEnumerable<UserResource>>(users);
            return resources;
        }
        
        /*
        [HttpPost]
        public async Task<IActionResult> PostAsync([FromBody] SaveUserResource resource)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState.GetErrorMessages());

            var user = _mapper.Map<SaveUserResource, Domain.Models.User>(resource);
            var result = await _userService.SaveAsync(user);

            if (!result.Success)
                return BadRequest(result.Message);

            var productResource = _mapper.Map<Domain.Models.User, UserResource>(result.Resource);
            return Ok(productResource);
        }
        */
        
        [HttpPut("{id}")]
        public async Task<IActionResult> PutAsync(int id, [FromBody] SaveUserResource resource)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState.GetErrorMessages());

            var user = _mapper.Map<SaveUserResource, Domain.Models.User>(resource);
            var result = await _userService.UpdateAsync(id, user);
            
            if (!result.Success)
                return BadRequest(result.Message);

            var userResource = _mapper.Map<Domain.Models.User, UserResource>(result.Resource);
            return Ok(userResource);

        }

        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteAsync(int id)
        {
            var result = await _userService.DeleteAsync(id);
            
            if (!result.Success)
                return BadRequest(result.Message);

            var productResource = _mapper.Map<Domain.Models.User, UserResource>(result.Resource);
            return Ok(productResource);
        }
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using GamingWorld.API.Publications.Domain.Models;
using GamingWorld.API.Publications.Domain.Services.Communication;
using GamingWorld.API.Publications.Resources;

namespace GamingWorld.API.Publications.Domain.Services
{
    public interface IPublicationService
    {
        Task<IEnumerable<Publication>> ListAsync();
        
        Task<IEnumerable<Publication>> ListByTypeAsync(int type);
        
        Task<Publication> GetById(int id);

        Task<PublicationResponse> SaveAsync(SavePublicationResource publication);

        Task<PublicationResponse> UpdateAsync(int id, Publication publication);

        Task<PublicationResponse> DeleteAsync(int id);
    }
}<file_sep>﻿using GamingWorld.API.Business.Domain.Models;
using GamingWorld.API.Shared.Domain.Services.Communication;

namespace GamingWorld.API.Business.Domain.Services.Communication
{
    public class ParticipantResponse : BaseResponse<Participant>
        {
            public ParticipantResponse(string message) : base(message)
            {
            }

            public ParticipantResponse(Participant participant) : base(participant)
            {
            }
        }
    
}<file_sep>﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapper;
using GamingWorld.API.Profiles.Domain.Models;
using GamingWorld.API.Profiles.Domain.Repositories;
using GamingWorld.API.Security.Authorization.Handlers.Interfaces;
using GamingWorld.API.Security.Domain.Models;
using GamingWorld.API.Security.Domain.Repositories;
using GamingWorld.API.Security.Domain.Services;
using GamingWorld.API.Security.Domain.Services.Communication;
using GamingWorld.API.Security.Exceptions;
using GamingWorld.API.Shared.Domain.Repositories;
using GamingWorld.API.Shared.Extensions;
using BCryptNet = BCrypt.Net.BCrypt;
using Profile = GamingWorld.API.Profiles.Domain.Models.Profile;

namespace GamingWorld.API.Security.Services
{
    public class UserService : IUserService
    {
        private readonly IUserRepository _userRepository;
        private readonly IProfileRepository _profileRepository;
        private readonly IUnitOfWork _unitOfWork;
        private readonly IJwtHandler _jwtHandler;
        private readonly IMapper _mapper;

        public UserService(IUserRepository userRepository, IProfileRepository profileRepository, IUnitOfWork unitOfWork, IMapper mapper, IJwtHandler jwtHandler)
        {
            _userRepository = userRepository;
            _profileRepository = profileRepository;
            _unitOfWork = unitOfWork;
            _mapper = mapper;
            _jwtHandler = jwtHandler;
        }

        public async Task<AuthenticateResponse> Authenticate(AuthenticateRequest request)
        {
            var user = await _userRepository.FindByUsernameAsync(request.Username);
            //Validate
            if (user == null || !BCryptNet.Verify(request.Password, user.PasswordHash))
                throw new AppException("Username or password is incorrect");
            
            
            
            //Authentication successful
            var response = _mapper.Map<AuthenticateResponse>(user);

            response.Token = _jwtHandler.GenerateToken(user);

            return response;
        }

        public async Task RegisterAsync(RegisterRequest request)
        {
            //Validate
            if (_userRepository.ExistsByUsername(request.Username))
                throw new AppException($"Username {request.Username} is already taken.");
            
            //Map request to User object
            var user = _mapper.Map<User>(request);
            
            
            //Has password
            user.PasswordHash = <PASSWORD>(request.Password);
            
            //Save User
            try
            {
                await _userRepository.AddAsync(user);
                await _unitOfWork.CompleteAsync();

                var profile = new Profile();
                profile.UserId = user.Id;
                profile.GamingLevel = EGamingLevel.Newbie;
                profile.IsStreamer = true;

                await _profileRepository.AddAsync(profile);
                await _unitOfWork.CompleteAsync();
            }
            catch (Exception e)
            {
                throw new AppException($"An error occurred while saving the user: {user}");
            }
        }

        public async Task<IEnumerable<User>> ListAsync()
        {
            return await _userRepository.ListAsync();
        }

        public async Task<User> GetByIdAsync(int userId)
        {
            return await _userRepository.FindByIdAsync(userId);
        }
        
        public async Task<User> ListByUserUsernameAsync(int userId)
        {
            return await _userRepository.FindByIdAsync(userId);
        }

        public async Task<UserResponse> SaveAsync(User user)
        {
            //Validate Username
            
            var existingUsername = await _userRepository.FindByUsernameAsync(user.Username);
            if (existingUsername != null)
                return new UserResponse("Username already exists.");

            try
            {
                await _userRepository.AddAsync(user);
                await _unitOfWork.CompleteAsync();

                return new UserResponse(user);
            }
            catch (Exception e)
            {
                return new UserResponse($"An error occurred while saving the user: {e.Message}");
            }
        }

        public async Task<UserResponse> UpdateAsync(int id, User user)
        {
            //Validate Id
            var existingUserById = await _userRepository.FindByIdAsync(id);

            if (existingUserById == null)
                return new UserResponse("User not found.");

            //Validate Username
            var existingUserByUsername = await _userRepository.FindByUsernameAsync(user.Username);
            if (existingUserByUsername != null && existingUserByUsername.Id != id)
                return new UserResponse("Username already used.");
            
            //Validate Email
            var existingUserByEmail = await _userRepository.FindByEmailAsync(user.Email);
            if (existingUserByEmail != null && existingUserByEmail.Id != id)
                return new UserResponse("Email already used.");

            existingUserById.Email = user.Email;
            existingUserById.Username = user.Username;
            existingUserById.Premium = user.Premium;

            try
            {
                _userRepository.Update(existingUserById);
                await _unitOfWork.CompleteAsync();

                return new UserResponse(existingUserById);

            }
            catch (Exception e)
            {
                return new UserResponse($"An error occurred while updating the user: {e.Message}");
            }

        }

        public async Task<UserResponse> DeleteAsync(int id)
        {
            //Validate User Id
            var existingUser = await _userRepository.FindByIdAsync(id);

            if (existingUser == null)
                return new UserResponse("User not found.");

            try
            {
                _userRepository.Remove(existingUser);
                await _unitOfWork.CompleteAsync();

                return new UserResponse(existingUser);

            }
            catch (Exception e)
            {
                return new UserResponse($"An error occurred while deleting the user: {e.Message}");
            }
        }
    }
}<file_sep>﻿using System;
using System.Collections.Generic;
using GamingWorld.API.Business.Domain.Models;
using GamingWorld.API.Profiles.Domain.Models;
using GamingWorld.API.Publications.Domain.Models;
using GamingWorld.API.Publications.Resources;

namespace GamingWorld.API.Business.Resources
{
    public class TournamentResource
    {
        public int Id { get; set; }
        
        public int ParticipantLimit { get; set; }
             
        public int PrizePool { get; set; }
        
        public string TournamentDate { get; set; }
             
        public string TournamentHour { get; set; }
        
        public int PublicationId { get; set; }
        
        public bool TournamentStatus { get; set; }

        //Relationships

        public IEnumerable<Participant> Participants { get; set; }
    }
}<file_sep>﻿using GamingWorld.API.Business.Domain.Models;
using GamingWorld.API.Profiles.Domain.Models;
using GamingWorld.API.Publications.Domain.Models;
using GamingWorld.API.Security.Domain.Models;
using GamingWorld.API.Shared.Extensions;
using GamingWorld.API.Shared.Inbound.ExternalAPIs.Domain.Models;
using Microsoft.EntityFrameworkCore;

namespace GamingWorld.API.Shared.Persistence.Contexts
{
    public class AppDbContext : DbContext
    {
        public DbSet<User> Users { get; set; }
        public DbSet<Publication> Publications { get; set; }
        public DbSet<Profile> Profiles { get; set; }
        public DbSet<Tournament> Tournaments { get; set; } 
        public DbSet<Participant> Participants { get; set; } 
        public DbSet<ExternalAPI> ExternalApis { get; set; }

        public AppDbContext(DbContextOptions options) : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
            
            // External APIs

            builder.Entity<ExternalAPI>().ToTable("ExternalApis");
            builder.Entity<ExternalAPI>().HasKey(api => api.Id);
            builder.Entity<ExternalAPI>().Property(api => api.Id).IsRequired().ValueGeneratedOnAdd();
            builder.Entity<ExternalAPI>().Property(api => api.Name).IsRequired();
            builder.Entity<ExternalAPI>().Property(api => api.Expiration).IsRequired();
            builder.Entity<ExternalAPI>().Property(api => api.Token).IsRequired();

            //Profiles
            builder.Entity<Profile>().ToTable("Profiles");
            builder.Entity<Profile>().HasKey(p => p.Id);
            builder.Entity<Profile>().Property(p => p.Id).IsRequired().ValueGeneratedOnAdd();
            builder.Entity<Profile>().Property(p => p.UserId).IsRequired();
            builder.Entity<Profile>().Property(p => p.GamingLevel).IsRequired();
            builder.Entity<Profile>().Property(p => p.IsStreamer).IsRequired();
            builder.Entity<Profile>().HasMany(p => p.GameExperiences).WithOne();
            builder.Entity<Profile>().HasMany(p => p.StreamingCategories).WithOne();
            builder.Entity<Profile>().HasMany(p => p.StreamerSponsors).WithOne();
            builder.Entity<Profile>().HasMany(p => p.FavoriteGames).WithOne();
            builder.Entity<Profile>().HasMany(p => p.TournamentExperiences).WithOne();

            /*builder.Entity<Profile>().HasData
            (
                new Profile {Id = 1, UserId = 1, GamingLevel = EGamingLevel.A, IsStreamer = true},
                new Profile {Id = 2, UserId = 2, GamingLevel = EGamingLevel.N, IsStreamer = true},
                new Profile {Id = 3, UserId = 3, GamingLevel = EGamingLevel.M, IsStreamer = false},
                new Profile {Id = 4, UserId = 4, GamingLevel = EGamingLevel.A, IsStreamer = false}
            );*/
            
            // Profiles: GameExperiences
            builder.Entity<GameExperience>().ToTable("GameExperiences");
            builder.Entity<GameExperience>().HasKey(ge => ge.Id);
            builder.Entity<GameExperience>().Property(ge => ge.Id).IsRequired().ValueGeneratedOnAdd();
            builder.Entity<GameExperience>().Property(ge => ge.GameName).IsRequired();
            builder.Entity<GameExperience>().Property(ge => ge.Time).IsRequired();
            builder.Entity<GameExperience>().Property(ge => ge.TimeUnit).IsRequired();
            builder.Entity<GameExperience>().Property(ge => ge.ProfileId).IsRequired();
            
            /*builder.Entity<GameExperience>().HasData
            (
                new GameExperience
                    {Id = 1, GameName = "Among Us", Time = 5, TimeUnit = EGameExperienceTime.M, ProfileId = 1},
                new GameExperience
                    {Id = 2, GameName = "Call of Duty", Time = 4, TimeUnit = EGameExperienceTime.Y, ProfileId = 2},
                new GameExperience
                    {Id = 3, GameName = "Manco's Combat", Time = 25, TimeUnit = EGameExperienceTime.D, ProfileId = 3}
            );*/

            // Profiles: StreamingCategories
            builder.Entity<StreamingCategory>().ToTable("StreamingCategories");
            builder.Entity<StreamingCategory>().HasKey(sc => sc.Id);
            builder.Entity<StreamingCategory>().Property(sc => sc.Id).IsRequired().ValueGeneratedOnAdd();
            builder.Entity<StreamingCategory>().Property(sc => sc.Name).IsRequired();
            builder.Entity<StreamingCategory>().Property(sc => sc.ProfileId).IsRequired();
            
            /*builder.Entity<StreamingCategory>().HasData
            (
                new StreamingCategory {Id = 1, Name = "Battle Royale", ProfileId = 1},
                new StreamingCategory {Id = 2, Name = "First Person Shooter", ProfileId = 2},
                new StreamingCategory {Id = 3, Name = "Battle Royale", ProfileId = 3}
            );*/

            // Profiles: StreamerSponsors
            builder.Entity<StreamerSponsor>().ToTable("StreamerSponsors");
            builder.Entity<StreamerSponsor>().HasKey(ss => ss.Id);
            builder.Entity<StreamerSponsor>().Property(ss => ss.Id).IsRequired().ValueGeneratedOnAdd();
            builder.Entity<StreamerSponsor>().Property(ss => ss.Name).IsRequired();
            builder.Entity<StreamerSponsor>().Property(ss => ss.ProfileId).IsRequired();

            /*builder.Entity<StreamerSponsor>().HasData
            (
                new StreamerSponsor {Id = 1, Name = "<NAME>", ProfileId = 1},
                new StreamerSponsor {Id = 2, Name = "Pepsi", ProfileId = 2},
                new StreamerSponsor {Id = 3, Name = "Fanta", ProfileId = 3}
            );*/
            

            // Profiles: TournamentExperiences
            builder.Entity<TournamentExperience>().ToTable("TournamentExperiences");
            builder.Entity<TournamentExperience>().HasKey(te => te.Id);
            builder.Entity<TournamentExperience>().Property(te => te.Id).IsRequired().ValueGeneratedOnAdd();
            builder.Entity<TournamentExperience>().Property(te => te.Name).IsRequired();
            builder.Entity<TournamentExperience>().Property(te => te.Position).IsRequired();
            builder.Entity<TournamentExperience>().Property(te => te.ProfileId).IsRequired();

            /*builder.Entity<TournamentExperience>().HasData
            (
                new TournamentExperience {Id = 1, Name = "<NAME>", Position = 23, ProfileId = 1},
                new TournamentExperience {Id = 2, Name = "<NAME>", Position = 1, ProfileId = 2},
                new TournamentExperience {Id = 3, Name = "CODM Championship", Position = 7, ProfileId = 3}
            );*/

            
            // Profiles: FavoriteGames
            builder.Entity<FavoriteGame>().ToTable("FavoriteGames");
            builder.Entity<FavoriteGame>().HasKey(fg => fg.Id);
            builder.Entity<FavoriteGame>().Property(fg => fg.Id).IsRequired().ValueGeneratedOnAdd();
            builder.Entity<FavoriteGame>().Property(fg => fg.GameName).IsRequired();
            builder.Entity<FavoriteGame>().Property(fg => fg.ProfileId).IsRequired();

            /*builder.Entity<FavoriteGame>().HasData
            (
                new FavoriteGame {Id = 1, GameName = "Among Us", ProfileId = 1},
                new FavoriteGame {Id = 2, GameName = "Call of Duty", ProfileId = 2},
                new FavoriteGame {Id = 3, GameName = "Free Fire", ProfileId = 3}
            );*/

            
            //Publications

            //Constraints
            builder.Entity<Publication>().ToTable("Publications");
            builder.Entity<Publication>().HasKey(p => p.Id);
            builder.Entity<Publication>().Property(p => p.Title).IsRequired();
            builder.Entity<Publication>().Property(p => p.Content).IsRequired();
            builder.Entity<Publication>().Property(p => p.CreatedAt).IsRequired();
            builder.Entity<Publication>().Property(p => p.PublicationType).IsRequired();
            builder.Entity<Publication>().Property(p => p.TournamentId).HasDefaultValue(null);
            builder.Entity<Publication>().Property(p => p.Id).IsRequired().ValueGeneratedOnAdd();
            builder.Entity<Publication>().HasOne(p => p.Tournament).WithOne(t => t.Publication).HasForeignKey<Tournament>(t => t.Id);
            
            //Users
            builder.Entity<User>().ToTable("Users");
            builder.Entity<User>().HasKey(p => p.Id);
            builder.Entity<User>().Property(p => p.Id).IsRequired().ValueGeneratedOnAdd();
            builder.Entity<User>().Property(p => p.Username).IsRequired().HasMaxLength(30);
            builder.Entity<User>().Property(p => p.FirstName).IsRequired().HasMaxLength(30);
            builder.Entity<User>().Property(p => p.LastName).IsRequired().HasMaxLength(30);
            builder.Entity<User>().Property(p => p.Email).IsRequired().HasMaxLength(50);
            builder.Entity<User>().Property(p => p.Premium).IsRequired();
            
            //Tournaments
            builder.Entity<Tournament>().ToTable("Tournaments");
            builder.Entity<Tournament>().HasKey(p => p.Id);
            builder.Entity<Tournament>().Property(p => p.Id).IsRequired().ValueGeneratedOnAdd();
            builder.Entity<Tournament>().HasMany(p => p.Participants).WithOne();
            
            /*builder.Entity<Tournament>().HasData
            (
                new Tournament {Id = 1, PublicationId = 1},
                new Tournament {Id = 2, PublicationId = 2},
                new Tournament {Id = 3, PublicationId = 3}
            );*/
            
            //Participants
            builder.Entity<Participant>().ToTable("Participants");
            builder.Entity<Participant>().HasKey(p => p.Id);
            builder.Entity<Participant>().Property(p => p.Id).IsRequired().ValueGeneratedOnAdd();
            builder.Entity<Participant>().Property(p => p.TournamentId).IsRequired();
            builder.Entity<Participant>().Property(p => p.UserId).IsRequired();

            /*builder.Entity<Participant>().HasData
            (
                new Participant {Id = 1, TournamentId = 1, Points = 12, UserId = 1},
                new Participant {Id = 2, TournamentId = 1, Points = 24, UserId = 2},
                new Participant {Id = 3, TournamentId = 2, Points = 2, UserId = 3}
            );*/

            builder.UseSnakeCaseNamingConvention();

        }
    }
}<file_sep>﻿namespace GamingWorld.API.Profiles.Domain.Models
{
    public class StreamerSponsor
    {
        public int Id { get; set; }
        public string Name { get; set; }

        // Relations
        public int ProfileId { get; set; }
    }
}<file_sep>﻿using System;
using System.Threading.Tasks;
using GamingWorld.API.Shared.Inbound.News.Domain.Services;
using Microsoft.AspNetCore.Mvc;

namespace GamingWorld.API.Shared.Inbound.News.Controllers
{
    [ApiController]
    [Route("/api/v1/[controller]")]
    public class NewsController : ControllerBase
    {
        private readonly INewsService _newsService;
        
        public NewsController(INewsService newsService)
        {
            _newsService = newsService;
        }
        
        [HttpGet]
        public async Task<String> GetRandomListAsync(string theme)
        {
            return await _newsService.ListByTheme(theme);
        }
    }
}<file_sep>﻿using AutoMapper;
using GamingWorld.API.Profiles.Domain.Models;
using GamingWorld.API.Profiles.Resources;
using Profile = GamingWorld.API.Profiles.Domain.Models.Profile;

namespace GamingWorld.API.Profiles.Mapping
{
    public class ResourceToModelProfile : AutoMapper.Profile
    {
        public ResourceToModelProfile()
        {
            CreateMap<SaveProfileResource, Profile>()
                .ForMember(target =>
                        target.GamingLevel,
                    options =>
                        options.MapFrom(source =>
                            (EGamingLevel) source.GamingLevel));
        }
    }
}<file_sep>﻿using GamingWorld.API.Publications.Domain.Models;
using GamingWorld.API.Shared.Domain.Services.Communication;

namespace GamingWorld.API.Publications.Domain.Services.Communication
{
    public class PublicationResponse : BaseResponse<Publication>
    {
        public PublicationResponse(string message) : base(message)
        {
        }

        public PublicationResponse(Publication publication) : base(publication)
        {
        }
    }
}<file_sep>﻿using System.Threading.Tasks;
using GamingWorld.API.Shared.Inbound.ExternalAPIs.Domain.Models;

namespace GamingWorld.API.Shared.Inbound.ExternalAPIs.Domain.Repositories
{
    public interface IExternalAPIRepository
    {
        Task AddAsync(ExternalAPI externalApi);
        Task<ExternalAPI> FindByNameAsync(string name);
        void Update(ExternalAPI externalApi);
        void Remove(ExternalAPI externalApi);
    }
}<file_sep>﻿using GamingWorld.API.Business.Resources;

namespace GamingWorld.API.Publications.Resources
{
    public class PublicationResource
    {
        public int Id { get; set; }
        
        public short PublicationType { get; set; }
             
        public string Title { get; set; }
             
        public string Content { get; set; }
             
        /*public int ParticipantLimit { get; set; }*/
             
        /*public int PrizePool { get; set; }
             
        public int TournamentId { get; set; }
             
        public string TournamentDate { get; set; }
             
        public string TournamentHour { get; set; }*/
        
        public string UrlToImage { get; set; }
             
        public string CreatedAt { get; set; }
        
        /*public int TournamentId { get; set; }*/
        
        public TournamentResource Tournament { get; set; }
             
        //Relationships
        public int GameId { get; set; }
             
        public int UserId { get; set; }
    }
}<file_sep>﻿using System.Collections.Generic;
using GamingWorld.API.Security.Domain.Models;

namespace GamingWorld.API.Profiles.Domain.Models
{
    public class Profile
    {
        public int Id { get; set; }
        public int UserId { get; set; }
        public EGamingLevel GamingLevel { get; set; }
        public bool IsStreamer { get; set; }
        
        public User User { get; set; }
        public IEnumerable<GameExperience> GameExperiences { get; set; } = new List<GameExperience>();
        public IEnumerable<StreamingCategory> StreamingCategories { get; set; } = new List<StreamingCategory>();
        public IEnumerable<StreamerSponsor> StreamerSponsors { get; set; } = new List<StreamerSponsor>();
        public IEnumerable<TournamentExperience> TournamentExperiences { get; set; } = new List<TournamentExperience>();
        public IEnumerable<FavoriteGame> FavoriteGames { get; set; } = new List<FavoriteGame>();
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapper;
using GamingWorld.API.Business.Domain.Models;
using GamingWorld.API.Business.Domain.Services;
using GamingWorld.API.Business.Resources;
using GamingWorld.API.Business.Services;
using GamingWorld.API.Publications.Domain.Models;
using GamingWorld.API.Publications.Domain.Services;
using GamingWorld.API.Publications.Resources;
using GamingWorld.API.Shared.Extensions;
using Microsoft.AspNetCore.Mvc;

namespace GamingWorld.API.Publications.Controllers
{
    [ApiController]
    [Route("/api/v1/[controller]")]
    public class PublicationsController : ControllerBase
    {
        private readonly IPublicationService _publicationService;
        private readonly ITournamentService _tournamentService;
        private readonly IMapper _mapper;

        public PublicationsController(IPublicationService publicationService, IMapper mapper, ITournamentService tournamentService)
        {
            _publicationService = publicationService;
            _mapper = mapper;
            _tournamentService = tournamentService;
        }
        
        [HttpGet]
        public async Task<IEnumerable<PublicationResource>> GetAllAsync([FromQuery] int ?publicationType)
        {
            
            if (publicationType != null)
            {
                var publications = await _publicationService.ListByTypeAsync((int)publicationType);
                var resources = _mapper.Map<IEnumerable<Publication>, IEnumerable<PublicationResource>>(publications);
                return resources;
            }
            else
            {
                var publications = await _publicationService.ListAsync();
                var resources = _mapper.Map<IEnumerable<Publication>, IEnumerable<PublicationResource>>(publications);
                return resources;
            }
            
        }
        
        [HttpGet("{id}")]
        public async Task<PublicationResource> GetById(int id)
        {
            var publication = await _publicationService.GetById(id);
            var resource = _mapper.Map<Publication, PublicationResource>(publication);
            return resource;
        }
        
        [HttpPost]
        public async Task<IActionResult> PostAsync([FromBody] SavePublicationResource resource)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState.GetErrorMessages());

            var publicationResult = await _publicationService.SaveAsync(resource);


            if (!publicationResult.Success)
                return BadRequest(publicationResult.Message);

            var publicationResource = _mapper.Map<Publication, PublicationResource>(publicationResult.Resource);
            return Ok(publicationResource);
        }

        [HttpPut("{id}")]
        public async Task<IActionResult> PutAsync(int id, [FromBody] SavePublicationResource resource)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState.GetErrorMessages());

            var publication = _mapper.Map<SavePublicationResource, Publication>(resource);
            var result = await _publicationService.UpdateAsync(id, publication);
            
            if (!result.Success)
                return BadRequest(result.Message);

            var publicationResource = _mapper.Map<Publication, PublicationResource>(result.Resource);
            return Ok(publicationResource);
        }
        
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteAsync(int id)
        {
            var result = await _publicationService.DeleteAsync(id);
            if (!result.Success)
                return BadRequest(result.Message);
            
            var categoryResource = _mapper.Map<Publication, PublicationResource>(result.Resource);
            return Ok(categoryResource);
        }
        
        
    }
}<file_sep>﻿using AutoMapper;
using GamingWorld.API.Publications.Domain.Models;
using GamingWorld.API.Publications.Resources;

namespace GamingWorld.API.Publications.Mapping
{
    public class ResourceToModelProfile : Profile
    {
        public ResourceToModelProfile()
        {
            CreateMap<SavePublicationResource, Publication>();
        }
    }
}<file_sep>﻿using AutoMapper;
using GamingWorld.API.Profiles.Domain.Models;
using GamingWorld.API.Profiles.Resources;
using GamingWorld.API.Shared.Extensions;
using Profile = GamingWorld.API.Profiles.Domain.Models.Profile;

namespace GamingWorld.API.Profiles.Mapping
{
    public class ModelToResourceProfile : AutoMapper.Profile
    {
        public ModelToResourceProfile()
        {
            CreateMap<Profile, ProfileResource>()
                .ForMember(
                    target =>
                        target.GamingLevel,
                    options =>
                        options.MapFrom(source =>
                            source.GamingLevel.ToDescriptionString()));
        }
        
    }
}<file_sep>﻿using System;
using System.Threading.Tasks;

namespace GamingWorld.API.Shared.Inbound.News.Domain.Services
{
    public interface INewsService
    {
        Task<String> ListByTheme(string theme);
    }
}<file_sep>﻿using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using GamingWorld.API.Profiles.Domain.Models;

namespace GamingWorld.API.Business.Resources
{
    public class SaveParticipantResource
    {

        [Required]
        public int UserId { get; set; }
        
        
        public int Points { get; set; }


        
    }
}<file_sep>﻿using GamingWorld.API.Profiles.Domain.Models;
using GamingWorld.API.Shared.Domain.Services.Communication;

namespace GamingWorld.API.Profiles.Domain.Services.Communication
{
    public class ProfileResponse : BaseResponse<Profile>
    {
        public ProfileResponse(string message) : base(message)
        {
        }

        public ProfileResponse(Profile profile) : base(profile)
        {
        }
    }
}<file_sep>﻿using System.ComponentModel;

namespace GamingWorld.API.Profiles.Domain.Models
{
    public enum EGameExperienceTime: short
    {
        [Description("Days")]
        D = 1,
        [Description("Months")]
        M = 2,
        [Description("Years")]
        Y = 3,
    }
}<file_sep>﻿using System.Collections.Generic;
using GamingWorld.API.Profiles.Domain.Models;
using GamingWorld.API.Security.Domain.Models;
using GamingWorld.API.Security.Resources;

namespace GamingWorld.API.Profiles.Resources
{
    public class ProfileResource
    {
        public int Id { get; set;  }
        public int UserId { get; set;  }
        public string GamingLevel { get; set; }
        
        public bool IsStreamer { get; set; }
        
        // Relations
        public IEnumerable<GameExperience> GameExperiences { get; set; } = new List<GameExperience>();
        public IEnumerable<StreamingCategory> StreamingCategories { get; set; } = new List<StreamingCategory>();
        public IEnumerable<StreamerSponsor> StreamerSponsors { get; set; } = new List<StreamerSponsor>();
        public IEnumerable<TournamentExperience> TournamentExperiences { get; set; } = new List<TournamentExperience>();
        public IEnumerable<FavoriteGame> FavoriteGames { get; set; } = new List<FavoriteGame>();
    }
}<file_sep>﻿using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using GamingWorld.API.Profiles.Domain.Models;

namespace GamingWorld.API.Profiles.Resources
{
    public class SaveProfileResource
    {
        [Required]
        [Range(1, 3)]
        public int GamingLevel { get; set; }

        [Required]
        public bool IsStreamer { get; set; }
        
        // Relations
        public IEnumerable<GameExperience> GameExperiences { get; set; } = new List<GameExperience>();
        public IEnumerable<StreamingCategory> StreamingCategories { get; set; } = new List<StreamingCategory>();
        public IEnumerable<StreamerSponsor> StreamerSponsors { get; set; } = new List<StreamerSponsor>();
        public IEnumerable<TournamentExperience> TournamentExperiences { get; set; } = new List<TournamentExperience>();
        public IEnumerable<FavoriteGame> FavoriteGames { get; set; } = new List<FavoriteGame>();
    }
}<file_sep>namespace GamingWorld.API.Security.Authorization.Settings
{
    public class AppSettings
    {
        public string Secret {get; set;}
    }
}<file_sep>﻿using System.Linq;
using System.Threading.Tasks;
using GamingWorld.API.Shared.Inbound.ExternalAPIs.Domain.Models;
using GamingWorld.API.Shared.Inbound.ExternalAPIs.Domain.Repositories;
using GamingWorld.API.Shared.Persistence.Contexts;
using GamingWorld.API.Shared.Persistence.Repositories;
using Microsoft.EntityFrameworkCore;

namespace GamingWorld.API.Shared.Inbound.ExternalAPIs.Persistence.Repositories
{
    public class ExternalAPIRepository : BaseRepository, IExternalAPIRepository
    {
        public ExternalAPIRepository(AppDbContext context) : base(context)
        {
        }
        
        public async Task AddAsync(ExternalAPI externalApi)
        {
            await _context.ExternalApis.AddAsync(externalApi);
        }

        public async Task<ExternalAPI> FindByNameAsync(string name)
        {
            return await _context.ExternalApis.OrderByDescending(api => api.Expiration).FirstOrDefaultAsync(api => api.Name == name);
        }

        public void Update(ExternalAPI externalApi)
        {
            _context.Update(externalApi);
        }

        public void Remove(ExternalAPI externalApi)
        {
            _context.Remove(externalApi);
        }
    }
}<file_sep>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using System.Web;
using GamingWorld.API.Security.Exceptions;
using GamingWorld.API.Shared.Domain.Repositories;
using GamingWorld.API.Shared.Inbound.ExternalAPIs.Domain.Models;
using GamingWorld.API.Shared.Inbound.ExternalAPIs.Domain.Repositories;
using GamingWorld.API.Shared.Inbound.Games.Domain.Models;
using GamingWorld.API.Shared.Inbound.Games.Domain.Services;
using Microsoft.IdentityModel.Tokens;

namespace GamingWorld.API.Shared.Inbound.Games.Services
{
    public class GamesService : IGamesService
    {
        private readonly string TWITCH_OAUTH_URL = "https://id.twitch.tv/oauth2/token";
        private readonly string TWITCH_CLIENT_ID = "8en9cck6wbdrkinl4i0oahhxf3ali1";
        private readonly string TWITCH_CLIENT_SECRET = "<KEY>";
        private readonly string TWITCH_GRANT_TYPE = "client_credentials";

        private readonly string IGDB_GAMES_ENDPOINT_URL = "https://api.igdb.com/v4/games";

        private readonly string TWITCH_TOP_GAMES_ENDPOINT = "https://api.twitch.tv/helix/games/top";
        
        private readonly IExternalAPIRepository _externalApiRepository;
        private readonly IUnitOfWork _unitOfWork;

        public GamesService(IExternalAPIRepository externalApiRepository, IUnitOfWork unitOfWork)
        {
            _externalApiRepository = externalApiRepository;
            _unitOfWork = unitOfWork;
        }

        public async Task<IEnumerable<Game>> ListRandomAsync(int limit)
        {
            var body = "fields name, cover.url; sort date desc; limit " + limit + ";";

            var response = await MakeIGDBRequest(body);

            return JsonSerializer.Deserialize<List<Game>>(response);
        }

        public async Task<Game> GetById(int id)
        {
            var body = "fields name, cover.url; where id=" + id + ";";

            var response = await MakeIGDBRequest(body);

            var responseObject = JsonSerializer.Deserialize<List<Game>>(response);

            if (responseObject == null)
                return null;

            return responseObject.Count > 0 ? responseObject[0] : null;
        }

        public async Task<IEnumerable<Game>> FindByName(string name, int limit)
        {
            if (name.IsNullOrEmpty())
                throw new AppException("Name must not be empty");
            
            var body = "fields name, cover.url; search \"" + name + "\"; limit " + limit + ";";

            var response = await MakeIGDBRequest(body);

            return JsonSerializer.Deserialize<List<Game>>(response);
        }

        public async Task<string> FindTopGames(int limit)
        {
            ExternalAPI credentials = await GetIGDBCredentials();
            
            if (credentials.Token.IsNullOrEmpty())
                throw new AppException("Internal token error.");
            
            using var client = new HttpClient();

            client.DefaultRequestHeaders.Authorization =  new AuthenticationHeaderValue("Bearer", credentials.Token);
            client.DefaultRequestHeaders.Add("Client-ID", TWITCH_CLIENT_ID);
            
            var builder = new UriBuilder(TWITCH_TOP_GAMES_ENDPOINT);
            builder.Port = -1;
            var query = HttpUtility.ParseQueryString(builder.Query);
            query["first"] = limit.ToString();
            builder.Query = query.ToString();
            
            string requestURL = builder.ToString();
            
            var response = await client.GetAsync(requestURL);

            return await response.Content.ReadAsStringAsync();
        }

        public async Task<ExternalAPI> GetIGDBCredentials()
        {
            var externalAPI = await _externalApiRepository.FindByNameAsync("TWITCH_AUTH");

            if (externalAPI == null)
            {
                if (!await GetNewIGDBCredentials())
                    return null;
            }
            else if (DateTime.Now.ToFileTimeUtc() >= externalAPI.Expiration.ToFileTimeUtc())
            {
                if (!await GetNewIGDBCredentials())
                    return null;
            }
            
            return await _externalApiRepository.FindByNameAsync("TWITCH_AUTH");
        }

        public async Task<bool> GetNewIGDBCredentials()
        {
            
            //throw new Exception("HERE");
            using var client = new HttpClient();
            
            var data = new StringContent("", Encoding.UTF8, "application/json");
            
            var builder = new UriBuilder(TWITCH_OAUTH_URL);
            builder.Port = -1;
            var query = HttpUtility.ParseQueryString(builder.Query);
            query["client_id"] = TWITCH_CLIENT_ID;
            query["client_secret"] = TWITCH_CLIENT_SECRET;
            query["grant_type"] = TWITCH_GRANT_TYPE;
            builder.Query = query.ToString();
            
            string requestURL = builder.ToString();
            
            var response = await client.PostAsync(requestURL, data);

            var tokenResponse = JsonSerializer.Deserialize<TwitchOAuthResponse>(await response.Content.ReadAsStringAsync());

            if (tokenResponse == null || tokenResponse.access_token == null)
                return false;

            var externalAPI = new ExternalAPI();

            externalAPI.Name = "TWITCH_AUTH";
            externalAPI.Token = tokenResponse.access_token;
            externalAPI.Expiration = DateTime.Now.AddSeconds(tokenResponse.expires_in).AddDays(-1);

            await _externalApiRepository.AddAsync(externalAPI);
            await _unitOfWork.CompleteAsync();

            return true;
        }

        public async Task<string> MakeIGDBRequest(string content)
        {
            ExternalAPI credentials = await GetIGDBCredentials();
            
            if (credentials.Token.IsNullOrEmpty())
                throw new AppException("Internal token error.");
            
            using var client = new HttpClient();

            var data = new StringContent(content, Encoding.UTF8, "application/json");
            
            client.DefaultRequestHeaders.Authorization =  new AuthenticationHeaderValue("Bearer", credentials.Token);
            client.DefaultRequestHeaders.Add("Client-ID", TWITCH_CLIENT_ID);
            
            var response = await client.PostAsync(IGDB_GAMES_ENDPOINT_URL, data);

            return await response.Content.ReadAsStringAsync();
        }
    }
}<file_sep>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using GamingWorld.API.Business.Domain.Models;
using GamingWorld.API.Business.Domain.Repositories;
using GamingWorld.API.Publications.Domain.Models;
using GamingWorld.API.Publications.Domain.Repositories;
using GamingWorld.API.Publications.Domain.Services;
using GamingWorld.API.Publications.Domain.Services.Communication;
using GamingWorld.API.Publications.Resources;
using GamingWorld.API.Security.Exceptions;
using IUnitOfWork = GamingWorld.API.Shared.Domain.Repositories.IUnitOfWork;

namespace GamingWorld.API.Publications.Services
{
    public class PublicationService : IPublicationService
    {
        private readonly IPublicationRepository _publicationRepository;
        private readonly ITournamentRepository _tournamentRepository;
        private readonly IUnitOfWork _unitOfWork;
        private readonly IMapper _mapper;

        public PublicationService(IPublicationRepository publicationRepository,ITournamentRepository tournamentRepository, IUnitOfWork unitOfWork, IMapper mapper)
        {
            _tournamentRepository = tournamentRepository;
            _publicationRepository = publicationRepository;
            _unitOfWork = unitOfWork;
            _mapper = mapper;
        }

        public async Task<IEnumerable<Publication>> ListAsync()
        {
            return await _publicationRepository.ListAsync();
        }

        public async Task<IEnumerable<Publication>> ListByTypeAsync(int type)
        {
            var publications = await _publicationRepository.ListAsync();
            
            var filter = publications.Where(c => c.PublicationType == type).ToArray();
            
            return filter;
        }

        public async Task<Publication> GetById(int id)
        {
            return await _publicationRepository.FindByIdAsync(id);
        }

        public async Task<PublicationResponse> SaveAsync(SavePublicationResource publicationResource)
        {
            var publication = _mapper.Map<SavePublicationResource, Publication>(publicationResource);

            try
            {

                await _publicationRepository.AddAsync(publication);
                await _unitOfWork.CompleteAsync();

                if (publication.PublicationType == 3)
                {
                    Tournament tournament = new Tournament();
                    tournament.ParticipantLimit = publicationResource.ParticipantLimit;
                    tournament.TournamentDate = publicationResource.TournamentDate;
                    tournament.TournamentHour = publicationResource.TournamentHour;
                    tournament.PrizePool = publicationResource.PrizePool;
                    tournament.TournamentStatus = true;
                    tournament.Publication = publication;
                    await _tournamentRepository.AddAsync(tournament);
                    await _unitOfWork.CompleteAsync();
                    publication.TournamentId = tournament.Id;
                    publication.Tournament = tournament;
                }

                _publicationRepository.Update(publication);
                await _unitOfWork.CompleteAsync();

                return new PublicationResponse(publication);
            }
            catch (Exception e)
            {
                return new PublicationResponse($"An error occured while saving the publication: {e.Message}");
            }
        }

        public async Task<PublicationResponse> UpdateAsync(int id, Publication publication)
        {
            var existingPublication = await _publicationRepository.FindByIdAsync(id);
            if (existingPublication == null)
                return new PublicationResponse("Publication Not Found");
            existingPublication.Title = publication.Title;

            try
            {
                _publicationRepository.Update(existingPublication);
                await _unitOfWork.CompleteAsync();

                return new PublicationResponse(existingPublication);
            }
            catch (Exception e)
            {
                return new PublicationResponse($"An error occurred while updating the publication: {e.Message}");
            }
        }

        public async Task<PublicationResponse> DeleteAsync(int id)
        {
            var existingPublication = await _publicationRepository.FindByIdAsync(id);

            if (existingPublication == null)
                return new PublicationResponse("Publication not found");
            try
            {
                _publicationRepository.Remove(existingPublication);
                await _unitOfWork.CompleteAsync();

                return new PublicationResponse(existingPublication);
            }
            catch (Exception e)
            {
                return new PublicationResponse($"An error occurred while deleting publication:{e.Message}");
            }
        }
    }
}<file_sep>﻿namespace GamingWorld.API.Profiles.Domain.Models
{
    public class GameExperience
    {
        public int Id { get; set; }
        public string GameName { get; set; }
        public int Time { get; set; }
        public EGameExperienceTime TimeUnit { get; set; }
        
        // Relations
        public int ProfileId { get; set; }
        //public Profile Profile { get; set; }
    }
}<file_sep>﻿using AutoMapper;
using GamingWorld.API.Security.Domain.Models;
using GamingWorld.API.Security.Domain.Services.Communication;
using GamingWorld.API.Security.Resources;

namespace GamingWorld.API.Security.Mapping
{
    public class ResourceToModelProfile : Profile
    {
        public ResourceToModelProfile()
        {
            CreateMap<SaveUserResource, User>();
            CreateMap<RegisterRequest, User>();
            CreateMap<UpdateRequest, User>()
                .ForAllMembers(options=>options.Condition(
                    (source, Target, property) =>
                    {
                        if (property == null) return false;
                        if(property.GetType() == typeof(string) && string.IsNullOrEmpty((string)property)) return false;
                        return true;
                    }));
           
        }
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using GamingWorld.API.Profiles.Domain.Models;
using GamingWorld.API.Profiles.Domain.Services.Communication;

namespace GamingWorld.API.Profiles.Domain.Services
{
    public interface IUProfileService
    {
        Task<IEnumerable<Profile>> ListAsync();
        Task<Profile> ListByUserIdAsync(int userId);
        Task<Profile> ListByIdAsync(int id);
        Task<ProfileResponse> SaveAsync(Profile profile);
        Task<ProfileResponse> UpdateAsync(int id, Profile profile);
        Task<ProfileResponse> DeleteAsync(int id);
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using GamingWorld.API.Profiles.Domain.Models;

namespace GamingWorld.API.Profiles.Domain.Repositories
{
    public interface IProfileRepository
    {
        Task<IEnumerable<Profile>> ListAsync();
        Task AddAsync(Profile profile);
        Task<Profile> FindByIdAsync (int id);
        Task<Profile> FindByUserId(int profileId);
        void Update(Profile profile);
        void Remove(Profile profile);
    }
}<file_sep>﻿using AutoMapper;
using GamingWorld.API.Business.Domain.Models;
using GamingWorld.API.Business.Resources;
using GamingWorld.API.Publications.Domain.Models;
using GamingWorld.API.Publications.Resources;

namespace GamingWorld.API.Business.Mapping
{
    public class ModelToResourceProfile : AutoMapper.Profile
    {
        public ModelToResourceProfile()
        {
            CreateMap<Tournament, TournamentResource>();
            CreateMap<Participant, SaveParticipantResource>();
            CreateMap<Participant, ParticipantResource>();
            CreateMap<Tournament, SaveTournamentResource>();
        }
        
    }
}<file_sep>﻿using GamingWorld.API.Business.Domain.Models;
using GamingWorld.API.Shared.Domain.Services.Communication;

namespace GamingWorld.API.Business.Domain.Services.Communication
{
    public class TournamentResponse : BaseResponse<Tournament>
        {
            public TournamentResponse(string message) : base(message)
            {
            }

            public TournamentResponse(Tournament tournament) : base(tournament)
            {
            }
        }
    
}<file_sep>﻿namespace GamingWorld.API.Shared.Inbound.Games.Domain.Models
{
    public class Game
    {
        // NOT FOLLOWING THE NAMING CONVENTION BECAUSE WE NEED TO MAP EXACT FIELD NAMES FROM OBJECT RESPONSE FROM IGDB.
        public int id { get; set; }
        
        public string name { get; set; }
        
        public GameCover cover { get; set; }
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapper;
using GamingWorld.API.Business.Domain.Models;
using GamingWorld.API.Business.Domain.Services;
using GamingWorld.API.Business.Resources;
using GamingWorld.API.Publications.Domain.Models;
using GamingWorld.API.Publications.Resources;
using GamingWorld.API.Shared.Extensions;
using Microsoft.AspNetCore.Mvc;

namespace GamingWorld.API.Business.Controllers
{
    [ApiController]
    [Route("/api/v1/[controller]")]
    public class TournamentsController : ControllerBase
    {
        private readonly ITournamentService _tournamentService;
        private readonly IParticipantService _participantService;
        private readonly IMapper _mapper;

        public TournamentsController(IMapper mapper, ITournamentService tournamentService, IParticipantService participantService)
        {
            _mapper = mapper;
            _tournamentService = tournamentService;
            _participantService = participantService;
        }

        [HttpGet]
        public async Task<IEnumerable<TournamentResource>> GetAllAsync()
        {
                var publications = await _tournamentService.ListAsync();
                var resources = _mapper.Map<IEnumerable<Tournament>, IEnumerable<TournamentResource>>(publications);
                return resources;
        }
        [HttpGet("{id}")]
        public async Task<TournamentResource> GetByIdAsync(int id)
        {
            var tournament =  await _tournamentService.GetById(id);
            var resource = _mapper.Map<Tournament, TournamentResource>(tournament);
            return resource;
        }
        [HttpGet("{id}/participants")]
        public async Task<IEnumerable<ParticipantResource>> GetAllParticipantsByTournamentIdAsync(int id)
        {
            var tournament =  await _tournamentService.ListWithParticipantsByIdAsync(id);
            var resources = _mapper.Map<IEnumerable<Participant>, IEnumerable<ParticipantResource>>(tournament.Participants);
            return resources;
        }
        [HttpPost]
        public async Task<IActionResult> PostAsync([FromBody] SaveTournamentResource resource)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState.GetErrorMessages());

            var publication = _mapper.Map<SaveTournamentResource, Tournament>(resource);
            var result = await _tournamentService.SaveAsync(publication);

            if (!result.Success)
                return BadRequest(result.Message);
            var publicationResource = _mapper.Map<Tournament, SaveTournamentResource>(result.Resource);
            return Ok(publicationResource);
        }

        [HttpPost( "{id}/participants")]

        public async Task<IActionResult> PostParticipantAsync([FromBody] SaveParticipantResource resource, int id)
        {

            if (!ModelState.IsValid)
                 return BadRequest(ModelState.GetErrorMessages());

            var participant = _mapper.Map<SaveParticipantResource, Participant>(resource);
            var result = await _participantService.SaveAsync(id, participant);

            if (!result.Success)
                return BadRequest(result.Message);

            var participantResource = _mapper.Map<Participant, ParticipantResource>(result.Resource);
            return Ok(participantResource);
        }
        [HttpPut("{id}/participants/{participantId}")]
        public async Task<IActionResult> PutAsync(int id,int participantId, [FromQuery] int points)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState.GetErrorMessages());
            
            var result = await _tournamentService.UpdateParticipantPoints(id,participantId, points);
            
            if (!result.Success)
                return BadRequest(result.Message);

            var profileResource = _mapper.Map<Participant, ParticipantResource>(result.Resource);
            return Ok(profileResource);

        }
        
        [HttpPut("{id}")]
        public async Task<IActionResult> PutAsync([FromQuery] bool tournamentStatus, int id)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState.GetErrorMessages());
            
            var result = await _tournamentService.EndTournament(id, tournamentStatus);
            
            if (!result.Success)
                return BadRequest(result.Message);

            var tournamentResource = _mapper.Map<Tournament, TournamentResource>(result.Resource);
            return Ok(tournamentResource);

        }
    }
}<file_sep>﻿using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using GamingWorld.API.Shared.Inbound.ExternalAPIs.Domain.Models;
using GamingWorld.API.Shared.Inbound.Games.Domain.Models;

namespace GamingWorld.API.Shared.Inbound.Games.Domain.Services
{
    public interface IGamesService
    {
        Task<IEnumerable<Game>> ListRandomAsync(int limit);
        Task<Game> GetById(int id);
        Task<IEnumerable<Game>> FindByName(string name, int limit);
        Task<string> FindTopGames(int limit);
        Task<ExternalAPI> GetIGDBCredentials();

        Task<bool> GetNewIGDBCredentials();

        Task<string> MakeIGDBRequest(string content);
    }
}<file_sep>﻿using Microsoft.EntityFrameworkCore;

namespace GamingWorld.API.Shared.Extensions
{
    public static class ModelBuilderExtensions
    {
        public static void UseSnakeCaseNamingConvention(this ModelBuilder builder)
        {
            //Apply Naming Convention for Each Entity

            foreach (var entity in builder.Model.GetEntityTypes())
            {
                entity.SetTableName(StringExtensions.ToSnakeCase(entity.GetTableName()));

                foreach (var property in entity.GetProperties())
                    property.SetColumnName(StringExtensions.ToSnakeCase(property.GetColumnName()));

                foreach (var key in entity.GetKeys()) key.SetName(StringExtensions.ToSnakeCase(key.GetName()));

                foreach (var foreignKey in entity.GetForeignKeys())
                    foreignKey.SetConstraintName(StringExtensions.ToSnakeCase(foreignKey.GetConstraintName()));

                foreach (var index in entity.GetIndexes()) index.SetDatabaseName(StringExtensions.ToSnakeCase(index.GetDatabaseName()));
            }
        }
    }
}<file_sep>using GamingWorld.API.Security.Domain.Models;

namespace GamingWorld.API.Security.Authorization.Handlers.Interfaces
{
    public interface IJwtHandler
    {
        public string GenerateToken(User user);
        public int? ValidateToken(string token);
    }
}<file_sep>﻿namespace GamingWorld.API.Shared.Inbound.Games.Domain.Models
{
    public class TwitchOAuthResponse
    {
        // NOT FOLLOWING THE NAMING CONVENTION BECAUSE WE NEED TO MAP EXACT FIELD NAMES FROM OBJECT RESPONSE FROM TWITCH OAUTH.
        public string access_token { get; set; }

        public long expires_in { get; set; }

        public string token_type { get; set; }
    }
}<file_sep>﻿using System.Collections.Generic;
using System.Threading.Tasks;
using GamingWorld.API.Profiles.Domain.Models;
using GamingWorld.API.Profiles.Domain.Repositories;
using GamingWorld.API.Shared.Persistence.Contexts;
using GamingWorld.API.Shared.Persistence.Repositories;
using Microsoft.EntityFrameworkCore;

namespace GamingWorld.API.Profiles.Persistence.Repositories
{
    public class ProfileRepository : BaseRepository, IProfileRepository
    {
        public ProfileRepository(AppDbContext context) : base(context)
        {
        }

        public async Task<IEnumerable<Profile>> ListAsync()
        {
            // return await _context.Profiles.Include(up => up.User).ToListAsync(); // Pending to check if we really need the full user info when retrieving profiles.
            return await _context.Profiles
                .Include(up => up.GameExperiences)
                .Include(up => up.StreamingCategories)
                .Include(up => up.StreamerSponsors)
                .Include(up => up.TournamentExperiences)
                .Include(up => up.FavoriteGames)
                .Include(up => up.User)
                .ToListAsync();
        }

        public async Task AddAsync(Profile profile)
        {
            await _context.Profiles.AddAsync(profile);
        }

        public async Task<Profile> FindByIdAsync(int id)
        {
            // return await _context.Profiles.Include(up => up.User).FirstOrDefaultAsync(up => up.Id == id); // Pending to check if we really need the full user info when retrieving profiles.
            return await _context.Profiles
                .Include(up => up.GameExperiences)
                .Include(up => up.StreamingCategories)
                .Include(up => up.StreamerSponsors)
                .Include(up => up.TournamentExperiences)
                .Include(up => up.FavoriteGames)
                .FirstOrDefaultAsync(up => up.Id == id);
        }

        public async Task<Profile> FindByUserId(int userId)
        {
            //return await _context.Profiles.Include(up => up.User).FirstOrDefaultAsync(up => up.UserId == userId); // Pending to check if we really need the full user info when retrieving profiles.
            return await _context.Profiles
                .Include(up => up.GameExperiences)
                .Include(up => up.StreamingCategories)
                .Include(up => up.StreamerSponsors)
                .Include(up => up.TournamentExperiences)
                .Include(up => up.FavoriteGames)
                .FirstOrDefaultAsync(up => up.UserId == userId);
        }

        public void Update(Profile profile)
        {
            _context.Profiles.Update(profile);
        }

        public void Remove(Profile profile)
        {
            _context.Profiles.Remove(profile);
        }
    }
}