Skip to content

Commit

Permalink
Merge pull request #64 from PinguApps/add-request-validation
Browse files Browse the repository at this point in the history
Added validators, and validation of request objects within SDK
  • Loading branch information
pingu2k4 authored Jul 9, 2024
2 parents 950b19d + abaab4e commit 62d4c69
Show file tree
Hide file tree
Showing 20 changed files with 482 additions and 12 deletions.
10 changes: 10 additions & 0 deletions src/PinguApps.Appwrite.Client/Clients/AccountClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public async Task<AppwriteResult<User>> Create(CreateAccountRequest request)
{
try
{
request.Validate(true);

var result = await _accountApi.CreateAccount(request);

return result.GetApiResponse();
Expand All @@ -67,6 +69,8 @@ public async Task<AppwriteResult<User>> UpdateEmail(UpdateEmailRequest request)
{
try
{
request.Validate(true);

var result = await _accountApi.UpdateEmail(Session, request);

return result.GetApiResponse();
Expand All @@ -82,6 +86,8 @@ public async Task<AppwriteResult<User>> UpdateName(UpdateNameRequest request)
{
try
{
request.Validate(true);

var result = await _accountApi.UpdateName(Session, request);

return result.GetApiResponse();
Expand All @@ -97,6 +103,8 @@ public async Task<AppwriteResult<User>> UpdatePassword(UpdatePasswordRequest req
{
try
{
request.Validate(true);

var result = await _accountApi.UpdatePassword(Session, request);

return result.GetApiResponse();
Expand All @@ -112,6 +120,8 @@ public async Task<AppwriteResult<User>> UpdatePhone(UpdatePhoneRequest request)
{
try
{
request.Validate(true);

var result = await _accountApi.UpdatePhone(Session, request);

return result.GetApiResponse();
Expand Down
6 changes: 4 additions & 2 deletions src/PinguApps.Appwrite.Playground/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ public async Task Run(string[] args)

var request = new UpdatePhoneRequest
{
Password = "MyNewPassword",
Phone = "+14155552671"
Password = "sword",
Phone = "14155552671"
};

var f = request.IsValid();

var result = await _client.Account.UpdatePhone(request);

result.Result.Switch(
Expand Down
2 changes: 2 additions & 0 deletions src/PinguApps.Appwrite.Server/Servers/AccountServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public async Task<AppwriteResult<User>> Create(CreateAccountRequest request)
{
try
{
request.Validate(true);

var result = await _accountApi.CreateAccount(request);

return result.GetApiResponse();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentValidation" Version="11.9.2" />
<PackageReference Include="OneOf" Version="3.0.271" />
<PackageReference Include="Refit" Version="7.1.2" />
<PackageReference Include="Refit.HttpClientFactory" Version="7.1.2" />
Expand Down
20 changes: 20 additions & 0 deletions src/PinguApps.Appwrite.Shared/Requests/BaseRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using FluentValidation;
using FluentValidation.Results;

namespace PinguApps.Appwrite.Shared.Requests;
public abstract class BaseRequest<TRequest, TValidator>
where TRequest : class
where TValidator : IValidator<TRequest>, new()
{
public bool IsValid() => Validate().IsValid;

public ValidationResult Validate(bool throwOnFailures = false)
{
var validator = new TValidator();

if (throwOnFailures)
return validator.Validate(this as TRequest, options => options.ThrowOnFailures());

return validator.Validate((this as TRequest)!);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System.Text.Json.Serialization;
using PinguApps.Appwrite.Shared.Requests.Validators;
using PinguApps.Appwrite.Shared.Utils;

namespace PinguApps.Appwrite.Shared.Requests;

/// <summary>
/// The request for creating an account
/// </summary>
public class CreateAccountRequest
public class CreateAccountRequest : BaseRequest<CreateAccountRequest, CreateAccountRequestValidator>
{
/// <summary>
/// User ID. Choose a custom ID or generate a random ID with <see cref="IdUtils.GenerateUniqueId(int)"/>. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars
Expand All @@ -32,3 +33,5 @@ public class CreateAccountRequest
[JsonPropertyName("name")]
public string? Name { get; set; }
}


3 changes: 2 additions & 1 deletion src/PinguApps.Appwrite.Shared/Requests/UpdateEmailRequest.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System.Text.Json.Serialization;
using PinguApps.Appwrite.Shared.Requests.Validators;

namespace PinguApps.Appwrite.Shared.Requests;

/// <summary>
/// The request for updating a users email
/// </summary>
public class UpdateEmailRequest
public class UpdateEmailRequest : BaseRequest<UpdateEmailRequest, UpdateEmailRequestValidator>
{
/// <summary>
/// User email
Expand Down
3 changes: 2 additions & 1 deletion src/PinguApps.Appwrite.Shared/Requests/UpdateNameRequest.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System.Text.Json.Serialization;
using PinguApps.Appwrite.Shared.Requests.Validators;

namespace PinguApps.Appwrite.Shared.Requests;

/// <summary>
/// The request for updating a users name
/// </summary>
public class UpdateNameRequest
public class UpdateNameRequest : BaseRequest<UpdateNameRequest, UpdateNameRequestValidator>
{
/// <summary>
/// User name. Max length: 128 chars
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System.Text.Json.Serialization;
using PinguApps.Appwrite.Shared.Requests.Validators;

namespace PinguApps.Appwrite.Shared.Requests;

/// <summary>
/// The request for updating a users password
/// </summary>
public class UpdatePasswordRequest
public class UpdatePasswordRequest : BaseRequest<UpdatePasswordRequest, UpdatePasswordRequestValidator>
{
/// <summary>
/// New user password. Must be at least 8 chars
Expand Down
4 changes: 3 additions & 1 deletion src/PinguApps.Appwrite.Shared/Requests/UpdatePhoneRequest.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System.Text.Json.Serialization;
using PinguApps.Appwrite.Shared.Requests.Validators;

namespace PinguApps.Appwrite.Shared.Requests;

/// <summary>
/// The request for updating a users phone
/// </summary>
public class UpdatePhoneRequest
public class UpdatePhoneRequest : BaseRequest<UpdatePhoneRequest, UpdatePhoneRequestValidator>
{
/// <summary>
/// Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212
Expand All @@ -19,3 +20,4 @@ public class UpdatePhoneRequest
[JsonPropertyName("password")]
public string Password { get; set; } = string.Empty;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using FluentValidation;

namespace PinguApps.Appwrite.Shared.Requests.Validators;
public class CreateAccountRequestValidator : AbstractValidator<CreateAccountRequest>
{
public CreateAccountRequestValidator()
{
RuleFor(x => x.UserId).NotEmpty().Matches("^[a-zA-Z0-9][a-zA-Z0-9._-]{0,35}$");
RuleFor(x => x.Email).NotEmpty().EmailAddress();
RuleFor(x => x.Password).NotEmpty().MinimumLength(8).MaximumLength(256);
RuleFor(x => x.Name).MaximumLength(128);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using FluentValidation;

namespace PinguApps.Appwrite.Shared.Requests.Validators;
public class UpdateEmailRequestValidator : AbstractValidator<UpdateEmailRequest>
{
public UpdateEmailRequestValidator()
{
RuleFor(x => x.Email).NotEmpty().EmailAddress();
RuleFor(x => x.Password).NotEmpty().MinimumLength(8);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using FluentValidation;

namespace PinguApps.Appwrite.Shared.Requests.Validators;
public class UpdateNameRequestValidator : AbstractValidator<UpdateNameRequest>
{
public UpdateNameRequestValidator()
{
RuleFor(x => x.Name).NotEmpty().MaximumLength(128);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using FluentValidation;

namespace PinguApps.Appwrite.Shared.Requests.Validators;
public class UpdatePasswordRequestValidator : AbstractValidator<UpdatePasswordRequest>
{
public UpdatePasswordRequestValidator()
{
RuleFor(x => x.NewPassword).NotEmpty().MinimumLength(8);
RuleFor(x => x.OldPassword).MinimumLength(8).When(x => x.OldPassword is not null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using FluentValidation;

namespace PinguApps.Appwrite.Shared.Requests.Validators;
public class UpdatePhoneRequestValidator : AbstractValidator<UpdatePhoneRequest>
{
public UpdatePhoneRequestValidator()
{
RuleFor(x => x.Phone).NotEmpty().Matches("^\\+\\d*$");
RuleFor(x => x.Password).NotEmpty().MinimumLength(8);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using PinguApps.Appwrite.Shared.Requests;
using FluentValidation;
using PinguApps.Appwrite.Shared.Requests;

namespace PinguApps.Appwrite.Shared.Tests.Requests;
public class CreateAccountRequestTests
Expand Down Expand Up @@ -35,4 +36,96 @@ public void Properties_CanBeSet(string email, string password, string? name)
Assert.Equal(password, request.Password);
Assert.Equal(name, request.Name);
}

[Theory]
[InlineData(null, "[email protected]", "Password", null)]
[InlineData("321654987", "[email protected]", "12345678", "Pingu")]
[InlineData("a.s-d_f", "[email protected]", "12345678", "Pingu")]
public void IsValid_WithValidData_ReturnsTrue(string? userId, string email, string password, string? name)
{
// Arrange
var request = new CreateAccountRequest
{
Email = email,
Password = password,
Name = name
};

if (userId is not null)
{
request.UserId = userId;
}

// Act
var isValid = request.IsValid();

// Assert
Assert.True(isValid);
}

[Theory]
[InlineData("badChar^", "[email protected]", "Password", "Pingu")]
[InlineData(".bad", "[email protected]", "Password", "Pingu")]
[InlineData("_bad", "[email protected]", "Password", "Pingu")]
[InlineData("-bad", "[email protected]", "Password", "Pingu")]
[InlineData("", "[email protected]", "Password", "Pingu")]
[InlineData("1234567890123456789012345678901234567", "[email protected]", "Password", "Pingu")]
[InlineData(null, "not an email", "Password", "Pingu")]
[InlineData(null, "", "Password", "Pingu")]
[InlineData(null, "[email protected]", "short", "Pingu")]
[InlineData(null, "[email protected]", "", "Pingu")]
public void IsValid_WithInvalidData_ReturnsFalse(string? userId, string email, string password, string? name)
{
// Arrange
var request = new CreateAccountRequest
{
Email = email,
Password = password,
Name = name
};

if (userId is not null)
{
request.UserId = userId;
}

// Act
var isValid = request.IsValid();

// Assert
Assert.False(isValid);
}

[Fact]
public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure()
{
// Arrange
var request = new CreateAccountRequest
{
UserId = ".badChar^",
Email = "not an email",
Password = "short"
};

// Assert
Assert.Throws<ValidationException>(() => request.Validate(true));
}

[Fact]
public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure()
{
// Arrange
var request = new CreateAccountRequest
{
UserId = ".badChar^",
Email = "not an email",
Password = "short"
};

// Act
var result = request.Validate(false);

// Assert
Assert.False(result.IsValid);
}
}
Loading

0 comments on commit 62d4c69

Please sign in to comment.