init backend

This commit is contained in:
2026-02-04 21:11:51 +01:00
parent 537d49e797
commit b73fd8eb05
31 changed files with 673 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
using AipsCore.Domain.Common.Validation;
namespace AipsCore.Domain.Abstract.Rule;
public abstract class AbstractRule : IRule
{
protected abstract string ErrorCode { get; }
protected abstract string ErrorMessage { get; }
public string ValueObjectName { protected get; set; } = "Unknown";
public ValidationError GetError()
{
return new ValidationError(ErrorCode, ErrorMessage);
}
public abstract bool Validate();
}

View File

@@ -0,0 +1,10 @@
using AipsCore.Domain.Common.Validation;
namespace AipsCore.Domain.Abstract.Rule;
public interface IRule
{
ValidationError GetError();
bool Validate();
string ValueObjectName { set; }
}

View File

@@ -0,0 +1,24 @@
using AipsCore.Domain.Abstract.Rule;
using AipsCore.Domain.Common.Validation;
namespace AipsCore.Domain.Abstract.ValueObject;
public abstract record AbstractValueObject
{
protected abstract ICollection<IRule> GetValidationRules();
protected void Validate()
{
var rules = GetValidationRules();
var validator = new Validator(rules, ValueObjectName);
validator.Validate();
if (!validator.Success)
{
throw validator.GetValidationException();
}
}
private string ValueObjectName => this.GetType().Name;
}

View File

@@ -0,0 +1,31 @@
using AipsCore.Domain.Abstract;
using AipsCore.Domain.Abstract.Rule;
namespace AipsCore.Domain.Common.Validation.Rules;
public class CharsetRule : AbstractRule
{
private readonly string _stringValue;
private readonly char[] _charset;
protected CharsetRule(string stringValue, char[] charset)
{
_stringValue = stringValue;
_charset = charset;
}
protected override string ErrorCode => "charset";
protected override string ErrorMessage => $"Forbidden characters in '{ValueObjectName}'";
public override bool Validate()
{
foreach (char character in _stringValue)
{
if (!_charset.Contains(character))
{
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,32 @@
using System.Net.Mail;
using AipsCore.Domain.Abstract;
using AipsCore.Domain.Abstract.Rule;
namespace AipsCore.Domain.Common.Validation.Rules;
public class EmailRule : AbstractRule
{
protected override string ErrorCode => "email_invalid";
protected override string ErrorMessage => "Email is not in the valid format";
private readonly string _emailValue;
public EmailRule(string emailValue)
{
_emailValue = emailValue;
}
public override bool Validate()
{
try
{
_ = new MailAddress(_emailValue);
}
catch
{
return false;
}
return true;
}
}

View File

@@ -0,0 +1,24 @@
using AipsCore.Domain.Abstract;
using AipsCore.Domain.Abstract.Rule;
namespace AipsCore.Domain.Common.Validation.Rules;
public class ExactLength : AbstractRule
{
private readonly string _stringValue;
private readonly int _exactLentgh;
protected override string ErrorCode => "exact_length";
protected override string ErrorMessage
=> $"Length of '{ValueObjectName}' must be {_exactLentgh} characters";
public ExactLength(string stringValue, int exactLentgh)
{
_stringValue = stringValue;
_exactLentgh = exactLentgh;
}
public override bool Validate()
{
return _stringValue.Length == _exactLentgh;
}
}

View File

@@ -0,0 +1,24 @@
using AipsCore.Domain.Abstract;
using AipsCore.Domain.Abstract.Rule;
namespace AipsCore.Domain.Common.Validation.Rules;
public class MaxLengthRule : AbstractRule
{
private readonly string _stringValue;
private readonly int _maximumLentgh;
protected override string ErrorCode => "minimum_length";
protected override string ErrorMessage
=> $"Length of '{ValueObjectName}' must be at most {_maximumLentgh} characters";
public MaxLengthRule(string stringValue, int maximumLentgh)
{
_stringValue = stringValue;
_maximumLentgh = maximumLentgh;
}
public override bool Validate()
{
return _stringValue.Length <= _maximumLentgh;
}
}

View File

@@ -0,0 +1,24 @@
using AipsCore.Domain.Abstract;
using AipsCore.Domain.Abstract.Rule;
namespace AipsCore.Domain.Common.Validation.Rules;
public class MinLengthRule : AbstractRule
{
private readonly string _stringValue;
private readonly int _minimumLentgh;
protected override string ErrorCode => "minimum_length";
protected override string ErrorMessage
=> $"Length of '{ValueObjectName}' must be at least {_minimumLentgh} characters";
public MinLengthRule(string stringValue, int minimumLentgh)
{
_stringValue = stringValue;
_minimumLentgh = minimumLentgh;
}
public override bool Validate()
{
return _stringValue.Length >= _minimumLentgh;
}
}

View File

@@ -0,0 +1,3 @@
namespace AipsCore.Domain.Common.Validation;
public record ValidationError(string Code, string Message);

View File

@@ -0,0 +1,13 @@
using System.Collections.Immutable;
namespace AipsCore.Domain.Common.Validation;
public class ValidationException : Exception
{
public ICollection<ValidationError> ValidationErrors { get; private init; }
public ValidationException(ICollection<ValidationError> validationErrors)
{
ValidationErrors = validationErrors;
}
}

View File

@@ -0,0 +1,55 @@
using AipsCore.Domain.Abstract;
using AipsCore.Domain.Abstract.Rule;
namespace AipsCore.Domain.Common.Validation;
public class Validator
{
private readonly ICollection<IRule> _validationRules;
private readonly string _valueObjectName;
public Validator(ICollection<IRule> validationRules, string valueObjectName)
{
_validationRules = validationRules;
_valueObjectName = valueObjectName;
}
public bool Success { get; private set; } = false;
private ValidationException? _validationException = null;
public ValidationException GetValidationException()
{
if (_validationException is null)
{
return new ValidationException([]);
}
return _validationException;
}
public void Validate()
{
List<ValidationError> errors = [];
foreach (var validationRule in _validationRules)
{
validationRule.ValueObjectName = _valueObjectName;
if (!validationRule.Validate())
{
errors.Add(validationRule.GetError());
}
}
if (errors.Any())
{
Success = false;
_validationException = new ValidationException(errors.ToArray());
}
else
{
Success = true;
_validationException = null;
}
}
}

View File

@@ -0,0 +1,22 @@
using AipsCore.Domain.Abstract;
using AipsCore.Domain.Abstract.Rule;
using AipsCore.Domain.Abstract.ValueObject;
using AipsCore.Domain.Common.Validation;
namespace AipsCore.Domain.Common.ValueObjects;
public record DomainId : AbstractValueObject
{
public string IdValue { get; init; }
public DomainId(string IdValue)
{
this.IdValue = IdValue;
Validate();
}
protected override ICollection<IRule> GetValidationRules()
{
return [];
}
}

View File

@@ -0,0 +1,26 @@
using AipsCore.Domain.Abstract;
using AipsCore.Domain.Abstract.Rule;
using AipsCore.Domain.Abstract.ValueObject;
using AipsCore.Domain.Common.Validation;
using AipsCore.Domain.Common.Validation.Rules;
namespace AipsCore.Domain.Common.ValueObjects;
public record Email : AbstractValueObject
{
public string EmailValue { get; init; }
public Email(string EmailValue)
{
this.EmailValue = EmailValue;
Validate();
}
protected override ICollection<IRule> GetValidationRules()
{
return
[
new EmailRule(EmailValue)
];
}
}

View File

@@ -0,0 +1,18 @@
using AipsCore.Domain.Common.ValueObjects;
using AipsCore.Domain.Models.User.ValueObjects;
namespace AipsCore.Domain.Models.User;
public class User
{
public UserId Id { get; private set; }
public Email Email { get; private set; }
public Username Username { get; private set; }
public User(UserId id, Email email, Username username)
{
Id = id;
Email = email;
Username = username;
}
}

View File

@@ -0,0 +1,26 @@
using AipsCore.Domain.Common.Validation.Rules;
using AipsCore.Utility.Text;
namespace AipsCore.Domain.Models.User.Validation;
public class UsernameCharsetRule : CharsetRule
{
public UsernameCharsetRule(string stringValue)
: base(stringValue, GetUsernameCharset())
{
}
private static char[] GetUsernameCharset()
{
var alphanumericCharset = Charset.GetAlphanumericCharset();
char[] usernameCharset = [..alphanumericCharset, '_'];
return usernameCharset;
}
protected override string ErrorCode => "username_charset";
protected override string ErrorMessage =>
"Username contains invalid characters, only alphanumeric characters and '_' are allowed";
}

View File

@@ -0,0 +1,5 @@
using AipsCore.Domain.Common.ValueObjects;
namespace AipsCore.Domain.Models.User.ValueObjects;
public record UserId(string IdValue) : DomainId(IdValue);

View File

@@ -0,0 +1,33 @@
using AipsCore.Domain.Abstract;
using AipsCore.Domain.Abstract.Rule;
using AipsCore.Domain.Abstract.ValueObject;
using AipsCore.Domain.Common.Validation;
using AipsCore.Domain.Common.Validation.Rules;
using AipsCore.Domain.Common.ValueObjects;
using AipsCore.Domain.Models.User.Validation;
namespace AipsCore.Domain.Models.User.ValueObjects;
public record Username : AbstractValueObject
{
public string UsernameValue { get; init; }
public Username(string UsernameValue)
{
this.UsernameValue = UsernameValue;
Validate();
}
private const int MinimumLength = 8;
private const int MaximumLength = 20;
protected override ICollection<IRule> GetValidationRules()
{
return
[
new MinLengthRule(UsernameValue, MinimumLength),
new MaxLengthRule(UsernameValue, MaximumLength),
new UsernameCharsetRule(UsernameValue)
];
}
}

View File

@@ -0,0 +1,20 @@
using AipsCore.Domain.Common.Validation.Rules;
using AipsCore.Utility.Text;
namespace AipsCore.Domain.Models.Whiteboard.Validation;
public class WhitebordCodeCharsetRule : CharsetRule
{
public WhitebordCodeCharsetRule(string stringValue)
: base(stringValue, GetWhiteboardCodeCharset())
{
}
private static char[] GetWhiteboardCodeCharset()
{
return Charset.GetNumericCharset();
}
protected override string ErrorCode => "whiteboard_code_charset";
protected override string ErrorMessage => "Whiteboard code must contain only numbers";
}

View File

@@ -0,0 +1,31 @@
using AipsCore.Domain.Abstract;
using AipsCore.Domain.Abstract.Rule;
using AipsCore.Domain.Abstract.ValueObject;
using AipsCore.Domain.Common.Validation;
using AipsCore.Domain.Common.Validation.Rules;
using AipsCore.Domain.Common.ValueObjects;
using AipsCore.Domain.Models.Whiteboard.Validation;
namespace AipsCore.Domain.Models.Whiteboard.ValueObjects;
public record WhiteboardCode : AbstractValueObject
{
public string CodeValue { get; init; }
public WhiteboardCode(string CodeValue)
{
this.CodeValue = CodeValue;
Validate();
}
private const int CodeLength = 8;
protected override ICollection<IRule> GetValidationRules()
{
return
[
new ExactLength(CodeValue, CodeLength),
new WhitebordCodeCharsetRule(CodeValue)
];
}
}

View File

@@ -0,0 +1,5 @@
using AipsCore.Domain.Common.ValueObjects;
namespace AipsCore.Domain.Models.Whiteboard.ValueObjects;
public record WhiteboardId(string IdValue) : DomainId(IdValue);

View File

@@ -0,0 +1,18 @@
using AipsCore.Domain.Models.User.ValueObjects;
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
namespace AipsCore.Domain.Models.Whiteboard;
public class Whiteboard
{
public WhiteboardId Id { get; private set; }
public UserId WhiteboardOwnerId { get; private set; }
public WhiteboardCode Code { get; private set; }
public Whiteboard(WhiteboardId id, User.User whiteboardOwner, WhiteboardCode code)
{
Id = id;
WhiteboardOwnerId = whiteboardOwner.Id;
Code = code;
}
}