Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions EstateManagementUI.BlazorServer/Components/Pages/Home.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,19 +120,27 @@ private async Task<Result> LoadDashboardData(CorrelationId correlationId, Guid e
await Task.WhenAll(kpiTask, salesTask, failedSalesTask, merchantsTask);

// Process results
if (kpiTask.Result.IsSuccess)
merchantKpi = ModelFactory.ConvertFrom(kpiTask.Result.Data);
if (kpiTask.Result.IsFailed)
return ResultHelpers.CreateFailure(kpiTask.Result);

if (salesTask.Result.IsSuccess)
todaysSales = ModelFactory.ConvertFrom(salesTask.Result.Data);
merchantKpi = ModelFactory.ConvertFrom(kpiTask.Result.Data);

if (failedSalesTask.Result.IsSuccess)
todaysFailedSales = ModelFactory.ConvertFrom(failedSalesTask.Result.Data);
if (salesTask.Result.IsFailed)
return ResultHelpers.CreateFailure(salesTask.Result);

if (merchantsTask.Result.IsSuccess)
// Note: API returns merchants in creation order (newest first)
// If ordering is incorrect, would need CreatedDate field in the model
recentMerchants = ModelFactory.ConvertFrom(merchantsTask.Result.Data?.ToList());
todaysSales = ModelFactory.ConvertFrom(salesTask.Result.Data);

if (failedSalesTask.Result.IsFailed)
return ResultHelpers.CreateFailure(failedSalesTask.Result);

todaysFailedSales = ModelFactory.ConvertFrom(failedSalesTask.Result.Data);

if (merchantsTask.Result.IsFailed)
return ResultHelpers.CreateFailure(merchantsTask.Result);

// Note: API returns merchants in creation order (newest first)
// If ordering is incorrect, would need CreatedDate field in the model
recentMerchants = ModelFactory.ConvertFrom(merchantsTask.Result.Data);

return Result.Success();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
[JsonProperty("date")]
public DateTime Date { get; set; }
[JsonProperty("description")]
public String Description { get; set; }

Check warning on line 12 in EstateManagmentUI.BusinessLogic/BackendAPI/DataTransferObjects/DataTransferObjects.cs

View workflow job for this annotation

GitHub Actions / Build and Unit Test

Non-nullable property 'Description' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
}

public class MerchantKpi
Expand All @@ -21,4 +21,20 @@
[JsonProperty("merchants_with_no_sale_in_last7_days")]
public Int32 MerchantsWithNoSaleInLast7Days { get; set; }
}

public class TodaysSales
{
[JsonProperty("todays_average_sales_value")]
public Decimal TodaysAverageSalesValue { get; set; }
[JsonProperty("todays_sales_value")]
public Decimal TodaysSalesValue { get; set; }
[JsonProperty("todays_sales_count")]
public Int32 TodaysSalesCount { get; set; }
[JsonProperty("comparison_sales_value")]
public Decimal ComparisonSalesValue { get; set; }
[JsonProperty("comparison_sales_count")]
public Int32 ComparisonSalesCount { get; set; }
[JsonProperty("comparison_average_sales_value")]
public Decimal ComparisonAverageSalesValue { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
{
Task<Result<List<ComparisonDate>>> GetComparisonDates(String accessToken, Guid estateId, CancellationToken cancellationToken);
Task<Result<MerchantKpi>> GetMerchantKpi(String accessToken, Guid estateId, CancellationToken cancellationToken);

Task<Result<TodaysSales>> GetTodaysSales(String accessToken,
Guid estateId,
Int32 merchantReportingId,
Int32 operatorReportingId,
DateTime comparisonDate,
CancellationToken cancellationToken);
}

public class EstateReportingApiClient : ClientProxyBase.ClientProxyBase, IEstateReportingApiClient {
Expand Down Expand Up @@ -74,6 +81,36 @@
}
}

public async Task<Result<TodaysSales>> GetTodaysSales(String accessToken, Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate, CancellationToken cancellationToken)
{
QueryStringBuilder builder = new QueryStringBuilder();
builder.AddParameter("comparisonDate", $"{comparisonDate.Date:yyyy-MM-dd}");
builder.AddParameter("merchantReportingId", merchantReportingId);
builder.AddParameter("operatorReportingId", operatorReportingId);

String requestUri = this.BuildRequestUrl($"/api/transactions/todayssales?{builder.BuildQueryString()}");

try
{
List<(String headerName, String headerValue)> additionalHeaders = [
(EstateIdHeaderName, estateId.ToString())
];
Result<TodaysSales>? result = await this.SendHttpGetRequest<TodaysSales>(requestUri, accessToken, additionalHeaders, cancellationToken);

if (result.IsFailed)
return ResultHelpers.CreateFailure(result);

return result;
}
catch (Exception ex)
{
// An exception has occurred, add some additional information to the message
Exception exception = new Exception($"Error getting todays sales for estate {estateId}.", ex);

return Result.Failure(exception.Message);
}
}

private String BuildRequestUrl(String route)
{
String baseAddress = this.BaseAddressResolver("EstateReportingApi");
Expand All @@ -84,3 +121,79 @@
}
}
}


public class QueryStringBuilder
{
private Dictionary<string, (object value, Boolean alwaysInclude)> parameters = new Dictionary<String, (Object value, Boolean alwaysInclude)>();

public QueryStringBuilder AddParameter(string key, object value, Boolean alwaysInclude = false)

Check warning on line 130 in EstateManagmentUI.BusinessLogic/BackendAPI/IEstateReportingApiClient.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

EstateManagmentUI.BusinessLogic/BackendAPI/IEstateReportingApiClient.cs#L130

Use the overloading mechanism instead of the optional parameters.
{
this.parameters.Add(key, (value, alwaysInclude));
return this;
}

static Dictionary<string, object> FilterDictionary(Dictionary<string, (object value, Boolean alwaysInclude)> inputDictionary)
{
Dictionary<string, object> result = new Dictionary<string, object>();

foreach (KeyValuePair<String, (object value, Boolean alwaysInclude)> entry in inputDictionary)
{
if (entry.Value.value != null && !IsDefaultValue(entry.Value.value, entry.Value.alwaysInclude))
{
result.Add(entry.Key, entry.Value.value);
}
}

return result;
}

static bool IsDefaultValue<T>(T value, Boolean alwaysInclude)
{
if (alwaysInclude)
return false;

Object? defaultValue = GetDefault(value.GetType());

Check warning on line 156 in EstateManagmentUI.BusinessLogic/BackendAPI/IEstateReportingApiClient.cs

View workflow job for this annotation

GitHub Actions / Build and Unit Test

Dereference of a possibly null reference.

if (defaultValue == null && value.GetType() == typeof(String))
{
defaultValue = String.Empty;
}
return defaultValue.Equals(value);

Check warning on line 162 in EstateManagmentUI.BusinessLogic/BackendAPI/IEstateReportingApiClient.cs

View workflow job for this annotation

GitHub Actions / Build and Unit Test

Dereference of a possibly null reference.
}

public static object GetDefault(Type t)
{
Func<object> f = GetDefault<object>;
return f.Method.GetGenericMethodDefinition().MakeGenericMethod(t).Invoke(null, null);

Check warning on line 168 in EstateManagmentUI.BusinessLogic/BackendAPI/IEstateReportingApiClient.cs

View workflow job for this annotation

GitHub Actions / Build and Unit Test

Possible null reference return.
}

private static T GetDefault<T>()
{
return default(T);

Check warning on line 173 in EstateManagmentUI.BusinessLogic/BackendAPI/IEstateReportingApiClient.cs

View workflow job for this annotation

GitHub Actions / Build and Unit Test

Possible null reference return.
}

public string BuildQueryString()
{
Dictionary<String, Object> filtered = FilterDictionary(this.parameters);

if (filtered.Count == 0)
{
return string.Empty;
}

StringBuilder queryString = new StringBuilder();

foreach (KeyValuePair<String, Object> kvp in filtered)
{
if (queryString.Length > 0)
{
queryString.Append("&");
}

queryString.Append($"{Uri.EscapeDataString(kvp.Key)}={Uri.EscapeDataString(kvp.Value.ToString())}");

Check warning on line 194 in EstateManagmentUI.BusinessLogic/BackendAPI/IEstateReportingApiClient.cs

View workflow job for this annotation

GitHub Actions / Build and Unit Test

Possible null reference argument for parameter 'stringToEscape' in 'string Uri.EscapeDataString(string stringToEscape)'.
}

return queryString.ToString();
}
}
12 changes: 12 additions & 0 deletions EstateManagmentUI.BusinessLogic/Client/APIModelFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,16 @@ public static MerchantKpiModel ConvertFrom(MerchantKpi apiResult) {

return model;
}

public static TodaysSalesModel ConvertFrom(TodaysSales apiResultData) {
TodaysSalesModel model = new TodaysSalesModel {
ComparisonAverageValue = apiResultData.ComparisonAverageSalesValue,
ComparisonSalesCount = apiResultData.ComparisonSalesCount,
ComparisonSalesValue = apiResultData.ComparisonSalesValue,
TodaysAverageValue = apiResultData.TodaysAverageSalesValue,
TodaysSalesCount = apiResultData.TodaysSalesCount,
TodaysSalesValue = apiResultData.TodaysSalesValue
};
return model;
}
}
40 changes: 28 additions & 12 deletions EstateManagmentUI.BusinessLogic/Client/DateMethods.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
using EstateManagementUI.BusinessLogic.Models;
using EstateManagementUI.BusinessLogic.BackendAPI;
using EstateManagementUI.BusinessLogic.BackendAPI.DataTransferObjects;
using EstateManagementUI.BusinessLogic.Models;
using EstateManagementUI.BusinessLogic.Requests;
using SecurityService.Client;
using SecurityService.DataTransferObjects.Responses;
using Shared.Results;
using SimpleResults;
using System;
using System.Collections.Generic;
using System.Text;
using EstateManagementUI.BusinessLogic.BackendAPI;
using EstateManagementUI.BusinessLogic.BackendAPI.DataTransferObjects;
using Shared.Results;
using System.Threading;

namespace EstateManagementUI.BusinessLogic.Client
{
public partial interface IApiClient {
Task<Result<List<ComparisonDateModel>>> GetComparisonDates(String accessToken,
Guid actionId,
Guid estateId,
Task<Result<List<ComparisonDateModel>>> GetComparisonDates(Queries.GetComparisonDatesQuery request,
CancellationToken cancellationToken);
}

public partial class ApiClient : IApiClient {
private readonly IEstateReportingApiClient EstateReportingApiClient;
private readonly ISecurityServiceClient SecurityServiceClient;

public ApiClient(IEstateReportingApiClient estateReportingApiClient) {
public ApiClient(IEstateReportingApiClient estateReportingApiClient, ISecurityServiceClient securityServiceClient) {
this.EstateReportingApiClient = estateReportingApiClient;
this.SecurityServiceClient = securityServiceClient;
}
public async Task<Result<List<ComparisonDateModel>>> GetComparisonDates(String accessToken,
Guid actionId,
Guid estateId,
public async Task<Result<List<ComparisonDateModel>>> GetComparisonDates(Queries.GetComparisonDatesQuery request,
CancellationToken cancellationToken) {
Result<List<ComparisonDate>> apiResult = await this.EstateReportingApiClient.GetComparisonDates(accessToken, estateId, cancellationToken);

// Get a token here
var token = await this.GetToken(cancellationToken);
if (token.IsFailed)
return ResultHelpers.CreateFailure(token);
Result<List<ComparisonDate>> apiResult = await this.EstateReportingApiClient.GetComparisonDates(token.Data, request.EstateId, cancellationToken);

if (apiResult.IsFailed)
return ResultHelpers.CreateFailure(apiResult);
Expand All @@ -35,5 +42,14 @@

return Result.Success(comparisonDates);
}

private async Task<Result<String>> GetToken(CancellationToken cancellationToken) {
// Get a token here
// TODO: Add caching

Check warning on line 48 in EstateManagmentUI.BusinessLogic/Client/DateMethods.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

EstateManagmentUI.BusinessLogic/Client/DateMethods.cs#L48

Complete the task associated to this 'TODO' comment.
Result<TokenResponse>? token = await this.SecurityServiceClient.GetToken("serviceClient", "d192cbc46d834d0da90e8a9d50ded543", cancellationToken);
if (token.IsFailed)
return ResultHelpers.CreateFailure(token);
return Result.Success<String>(token.Data.AccessToken);
}
}
}
16 changes: 11 additions & 5 deletions EstateManagmentUI.BusinessLogic/Client/MerchantMethods.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
using EstateManagementUI.BusinessLogic.Models;
using EstateManagementUI.BusinessLogic.Requests;
using SecurityService.DataTransferObjects.Responses;
using Shared.Results;
using SimpleResults;

namespace EstateManagementUI.BusinessLogic.Client
{
public partial interface IApiClient
{
Task<Result<MerchantKpiModel>> GetMerchantKpi(String accessToken, Guid actionId, Guid estateId, CancellationToken cancellationToken);
Task<Result<MerchantKpiModel>> GetMerchantKpi(Queries.GetMerchantKpiQuery request, CancellationToken cancellationToken);
}

public partial class ApiClient : IApiClient {
public async Task<Result<MerchantKpiModel>> GetMerchantKpi(String accessToken,
Guid actionId,
Guid estateId,
public async Task<Result<MerchantKpiModel>> GetMerchantKpi(Queries.GetMerchantKpiQuery request,
CancellationToken cancellationToken) {
var apiResult = await this.EstateReportingApiClient.GetMerchantKpi(accessToken, estateId, cancellationToken);

// Get a token here
var token = await this.GetToken(cancellationToken);
if (token.IsFailed)
return ResultHelpers.CreateFailure(token);

var apiResult = await this.EstateReportingApiClient.GetMerchantKpi(token.Data, request.EstateId, cancellationToken);

if (apiResult.IsFailed)
return ResultHelpers.CreateFailure(apiResult);
Expand Down
34 changes: 34 additions & 0 deletions EstateManagmentUI.BusinessLogic/Client/TransactionMethods.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using EstateManagementUI.BusinessLogic.Models;
using Shared.Results;
using SimpleResults;
using System;
using System.Collections.Generic;
using System.Text;
using EstateManagementUI.BusinessLogic.Requests;

namespace EstateManagementUI.BusinessLogic.Client
{
public partial interface IApiClient
{
Task<Result<TodaysSalesModel>> GetTodaysSales(Queries.GetTodaysSalesQuery request, CancellationToken cancellationToken);
}

public partial class ApiClient : IApiClient {
public async Task<Result<TodaysSalesModel>> GetTodaysSales(Queries.GetTodaysSalesQuery request,
CancellationToken cancellationToken) {
// Get a token here
var token = await this.GetToken(cancellationToken);
if (token.IsFailed)
return ResultHelpers.CreateFailure(token);

var apiResult = await this.EstateReportingApiClient.GetTodaysSales(token.Data, request.EstateId, 0, 0, request.ComparisonDate, cancellationToken);

if (apiResult.IsFailed)
return ResultHelpers.CreateFailure(apiResult);

TodaysSalesModel todaysSalesModel = APIModelFactory.ConvertFrom(apiResult.Data);

return Result.Success(todaysSalesModel);
}
}
}
Loading
Loading