Skip to content

Commit

Permalink
Merge pull request #33 from davishoang96/view-and-delete-message
Browse files Browse the repository at this point in the history
View and delete message
  • Loading branch information
davishoang96 authored May 26, 2024
2 parents bea3631 + e7cee78 commit bdde2f2
Show file tree
Hide file tree
Showing 13 changed files with 193 additions and 66 deletions.
33 changes: 11 additions & 22 deletions privatext.Client/HttpClient/ApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ public partial interface IApiClient

/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
System.Threading.Tasks.Task<GetMessageResponse> GetMessageEndpointAsync(GetMessageRequest getMessageRequest);
System.Threading.Tasks.Task<GetMessageResponse> GetMessageEndpointAsync(string messageId);

/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
System.Threading.Tasks.Task<GetMessageResponse> GetMessageEndpointAsync(GetMessageRequest getMessageRequest, System.Threading.CancellationToken cancellationToken);
System.Threading.Tasks.Task<GetMessageResponse> GetMessageEndpointAsync(string messageId, System.Threading.CancellationToken cancellationToken);

/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
Expand Down Expand Up @@ -182,26 +182,26 @@ public virtual async System.Threading.Tasks.Task<bool> CreateMessageEndpointAsyn

/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task<GetMessageResponse> GetMessageEndpointAsync(GetMessageRequest getMessageRequest)
public virtual System.Threading.Tasks.Task<GetMessageResponse> GetMessageEndpointAsync(string messageId)
{
return GetMessageEndpointAsync(getMessageRequest, System.Threading.CancellationToken.None);
return GetMessageEndpointAsync(messageId, System.Threading.CancellationToken.None);
}

/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task<GetMessageResponse> GetMessageEndpointAsync(GetMessageRequest getMessageRequest, System.Threading.CancellationToken cancellationToken)
public virtual async System.Threading.Tasks.Task<GetMessageResponse> GetMessageEndpointAsync(string messageId, System.Threading.CancellationToken cancellationToken)
{
if (getMessageRequest == null)
throw new System.ArgumentNullException("getMessageRequest");
if (messageId == null)
throw new System.ArgumentNullException("messageId");

var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(getMessageRequest, _settings.Value);
var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(messageId, _settings.Value);
var content_ = new System.Net.Http.StringContent(json_);
content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");
request_.Content = content_;
Expand Down Expand Up @@ -486,22 +486,11 @@ public partial class MessageDTO
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")]
public partial class GetMessageResponse
{
[Newtonsoft.Json.JsonProperty("MessageDTO", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public MessageDTO2 MessageDTO { get; set; }

}

[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")]
public partial class MessageDTO2
{
[Newtonsoft.Json.JsonProperty("MessageId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string MessageId { get; set; }

[Newtonsoft.Json.JsonProperty("Content", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
[Newtonsoft.Json.JsonProperty("Content", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string Content { get; set; }

[Newtonsoft.Json.JsonProperty("DateCreated", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public System.DateTimeOffset DateCreated { get; set; }
[Newtonsoft.Json.JsonProperty("DateCreated", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public System.DateTimeOffset? DateCreated { get; set; }

}

Expand Down
1 change: 1 addition & 0 deletions privatext.Client/Pages/Home.razor
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
{
base.OnInitialized();
ShowTextInput = !ShowLink;
Content = "qj;4o3mq;o34mvq34nv";
}

private async Task CreateMessage()
Expand Down
79 changes: 79 additions & 0 deletions privatext.Client/Pages/ViewText.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
@page "/text/{id}"
@inject IApiClient ApiClient
@using Newtonsoft.Json
@using privatext.Client.HttpClient
@using privatext.Common.Response


<RadzenRow JustifyContent="JustifyContent.Center" AlignItems="AlignItems.Center">
<RadzenColumn Size="3" />
<RadzenColumn Size="6">
<RadzenStack Orientation="Orientation.Vertical" Visible="ShowViewTextBtn">
<h3>ViewText</h3>
<RadzenText>The text will be automatically remove from the server once you view it.</RadzenText>
<RadzenButton Click="@ExecuteViewText">View Text</RadzenButton>
</RadzenStack>
<RadzenStack Orientation="Orientation.Vertical" Visible="IsTextViewed">
<h3>Your text message</h3>
<RadzenTextArea @bind-Value="@Content" Rows="15" Cols="100"></RadzenTextArea>
</RadzenStack>
<RadzenStack Orientation="Orientation.Vertical" Visible="ShowDeletedNotification">
<h3>@ErrorMessage</h3>
</RadzenStack>
</RadzenColumn>
<RadzenColumn Size="3" />
</RadzenRow>

@code {

[Parameter]
public string Id { get; set; }
private bool IsTextViewed { get; set; }
private bool ShowViewTextBtn { get; set; }
private bool ShowDeletedNotification { get; set; }
private string ErrorMessage { get; set; }

private string Content { get; set; }
private string DateCreated { get; set; }

protected override void OnInitialized()
{
base.OnInitialized();
ShowViewTextBtn = true;
ShowDeletedNotification = false;
}

private async Task ExecuteViewText()
{
ShowViewTextBtn = false;
try
{
var m = await ApiClient.GetMessageEndpointAsync(Id);
if (m is not null)
{
IsTextViewed = true;

Content = m.Content;
DateCreated = m.DateCreated.Value.ToString("dd/MM/yyyy");
}
else
{
IsTextViewed = false;
ShowDeletedNotification = true;
}
}
catch (ApiException apiEx)
{
var err = JsonConvert.DeserializeObject<ErrorResponse>(apiEx.Response);
ErrorMessage = err.Message;
}
catch (Exception ex)
{
ErrorMessage = "Something wrong. Please contact administrator";
}
finally
{
ShowDeletedNotification = true;
}
}
}
6 changes: 6 additions & 0 deletions privatext.Common/Constant.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace privatext.Common;

public class Constant
{
public const string MessageDeleted = "The message has been deleted";
}
2 changes: 1 addition & 1 deletion privatext.Common/DTO/MessageDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

public class MessageDTO
{
public string MessageId { get; set; }
public required string MessageId { get; set; }
public required string Content { get; set; }
public DateTime DateCreated { get; set; }
}
12 changes: 7 additions & 5 deletions privatext.Common/Request/GetMessageRequest.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
namespace privatext.Common.Request
using FastEndpoints;

namespace privatext.Common.Request;

public class GetMessageRequest
{
public class GetMessageRequest
{
public string MessageId { get; set; }
}
[FromBody]
public string MessageId { get; set; }

Check warning on line 8 in privatext.Common/Request/GetMessageRequest.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'MessageId' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
}
2 changes: 1 addition & 1 deletion privatext.Common/Response/BaseEndpointResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public class BaseEndpointResponse

public IList<string> Errors => errors;

public bool Success => Errors != null;
public bool Success => !Errors.Any();

public void AddError(string errorMessage)
{
Expand Down
12 changes: 12 additions & 0 deletions privatext.Common/Response/ErrorResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Newtonsoft.Json;

namespace privatext.Common.Response;

public class ErrorResponse
{
[JsonProperty("StatusCode")]
public int StatusCode { get; set; }

[JsonProperty("Message")]
public string Message { get; set; }

Check warning on line 11 in privatext.Common/Response/ErrorResponse.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Message' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
}
10 changes: 4 additions & 6 deletions privatext.Common/Response/GetMessageResponse.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using privatext.Common.DTO;
namespace privatext.Common.Response;

namespace privatext.Common.Response
public class GetMessageResponse
{
public class GetMessageResponse : BaseEndpointResponse
{
public MessageDTO MessageDTO { get; set; }
}
public string Content { get; set; }

Check warning on line 5 in privatext.Common/Response/GetMessageResponse.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Content' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
public DateTime DateCreated { get; set; }
}
1 change: 1 addition & 0 deletions privatext.Common/privatext.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

<ItemGroup>
<PackageReference Include="FastEndpoints.Attributes" Version="5.25.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>

</Project>
6 changes: 3 additions & 3 deletions privatext/Endpoints/CreateMessageEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ public override async Task HandleAsync(CreateMessageRequest r, CancellationToken
{
var messageContent = await cryptoService.Encrypt(r.MessageDTO.Content, r.MessageDTO.MessageId);
var messageId = r.MessageDTO.MessageId;
//var midpoint = messageId.Length / 2;
//var secondHalf = messageId.Substring(midpoint);
var midpoint = messageId.Length / 2;
var secondHalf = messageId.Substring(midpoint);
var encMessageDTO = new Common.DTO.MessageDTO
{
MessageId = messageId,
MessageId = secondHalf,
Content = messageContent,
};

Expand Down
68 changes: 40 additions & 28 deletions privatext/Endpoints/GetMessageEndpoint.cs
Original file line number Diff line number Diff line change
@@ -1,45 +1,57 @@
using FastEndpoints;
using FluentValidation.Results;
using privatext.Common.Request;
using privatext.Common.Response;
using privatext.Services;

namespace privatext.Endpoints
namespace privatext.Endpoints;

public class GetMessageEndpoint : Endpoint<GetMessageRequest, GetMessageResponse>
{
public class GetMessageEndpoint : Endpoint<GetMessageRequest, GetMessageResponse>
private readonly ICryptoService cryptoService;
private readonly IMessageService messageService;
public GetMessageEndpoint(IMessageService messageService, ICryptoService cryptoService)
{
private readonly ICryptoService cryptoService;
private readonly IMessageService messageService;
public GetMessageEndpoint(IMessageService messageService, ICryptoService cryptoService)
{
this.messageService = messageService;
this.cryptoService = cryptoService;
}
this.messageService = messageService;
this.cryptoService = cryptoService;
}

public override void Configure()
{
Post("/getMessage/");
AllowAnonymous();
}
public override void Configure()
{
Post("/getMessage/");
AllowAnonymous();
DontCatchExceptions();
}

public override async Task HandleAsync(GetMessageRequest r, CancellationToken c)
public override async Task HandleAsync(GetMessageRequest r, CancellationToken c)
{
var res = new GetMessageResponse();
var midpoint = r.MessageId.Length / 2;
var secondHalf = r.MessageId.Substring(midpoint);
var model = messageService.GetMessage(secondHalf);
if (model == null)
{
var res = new GetMessageResponse();
var model = messageService.GetMessage(r.MessageId);
if (model == null)
ThrowError(new ValidationFailure
{
res.AddError("Message has been deleted");
await SendAsync(res);
}
ErrorMessage = $"Message id = {r.MessageId} has been deleted",
Severity = FluentValidation.Severity.Error,
PropertyName = nameof(GetMessageEndpoint),
});
}

var decryptedMessage = await cryptoService.Decrypt(model.Content, r.MessageId);
await SendAsync(new GetMessageResponse
if (!await messageService.DeleteMessage(secondHalf))
{
ThrowError(new ValidationFailure
{
MessageDTO = new Common.DTO.MessageDTO
{
Content = decryptedMessage,
DateCreated = model.DateCreated,
}
ErrorMessage = "Error when deleting a message",
Severity = FluentValidation.Severity.Error,
PropertyName = nameof(GetMessageEndpoint),
});
}

var decryptedMessage = await cryptoService.Decrypt(model.Content, r.MessageId);
res.Content = decryptedMessage;
res.DateCreated = model.DateCreated;
await SendAsync(res);
}
}
27 changes: 27 additions & 0 deletions privatext/Program.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
using FastEndpoints;
using FastEndpoints.ClientGen;
using FastEndpoints.Swagger;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.EntityFrameworkCore;
using NJsonSchema.CodeGeneration.CSharp;
using privatext.Client.HttpClient;
using privatext.Components;
using privatext.Database;
using privatext.Services;
using Radzen;
using System.Net;

var builder = WebApplication.CreateBuilder(args);

Expand Down Expand Up @@ -35,6 +37,31 @@
new HttpClient { BaseAddress = new Uri(builder.Configuration["BaseUrl"]) }));

var app = builder.Build();
app.UseExceptionHandler(errApp =>
{
errApp.Run(async ctx =>
{
var exHandlerFeature = ctx.Features.Get<IExceptionHandlerFeature>();
if (exHandlerFeature != null)
{
var reason = exHandlerFeature.Error.Message;
string errorMessage = reason;
if(errorMessage.Contains("ThrowError() called! - "))
{
errorMessage = errorMessage.Replace("ThrowError() called! - ", string.Empty);
}

ctx.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
ctx.Response.ContentType = "application/problem+json";
await ctx.Response.WriteAsJsonAsync(
new ErrorResponse
{
StatusCode = ctx.Response.StatusCode,
Message = errorMessage,
});
}
});
});

// Apply migration
using (var scope = ((IApplicationBuilder)app).ApplicationServices.GetService<IServiceScopeFactory>()!.CreateScope())
Expand Down

0 comments on commit bdde2f2

Please sign in to comment.