Skip to content

Commit

Permalink
feat(registration): instrument tracing (#137)
Browse files Browse the repository at this point in the history
  • Loading branch information
aldy505 authored Sep 3, 2023
1 parent 3c1bfea commit 898c936
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 71 deletions.
9 changes: 4 additions & 5 deletions registration/.editorconfig
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@

[*]
[*]
charset = utf-8
end_of_line = crlf
trim_trailing_whitespace = false
Expand All @@ -11,7 +10,7 @@ indent_size = 4
csharp_new_line_before_else = false
csharp_new_line_before_finally = false
csharp_new_line_before_members_in_object_initializers = false
csharp_new_line_before_open_brace = control_blocks,local_functions,methods,types
csharp_new_line_before_open_brace = control_blocks, local_functions, methods, types
csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
csharp_style_var_elsewhere = true:suggestion
Expand All @@ -25,7 +24,7 @@ dotnet_naming_rule.unity_serialized_field_rule.style = lower_camel_case_style
dotnet_naming_rule.unity_serialized_field_rule.symbols = unity_serialized_field_symbols
dotnet_naming_style.lower_camel_case_style.capitalization = camel_case
dotnet_naming_symbols.unity_serialized_field_symbols.applicable_accessibilities = *
dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds =
dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds =
dotnet_naming_symbols.unity_serialized_field_symbols.resharper_applicable_kinds = unity_serialised_field
dotnet_naming_symbols.unity_serialized_field_symbols.resharper_required_modifiers = instance
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
Expand Down Expand Up @@ -57,7 +56,7 @@ resharper_csharp_remove_blank_lines_near_braces_in_declarations = false
resharper_csharp_wrap_after_invocation_lpar = true
resharper_csharp_wrap_arguments_style = chop_if_long
resharper_force_attribute_style = join
resharper_instance_members_qualify_declared_in =
resharper_instance_members_qualify_declared_in =
resharper_keep_existing_declaration_block_arrangement = true
resharper_place_accessorholder_attribute_on_same_line = false
resharper_place_simple_embedded_statement_on_same_line = true
Expand Down
62 changes: 41 additions & 21 deletions registration/Controllers/RegistrationController.cs
Original file line number Diff line number Diff line change
@@ -1,41 +1,46 @@
using Microsoft.AspNetCore.Mvc;
using Registration.Models;
using Registration.Services;
using Sentry;
using User = Registration.Models.User;

namespace Registration.Controllers;

[ApiController]
public class RegistrationController : ControllerBase
{
private readonly WaitingListService _waitingListService;
private readonly ApprovalService _approvalService;
private readonly IHub _sentryHub;
private readonly TrialService _trialService;
private readonly WaitingListService _waitingListService;

public RegistrationController(WaitingListService waitingListService, ApprovalService approvalService, TrialService trialService)
public RegistrationController(WaitingListService waitingListService, ApprovalService approvalService,
TrialService trialService, IHub sentryHub)
{
_waitingListService = waitingListService;
_approvalService = approvalService;
_trialService = trialService;
_sentryHub = sentryHub;
}

/// <summary>
/// Register a user into waiting list
/// Register a user into waiting list
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns>Nothing</returns>
/// <response code="201">User successfully registered</response>
/// <response code="204">User already registered, but no worries</response>
[HttpPost]
[Route("/api/register")]
[HttpPost, Route("/api/register")]
public async Task<IActionResult> RegisterAsync([FromBody] User user, CancellationToken cancellationToken)
{
try
{
var childSpan = _sentryHub.GetSpan()?.StartChild("waiting_list.put_user");
await _waitingListService.PutUserAsync(user, cancellationToken);
childSpan?.Finish();

return Created(new Uri("/register"), null);
return Created(new Uri("/register"), value: null);
}
catch (EmailExistsException)
{
Expand All @@ -44,30 +49,33 @@ public async Task<IActionResult> RegisterAsync([FromBody] User user, Cancellatio
}

/// <summary>
/// List users on the waiting list
/// List users on the waiting list
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("/api/pending")]
[HttpGet, Route("/api/pending")]
public async Task<IActionResult> PendingAsync(CancellationToken cancellationToken)
{
var childSpan = _sentryHub.GetSpan()?.StartChild("waiting_list.get_users");
var waitingList = await _waitingListService.GetUsersAsync(cancellationToken);
childSpan?.Finish(SpanStatus.Ok);

return Ok(waitingList);
}

/// <summary>
/// Approve user from the waiting list
/// Approve user from the waiting list
/// </summary>
/// <param name="userToken"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <response code="400">User does not exists</response>
/// <response code="200">Successfully registered a user</response>
[HttpPut]
[Route("/api/approve")]
[HttpPut, Route("/api/approve")]
public async Task<IActionResult> ApproveAsync([FromBody] UserToken userToken, CancellationToken cancellationToken)
{
var childSpan = _sentryHub.GetSpan()?.StartChild("waiting_list.get_users");
var waitingList = await _waitingListService.GetUsersAsync(cancellationToken);
childSpan?.Finish(SpanStatus.Ok);

// Check if email exists on the waiting list
var user = (from x in waitingList
Expand All @@ -79,29 +87,40 @@ where x.Email.Equals(userToken.Email)
return BadRequest("Email does not exists");
}

childSpan = _sentryHub.GetSpan()?.StartChild("approval.approve_user");
await _approvalService.ApproveUserAsync(userToken, cancellationToken);
childSpan?.Finish();

childSpan = _sentryHub.GetSpan()?.StartChild("waiting_list.remove_user");
await _waitingListService.RemoveUserAsync(user, cancellationToken);
childSpan?.Finish();

return Ok();
}

/// <summary>
/// Revoke registered user
/// Revoke registered user
/// </summary>
/// <param name="userToken"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <response code="400">User does not exists</response>
/// <response code="200">Successfully revoked a user</response>
[HttpPut]
[Route("/api/revoke")]
[HttpPut, Route("/api/revoke")]
public async Task<IActionResult> RevokeAsync([FromBody] UserToken userToken, CancellationToken cancellationToken)
{
try
{
var childSpan = _sentryHub.GetSpan()?.StartChild("approval.get_user_by_token");
var registeredUser = await _approvalService.GetUserByTokenAsync(userToken.Token, cancellationToken);
childSpan?.Finish();

childSpan = _sentryHub.GetSpan()?.StartChild("approval.revoke_user");
await _approvalService.RevokeUserAsync(
new UserToken { Email = registeredUser.UserEmail, Token = userToken.Token, Limit = 0 },
CancellationToken.None);
cancellationToken);

childSpan?.Finish();

return Ok();
}
Expand All @@ -110,18 +129,19 @@ await _approvalService.RevokeUserAsync(
return BadRequest("User does not exists");
}
}

/// <summary>
/// Create a temporary token for a trial user
/// Create a temporary token for a trial user
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpPost]
[Route("/api/trial")]
[HttpPost, Route("/api/trial")]
public async Task<IActionResult> TrialAsync(CancellationToken cancellationToken)
{
var childSpan = _sentryHub.GetSpan()?.StartChild("trial.create_token");
var token = await _trialService.CreateTokenAsync(cancellationToken);
childSpan?.Finish();

return Ok(token);
}
}
}
5 changes: 2 additions & 3 deletions registration/Controllers/StatusController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@ public StatusController(IConnectionMultiplexer redis)
}

/// <summary>
/// Health check
/// Health check
/// </summary>
/// <returns>Empty body</returns>
/// <response code="200"></response>
[HttpGet]
[Route("/healthz")]
[HttpGet, Route("/healthz")]
public async Task<IActionResult> Status(CancellationToken cancellationToken)
{
var db = _redis.GetDatabase();
Expand Down
7 changes: 4 additions & 3 deletions registration/Models/RegisteredUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

public class RegisteredUser
{
public string UserEmail { get; set; }
public int MonthlyLimit { get; set; }
public bool Revoked { get; set; }

public RegisteredUser(string userEmail, int monthlyLimit, bool revoked)
{
UserEmail = userEmail;
MonthlyLimit = monthlyLimit;
Revoked = revoked;
}

public string UserEmail { get; set; }
public int MonthlyLimit { get; set; }
public bool Revoked { get; set; }
}
2 changes: 1 addition & 1 deletion registration/Models/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ public class User
public string Building { get; set; }
[Required, JsonPropertyName("calls")]
public int Calls { get; set; }
};
}
6 changes: 3 additions & 3 deletions registration/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using StackExchange.Redis;
using Registration.Services;
using Sentry;
using StackExchange.Redis;

var builder = WebApplication.CreateBuilder(args);

Expand Down Expand Up @@ -30,10 +30,10 @@
return context.TransactionContext.Name switch {
"GET /healthz" => 0,
"GET healthz" => 0,
_ => 0.4
_ => 0.4
};
};
options.StackTraceMode = StackTraceMode.Enhanced;
});

Expand Down
3 changes: 2 additions & 1 deletion registration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ In the future, we hope to send the email and token generation process automatica
## Development

Assuming you have [.NET 6 SDK](https://dotnet.microsoft.com/en-us/download/visual-studio-sdks) installed manually or
via IDE (either via [Visual Studio 2022 Community](https://visualstudio.microsoft.com/) or [JetBrains Rider](https://www.jetbrains.com/rider/)).
via IDE (either via [Visual Studio 2022 Community](https://visualstudio.microsoft.com/)
or [JetBrains Rider](https://www.jetbrains.com/rider/)).

Open `Registration.sln` from current working directory.
8 changes: 4 additions & 4 deletions registration/Registration.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Sentry" Version="3.31.0" />
<PackageReference Include="Sentry.AspNetCore" Version="3.31.0" />
<PackageReference Include="StackExchange.Redis" Version="2.6.111" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Sentry" Version="3.36.0" />
<PackageReference Include="Sentry.AspNetCore" Version="3.36.0" />
<PackageReference Include="StackExchange.Redis" Version="2.6.122" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0"/>
</ItemGroup>

</Project>
22 changes: 11 additions & 11 deletions registration/Services/ApprovalService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,47 +14,47 @@ public ApprovalService(IConnectionMultiplexer redis)
}

/// <summary>
/// Approve the user from the waiting list into the proper registered user.
/// Approve the user from the waiting list into the proper registered user.
/// </summary>
/// <param name="userToken">User object including the generated token</param>
/// <param name="cancellationToken"></param>
public async Task ApproveUserAsync(UserToken userToken, CancellationToken cancellationToken)
{
var registeredUser = new RegisteredUser(
userEmail: userToken.Email,
monthlyLimit: userToken.Limit,
userToken.Email,
userToken.Limit,
revoked: false);

var serializedUser = JsonSerializer.Serialize(registeredUser);
var db = _redis.GetDatabase();
await db.StringSetAsync(
key: userToken.Token,
value: serializedUser,
userToken.Token,
serializedUser,
expiry: null);
}

/// <summary>
/// Revoke a user from the list of registered users.
/// Revoke a user from the list of registered users.
/// </summary>
/// <param name="userToken">User object including the generated token</param>
/// <param name="cancellationToken"></param>
public async Task RevokeUserAsync(UserToken userToken, CancellationToken cancellationToken)
{
var registeredUser = new RegisteredUser(
userEmail: userToken.Email,
monthlyLimit: userToken.Limit,
userToken.Email,
userToken.Limit,
revoked: true);

var serializedUser = JsonSerializer.Serialize(registeredUser);
var db = _redis.GetDatabase();
await db.StringSetAsync(
key: userToken.Token,
value: serializedUser,
userToken.Token,
serializedUser,
expiry: null);
}

/// <summary>
/// Get a user by their token.
/// Get a user by their token.
/// </summary>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
Expand Down
Loading

0 comments on commit 898c936

Please sign in to comment.