Skip to content

Commit

Permalink
enable search on concentrator page (#1755)
Browse files Browse the repository at this point in the history
* enable search on concentrator page

* update unit tests ConcentratorSearch component

* update unit tests services + list page

* update Lorawan Concentrator Service

* update concentrator list page
  • Loading branch information
GuillaumeM-2ISA authored Feb 15, 2023
1 parent 9271f83 commit ee3547a
Show file tree
Hide file tree
Showing 12 changed files with 346 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ namespace AzureIoTHub.Portal.Application.Services
using System.Threading.Tasks;
using AzureIoTHub.Portal.Models.v10.LoRaWAN;
using AzureIoTHub.Portal.Shared.Models.v1._0;
using AzureIoTHub.Portal.Shared.Models.v10.Filters;

public interface ILoRaWANConcentratorService
{
Task<PaginatedResult<ConcentratorDto>> GetAllDeviceConcentrator(int pageSize = 10, int pageNumber = 0, string[] orderBy = null);
Task<PaginatedResult<ConcentratorDto>> GetAllDeviceConcentrator(ConcentratorFilter concentratorFilter);
Task<ConcentratorDto> GetConcentrator(string deviceId);
Task<ConcentratorDto> CreateDeviceAsync(ConcentratorDto concentrator);
Task<ConcentratorDto> UpdateDeviceAsync(ConcentratorDto concentrator);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<MudExpansionPanels>
<MudExpansionPanel Text="Search panel">
<MudGrid>
<MudItem xs="12" md="6">
<MudTextField @bind-Value="searchKeyword" Placeholder="DeviceID / DeviceName" id="searchKeyword"></MudTextField>
</MudItem>
<MudGrid>
<MudItem sm="12" md="6">
<MudText>Status</MudText>
<MudRadioGroup @bind-SelectedOption="@searchStatus" Style="display:flex;align-items:baseline" id="searchStatus">
<MudItem md="4" sm="12">
<MudRadio Option=@("true") Color="Color.Primary" id="searchStatusEnabled">Enabled</MudRadio>
</MudItem>
<MudItem md="4" sm="12">
<MudRadio Option=@("false") Color="Color.Primary" id="searchDisabled">Disabled</MudRadio>
</MudItem>
<MudItem md="4" sm="12">
<MudRadio Option=@("") Color="Color.Secondary" id="searchStatusAll">All</MudRadio>
</MudItem>
</MudRadioGroup>

</MudItem>
<MudItem xs="12" md="6">
<MudText>Connection state</MudText>
<MudRadioGroup @bind-SelectedOption="@searchState" Style="display:flex;align-items:baseline">
<MudItem md="4" sm="12">
<MudRadio Option=@("true") Color="Color.Primary" id="searchStateConnected">Connected</MudRadio>
</MudItem>
<MudItem md="4" sm="12">
<MudRadio Option=@("false") Color="Color.Primary" id="searchStateDisconnected">Disconnected</MudRadio>
</MudItem>
<MudItem md="4" sm="12">
<MudRadio Option=@("") Color="Color.Secondary" id="searchStateAll">All</MudRadio>
</MudItem>
</MudRadioGroup>
</MudItem>
</MudGrid>

<MudItem xs="12">
<MudButton Variant="Variant.Outlined" Color="Color.Success" Style="margin:0.5em;" id="searchButton" OnClick=Search>Search</MudButton>
<MudButton Variant="Variant.Outlined" Color="Color.Primary" Style="margin:0.5em;" OnClick="Reset" id="resetSearch">Reset</MudButton>
</MudItem>

</MudGrid>

</MudExpansionPanel>
</MudExpansionPanels>

@code {
[Parameter]
public EventCallback<ConcentratorSearchInfo> OnSearch { get; set; }

private string? searchKeyword = string.Empty;
private string? searchStatus = string.Empty;
private string? searchState = string.Empty;

private async Task Search()
{
var searchInfo = new ConcentratorSearchInfo
{
SearchKeyword = searchKeyword,
SearchStatus = searchStatus,
SearchState = searchState
};
await OnSearch.InvokeAsync(searchInfo);
}

private async Task Reset()
{
searchKeyword = string.Empty;
searchStatus = string.Empty;
searchState = string.Empty;

await Search();
}
}
12 changes: 12 additions & 0 deletions src/AzureIoTHub.Portal.Client/Models/ConcentratorSearchInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) CGI France. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace AzureIoTHub.Portal.Client.Models
{
public class ConcentratorSearchInfo
{
public string? SearchKeyword { get; set; }
public string? SearchStatus { get; set; }
public string? SearchState { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@page "/lorawan/concentrators"
@using AzureIoTHub.Portal
@using AzureIoTHub.Portal.Client.Components.Concentrators
@using AzureIoTHub.Portal.Models.v10.LoRaWAN

@attribute [Authorize]
Expand All @@ -8,7 +9,9 @@
@inject ILoRaWanConcentratorClientService LoRaWanConcentratorsClientService

<MudGrid>

<MudItem xs="12">
<ConcentratorSearch OnSearch=@(async args => await Search(args)) />
</MudItem>
<MudItem xs="12">
<MudTable T="ConcentratorDto" id="concentrators-listing" ServerData=@LoadItems Dense=true Hover=true Bordered=true Striped=true @ref="table" Loading="@IsLoading" OnRowClick="@((e) => GoToDetails(e.Item))" RowStyle="cursor: pointer;">
<ColGroup>
Expand Down Expand Up @@ -96,13 +99,15 @@
@code {
[CascadingParameter]
public Error Error {get; set;}

private MudTable<ConcentratorDto> table;

private readonly Dictionary<int, string> pages = new();

private bool IsLoading { get; set; } = true;

private ConcentratorSearchInfo concentratorSearchInfo = new();

private async Task<TableData<ConcentratorDto>> LoadItems(TableState state)
{
try
Expand All @@ -119,7 +124,7 @@
break;
}

var uri = $"api/lorawan/concentrators?pageNumber={state.Page}&pageSize={state.PageSize}&orderBy={orderBy}";
var uri = $"api/lorawan/concentrators?pageNumber={state.Page}&pageSize={state.PageSize}&orderBy={orderBy}&keyword={this.concentratorSearchInfo.SearchKeyword}&status={this.concentratorSearchInfo.SearchStatus}&state={this.concentratorSearchInfo.SearchState}";

var result = await LoRaWanConcentratorsClientService.GetConcentrators(uri);

Expand Down Expand Up @@ -148,7 +153,7 @@
private async Task DeleteDevice(ConcentratorDto device)
{
var parameters = new DialogParameters {{"deviceId", device.DeviceId}};

var result = await DialogService.Show<DeleteConcentratorPage>("Confirm Deletion", parameters).Result;

if (result.Cancelled)
Expand All @@ -157,12 +162,19 @@
}

// Update the list of devices after the deletion
Search();
await Search();
}

private void Search()
private async Task Search(ConcentratorSearchInfo concentratorSearchInfo = null)
{
pages.Clear();
if (concentratorSearchInfo == null)
{
this.concentratorSearchInfo = new();
}
else
{
this.concentratorSearchInfo = concentratorSearchInfo;
}

table.ReloadServerData();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace AzureIoTHub.Portal.Server.Controllers.V10.LoRaWAN
using System.Threading.Tasks;
using AzureIoTHub.Portal.Application.Services;
using AzureIoTHub.Portal.Models.v10.LoRaWAN;
using AzureIoTHub.Portal.Shared.Models.v10.Filters;
using Filters;
using Hellang.Middleware.ProblemDetails;
using Microsoft.AspNetCore.Authorization;
Expand Down Expand Up @@ -51,15 +52,9 @@ public LoRaWANConcentratorsController(
/// </summary>
[HttpGet(Name = "GET LoRaWAN Concentrator list")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<PaginationResult<ConcentratorDto>>> GetAllDeviceConcentrator(
int pageSize = 10,
int pageNumber = 0,
[FromQuery] string[] orderBy = null)
public async Task<ActionResult<PaginationResult<ConcentratorDto>>> GetAllDeviceConcentrator([FromQuery] ConcentratorFilter concentratorFilter)
{
var paginatedDevices = await this.loRaWANConcentratorService.GetAllDeviceConcentrator(
pageSize,
pageNumber,
orderBy);
var paginatedDevices = await this.loRaWANConcentratorService.GetAllDeviceConcentrator(concentratorFilter);

var nextPage = string.Empty;

Expand All @@ -70,9 +65,9 @@ public async Task<ActionResult<PaginationResult<ConcentratorDto>>> GetAllDeviceC
RouteName = "GET LoRaWAN Concentrator list",
Values = new
{
pageSize,
pageNumber = pageNumber + 1,
orderBy
concentratorFilter.PageSize,
pageNumber = concentratorFilter.PageNumber + 1,
concentratorFilter.OrderBy
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ namespace AzureIoTHub.Portal.Server.Services
using AutoMapper;
using AzureIoTHub.Portal.Application.Mappers;
using AzureIoTHub.Portal.Application.Services;
using AzureIoTHub.Portal.Infrastructure.Repositories;
using AzureIoTHub.Portal.Shared.Models.v10.Filters;
using Domain;
using Domain.Entities;
using Domain.Exceptions;
Expand Down Expand Up @@ -55,11 +57,26 @@ IConcentratorRepository concentratorRepository
}

public async Task<PaginatedResult<ConcentratorDto>> GetAllDeviceConcentrator(
int pageSize = 10,
int pageNumber = 0,
string[] orderBy = null)
ConcentratorFilter concentratorFilter)
{
var paginatedConcentrator = await this.concentratorRepository.GetPaginatedListAsync(pageNumber, pageSize, orderBy);
var concentratorPredicate = PredicateBuilder.True<Concentrator>();

if (!string.IsNullOrWhiteSpace(concentratorFilter.Keyword))
{
concentratorPredicate = concentratorPredicate.And(concentrator => concentrator.Id.ToLower().Contains(concentratorFilter.Keyword) || concentrator.Name.ToLower().Contains(concentratorFilter.Keyword));
}

if (concentratorFilter.Status != null)
{
concentratorPredicate = concentratorPredicate.And(concentrator => concentrator.IsEnabled == concentratorFilter.Status);
}

if (concentratorFilter.State != null)
{
concentratorPredicate = concentratorPredicate.And(concentrator => concentrator.IsConnected == concentratorFilter.State);
}

var paginatedConcentrator = await this.concentratorRepository.GetPaginatedListAsync(concentratorFilter.PageNumber, concentratorFilter.PageSize, concentratorFilter.OrderBy, concentratorPredicate);

return this.mapper.Map<PaginatedResult<ConcentratorDto>>(paginatedConcentrator);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) CGI France. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace AzureIoTHub.Portal.Shared.Models.v10.Filters
{

public class ConcentratorFilter : PaginationFilter
{
public string Keyword { get; set; }

public bool? Status { get; set; }

public bool? State { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright (c) CGI France. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace AzureIoTHub.Portal.Tests.Unit.Client.Components.Concentrators
{
using AutoFixture;
using System.Collections.Generic;
using AzureIoTHub.Portal.Tests.Unit.UnitTests.Bases;
using Bunit;
using NUnit.Framework;
using AzureIoTHub.Portal.Client.Components.Concentrators;
using FluentAssertions;
using System.Linq;
using AzureIoTHub.Portal.Client.Models;

[TestFixture]
public class ConcentratorSearchTests : BlazorUnitTest
{
public override void Setup()
{
base.Setup();
}

[Test]
public void SearchConcentrators_ClickOnSearch_SearchIsFired()
{
// Arrange
var searchKeyword = Fixture.Create<string>();
var receivedEvents = new List<ConcentratorSearchInfo>();
var expectedConcentratorSearchInfo = new ConcentratorSearchInfo
{
SearchKeyword = searchKeyword,
SearchState = string.Empty,
SearchStatus = string.Empty
};

var cut = RenderComponent<ConcentratorSearch>(parameters => parameters.Add(p => p.OnSearch, (searchInfo) =>
{
receivedEvents.Add(searchInfo);
}));

cut.WaitForElement("#searchKeyword").Change(searchKeyword);
cut.WaitForElement("#searchStatusAll").Click();
cut.WaitForElement("#searchStateAll").Click();

// Act
cut.WaitForElement("#searchButton").Click();

// Assert
cut.WaitForAssertion(() => receivedEvents.Count.Should().Be(1));
_ = receivedEvents.First().Should().BeEquivalentTo(expectedConcentratorSearchInfo);
cut.WaitForAssertion(() => MockRepository.VerifyAll());
}

[Test]
public void SearchConcentrators_ClickOnReset_SearchKeyworkIsSetToEmptyAndSearchIsFired()
{
// Arrange
var searchKeyword = Fixture.Create<string>();
var receivedEvents = new List<ConcentratorSearchInfo>();
var expectedConcentratorSearchInfo = new ConcentratorSearchInfo
{
SearchKeyword = string.Empty,
SearchState = string.Empty,
SearchStatus = string.Empty
};

var cut = RenderComponent<ConcentratorSearch>(parameters => parameters.Add(p => p.OnSearch, (searchInfo) =>
{
receivedEvents.Add(searchInfo);
}));

cut.WaitForElement("#searchKeyword").Input(searchKeyword);
cut.WaitForElement("#searchStatusAll").Click();
cut.WaitForElement("#searchStateAll").Click();

// Act
cut.WaitForElement("#resetSearch").Click();

// Assert
cut.WaitForAssertion(() => receivedEvents.Count.Should().Be(1));
_ = receivedEvents.First().Should().BeEquivalentTo(expectedConcentratorSearchInfo);
_ = cut.Find("#searchKeyword").TextContent.Should().Be(string.Empty);
cut.WaitForAssertion(() => MockRepository.VerifyAll());
}
}
}
Loading

0 comments on commit ee3547a

Please sign in to comment.