From 2ec48632699661fb4ebaa5d6196a9e2e06bcc12c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Emma=20=C2=AB=20Metal-Mighty=20=C2=BB=20Barlow?= <5247527+Metal-Mighty@users.noreply.github.com> Date: Mon, 28 Oct 2024 11:30:46 +0100 Subject: [PATCH] #3242 - WIP of in-table filtering and sorting --- .../Commons/DeviceModelDataGridFilter.razor | 22 + .../DeviceModelDataGridFilter.razor.cs | 68 +++ .../Concentrators/ConcentratorSearch.razor | 2 +- .../Devices/DeviceToDuplicateSelector.razor | 4 +- .../Dialogs/Layer/LinkDeviceLayerDialog.razor | 4 +- src/IoTHub.Portal.Client/GlobalUsings.cs | 5 + .../IoTHub.Portal.Client.csproj | 1 + .../Pages/Devices/DeviceListPage.razor | 544 ++++-------------- .../Pages/Devices/DeviceListPage.razor.cs | 226 ++++++++ .../Pages/Layer/LayerListPage.razor | 2 +- .../Services/DeviceModelsClientService.cs | 27 +- .../Validators/ConcentratorValidator.cs | 2 +- .../Validators/DeviceDetailsValidator.cs | 2 +- src/IoTHub.Portal.Client/_Imports.razor | 1 + .../Options/DeviceModelImageOptions.cs | 2 +- .../Mappers/DeviceTwinMapper.cs | 2 +- .../Mappers/LoRaDeviceMapper.cs | 2 +- .../Services/DeviceServiceBase.cs | 2 +- .../Managers/ExportManager.cs | 7 +- .../Properties/launchSettings.json | 4 +- .../Services/DeviceModelService.cs | 30 +- .../Models/v1.0/DeviceListItem.cs | 2 +- .../Components/Devices/EditDeviceTests.cs | 6 +- .../Pages/Devices/DevicesListPageTests.cs | 8 +- .../Validators/ConcentratorValidatorTests.cs | 2 +- .../Validators/DeviceDetailValidatorTests.cs | 4 +- .../Mappers/LoRaDeviceTwinMapperTests.cs | 2 +- .../Server/Managers/ExportManagerTests.cs | 2 +- .../Server/Services/DeviceServiceTests.cs | 2 +- src/IoTHub.Portal.sln | 2 - 30 files changed, 513 insertions(+), 476 deletions(-) create mode 100644 src/IoTHub.Portal.Client/Components/Commons/DeviceModelDataGridFilter.razor create mode 100644 src/IoTHub.Portal.Client/Components/Commons/DeviceModelDataGridFilter.razor.cs create mode 100644 src/IoTHub.Portal.Client/GlobalUsings.cs create mode 100644 src/IoTHub.Portal.Client/Pages/Devices/DeviceListPage.razor.cs diff --git a/src/IoTHub.Portal.Client/Components/Commons/DeviceModelDataGridFilter.razor b/src/IoTHub.Portal.Client/Components/Commons/DeviceModelDataGridFilter.razor new file mode 100644 index 000000000..0675a1149 --- /dev/null +++ b/src/IoTHub.Portal.Client/Components/Commons/DeviceModelDataGridFilter.razor @@ -0,0 +1,22 @@ +@* + + + @foreach (var item in @Items) + { + + + @(item.Image) device model image + @item.Name + + } + + + Filter + + + *@ diff --git a/src/IoTHub.Portal.Client/Components/Commons/DeviceModelDataGridFilter.razor.cs b/src/IoTHub.Portal.Client/Components/Commons/DeviceModelDataGridFilter.razor.cs new file mode 100644 index 000000000..5482cc861 --- /dev/null +++ b/src/IoTHub.Portal.Client/Components/Commons/DeviceModelDataGridFilter.razor.cs @@ -0,0 +1,68 @@ +//// Copyright (c) CGI France. All rights reserved. +//// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +//namespace IoTHub.Portal.Client.Components.Commons +//{ +// using Enums; +// using MudBlazor; + +// public partial class DeviceModelDataGridFilter +// { +// private bool filterOpen = false; +// private bool selectAll = true; +// private HashSet selectedItems = new(); +// private HashSet filterItems = new(); +// private FilterDefinition filterDefinition; + + +// [Parameter] public IEnumerable Items { get; set; } + +// //[Parameter] public FilterContext Context {get;} + +// protected override Task OnInitializedAsync() +// { +// this.selectedItems = Items.ToHashSet(); +// this.filterItems = Items.ToHashSet(); +// this.filterDefinition = new FilterDefinition +// { +// FilterFunction = x => this.filterItems.Contains(x) +// }; + +// return base.OnInitializedAsync(); +// } + +// //private void OpenFilter() +// //{ +// // this.filterOpen = true; +// //} + +// private void SelectedChanged(bool value, DeviceModelDto item) +// { +// _ = value ? this.selectedItems.Add(item) : this.selectedItems.Remove(item); + +// this.selectAll = this.selectedItems.Count == Items.Count(); +// } + +// //private async Task ApplyFilterAsync(FilterContext context) +// //{ +// // filterItems = selectedItems.ToHashSet(); +// // //_icon = _filterItems.Count == Items.Count() ? Icons.Material.Outlined.FilterAlt : Icons.Material.Filled.FilterAlt; +// // await context.Actions.ApplyFilterAsync(filterDefinition); +// // filterOpen = false; +// //} + +// private void SelectAll(bool value) +// { +// selectAll = value; + +// if (value) +// { +// selectedItems = Items.ToHashSet(); +// } +// else +// { +// selectedItems.Clear(); +// } +// } +// } +//} diff --git a/src/IoTHub.Portal.Client/Components/Concentrators/ConcentratorSearch.razor b/src/IoTHub.Portal.Client/Components/Concentrators/ConcentratorSearch.razor index 633db9ded..85fdd3f74 100644 --- a/src/IoTHub.Portal.Client/Components/Concentrators/ConcentratorSearch.razor +++ b/src/IoTHub.Portal.Client/Components/Concentrators/ConcentratorSearch.razor @@ -2,7 +2,7 @@ - + diff --git a/src/IoTHub.Portal.Client/Components/Devices/DeviceToDuplicateSelector.razor b/src/IoTHub.Portal.Client/Components/Devices/DeviceToDuplicateSelector.razor index 51f3ac697..8a74707a2 100644 --- a/src/IoTHub.Portal.Client/Components/Devices/DeviceToDuplicateSelector.razor +++ b/src/IoTHub.Portal.Client/Components/Devices/DeviceToDuplicateSelector.razor @@ -13,7 +13,7 @@ T="DeviceListItem" Label="Search a device to duplicate" SearchFunc="@SearchDevicesToDuplicate" - ToStringFunc="@(x => x?.DeviceName)" + ToStringFunc="@(x => x?.Name)" DebounceInterval="300" ValueChanged="OnDeviceSelected" Dense=true @@ -23,7 +23,7 @@ Variant="Variant.Outlined" Required="true"> - @context.DeviceName + @context.Name Id: @context.DeviceID diff --git a/src/IoTHub.Portal.Client/Dialogs/Layer/LinkDeviceLayerDialog.razor b/src/IoTHub.Portal.Client/Dialogs/Layer/LinkDeviceLayerDialog.razor index 66b9a1dc6..2bf5da9c3 100644 --- a/src/IoTHub.Portal.Client/Dialogs/Layer/LinkDeviceLayerDialog.razor +++ b/src/IoTHub.Portal.Client/Dialogs/Layer/LinkDeviceLayerDialog.razor @@ -46,7 +46,7 @@ - @context.DeviceName + @context.Name @@ -223,7 +223,7 @@ if (device == null) return deviceDetails; deviceDetails.DeviceID = device.DeviceID; - deviceDetails.DeviceName = device.DeviceName; + deviceDetails.DeviceName = device.Name; deviceDetails.ModelId = device.DeviceModelId; deviceDetails.Image = device.Image; deviceDetails.IsConnected = device.IsConnected; diff --git a/src/IoTHub.Portal.Client/GlobalUsings.cs b/src/IoTHub.Portal.Client/GlobalUsings.cs new file mode 100644 index 000000000..46911a4a9 --- /dev/null +++ b/src/IoTHub.Portal.Client/GlobalUsings.cs @@ -0,0 +1,5 @@ +// Copyright (c) CGI France. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +global using Microsoft.AspNetCore.Components; +global using IoTHub.Portal.Models.v10; diff --git a/src/IoTHub.Portal.Client/IoTHub.Portal.Client.csproj b/src/IoTHub.Portal.Client/IoTHub.Portal.Client.csproj index 50b7cda91..3c70186c7 100644 --- a/src/IoTHub.Portal.Client/IoTHub.Portal.Client.csproj +++ b/src/IoTHub.Portal.Client/IoTHub.Portal.Client.csproj @@ -35,6 +35,7 @@ + diff --git a/src/IoTHub.Portal.Client/Pages/Devices/DeviceListPage.razor b/src/IoTHub.Portal.Client/Pages/Devices/DeviceListPage.razor index a69a278f9..6a6707fbe 100644 --- a/src/IoTHub.Portal.Client/Pages/Devices/DeviceListPage.razor +++ b/src/IoTHub.Portal.Client/Pages/Devices/DeviceListPage.razor @@ -1,138 +1,135 @@ @page "/devices" -@using IoTHub.Portal -@using IoTHub.Portal.Models.v10 -@using IoTHub.Portal.Shared.Models.v10; -@using IoTHub.Portal.Client.Dialogs.Devices -@using System.Web -@using System.IO -@using System.Text.RegularExpressions -@using System.Net.Http.Headers -@using IoTHub.Portal.Shared.Models.v10.Filters; @attribute [Authorize] @inject IDialogService DialogService -@inject NavigationManager navigationManager +@inject NavigationManager NavigationManager @inject PortalSettings Portal @inject IDeviceTagSettingsClientService DeviceTagSettingsClientService @inject IDeviceClientService DeviceClientService -@inject IJSRuntime JS +@inject IJSRuntime Js @inject IDeviceModelsClientService DeviceModelsClientService - - - - - - - @foreach (DeviceTagDto tag in TagList) - { - if (tag.Searchable) - { - - - - } - } - - - - @foreach (var label in labels) - { - - - - } - - - - - this.Model) - Variant="Variant.Outlined" - ToStringFunc="@(x => x?.Name)" - ResetValueOnEmptyText=true - Immediate=true - Clearable=true - CoerceText=true - CoerceValue=false> - - @context.Name - - @((!string.IsNullOrEmpty(@context.Description) && @context.Description.Length > 100) ? @context.Description.Substring(0, 100) + "..." : @context.Description) - - - - - - - Status - - - Enabled - - - Disabled - - - All - - - + + + + + Device model image + + + @* *@ + + @* *@ + + @foreach (var item in deviceModels) + { + + + @(item.Image) device model image + @item.Name + + } + + + Filter + + + + + + + + + @context.Item.Name + @context.Item?.DeviceID - - Connection state - - - Connected - - - Disconnected - - - All - - + + - - - - Search - Reset - - - + + + + + @if (context.Item?.IsEnabled != null && (bool)context.Item?.IsEnabled) + { + + + + } + else + { + + + + } + + + + + @if (context.Item?.IsConnected != null && (bool)context.Item?.IsConnected) + { + + + + } + else + { + + + + } + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - @if (Portal.IsLoRaSupported) - { - - } - - - Devices + @@ -164,324 +161,19 @@ - - - - Device - Allowed - Connection state - Last status update - @if (Portal.IsLoRaSupported) - { - Telemetry - } - See details - Delete - - - - - - - - @context.DeviceName - @context.DeviceID - - - - - - - - @if (context.IsEnabled) - { - - - - } - else - { - - - - } - - - - @if (context.IsConnected) - { - - - - } - else - { - - - - } - - @context.StatusUpdatedTime - - @if (Portal.IsLoRaSupported) - { - - @if (context.HasLoRaTelemetry) - { - - - - } - - } - - - - - - - - - - - No matching records found + Loading... + - + - + - -@code { - [CascadingParameter] - public Error? Error { get; set; } - - private string? searchID = ""; - private string? searchStatus; - private string? searchState; - - private MudTable? table; - private Dictionary searchTags = new(); - - private bool IsLoading { get; set; } = true; - - private DeviceModelDto Model { get; set; } = default!; - - private IEnumerable TagList { get; set; } = new List(); - - private IEnumerable labels = new List(); - private IEnumerable selectedLabels = new List(); - - private int[] pageSizeOptions = new int[] { 2, 5, 10 }; - - protected override async Task OnInitializedAsync() - { - try - { - // Gets the custom tags that can be searched via the panel - TagList = await DeviceTagSettingsClientService.GetDeviceTags(); - foreach (var tag in TagList) - { - if (tag.Searchable) - searchTags.Add(tag.Name, ""); - } - labels = await DeviceClientService.GetAvailableLabels(); - } - catch (ProblemDetailsException exception) - { - Error?.ProcessProblemDetails(exception); - } - } - - private async Task> LoadItems(TableState state) - { - try - { - string? orderBy = null; - - switch (state.SortDirection) - { - case SortDirection.Ascending: - orderBy = $"{state.SortLabel} asc"; - break; - case SortDirection.Descending: - orderBy = $"{state.SortLabel} desc"; - break; - } - - var uri = $"api/devices?pageNumber={state.Page}&pageSize={state.PageSize}&searchText={HttpUtility.UrlEncode(searchID)}&searchStatus={searchStatus}&searchState={searchState}&orderBy={orderBy}&modelId={this.Model?.ModelId}"; - - foreach (var searchTag in searchTags.Where(c => !string.IsNullOrEmpty(c.Value))) - { - uri += $"&tag.{searchTag.Key}={searchTag.Value}"; - } - - foreach (var label in selectedLabels) - { - uri += $"&labels={label.Name}"; - } - - var result = await DeviceClientService.GetDevices(uri); - - return new TableData - { - Items = result.Items, - TotalItems = result.TotalItems - }; - } - catch (ProblemDetailsException exception) - { - Error?.ProcessProblemDetails(exception); - - return new TableData(); - } - finally - { - IsLoading = false; - } - } - - private void AddDevice() - { - navigationManager.NavigateTo("devices/new"); - } - - /// - /// Reset all the fields in the search panel - /// - /// - private void Reset() - { - searchID = ""; - searchStatus = ""; - searchState = ""; - searchTags = searchTags.ToDictionary(tag => tag.Key, tag => ""); - selectedLabels = new List(); - - Search(); - } - - private void Search() - { - table?.ReloadServerData(); - } - - private void Refresh() - { - table?.ReloadServerData(); - } - - /// - /// Prompts a pop-up windows to confirm the device's deletion. - /// - /// Device to delete from the hub - /// - private async Task DeleteDevice(DeviceListItem device) - { - var parameters = new DialogParameters(); - parameters.Add("deviceID", device.DeviceID); - parameters.Add("deviceName", device.DeviceName); - parameters.Add("IsLoRaWan", device.SupportLoRaFeatures); - - var result = await DialogService.Show("Confirm Deletion", parameters).Result; - - if (result.Canceled) - { - return; - } - - Search(); - } - - private void GoToDetails(DeviceListItem item) - { - navigationManager.NavigateTo($"devices/{item.DeviceID}{((item.SupportLoRaFeatures && Portal.IsLoRaSupported) ? "?isLora=true" : "")}"); - } - - /// - /// Allows to autocomplete the Device Model field in the form. - /// - /// Text entered in the field - /// Item of the device model list that matches the user's value - public async Task> Search(string value) - { - var filter = new DeviceModelFilter - { - SearchText = value, - PageNumber = 0, - PageSize = 10, - OrderBy = new string[] - { - string.Empty - } - }; - return (await DeviceModelsClientService.GetDeviceModelsAsync(filter)).Items.ToList(); - } - - private async Task ExportDeviceList() - { - menuIsOpen = false; - - var export = await this.DeviceClientService.ExportDeviceList(); - var fileName = export?.Headers?.ContentDisposition?.Parameters.Single(c => c.Name == "filename").Value; - - using var streamRef = new DotNetStreamReference(stream: await (export?.ReadAsStreamAsync())!); - await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef); - } - - private async Task ExportTemplateFile() - { - menuIsOpen = false; - - var export = await this.DeviceClientService.ExportTemplateFile(); - var fileName = export?.Headers?.ContentDisposition?.Parameters.Single(c => c.Name == "filename").Value; - - using var streamRef = new DotNetStreamReference(stream: await (export?.ReadAsStreamAsync())!); - await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef); - } - - private async Task ShowDeviceTelemetry(DeviceListItem device) - { - var parameters = new DialogParameters { { "DeviceID", device.DeviceID } }; - var options = new DialogOptions { MaxWidth = MaxWidth.Medium, FullWidth = true, CloseButton = true }; - - await DialogService.Show(string.Empty, parameters, options).Result; - } - - private async Task ImportDeviceList(IBrowserFile file) - { - menuIsOpen = false; - - var fileContent = new StreamContent(file.OpenReadStream()); - fileContent.Headers.ContentType = new MediaTypeHeaderValue(file.ContentType); - - var content = new MultipartFormDataContent(); - content.Add(content: fileContent, - name: "\"file\"", - fileName: file.Name); - - var parameters = new DialogParameters { { "Content", content } }; - var options = new DialogOptions { MaxWidth = MaxWidth.Medium, FullWidth = true, CloseButton = true }; - await DialogService.Show("Import summary", parameters, options).Result; - } - - public bool menuIsOpen; - - public void ToggleOpen() - { - menuIsOpen = !menuIsOpen; - } - - private string GetMultiSelectionText(List selectedValues) - { - return $"{selectedValues.Count} label{(selectedValues.Count > 1 ? "s have" : " has")} been selected"; - } -} diff --git a/src/IoTHub.Portal.Client/Pages/Devices/DeviceListPage.razor.cs b/src/IoTHub.Portal.Client/Pages/Devices/DeviceListPage.razor.cs new file mode 100644 index 000000000..7e3ea62d8 --- /dev/null +++ b/src/IoTHub.Portal.Client/Pages/Devices/DeviceListPage.razor.cs @@ -0,0 +1,226 @@ +namespace IoTHub.Portal.Client.Pages.Devices +{ + using System.Collections.Generic; + using System.Net.Http.Headers; + using System.Web; + using Dialogs.Devices; + using Exceptions; + using Microsoft.AspNetCore.Components; + using Microsoft.AspNetCore.Components.Forms; + using Microsoft.JSInterop; + using MudBlazor; + using Portal.Models.v10; + using Portal.Shared.Models.v10; + using Shared; + + public partial class DeviceListPage + { + public MudDataGrid devicesGrid = new(); + + private bool menuIsOpen; + private readonly string searchString = string.Empty; + private readonly string? searchStatus = string.Empty; + private readonly string? searchState = string.Empty; + + private readonly Dictionary searchTags = new(); + + private List deviceModels = new(); + private readonly List selectedDeviceModels = new(); + + private bool IsLoading { get; set; } = true; + private List TagList { get; set; } = new(); + private List labels = new(); + private readonly List selectedLabels = new(); + private readonly int[] pageSizeOptions = { 10, 20, 50, 100, int.MaxValue }; + + [CascadingParameter] public Error? Error { get; set; } + + protected override async Task OnInitializedAsync() + { + try + { + // Gets the custom tags that can be searched via the panel + TagList = (await DeviceTagSettingsClientService.GetDeviceTags()).ToList(); + foreach (var tag in TagList.Where(tag => tag.Searchable)) + this.searchTags.Add(tag.Name, ""); + + this.labels = (await DeviceClientService.GetAvailableLabels()).ToList(); + } + catch (ProblemDetailsException exception) + { + Error?.ProcessProblemDetails(exception); + } + } + + private async Task> LoadItems(GridState state) + { + try + { + var sortByDefinition = state.SortDefinitions.FirstOrDefault(); + var columnName = this.devicesGrid.RenderedColumns + .Find(x => x.PropertyName == sortByDefinition?.SortBy) + ?.Title; + var propertyName = columnName switch + { + "Device" => nameof(DeviceListItem.Name), + "Allowed" => nameof(DeviceListItem.IsEnabled), + "Connection state" => nameof(DeviceListItem.IsConnected), + _ => nameof(DeviceListItem.Name) + }; + + var orderBy = sortByDefinition == null + ? "Name asc" + : sortByDefinition.Descending switch + { + false => // ascending + $"{propertyName} asc", + true => // descending + $"{propertyName} desc", + }; + + var uri = $"api/devices?pageNumber={state.Page}" + + $"&pageSize={state.PageSize}" + + $"&searchText={HttpUtility.UrlEncode(this.searchString)}" + + $"&searchStatus={this.searchStatus}" + + $"&searchState={this.searchState}" + + $"&orderBy={orderBy}"; + + uri = this.selectedDeviceModels.Aggregate(uri, + (current, model) => current + $"&modelId={model.ModelId}"); + + uri = this.searchTags.Where(c => !string.IsNullOrEmpty(c.Value)).Aggregate(uri, + (current, searchTag) => current + $"&tag.{searchTag.Key}={searchTag.Value}"); + + uri = this.selectedLabels.Aggregate(uri, + (current, label) => current + $"&labels={label.Name}"); + + var result = await DeviceClientService.GetDevices(uri); + this.deviceModels = await GetDeviceModels(); + + return new GridData + { + Items = result.Items, + TotalItems = result.TotalItems + }; + } + catch (ProblemDetailsException exception) + { + Error?.ProcessProblemDetails(exception); + + return new GridData(); + } + finally + { + IsLoading = false; + } + } + + private void AddDevice() + { + NavigationManager.NavigateTo("devices/new"); + } + + private void Refresh() + { + _ = this.devicesGrid.ReloadServerData(); + } + + /// + /// Prompts a pop-up windows to confirm the device's deletion. + /// + /// Device to delete from the hub + /// + private async Task DeleteDevice(DeviceListItem device) + { + var parameters = new DialogParameters + { + { "deviceID", device.DeviceID }, + { "deviceName", device.Name }, + { "IsLoRaWan", device.SupportLoRaFeatures } + }; + + _ = await DialogService.Show("Confirm Deletion", parameters).Result; + } + + private void GoToDetails(DeviceListItem item) + { + NavigationManager.NavigateTo( + $"devices/{item.DeviceID}{((item.SupportLoRaFeatures && Portal.IsLoRaSupported) ? "?isLora=true" : "")}"); + } + + /// + /// Allows to autocomplete the Device Model field in the form. + /// + /// Text entered in the field + /// Item of the device model list that matches the user's value + public async Task Search() + { + _ = this.devicesGrid.ReloadServerData(); + //var filter = new DeviceModelFilter + //{ + // SearchText = value, + // PageNumber = 0, + // PageSize = 10, + // OrderBy = new[] { string.Empty } + //}; + //return (await DeviceModelsClientService.GetDeviceModelsAsync(filter)).Items.ToList(); + } + + private async Task ExportDeviceList() + { + this.menuIsOpen = false; + + var export = await DeviceClientService.ExportDeviceList(); + var fileName = export.Headers.ContentDisposition?.Parameters.Single(c => c.Name == "filename").Value; + + using var streamRef = new DotNetStreamReference(stream: await (export?.ReadAsStreamAsync())!); + await Js.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef); + } + + private async Task ExportTemplateFile() + { + this.menuIsOpen = false; + + var export = await DeviceClientService.ExportTemplateFile(); + var fileName = export.Headers.ContentDisposition?.Parameters.Single(c => c.Name == "filename").Value; + + using var streamRef = new DotNetStreamReference(stream: await (export?.ReadAsStreamAsync())!); + await Js.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef); + } + + private async Task ShowDeviceTelemetry(DeviceListItem device) + { + var parameters = new DialogParameters { { "DeviceID", device.DeviceID } }; + var options = new DialogOptions { MaxWidth = MaxWidth.Medium, FullWidth = true, CloseButton = true }; + + _ = await (await DialogService.ShowAsync(string.Empty, parameters, options)).Result; + } + + private async Task ImportDeviceList(IBrowserFile? file) + { + this.menuIsOpen = false; + + var fileContent = new StreamContent(file.OpenReadStream()); + fileContent.Headers.ContentType = new MediaTypeHeaderValue(file.ContentType); + + var content = new MultipartFormDataContent(); + content.Add(content: fileContent, + name: "\"file\"", + fileName: file.Name); + + var parameters = new DialogParameters { { "Content", content } }; + var options = new DialogOptions { MaxWidth = MaxWidth.Medium, FullWidth = true, CloseButton = true }; + _ = await DialogService.Show("Import summary", parameters, options).Result; + } + + private void ToggleOpen() + { + this.menuIsOpen = !this.menuIsOpen; + } + + private async Task> GetDeviceModels() + { + return (await DeviceModelsClientService.GetDeviceModelsAsync()).Items.ToList(); + } + } +} diff --git a/src/IoTHub.Portal.Client/Pages/Layer/LayerListPage.razor b/src/IoTHub.Portal.Client/Pages/Layer/LayerListPage.razor index 4119b8856..12e6dc77c 100644 --- a/src/IoTHub.Portal.Client/Pages/Layer/LayerListPage.razor +++ b/src/IoTHub.Portal.Client/Pages/Layer/LayerListPage.razor @@ -149,7 +149,7 @@ if (device == null) return deviceDetails; deviceDetails.DeviceID = device.DeviceID; - deviceDetails.DeviceName = device.DeviceName; + deviceDetails.DeviceName = device.Name; deviceDetails.ModelId = device.DeviceModelId; deviceDetails.Image = device.Image; deviceDetails.IsConnected = device.IsConnected; diff --git a/src/IoTHub.Portal.Client/Services/DeviceModelsClientService.cs b/src/IoTHub.Portal.Client/Services/DeviceModelsClientService.cs index b047b8264..ffac8237b 100644 --- a/src/IoTHub.Portal.Client/Services/DeviceModelsClientService.cs +++ b/src/IoTHub.Portal.Client/Services/DeviceModelsClientService.cs @@ -14,7 +14,7 @@ namespace IoTHub.Portal.Client.Services public class DeviceModelsClientService : IDeviceModelsClientService { private readonly HttpClient http; - private readonly string apiUrlBase = "api/models"; + private const string ApiUrlBase = "api/models"; public DeviceModelsClientService(HttpClient http) { @@ -23,17 +23,22 @@ public DeviceModelsClientService(HttpClient http) public async Task> GetDeviceModelsAsync(DeviceModelFilter? deviceModelFilter = null) { - var query = new Dictionary + var query = new Dictionary(); + if (deviceModelFilter != null) { - { nameof(DeviceModelFilter.SearchText), deviceModelFilter?.SearchText ?? string.Empty }, -#pragma warning disable CA1305 - { nameof(DeviceModelFilter.PageNumber), deviceModelFilter?.PageNumber.ToString() ?? string.Empty }, - { nameof(DeviceModelFilter.PageSize), deviceModelFilter?.PageSize.ToString() ?? string.Empty }, -#pragma warning restore CA1305 - { nameof(DeviceModelFilter.OrderBy), string.Join("", deviceModelFilter?.OrderBy!) ?? string.Empty } - }; - - var uri = QueryHelpers.AddQueryString(this.apiUrlBase, query); + query = new Dictionary + { + { nameof(DeviceModelFilter.SearchText), deviceModelFilter?.SearchText ?? string.Empty }, + { nameof(DeviceModelFilter.PageNumber), deviceModelFilter?.PageNumber.ToString() ?? string.Empty }, + { nameof(DeviceModelFilter.PageSize), deviceModelFilter?.PageSize.ToString() ?? string.Empty }, + { + nameof(DeviceModelFilter.OrderBy), + deviceModelFilter != null ? string.Join("", deviceModelFilter?.OrderBy!) : string.Empty + } + }; + } + + var uri = QueryHelpers.AddQueryString(ApiUrlBase, query); return await this.http.GetFromJsonAsync>(uri) ?? new PaginationResult(); } diff --git a/src/IoTHub.Portal.Client/Validators/ConcentratorValidator.cs b/src/IoTHub.Portal.Client/Validators/ConcentratorValidator.cs index cd3735bd9..c19db6594 100644 --- a/src/IoTHub.Portal.Client/Validators/ConcentratorValidator.cs +++ b/src/IoTHub.Portal.Client/Validators/ConcentratorValidator.cs @@ -18,7 +18,7 @@ public ConcentratorValidator() _ = RuleFor(x => x.DeviceName) .NotEmpty() - .WithMessage("DeviceName is required."); + .WithMessage("Name is required."); _ = RuleFor(x => x.LoraRegion) .NotEmpty().WithMessage("LoraRegion is required.") diff --git a/src/IoTHub.Portal.Client/Validators/DeviceDetailsValidator.cs b/src/IoTHub.Portal.Client/Validators/DeviceDetailsValidator.cs index f659c24e8..253b93550 100644 --- a/src/IoTHub.Portal.Client/Validators/DeviceDetailsValidator.cs +++ b/src/IoTHub.Portal.Client/Validators/DeviceDetailsValidator.cs @@ -17,7 +17,7 @@ public DeviceDetailsValidator(string? cloudProvider = null) { _ = RuleFor(x => x.DeviceName) .NotEmpty() - .WithMessage("DeviceName is required."); + .WithMessage("Name is required."); _ = RuleFor(x => x.ModelId) .NotEmpty() diff --git a/src/IoTHub.Portal.Client/_Imports.razor b/src/IoTHub.Portal.Client/_Imports.razor index 0725480db..57400ea6a 100644 --- a/src/IoTHub.Portal.Client/_Imports.razor +++ b/src/IoTHub.Portal.Client/_Imports.razor @@ -28,5 +28,6 @@ @using IoTHub.Portal.Client.Components.EdgeDevices @using IoTHub.Portal.Client.Components.EdgeModels @using IoTHub.Portal.Client.Dialogs.EdgeModels +@using IoTHub.Portal.Models.v10 @using MudBlazor @using ChartJs.Blazor; diff --git a/src/IoTHub.Portal.Domain/Options/DeviceModelImageOptions.cs b/src/IoTHub.Portal.Domain/Options/DeviceModelImageOptions.cs index fa85f4e31..d40ddd7da 100644 --- a/src/IoTHub.Portal.Domain/Options/DeviceModelImageOptions.cs +++ b/src/IoTHub.Portal.Domain/Options/DeviceModelImageOptions.cs @@ -9,7 +9,7 @@ public class DeviceModelImageOptions { public Uri BaseUri { get; set; } = default!; - public const string ImageContainerName = "device-images-2"; + public const string ImageContainerName = "device-images"; public const string DefaultImageName = "default-template-icon"; diff --git a/src/IoTHub.Portal.Infrastructure/Mappers/DeviceTwinMapper.cs b/src/IoTHub.Portal.Infrastructure/Mappers/DeviceTwinMapper.cs index d8cfb5acd..1aa3205a7 100644 --- a/src/IoTHub.Portal.Infrastructure/Mappers/DeviceTwinMapper.cs +++ b/src/IoTHub.Portal.Infrastructure/Mappers/DeviceTwinMapper.cs @@ -65,7 +65,7 @@ public DeviceListItem CreateDeviceListItem(Twin twin) IsConnected = twin.ConnectionState == DeviceConnectionState.Connected, IsEnabled = twin.Status == DeviceStatus.Enabled, StatusUpdatedTime = twin.StatusUpdatedTime ?? DateTime.MinValue, - DeviceName = DeviceHelper.RetrieveTagValue(twin, nameof(DeviceListItem.DeviceName)), + Name = DeviceHelper.RetrieveTagValue(twin, nameof(DeviceListItem.Name)), Image = this.deviceModelImageManager.GetDeviceModelImageAsync(DeviceHelper.RetrieveTagValue(twin, nameof(DeviceDetails.ModelId))!).Result, SupportLoRaFeatures = bool.Parse(DeviceHelper.RetrieveTagValue(twin, nameof(DeviceListItem.SupportLoRaFeatures)) ?? "false") }; diff --git a/src/IoTHub.Portal.Infrastructure/Mappers/LoRaDeviceMapper.cs b/src/IoTHub.Portal.Infrastructure/Mappers/LoRaDeviceMapper.cs index 7d2848f66..2eba3ef7d 100644 --- a/src/IoTHub.Portal.Infrastructure/Mappers/LoRaDeviceMapper.cs +++ b/src/IoTHub.Portal.Infrastructure/Mappers/LoRaDeviceMapper.cs @@ -151,7 +151,7 @@ public DeviceListItem CreateDeviceListItem(Twin twin) return new DeviceListItem { DeviceID = twin.DeviceId, - DeviceName = DeviceHelper.RetrieveTagValue(twin, nameof(LoRaDeviceDetails.DeviceName)), + Name = DeviceHelper.RetrieveTagValue(twin, nameof(LoRaDeviceDetails.DeviceName)), Image = this.deviceModelImageManager .GetDeviceModelImageAsync(DeviceHelper.RetrieveTagValue(twin, nameof(DeviceDetails.ModelId))!) .Result, diff --git a/src/IoTHub.Portal.Infrastructure/Services/DeviceServiceBase.cs b/src/IoTHub.Portal.Infrastructure/Services/DeviceServiceBase.cs index 7a534a112..df80e3c1a 100644 --- a/src/IoTHub.Portal.Infrastructure/Services/DeviceServiceBase.cs +++ b/src/IoTHub.Portal.Infrastructure/Services/DeviceServiceBase.cs @@ -121,7 +121,7 @@ public async Task> GetDevices(string searchText .Select(device => new DeviceListItem { DeviceID = device.Id, - DeviceName = device.Name, + Name = device.Name, IsEnabled = device.IsEnabled, IsConnected = device.IsConnected, StatusUpdatedTime = device.StatusUpdatedTime, diff --git a/src/IoTHub.Portal.Server/Managers/ExportManager.cs b/src/IoTHub.Portal.Server/Managers/ExportManager.cs index df05c3f67..b7e8ae9aa 100644 --- a/src/IoTHub.Portal.Server/Managers/ExportManager.cs +++ b/src/IoTHub.Portal.Server/Managers/ExportManager.cs @@ -22,7 +22,6 @@ namespace IoTHub.Portal.Server.Managers using CsvHelper; using CsvHelper.Configuration; using Microsoft.Extensions.Options; - using Microsoft.IdentityModel.Tokens; public class ExportManager : IExportManager { @@ -248,7 +247,7 @@ private static Dictionary ReadTags(CsvReader reader, IEnumerable private static bool TryReadMandatoryFields(CsvReader reader, int lineNumber, ref string deviceId, ref string deviceName, ref string modelId, ref List report) { - if (!reader.TryGetField("Id", out deviceId) || deviceId.IsNullOrEmpty()) + if (!reader.TryGetField("Id", out deviceId) || string.IsNullOrEmpty(deviceId)) { report.Add(new ImportResultLine { @@ -262,7 +261,7 @@ private static bool TryReadMandatoryFields(CsvReader reader, int lineNumber, ref return false; } - if (!reader.TryGetField("Name", out deviceName) || deviceName.IsNullOrEmpty()) + if (!reader.TryGetField("Name", out deviceName) || string.IsNullOrEmpty(deviceName)) { report.Add(new ImportResultLine { @@ -276,7 +275,7 @@ private static bool TryReadMandatoryFields(CsvReader reader, int lineNumber, ref return false; } - if (!reader.TryGetField("ModelId", out modelId) || modelId.IsNullOrEmpty()) + if (!reader.TryGetField("ModelId", out modelId) || string.IsNullOrEmpty(modelId)) { report.Add(new ImportResultLine { diff --git a/src/IoTHub.Portal.Server/Properties/launchSettings.json b/src/IoTHub.Portal.Server/Properties/launchSettings.json index e102b64dc..cd909de95 100644 --- a/src/IoTHub.Portal.Server/Properties/launchSettings.json +++ b/src/IoTHub.Portal.Server/Properties/launchSettings.json @@ -18,7 +18,7 @@ "ASPNETCORE_ENVIRONMENT": "Development" }, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "https://localhost:8001;http://localhost:8080", + "applicationUrl": "https://localhost:8001;http://localhost:8000", "dotnetRunMessages": true }, "WSL": { @@ -27,7 +27,7 @@ "launchUrl": "https://localhost:8443", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", - "ASPNETCORE_URLS": "https://localhost:8443;http://localhost:8080" + "ASPNETCORE_URLS": "https://localhost:8001;http://localhost:8000" }, "distributionName": "" } diff --git a/src/IoTHub.Portal.Server/Services/DeviceModelService.cs b/src/IoTHub.Portal.Server/Services/DeviceModelService.cs index 88fdb62f7..890dee69f 100644 --- a/src/IoTHub.Portal.Server/Services/DeviceModelService.cs +++ b/src/IoTHub.Portal.Server/Services/DeviceModelService.cs @@ -63,16 +63,36 @@ public DeviceModelService(IMapper mapper, this.externalDeviceService = externalDeviceService; } - public async Task> GetDeviceModels(DeviceModelFilter deviceModelFilter) + public async Task> GetDeviceModels(DeviceModelFilter? deviceModelFilter) { - var deviceModelPredicate = PredicateBuilder.True(); + var paginatedDeviceModels = new PaginatedResult(); - if (!string.IsNullOrWhiteSpace(deviceModelFilter.SearchText)) + if (deviceModelFilter == null) { - deviceModelPredicate = deviceModelPredicate.And(model => model.Name.ToLower().Contains(deviceModelFilter.SearchText.ToLower()) || model.Description.ToLower().Contains(deviceModelFilter.SearchText.ToLower())); + var models = await this.deviceModelRepository.GetAllAsync(); + paginatedDeviceModels = new PaginatedResult + { + CurrentPage = 0, + Data = models.ToList(), + PageSize = models.Count(), + TotalCount = models.Count() + }; } + else + { + var deviceModelPredicate = PredicateBuilder.True(); - var paginatedDeviceModels = await this.deviceModelRepository.GetPaginatedListAsync(deviceModelFilter.PageNumber, deviceModelFilter.PageSize, deviceModelFilter.OrderBy, deviceModelPredicate, includes: new Expression>[] { d => d.Labels }); + if (!string.IsNullOrWhiteSpace(deviceModelFilter.SearchText)) + { + deviceModelPredicate = deviceModelPredicate.And(model => + model.Name.ToLower().Contains(deviceModelFilter.SearchText.ToLower()) || model.Description + .ToLower().Contains(deviceModelFilter.SearchText.ToLower())); + } + + paginatedDeviceModels = await this.deviceModelRepository.GetPaginatedListAsync( + deviceModelFilter.PageNumber, deviceModelFilter.PageSize, deviceModelFilter.OrderBy, + deviceModelPredicate, includes: new Expression>[] { d => d.Labels }); + } var paginateDeviceModelsDto = new PaginatedResult { diff --git a/src/IoTHub.Portal.Shared/Models/v1.0/DeviceListItem.cs b/src/IoTHub.Portal.Shared/Models/v1.0/DeviceListItem.cs index 2a4eb1d3d..32be59140 100644 --- a/src/IoTHub.Portal.Shared/Models/v1.0/DeviceListItem.cs +++ b/src/IoTHub.Portal.Shared/Models/v1.0/DeviceListItem.cs @@ -20,7 +20,7 @@ public class DeviceListItem /// /// The device friendly name. /// - public string DeviceName { get; set; } = default!; + public string Name { get; set; } = default!; /// /// The device Model Identifier. diff --git a/src/IoTHub.Portal.Tests.Unit/Client/Components/Devices/EditDeviceTests.cs b/src/IoTHub.Portal.Tests.Unit/Client/Components/Devices/EditDeviceTests.cs index 8360d1b8a..8c14bb957 100644 --- a/src/IoTHub.Portal.Tests.Unit/Client/Components/Devices/EditDeviceTests.cs +++ b/src/IoTHub.Portal.Tests.Unit/Client/Components/Devices/EditDeviceTests.cs @@ -895,7 +895,7 @@ public void ReturnButtonMustNavigateToPreviousPage() // var mockDeviceDetails = new DeviceDetails // { - // DeviceName = Guid.NewGuid().ToString(), + // Name = Guid.NewGuid().ToString(), // ModelId = mockDeviceModel.ModelId, // DeviceID = Guid.NewGuid().ToString(), // Tags = new Dictionary() @@ -1025,7 +1025,7 @@ public void SaveShouldProcessProblemDetailsExceptionWhenIssueOccursOnUpdatingDev // var mockDeviceDetails = new DeviceDetails // { - // DeviceName = Guid.NewGuid().ToString(), + // Name = Guid.NewGuid().ToString(), // ModelId = mockDeviceModel.ModelId, // DeviceID = Guid.NewGuid().ToString(), // Tags = new Dictionary() @@ -1056,7 +1056,7 @@ public void SaveShouldProcessProblemDetailsExceptionWhenIssueOccursOnUpdatingDev // // Act // var cut = RenderComponent(parameters => parameters.Add(p => p.context, CreateEditMode.Edit).Add(p => p.DeviceID, mockDeviceDetails.DeviceID)); - // cut.WaitForElement($"#{nameof(DeviceDetails.DeviceName)}").Change(""); + // cut.WaitForElement($"#{nameof(DeviceDetails.Name)}").Change(""); // var saveButton = cut.WaitForElement("#saveButton"); // saveButton.Click(); diff --git a/src/IoTHub.Portal.Tests.Unit/Client/Pages/Devices/DevicesListPageTests.cs b/src/IoTHub.Portal.Tests.Unit/Client/Pages/Devices/DevicesListPageTests.cs index 23b443528..75859b04f 100644 --- a/src/IoTHub.Portal.Tests.Unit/Client/Pages/Devices/DevicesListPageTests.cs +++ b/src/IoTHub.Portal.Tests.Unit/Client/Pages/Devices/DevicesListPageTests.cs @@ -326,12 +326,12 @@ public async Task TypingSomeCharactersInTheAutocompleteShouldFilterTheDeviceMode new DeviceListItem() { DeviceID = Guid.NewGuid().ToString(), - DeviceName = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), }, new DeviceListItem() { DeviceID = Guid.NewGuid().ToString(), - DeviceName = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), } } }); @@ -363,11 +363,11 @@ public async Task TypingSomeCharactersInTheAutocompleteShouldFilterTheDeviceMode popoverProvider.WaitForAssertion(() => popoverProvider.FindAll(".mud-input-helper-text").Count.Should().Be(2)); - var newModelList = await cut.Instance.Search("01"); + await cut.Instance.Search(); // Assert cut.WaitForAssertion(() => cut.Markup.Should().NotContain("Loading...")); - _ = newModelList.Count().Should().Be(2); + _ = cut.Instance.devicesGrid.Items.Count().Should().Be(2); cut.WaitForAssertion(() => MockRepository.VerifyAll()); } diff --git a/src/IoTHub.Portal.Tests.Unit/Client/Validators/ConcentratorValidatorTests.cs b/src/IoTHub.Portal.Tests.Unit/Client/Validators/ConcentratorValidatorTests.cs index 95d54ae39..ccba91f73 100644 --- a/src/IoTHub.Portal.Tests.Unit/Client/Validators/ConcentratorValidatorTests.cs +++ b/src/IoTHub.Portal.Tests.Unit/Client/Validators/ConcentratorValidatorTests.cs @@ -30,7 +30,7 @@ public void ValidateValidConcentrator() Assert.AreEqual(0, concentratorValidation.Errors.Count); } - [TestCase("DeviceName", "", "LoraRegionValue")] + [TestCase("Name", "", "LoraRegionValue")] [TestCase("LoraRegion", "DeviceNameValue", "")] public void ValidateMissingFieldShouldReturnError( string testedValue, diff --git a/src/IoTHub.Portal.Tests.Unit/Client/Validators/DeviceDetailValidatorTests.cs b/src/IoTHub.Portal.Tests.Unit/Client/Validators/DeviceDetailValidatorTests.cs index 7de8b7723..42c82c167 100644 --- a/src/IoTHub.Portal.Tests.Unit/Client/Validators/DeviceDetailValidatorTests.cs +++ b/src/IoTHub.Portal.Tests.Unit/Client/Validators/DeviceDetailValidatorTests.cs @@ -53,7 +53,7 @@ public void ValidateValidAWSDevice() Assert.AreEqual(0, standardValidation.Errors.Count); } - [TestCase("DeviceName", "", "ModelIdValue", "DeviceIDValue")] + [TestCase("Name", "", "ModelIdValue", "DeviceIDValue")] [TestCase("ModelId", "DeviceNameValue", "", "DeviceIDValue")] [TestCase("DeviceID", "DeviceNameValue", "ModelIdValue", "")] public void ValidateMissingFieldShouldReturnError( @@ -80,7 +80,7 @@ public void ValidateMissingFieldShouldReturnError( Assert.AreEqual(standardValidation.Errors[0].ErrorMessage, $"{testedValue} is required."); } - [TestCase("DeviceName", "", "ModelIdValue", "DeviceIDValue")] + [TestCase("Name", "", "ModelIdValue", "DeviceIDValue")] [TestCase("ModelId", "DeviceNameValue", "", "DeviceIDValue")] public void ValidateMissingAWSFieldShouldReturnError( string testedValue, diff --git a/src/IoTHub.Portal.Tests.Unit/Infrastructure/Mappers/LoRaDeviceTwinMapperTests.cs b/src/IoTHub.Portal.Tests.Unit/Infrastructure/Mappers/LoRaDeviceTwinMapperTests.cs index fa4a6f8bd..b201e73cf 100644 --- a/src/IoTHub.Portal.Tests.Unit/Infrastructure/Mappers/LoRaDeviceTwinMapperTests.cs +++ b/src/IoTHub.Portal.Tests.Unit/Infrastructure/Mappers/LoRaDeviceTwinMapperTests.cs @@ -132,7 +132,7 @@ public void CreateDeviceListItemStateUnderTestExpectedBehavior() // Assert Assert.IsNotNull(result); Assert.AreEqual(twin.DeviceId, result.DeviceID); - Assert.AreEqual(DeviceHelper.RetrieveTagValue(twin, nameof(LoRaDeviceDetails.DeviceName)), result.DeviceName); + Assert.AreEqual(DeviceHelper.RetrieveTagValue(twin, nameof(LoRaDeviceDetails.DeviceName)), result.Name); Assert.AreEqual(modelImage, result.Image); diff --git a/src/IoTHub.Portal.Tests.Unit/Server/Managers/ExportManagerTests.cs b/src/IoTHub.Portal.Tests.Unit/Server/Managers/ExportManagerTests.cs index 97b7b711c..aa7fe7d0a 100644 --- a/src/IoTHub.Portal.Tests.Unit/Server/Managers/ExportManagerTests.cs +++ b/src/IoTHub.Portal.Tests.Unit/Server/Managers/ExportManagerTests.cs @@ -351,7 +351,7 @@ public async Task ImportDeviceListCorrectFileMissingMandatoryFieldShouldDisplayE _ = textContent.AppendLine("Id,Name,ModelId,TAG:supportLoRaFeatures,TAG:Tag1,TAG:Tag2,PROPERTY:Property1,PROPERTY:Property2,PROPERTY:AppKey,PROPERTY:AppEUI,PROPERTY:AppSKey,PROPERTY:NwkSKey,PROPERTY:DevAddr,PROPERTY:GatewayID,PROPERTY:Downlink,PROPERTY:ClassType,PROPERTY:PreferredWindow,PROPERTY:Deduplication,PROPERTY:RX1DROffset,PROPERTY:RX2DataRate,PROPERTY:RXDelay,PROPERTY:ABPRelaxMode,PROPERTY:SensorDecoder,PROPERTY:FCntUpStart,PROPERTY:FCntDownStart,PROPERTY:FCntResetCounter,PROPERTY:Supports32BitFCnt,PROPERTY:KeepAliveTimeout"); // Missing DeviceId _ = textContent.AppendLine(",ImportLoRa,dc1f171b-8e51-4c6d-a1c6-942b4a0f995b,true,Tag1-Value1,Tag2-Value1,,,AppKeyValue,AppEUIValue,,,,,true,C,1,Drop,,,1,,http://sensor-decoder-url/test,,,,,1"); - // Missing DeviceName + // Missing Name _ = textContent.AppendLine("0000000000000002,,f8b7a67a-345d-463e-ae0e-eeb0f6d24e38,false,Tag1-Value2,Tag2-Value2,Property1Value,Property1Value,,,,,,,,,,,,,,,,,,,,"); // Missing ModelId _ = textContent.AppendLine("0000000000000003,ImportNonLoRa,,false,Tag1-Value3,Tag2-Value3,Property1Value,Property1Value,,,,,,,,,,,,,,,,,,,,"); diff --git a/src/IoTHub.Portal.Tests.Unit/Server/Services/DeviceServiceTests.cs b/src/IoTHub.Portal.Tests.Unit/Server/Services/DeviceServiceTests.cs index 5e6474ebd..56706194d 100644 --- a/src/IoTHub.Portal.Tests.Unit/Server/Services/DeviceServiceTests.cs +++ b/src/IoTHub.Portal.Tests.Unit/Server/Services/DeviceServiceTests.cs @@ -195,7 +195,7 @@ public async Task GetDevices_CustomFilter_ReturnsExpectedDevices() _ = result.TotalCount.Should().Be(expectedTotalDevicesCount); _ = result.PageSize.Should().Be(expectedPageSize); _ = result.CurrentPage.Should().Be(expectedCurrentPage); - _ = result.Data[0].DeviceName.Should().Be(device1.Name); + _ = result.Data[0].Name.Should().Be(device1.Name); MockRepository.VerifyAll(); } diff --git a/src/IoTHub.Portal.sln b/src/IoTHub.Portal.sln index fd250ee6d..1aaf4ca15 100644 --- a/src/IoTHub.Portal.sln +++ b/src/IoTHub.Portal.sln @@ -95,9 +95,7 @@ Global EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {B9D2DE01-84DE-461F-998C-20B57E4AA021}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B9D2DE01-84DE-461F-998C-20B57E4AA021}.Debug|Any CPU.Build.0 = Debug|Any CPU {B9D2DE01-84DE-461F-998C-20B57E4AA021}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B9D2DE01-84DE-461F-998C-20B57E4AA021}.Release|Any CPU.Build.0 = Release|Any CPU {EF219BC4-0C53-4DE7-92D1-F970C6E56E71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EF219BC4-0C53-4DE7-92D1-F970C6E56E71}.Debug|Any CPU.Build.0 = Debug|Any CPU {EF219BC4-0C53-4DE7-92D1-F970C6E56E71}.Release|Any CPU.ActiveCfg = Release|Any CPU