Skip to content

Commit

Permalink
user update implemented
Browse files Browse the repository at this point in the history
user update implementation is done, but not without limitation, and that is user cannot change username. Because we are using username as gain identity, it will be too much hassle for user to change username.
  • Loading branch information
riza_ramadan committed Jan 13, 2021
1 parent 6b5d51f commit 5e3c57c
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 93 deletions.
51 changes: 0 additions & 51 deletions src/Conduit/Features/Articles/GetArticleByTag.cs

This file was deleted.

9 changes: 9 additions & 0 deletions src/Conduit/Features/Users/Inputs/UpdateUser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Conduit.Features.Users.Inputs
{
using Contracts.Users;

public class UpdateUserWrapper
{
public UpdateUser User { get; set; }
}
}
69 changes: 51 additions & 18 deletions src/Conduit/Features/Users/UserController.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
using Conduit.Infrastructure.Security;
using Conduit.Features.Users.Outputs;
using Contracts;
using Contracts.Users;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Orleans;
using System.Threading.Tasks;

namespace Conduit.Features.Users
namespace Conduit.Features.Users
{
using Conduit.Infrastructure.Security;
using Conduit.Features.Users.Outputs;
using Contracts;
using Contracts.Users;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Orleans;
using System.Threading.Tasks;
using Conduit.Features.Users.Inputs;

[Route("[controller]")]
[ApiController]
[Authorize]
Expand All @@ -35,16 +36,48 @@ public async Task<IActionResult> Get()
return UnprocessableEntity(error);
}

var user = _client.GetGrain<IUserGrain>(userId);
var userGrain = _client.GetGrain<IUserGrain>(userId);
(Contracts.Users.User User, Error Error) = await userGrain.Get();
if (Error.Exist())
{
return UnprocessableEntity(Error);
}

return Ok(new GetCurrentUserOutput(
user.GetPrimaryKeyString(),
(await user.GetEmail()).Email,
//TODO: update bio feature
"some bio",
//TODO: update image feature
"some image",
await _tokenGenerator.CreateToken(user.GetPrimaryKeyString())
userGrain.GetPrimaryKeyString(),
User.Email,
User.Bio,
User.Image,
await _tokenGenerator.CreateToken(userGrain.GetPrimaryKeyString())
));
}

[HttpPut]
public async Task<IActionResult> Edit(UpdateUserWrapper u)
{
var (userId, error) = _userService.GetCurrentUsername();
if (error.Exist())
{
return UnprocessableEntity(error);
}

var userGrain = _client.GetGrain<IUserGrain>(userId);
error = await userGrain.Update(u.User);
if (error.Exist())
{
return UnprocessableEntity(error);
}
var result = await userGrain.Get();
if (result.Error.Exist())
{
return UnprocessableEntity(error);
}
return Ok(new GetCurrentUserOutput(
userGrain.GetPrimaryKeyString(),
result.User.Email,
result.User.Bio,
result.User.Image,
await _tokenGenerator.CreateToken(userGrain.GetPrimaryKeyString())
));
}
}
Expand Down
21 changes: 12 additions & 9 deletions src/Conduit/Features/Users/UsersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,24 @@ public UsersController(IClusterClient c, IJwtTokenGenerator g, IUserService s)
[HttpPost]
public async Task<IActionResult> Create([FromBody] RegisterWrapper r)
{
var user = _client.GetGrain<IUserGrain>(r.User.Username);
var error = await user.Register(r.User.Email, r.User.Password);
var userGrain = _client.GetGrain<IUserGrain>(r.User.Username);
var error = await userGrain.Register(r.User.Email, r.User.Password);
if (error.Exist())
{
return UnprocessableEntity(error);
}

var result = await userGrain.Get();
if (result.Error.Exist())
{
return UnprocessableEntity(result.Error);
}
return Ok(new RegisterUserOutput(
user.GetPrimaryKeyString(),
r.User.Email,
//TODO: update bio feature
"some bio",
//TODO: update image feature
"some image",
await _tokenGenerator.CreateToken(user.GetPrimaryKeyString())
userGrain.GetPrimaryKeyString(),
result.User.Email,
result.User.Bio,
result.User.Image,
await _tokenGenerator.CreateToken(userGrain.GetPrimaryKeyString())
));
}

Expand Down
4 changes: 2 additions & 2 deletions src/Contracts/Users/IUserGrain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ public interface IUserGrain : IGrainWithStringKey
{
Task<(bool, Error)> HasRegistered();
Task<Error> Register(string email, string password);
Task<(string Email,Error Error)> GetEmail();
Task<Error> Login(string email, string password);

Task<Error> Update(UpdateUser user);
Task<(string Email,Error Error)> GetEmail();
Task<(User User, Error Error)> Get();
}
}
7 changes: 7 additions & 0 deletions src/Contracts/Users/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ public class User
public string Bio { get; set; }
public Guid Salt { get; set; }
public string Image { get; set; }
}

public class UpdateUser
{
public string Email { get; set; }
public string Bio { get; set; }
public string Image { get; set; }
public string Password { get; set; }
}
}
82 changes: 69 additions & 13 deletions src/Grains/Users/UserGrain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ public class UserGrain : Grain, IUserGrain
public static readonly Error UnregisteredUserLogin =
new Error("d7a011a1-3f86-4797-b6ef-210b4b041121", "login of unregistered user");

public static readonly Error UnregisteredUserUpdate =
new Error("86AEFE6B-D99D-4168-95E1-94E6E330F390", "unregistered user want to update");

public static readonly Error EmailPasswordMismatch =
new Error("069e089f-1ff9-49a6-8821-7091ab9fa0a7", "email or password mismatch");

Expand Down Expand Up @@ -46,6 +49,35 @@ IGrainFactory f
);
return (result, Error.None);
}
public async Task<Error> Register(string email, string password)
{
var (hasRegistered, error) = await HasRegistered();
if (error.Exist())
{
return error;
}
if (hasRegistered)
{
return UserAlreadyRegistered;
}
try
{
_userState.State.Email = email;
var passwordHasher = _factory.GetGrain<IPasswordHasher>(0);
_userState.State.Salt = Guid.NewGuid();
_userState.State.Password =
await passwordHasher.Hash(password, _userState.State.Salt.ToByteArray());
await _userState.WriteStateAsync();
var emailUserGrain = _factory.GetGrain<IEmailUserGrain>(_userState.State.Email);
await emailUserGrain.SetUsername(this.GetPrimaryKeyString());

return Error.None;
}
catch (Exception ex)
{
return new Error("b1890485-4204-4e1d-84d5-1eab7866dfbc", ex.Message);
}
}

public async Task<Error> Login(string email, string password)
{
Expand Down Expand Up @@ -80,35 +112,59 @@ public async Task<Error> Login(string email, string password)
}
}

public async Task<Error> Register(string email, string password)
public async Task<Error> Update(UpdateUser user)
{
var (hasRegistered, error) = await HasRegistered();
if (error.Exist())
{
return error;
}
if (hasRegistered)

if (!hasRegistered)
{
return UserAlreadyRegistered;
return UnregisteredUserUpdate;
}
try

if (!string.IsNullOrEmpty(user.Bio))
{
_userState.State.Bio = user.Bio;
}
if (!string.IsNullOrEmpty(user.Image))
{
_userState.State.Image = user.Image;
}
if (!string.IsNullOrWhiteSpace(user.Password))
{
_userState.State.Email = email;
var passwordHasher = _factory.GetGrain<IPasswordHasher>(0);
_userState.State.Salt = Guid.NewGuid();
_userState.State.Password =
await passwordHasher.Hash(password, _userState.State.Salt.ToByteArray());
await _userState.WriteStateAsync();
var emailUserGrain = _factory.GetGrain<IEmailUserGrain>(_userState.State.Email);
await emailUserGrain.SetUsername(this.GetPrimaryKeyString());
return Error.None;
await passwordHasher.Hash(user.Password, _userState.State.Salt.ToByteArray());
}
catch (Exception ex)

Task<Error> resetTask = null;
Task<Error> updateTask = null;
if (!string.IsNullOrWhiteSpace(user.Email))
{
return new Error("b1890485-4204-4e1d-84d5-1eab7866dfbc", ex.Message);
var oldEmailUserGrain = _factory.GetGrain<IEmailUserGrain>(_userState.State.Email);
resetTask = oldEmailUserGrain.SetUsername(string.Empty);
var newEmailUserGrain = _factory.GetGrain<IEmailUserGrain>(user.Email);
updateTask = newEmailUserGrain.SetUsername(this.GetPrimaryKeyString());
_userState.State.Email = user.Email;

}

var saveChangesTask = _userState.WriteStateAsync();
if (resetTask != null && updateTask != null)
{
await Task.WhenAll(resetTask, updateTask, saveChangesTask);
}
else
{
await saveChangesTask;
}
return Error.None;
}


public async Task<(string, Error)> GetEmail()
{
return await Task.FromResult((_userState.State.Email, Error.None));
Expand Down

0 comments on commit 5e3c57c

Please sign in to comment.