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
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void EstateIndex_RendersCorrectly()
.ReturnsAsync(Result.Success(new List<RecentMerchantsModel>()));
_mockMediator.Setup(x => x.Send(It.IsAny<Queries.GetRecentContractsQuery>(), default))
.ReturnsAsync(Result.Success(new List<RecentContractModel>()));
_mockMediator.Setup(x => x.Send(It.IsAny<Queries.GetAssignedOperatorsQuery>(), default))
_mockMediator.Setup(x => x.Send(It.IsAny<EstateQueries.GetAssignedOperatorsQuery>(), default))
.ReturnsAsync(Result.Success(new List<OperatorModel>()));
_mockMediator.Setup(x => x.Send(It.IsAny<Queries.GetOperatorsQuery>(), default))
.ReturnsAsync(Result.Success(new List<OperatorModel>()));
Expand Down Expand Up @@ -63,7 +63,7 @@ public void EstateIndex_DisplaysEstateDetails()
.ReturnsAsync(Result.Success(new List<RecentMerchantsModel>()));
_mockMediator.Setup(x => x.Send(It.IsAny<Queries.GetRecentContractsQuery>(), default))
.ReturnsAsync(Result.Success(new List<RecentContractModel>()));
_mockMediator.Setup(x => x.Send(It.IsAny<Queries.GetAssignedOperatorsQuery>(), default))
_mockMediator.Setup(x => x.Send(It.IsAny<EstateQueries.GetAssignedOperatorsQuery>(), default))
.ReturnsAsync(Result.Success(new List<OperatorModel>()));
_mockMediator.Setup(x => x.Send(It.IsAny<Queries.GetOperatorsQuery>(), default))
.ReturnsAsync(Result.Success(new List<OperatorModel>()));
Expand All @@ -85,7 +85,7 @@ public void EstateIndex_HasCorrectPageTitle()
.ReturnsAsync(Result.Success(new List<RecentMerchantsModel>()));
_mockMediator.Setup(x => x.Send(It.IsAny<Queries.GetRecentContractsQuery>(), default))
.ReturnsAsync(Result.Success(new List<RecentContractModel>()));
_mockMediator.Setup(x => x.Send(It.IsAny<Queries.GetAssignedOperatorsQuery>(), default))
_mockMediator.Setup(x => x.Send(It.IsAny<EstateQueries.GetAssignedOperatorsQuery>(), default))
.ReturnsAsync(Result.Success(new List<OperatorModel>()));
_mockMediator.Setup(x => x.Send(It.IsAny<Queries.GetOperatorsQuery>(), default))
.ReturnsAsync(Result.Success(new List<OperatorModel>()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private async Task<Result> LoadEstateData(CorrelationId correlationId, Guid esta
Task<Result<BusinessLogic.Models.EstateModel>> estateTask = Mediator.Send(new EstateQueries.GetEstateQuery(correlationId, estateId));
Task<Result<List<BusinessLogic.Models.RecentMerchantsModel>>> merchantTask = Mediator.Send(new MerchantQueries.GetRecentMerchantsQuery(correlationId, estateId));
Task<Result<List<BusinessLogic.Models.RecentContractModel>>> contractsTask = Mediator.Send(new Queries.GetRecentContractsQuery(correlationId, estateId));
Task<Result<List<BusinessLogic.Models.OperatorModel>>> assignedOperatorsTask = Mediator.Send(new Queries.GetAssignedOperatorsQuery(correlationId, estateId));
Task<Result<List<BusinessLogic.Models.OperatorModel>>> assignedOperatorsTask = Mediator.Send(new EstateQueries.GetAssignedOperatorsQuery(correlationId, estateId));
Task<Result<List<BusinessLogic.Models.OperatorModel>>> allOperatorsTask= Mediator.Send(new Queries.GetOperatorsQuery(correlationId, estateId));

await Task.WhenAll(estateTask, merchantTask, contractsTask, assignedOperatorsTask, allOperatorsTask);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@
<div class="space-y-2">
@foreach (var op in assignedOperators) {
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<span class="font-medium">@op.Name</span>
<span class="font-medium">@op.OperatorName</span>
<button type="button" @onclick="() => RemoveOperatorFromMerchant(op.OperatorId)" class="text-red-600 hover:text-red-800">
Remove
</button>
Expand Down Expand Up @@ -287,7 +287,7 @@
@foreach (var contract in assignedContracts) {
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<div>
<p class="font-medium">@contract.Description</p>
<p class="font-medium">@contract.ContractName</p>
<p class="text-sm text-gray-600">Operator: @contract.OperatorName</p>
</div>
<button type="button" @onclick="() => RemoveContractFromMerchant(contract.ContractId)" class="text-red-600 hover:text-red-800">
Expand Down Expand Up @@ -328,7 +328,7 @@
@foreach (var device in assignedDevices) {
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<span class="font-mono">@device</span>
<button type="button" @onclick="() => RemoveDevice(device)" class="text-red-600 hover:text-red-800">
<button type="button" @onclick="() => RemoveDevice(device.DeviceIdentifier)" class="text-red-600 hover:text-red-800">
Remove
</button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using EstateManagementUI.BusinessLogic.Requests;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Shared.Results;
using SimpleResults;
using System.ComponentModel.DataAnnotations;

Expand All @@ -27,7 +28,7 @@

// Operators
private List<OperatorModel>? availableOperators;
private List<OperatorModel> assignedOperators = new();
private List<MerchantOperatorModel> assignedOperators = new();
private bool showAddOperator = false;
private string? selectedOperatorId;
private OperatorModel? selectedOperator;
Expand All @@ -38,12 +39,12 @@

// Contracts
private List<ContractModel>? availableContracts;
private List<ContractModel> assignedContracts = new();
private List<MerchantContractModel> assignedContracts = new();
private bool showAddContract = false;
private string? selectedContractId;

// Devices
private List<string> assignedDevices = new();
private List<MerchantDeviceModel> assignedDevices = new();
private bool showAddDevice = false;
private string? deviceIdentifier;

Expand Down Expand Up @@ -86,43 +87,49 @@
var correlationId = new CorrelationId(Guid.NewGuid());
var estateId = await this.GetEstateId();

var result = await Mediator.Send(new MerchantQueries.GetMerchantQuery(correlationId, estateId, MerchantId));
var getMerchantResult = await Mediator.Send(new MerchantQueries.GetMerchantQuery(correlationId, estateId, MerchantId));

if (result.IsSuccess && result.Data != null)
{
merchant = ModelFactory.ConvertFrom(result.Data);
if (getMerchantResult.IsFailed)
return ResultHelpers.CreateFailure(getMerchantResult);

// Initialize unified model with current values
merchantEditModel = new MerchantEditModel
{
MerchantName = merchant.MerchantName,
SettlementSchedule = merchant.SettlementSchedule ?? "Immediate",
AddressId = this.merchant.AddressId,
AddressLine1 = merchant.AddressLine1,
AddressLine2 = merchant.AddressLine2,
Town = merchant.Town,
Region = merchant.Region,
PostalCode = merchant.PostalCode,
Country = merchant.Country,
ContactId = this.merchant.ContactId,
ContactName = merchant.ContactName,
ContactEmailAddress = merchant.ContactEmailAddress,
ContactPhoneNumber = merchant.ContactPhoneNumber,
};

// Mock some assigned data
assignedOperators = new List<OperatorModel>
{
new OperatorModel { OperatorId = Guid.NewGuid(), Name = "Safaricom" }
};
merchant = ModelFactory.ConvertFrom(getMerchantResult.Data);

assignedContracts = new List<ContractModel>
// Initialize unified model with current values
merchantEditModel = new MerchantEditModel
{
new ContractModel { ContractId = Guid.NewGuid(), Description = "Standard Transaction Contract", OperatorName = "Safaricom" }
MerchantName = merchant.MerchantName,
SettlementSchedule = merchant.SettlementSchedule ?? "Immediate",
AddressId = this.merchant.AddressId,
AddressLine1 = merchant.AddressLine1,
AddressLine2 = merchant.AddressLine2,
Town = merchant.Town,
Region = merchant.Region,
PostalCode = merchant.PostalCode,
Country = merchant.Country,
ContactId = this.merchant.ContactId,
ContactName = merchant.ContactName,
ContactEmailAddress = merchant.ContactEmailAddress,
ContactPhoneNumber = merchant.ContactPhoneNumber,
};

assignedDevices = new List<string> { "DEVICE001", "DEVICE002" };
}
var operatorsResultTask = Mediator.Send(new MerchantQueries.GetMerchantOperatorsQuery(correlationId, estateId, MerchantId));
var contractsResultTask = Mediator.Send(new MerchantQueries.GetMerchantContractsQuery(correlationId, estateId, MerchantId));
var devicesResultTask = Mediator.Send(new MerchantQueries.GetMerchantDevicesQuery(correlationId, estateId, MerchantId));

await Task.WhenAll(operatorsResultTask, contractsResultTask, devicesResultTask);

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

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

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

assignedOperators = ModelFactory.ConvertFrom(operatorsResultTask.Result.Data);
assignedContracts = ModelFactory.ConvertFrom(contractsResultTask.Result.Data);
this.assignedDevices = ModelFactory.ConvertFrom(devicesResultTask.Result.Data);

return Result.Success();
}
Expand Down Expand Up @@ -333,7 +340,10 @@
var op = availableOperators?.FirstOrDefault(o => o.OperatorId == operatorId);
if (op != null && !assignedOperators.Any(a => a.OperatorId == operatorId))
{
assignedOperators.Add(op);
assignedOperators.Add(new MerchantOperatorModel() {
OperatorId = op.OperatorId,
OperatorName = op.Name
});
}
}
else
Expand All @@ -354,12 +364,10 @@
try
{
var correlationId = new CorrelationId(Guid.NewGuid());
var estateId = Guid.Parse("11111111-1111-1111-1111-111111111111");
var accessToken = "stubbed-token";
var estateId = await this.GetEstateId();

var command = new Commands.RemoveOperatorFromMerchantCommand(
var command = new MerchantCommands.RemoveOperatorFromMerchantCommand(
correlationId,
accessToken,
estateId,
MerchantId,
operatorId
Expand Down Expand Up @@ -416,7 +424,12 @@
var contract = availableContracts?.FirstOrDefault(c => c.ContractId == contractId);
if (contract != null && !assignedContracts.Any(a => a.ContractId == contractId))
{
assignedContracts.Add(contract);
assignedContracts.Add(new MerchantContractModel() {
OperatorName = contract.OperatorName,
ContractName = contract.Description,
ContractId = contract.ContractId
// TODO: products

Check warning on line 431 in EstateManagementUI.BlazorServer/Components/Pages/Merchants/Edit.razor.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

EstateManagementUI.BlazorServer/Components/Pages/Merchants/Edit.razor.cs#L431

Complete the task associated to this 'TODO' comment.
});
}
}
else
Expand Down Expand Up @@ -491,7 +504,9 @@
if (result.IsSuccess)
{
successMessage = "Device added successfully";
assignedDevices.Add(deviceIdentifier);
assignedDevices.Add(new MerchantDeviceModel() {
DeviceIdentifier = this.deviceIdentifier
});
deviceIdentifier = null;
showAddDevice = false;
}
Expand All @@ -509,7 +524,8 @@
private void RemoveDevice(string device)
{
ClearMessages();
assignedDevices.Remove(device);
var d = this.assignedDevices.Single(d => d.DeviceIdentifier == device);
assignedDevices.Remove(d);
successMessage = "Device removed successfully";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
Guid estateId,
CancellationToken cancellationToken);

Task<Result<List<MerchantOperator>>> GetMerchantAssignedOperators(String accessToken,
Guid estateId, Guid merchantId,
CancellationToken cancellationToken);
Task<Result<List<Operator>>> GetOperators(String accessToken,
Guid estateId,
CancellationToken cancellationToken);
Expand Down Expand Up @@ -102,6 +105,34 @@
}
}

public async Task<Result<List<MerchantOperator>>> GetMerchantAssignedOperators(String accessToken,
Guid estateId,
Guid merchantId,
CancellationToken cancellationToken) {
String requestUri = this.BuildRequestUrl($"/api/merchants/{merchantId}/operators");

try
{
List<(String headerName, String headerValue)> additionalHeaders = [
(EstateIdHeaderName, estateId.ToString())
];

Result<List<MerchantOperator>> result = await this.SendHttpGetRequest<List<MerchantOperator>>(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 estate assigned operators {estateId}.", ex);

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

public async Task<Result<List<EstateOperator>>> GetEstateAssignedOperators(String accessToken,
Guid estateId,
CancellationToken cancellationToken)
Expand Down Expand Up @@ -243,11 +274,11 @@
String? postCode,
CancellationToken cancellationToken) {
QueryStringBuilder builder = new QueryStringBuilder();
builder.AddParameter("name",name);

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

View workflow job for this annotation

GitHub Actions / Build and Unit Test

Possible null reference argument for parameter 'value' in 'QueryStringBuilder QueryStringBuilder.AddParameter(string key, object value, bool alwaysInclude = false)'.
builder.AddParameter("reference", reference);

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

View workflow job for this annotation

GitHub Actions / Build and Unit Test

Possible null reference argument for parameter 'value' in 'QueryStringBuilder QueryStringBuilder.AddParameter(string key, object value, bool alwaysInclude = false)'.
builder.AddParameter("settlementSchedule", settlementSchedule);

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

View workflow job for this annotation

GitHub Actions / Build and Unit Test

Possible null reference argument for parameter 'value' in 'QueryStringBuilder QueryStringBuilder.AddParameter(string key, object value, bool alwaysInclude = false)'.
builder.AddParameter("region", region);

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

View workflow job for this annotation

GitHub Actions / Build and Unit Test

Possible null reference argument for parameter 'value' in 'QueryStringBuilder QueryStringBuilder.AddParameter(string key, object value, bool alwaysInclude = false)'.
builder.AddParameter("postCode", postCode);

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

View workflow job for this annotation

GitHub Actions / Build and Unit Test

Possible null reference argument for parameter 'value' in 'QueryStringBuilder QueryStringBuilder.AddParameter(string key, object value, bool alwaysInclude = false)'.

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

Expand Down Expand Up @@ -512,24 +543,24 @@
if (alwaysInclude)
return false;

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

Check warning on line 546 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 552 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 558 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 563 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()
Expand All @@ -550,7 +581,7 @@
queryString.Append("&");
}

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

Check warning on line 584 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();
Expand Down
6 changes: 3 additions & 3 deletions EstateManagmentUI.BusinessLogic/Client/EstateMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public partial interface IApiClient {
Task<Result<EstateModel>> GetEstate(EstateQueries.GetEstateQuery request,
CancellationToken cancellationToken);

Task<Result<List<OperatorModel>>> GetEstateAssignedOperators(Queries.GetAssignedOperatorsQuery request,
Task<Result<List<OperatorModel>>> GetEstateAssignedOperators(EstateQueries.GetAssignedOperatorsQuery request,
CancellationToken cancellationToken);

Task<Result> RemoveEstateOperator(EstateCommands.RemoveOperatorFromEstateCommand request,
Expand All @@ -42,8 +42,8 @@ public async Task<Result<EstateModel>> GetEstate(EstateQueries.GetEstateQuery re
return Result.Success(estate);
}

public async Task<Result<List<OperatorModel>>> GetEstateAssignedOperators(Queries.GetAssignedOperatorsQuery request,
CancellationToken cancellationToken) {
public async Task<Result<List<OperatorModel>>> GetEstateAssignedOperators(EstateQueries.GetAssignedOperatorsQuery request,
CancellationToken cancellationToken) {
// Get a token here
Result<String> token = await this.GetToken(cancellationToken);
if (token.IsFailed)
Expand Down
Loading