Sube carpetas API, API.Tests, Application y Domain del Backend

This commit is contained in:
FrancoRu
2026-09-18 13:35:59 -03:00
parent bbb09554bb
commit 2eb4eeef03
572 changed files with 56405 additions and 0 deletions
@@ -0,0 +1,30 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace Application.Utils.Constants.Validation;
/// <summary>
/// Validates that a string is a plausible Argentine phone number.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
public sealed class ArgentinePhoneNumberAttribute : ValidationAttribute
{
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
if (value is not string phone || string.IsNullOrWhiteSpace(phone))
{
return ValidationResult.Success;
}
string trimmed = phone.Trim();
bool hasOnlyAllowedChars = trimmed.All(c => char.IsDigit(c) || c is '+' or ' ' or '(' or ')' or '-');
int digitCount = trimmed.Count(char.IsDigit);
return hasOnlyAllowedChars && digitCount == 10
? ValidationResult.Success
: new ValidationResult(
ErrorMessage ?? ValidationPatterns.PhoneNumberError,
[validationContext.MemberName ?? string.Empty]);
}
}
@@ -0,0 +1,9 @@
namespace Application.Utils.Constants.Validation;
/// <summary>
/// Field length limits shared by every division-name-accepting request DTO and entity configuration.
/// </summary>
public static class DivisionFieldLengths
{
public const int NameMaxLength = 100;
}
@@ -0,0 +1,29 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Application.Utils.Constants.Validation;
/// <summary>
/// Validates that a DateTime birth date is at least minimumYears years in the past, meaning the person is at least that many years old today.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
public sealed class MinimumAgeAttribute(int minimumYears) : ValidationAttribute
{
private readonly int _minimumYears = minimumYears;
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
if (value is not DateTime birthDate)
{
return ValidationResult.Success;
}
DateTime cutoff = DateTime.UtcNow.Date.AddYears(-_minimumYears);
return birthDate.Date <= cutoff
? ValidationResult.Success
: new ValidationResult(
ErrorMessage ?? $"El jugador debe tener al menos {_minimumYears} años.",
[validationContext.MemberName ?? string.Empty]);
}
}
@@ -0,0 +1,12 @@
namespace Application.Utils.Constants.Validation;
/// <summary>
/// Field length limits shared by CreatePlayerRequest and UpdatePlayerRequest, so the two stay in sync.
/// </summary>
public static class PlayerFieldLengths
{
public const int NameMaxLength = 70;
public const int DocumentNumberMaxLength = 15;
public const int PhoneNumberMaxLength = 15;
public const int SocialSecurityMaxLength = 100;
}
@@ -0,0 +1,22 @@
namespace Application.Utils.Constants.Validation;
/// <summary>
/// Field length limits shared across the player sanction request DTOs, so they stay in sync.
/// </summary>
public static class SanctionFieldLengths
{
/// <summary>
/// Max length of the short sanction description on Create and Update requests.
/// </summary>
public const int DescriptionMaxLength = 255;
/// <summary>
/// Min length of the free-text appeal reason / resolution notes.
/// </summary>
public const int LongTextMinLength = 1;
/// <summary>
/// Max length of the free-text appeal reason / resolution notes.
/// </summary>
public const int LongTextMaxLength = 1000;
}
@@ -0,0 +1,9 @@
namespace Application.Utils.Constants.Validation;
/// <summary>
/// Field length limits shared by CreateSeasonRequest and UpdateSeasonRequest, so the two stay in sync.
/// </summary>
public static class SeasonFieldLengths
{
public const int NameMaxLength = 100;
}
@@ -0,0 +1,12 @@
namespace Application.Utils.Constants.Validation;
/// <summary>
/// Field length limits shared across the account and user request DTOs, so they stay in sync.
/// </summary>
public static class UserFieldLengths
{
public const int UsernameMinLength = 3;
public const int UsernameMaxLength = 50;
public const int PasswordMinLength = 8;
public const int PhoneMaxLength = 15;
}
@@ -0,0 +1,27 @@
namespace Application.Utils.Constants.Validation;
/// <summary>
/// Shared validation regex patterns kept in one place so every request DTO and the frontend validators enforce the exact same plausible contact-field rules.
/// </summary>
public static class ValidationPatterns
{
/// <summary>
/// Plausible phone number: only digits, spaces, plus, hyphen and parentheses, containing between 8 and 15 digits.
/// </summary>
public const string PhoneNumber = @"^(?=(?:\D*\d){8,15}\D*$)[+\d\s()-]+$";
/// <summary>
/// Human-facing message for a phone number that fails the pattern.
/// </summary>
public const string PhoneNumberError = "Invalid phone number format.";
/// <summary>
/// A player's DNI or document number: digits only, 6 to 15 of them.
/// </summary>
public const string DocumentNumber = @"^\d{6,15}$";
/// <summary>
/// Human-facing message for a document number that fails the pattern.
/// </summary>
public const string DocumentNumberError = "El documento debe contener solo números.";
}
@@ -0,0 +1,10 @@
namespace Application.Utils.Constants.Validation;
/// <summary>
/// Field length limits shared by CreateVenueRequest and UpdateVenueRequest, so the two stay in sync.
/// </summary>
public static class VenueFieldLengths
{
public const int NameMaxLength = 50;
public const int AddressMaxLength = 200;
}