Skip to content

Commit

Permalink
Feature/server side loading for tables (#461)
Browse files Browse the repository at this point in the history
* Enable pagination at server side for devices endpoint

* Add pagination

* Search server side on device list

* Fix unit tests

* Fix Code QL issues

* Add DeviceService tests

* Fix url helper issues on controller

* Increase coverage in device get list controller action

* Add server-side loading to LoRaWAN concentrators

* Add Edge Device server side filtering and pagination

* Fix unit testsFix devices loading parameters

* Fix unit tests
  • Loading branch information
kbeaugrand authored Mar 16, 2022
1 parent 6c1eeef commit 9055acd
Show file tree
Hide file tree
Showing 28 changed files with 1,271 additions and 518 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -568,9 +568,18 @@ public async Task DeleteShouldRemoveTheEntityCommandsAndAvatar()
.ReturnsAsync(mockResponse.Object);

_ = this.mockDeviceService.Setup(c => c.GetAllDevice(
It.Is<string>(x => string.IsNullOrEmpty(x)),
It.Is<string>(x => string.IsNullOrEmpty(x))))
.ReturnsAsync(new List<Twin>());
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<bool?>(),
It.IsAny<bool?>(),
It.IsAny<Dictionary<string, string>>(),
It.IsAny<int>()))
.ReturnsAsync(new Shared.PaginationResult<Twin>
{
Items = new List<Twin>()
});

_ = this.mockDeviceModelImageManager.Setup(c => c.DeleteDeviceModelImageAsync(It.Is<string>(x => x == id)))
.Returns(Task.CompletedTask);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,15 @@ namespace AzureIoTHub.Portal.Server.Tests.Unit.Controllers.V10
using AzureIoTHub.Portal.Shared.Models.v10;
using AzureIoTHub.Portal.Shared.Models.v10.Device;
using AzureIoTHub.Portal.Shared.Models.v10.DeviceModel;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Routing;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Shared;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Moq;
using NUnit.Framework;

Expand All @@ -33,6 +38,7 @@ public class DevicesControllerTests
private MockRepository mockRepository;

private Mock<IDeviceProvisioningServiceManager> mockProvisioningServiceManager;
private Mock<IUrlHelper> mockUrlHelper;
private Mock<ILogger<DevicesController>> mockLogger;
private Mock<IDeviceService> mockDeviceService;
private Mock<IDeviceTagService> mockDeviceTagService;
Expand All @@ -50,6 +56,7 @@ public void SetUp()
this.mockDeviceTagService = this.mockRepository.Create<IDeviceTagService>();
this.mockDeviceTwinMapper = this.mockRepository.Create<IDeviceTwinMapper<DeviceListItem, DeviceDetails>>();
this.mockTableClientFactory = this.mockRepository.Create<ITableClientFactory>();
this.mockUrlHelper = this.mockRepository.Create<IUrlHelper>();
}

private DevicesController CreateDevicesController()
Expand All @@ -60,7 +67,10 @@ private DevicesController CreateDevicesController()
this.mockDeviceTagService.Object,
this.mockProvisioningServiceManager.Object,
this.mockDeviceTwinMapper.Object,
this.mockTableClientFactory.Object);
this.mockTableClientFactory.Object)
{
Url = this.mockUrlHelper.Object
};
}

[Test]
Expand All @@ -70,32 +80,72 @@ public async Task GetListStateUnderTestExpectedBehavior()
var devicesController = this.CreateDevicesController();
var count = 100;
var twinCollection = new TwinCollection();

twinCollection["deviceType"] = "test";

var mockTagSearch = new StringValues("test");

var mockQueryCollection = new QueryCollection(new Dictionary<string, StringValues>()
{
{ "tag.deviceType", mockTagSearch }
});

var mockHttpRequest = this.mockRepository.Create<HttpRequest>();
var mockHttpContext = this.mockRepository.Create<HttpContext>();

_ = mockHttpContext.SetupGet(c => c.Request)
.Returns(mockHttpRequest.Object);

_ = mockHttpRequest.Setup(c => c.Query)
.Returns(mockQueryCollection);

devicesController.ControllerContext = new ControllerContext(
new ActionContext(mockHttpContext.Object, new RouteData(), new ControllerActionDescriptor()));

_ = this.mockDeviceService.Setup(c => c.GetAllDevice(
It.Is<string>(x => x == "aaa"),
It.Is<string>(x => string.IsNullOrEmpty(x)),
It.Is<string>(x => x == "LoRa Concentrator")))
.ReturnsAsync(Enumerable.Range(0, 100).Select(x => new Twin
It.Is<string>(x => x == "LoRa Concentrator"),
It.Is<string>(x => x == "bbb"),
It.Is<bool?>(x => x == true),
It.Is<bool?>(x => x == false),
It.Is<Dictionary<string, string>>(x => x["deviceType"] == "test"),
It.Is<int>(x => x == 2)))
.ReturnsAsync(new Shared.PaginationResult<Twin>
{
DeviceId = FormattableString.Invariant($"{x}"),
Tags = twinCollection
}));
Items = Enumerable.Range(0, 100).Select(x => new Twin
{
DeviceId = FormattableString.Invariant($"{x}"),
Tags = twinCollection
}),
TotalItems = 1000,
NextPage = Guid.NewGuid().ToString()
});

_ = this.mockDeviceTwinMapper.Setup(c => c.CreateDeviceListItem(It.IsAny<Twin>(), It.IsAny<IEnumerable<string>>()))
.Returns<Twin, IEnumerable<string>>((x, y) => new DeviceListItem
_ = this.mockDeviceTwinMapper.Setup(c => c.CreateDeviceListItem(It.IsAny<Twin>()))
.Returns<Twin>((x) => new DeviceListItem
{
DeviceID = x.DeviceId
});

_ = this.mockDeviceTagService.Setup(c => c.GetAllSearchableTagsNames())
.Returns(new List<string>());
.Returns(new string[] { "deviceType" });

_ = this.mockUrlHelper.Setup(c => c.RouteUrl(It.IsAny<UrlRouteContext>()))
.Returns(Guid.NewGuid().ToString());
// Act
var result = await devicesController.GetItems();

var result = await devicesController.GetItems(
continuationToken: "aaa",
searchText: "bbb",
searchStatus: true,
searchState: false,
pageSize: 2);

// Assert
Assert.IsNotNull(result);
Assert.AreEqual(count, result.Count());
Assert.AreEqual(count, result.Items.Count());
Assert.AreEqual(1000, result.TotalItems);
this.mockRepository.VerifyAll();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
namespace AzureIoTHub.Portal.Server.Tests.Unit.Controllers.V10
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AzureIoTHub.Portal.Server.Controllers.V10;
using AzureIoTHub.Portal.Server.Managers;
using AzureIoTHub.Portal.Server.Services;
using AzureIoTHub.Portal.Shared.Models.v10;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Routing;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Common.Exceptions;
using Microsoft.Azure.Devices.Shared;
Expand All @@ -29,6 +33,7 @@ public class EdgeDevicesControllerTests
private Mock<ILogger<EdgeDevicesController>> mockLogger;
private Mock<RegistryManager> mockRegistryManager;
private Mock<IDeviceService> mockDeviceService;
private Mock<IUrlHelper> mockUrlHelper;

[SetUp]
public void SetUp()
Expand All @@ -40,6 +45,7 @@ public void SetUp()
this.mockLogger = this.mockRepository.Create<ILogger<EdgeDevicesController>>();
this.mockRegistryManager = this.mockRepository.Create<RegistryManager>();
this.mockDeviceService = this.mockRepository.Create<IDeviceService>();
this.mockUrlHelper = this.mockRepository.Create<IUrlHelper>();
}

private EdgeDevicesController CreateEdgeDevicesController()
Expand All @@ -49,7 +55,10 @@ private EdgeDevicesController CreateEdgeDevicesController()
this.mockLogger.Object,
this.mockRegistryManager.Object,
this.mockDeviceService.Object,
this.mockProvisioningServiceManager.Object);
this.mockProvisioningServiceManager.Object)
{
Url = this.mockUrlHelper.Object
};
}

[Test]
Expand All @@ -59,17 +68,23 @@ public async Task GetAllDevicesShouldReturnListOfEdgeDevices()
var twin = new Twin("aaa");
twin.Tags["type"] = "test";

_ = this.mockDeviceService.Setup(x => x.GetAllEdgeDevice())
.ReturnsAsync(new[]
_ = this.mockDeviceService.Setup(x => x.GetAllEdgeDevice(
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<bool?>(),
It.IsAny<string>(),
It.IsAny<int>()))
.ReturnsAsync(new Shared.PaginationResult<Twin>
{
twin
Items = new[] { twin },
NextPage = Guid.NewGuid().ToString()
});

_ = this.mockDeviceService.Setup(x => x.GetDeviceTwin(It.Is<string>(c => c == twin.DeviceId)))
.ReturnsAsync(twin);

var edgeDevicesController = this.CreateEdgeDevicesController();

_ = this.mockUrlHelper.Setup(c => c.RouteUrl(It.IsAny<UrlRouteContext>()))
.Returns(Guid.NewGuid().ToString());

// Act
var result = await edgeDevicesController.Get();

Expand All @@ -81,11 +96,12 @@ public async Task GetAllDevicesShouldReturnListOfEdgeDevices()
Assert.IsNotNull(okObjectResult);
Assert.AreEqual(200, okObjectResult.StatusCode);
Assert.IsNotNull(okObjectResult.Value);
Assert.IsAssignableFrom<List<IoTEdgeListItem>>(okObjectResult.Value);
var gatewayList = okObjectResult.Value as List<IoTEdgeListItem>;
Assert.IsAssignableFrom<Shared.PaginationResult<IoTEdgeListItem>>(okObjectResult.Value);
var gatewayList = okObjectResult.Value as Shared.PaginationResult<IoTEdgeListItem>;
Assert.IsNotNull(gatewayList);
Assert.AreEqual(1, gatewayList.Count);
var gateway = gatewayList[0];
Assert.AreEqual(1, gatewayList.Items.Count());
var gateway = gatewayList.Items.ElementAt(0);

Assert.IsNotNull(gateway);
Assert.AreEqual(twin.DeviceId, gateway.DeviceId);
Assert.AreEqual("test", gateway.Type);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) CGI France. All rights reserved.
// 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.Server.Tests.Unit.Controllers.V10.LoRaWAN
Expand All @@ -11,8 +11,10 @@ namespace AzureIoTHub.Portal.Server.Tests.Unit.Controllers.V10.LoRaWAN
using AzureIoTHub.Portal.Server.Managers;
using AzureIoTHub.Portal.Server.Mappers;
using AzureIoTHub.Portal.Server.Services;
using AzureIoTHub.Portal.Shared;
using AzureIoTHub.Portal.Shared.Models.v10.LoRaWAN.Concentrator;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Shared;
using Microsoft.Extensions.Logging;
Expand All @@ -30,6 +32,7 @@ public class LoRaWANConcentratorsControllerTest
private Mock<IRouterConfigManager> mockRouterConfigManager;
private Mock<IConcentratorTwinMapper> mockConcentratorTwinMapper;
private Mock<ConfigHandler> mockConfigHandler;
private Mock<IUrlHelper> mockUrlHelper;

[SetUp]
public void SetUp()
Expand All @@ -40,6 +43,7 @@ public void SetUp()
this.mockRouterConfigManager = this.mockRepository.Create<IRouterConfigManager>();
this.mockConcentratorTwinMapper = this.mockRepository.Create<IConcentratorTwinMapper>();
this.mockConfigHandler = this.mockRepository.Create<ConfigHandler>();
this.mockUrlHelper = this.mockRepository.Create<IUrlHelper>();
}

private LoRaWANConcentratorsController CreateLoRaWANConcentratorsController()
Expand All @@ -48,7 +52,10 @@ private LoRaWANConcentratorsController CreateLoRaWANConcentratorsController()
this.mockLogger.Object,
this.mockDeviceService.Object,
this.mockRouterConfigManager.Object,
this.mockConcentratorTwinMapper.Object);
this.mockConcentratorTwinMapper.Object)
{
Url = this.mockUrlHelper.Object
}; ;
}

[Test]
Expand All @@ -61,20 +68,33 @@ public async Task GetAllDeviceConcentratorWithDeviceTypeShouldReturnNotEmptyList
twinCollection["deviceType"] = "LoRa Concentrator";

_ = this.mockDeviceService.Setup(c => c.GetAllDevice(
It.IsAny<string>(),
It.Is<string>(x => x == "LoRa Concentrator"),
It.Is<string>(x => string.IsNullOrEmpty(x))))
.ReturnsAsync(Enumerable.Range(0, 100).Select(x => new Twin
It.Is<string>(x => string.IsNullOrEmpty(x)),
It.IsAny<string>(),
It.IsAny<bool?>(),
It.IsAny<bool?>(),
It.IsAny<Dictionary<string, string>>(),
It.IsAny<int>()))
.ReturnsAsync(new Shared.PaginationResult<Twin>
{
DeviceId = FormattableString.Invariant($"{x}"),
Tags = twinCollection
}));
Items = Enumerable.Range(0, 100).Select(x => new Twin
{
DeviceId = FormattableString.Invariant($"{x}"),
Tags = twinCollection
}),
NextPage = Guid.NewGuid().ToString()
});

_ = this.mockConcentratorTwinMapper.Setup(c => c.CreateDeviceDetails(It.IsAny<Twin>()))
.Returns<Twin>(x => new Concentrator
{
DeviceId = x.DeviceId
});

_ = this.mockUrlHelper.Setup(c => c.RouteUrl(It.IsAny<UrlRouteContext>()))
.Returns(Guid.NewGuid().ToString());

// Act
var response = await concentratorsController.GetAllDeviceConcentrator().ConfigureAwait(false);

Expand All @@ -86,10 +106,10 @@ public async Task GetAllDeviceConcentratorWithDeviceTypeShouldReturnNotEmptyList
Assert.IsNotNull(okObjectResult);
Assert.AreEqual(200, okObjectResult.StatusCode);
Assert.IsNotNull(okObjectResult.Value);
var deviceList = okObjectResult.Value as IEnumerable<Concentrator>;
var deviceList = okObjectResult.Value as PaginationResult<Concentrator>;

Assert.IsNotNull(deviceList);
Assert.AreEqual(count, deviceList.Count());
Assert.AreEqual(count, deviceList.Items.Count());

this.mockRepository.VerifyAll();
}
Expand All @@ -101,12 +121,18 @@ public async Task GetAllDeviceConcentratorWithNoDeviceTypeShouldReturnEmptyList(
var concentratorsController = CreateLoRaWANConcentratorsController();

_ = this.mockDeviceService.Setup(c => c.GetAllDevice(
It.IsAny<string>(),
It.Is<string>(x => x == "LoRa Concentrator"),
It.Is<string>(x => string.IsNullOrEmpty(x))))
.ReturnsAsync(Enumerable.Range(0, 0).Select(x => new Twin
It.Is<string>(x => string.IsNullOrEmpty(x)),
It.IsAny<string>(),
It.IsAny<bool?>(),
It.IsAny<bool?>(),
It.IsAny<Dictionary<string, string>>(),
It.IsAny<int>()))
.ReturnsAsync(new Shared.PaginationResult<Twin>
{
DeviceId = FormattableString.Invariant($"{x}")
}));
Items = Enumerable.Range(0, 0).Select(x => new Twin(FormattableString.Invariant($"{x}")))
});

// Act
var response = await concentratorsController.GetAllDeviceConcentrator().ConfigureAwait(false);
Expand Down
Loading

0 comments on commit 9055acd

Please sign in to comment.