Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP:Make DPS Query testable in Consuming Application #3461

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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 @@ -37,7 +37,7 @@ public async Task ProvisioningServiceClient_QueryInvalidServiceCertificateHttp_F
{
using var provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString(
TestConfiguration.Provisioning.ConnectionStringInvalidServiceCertificate);
Query q = provisioningServiceClient.CreateEnrollmentGroupQuery(
Microsoft.Azure.Devices.Provisioning.Service.IQuery q = provisioningServiceClient.CreateEnrollmentGroupQuery(
new QuerySpecification("SELECT * FROM enrollmentGroups"));

ProvisioningServiceClientTransportException exception = await Assert.ThrowsExceptionAsync<ProvisioningServiceClientTransportException>(
Expand Down
5 changes: 2 additions & 3 deletions e2e/test/provisioning/ProvisioningServiceClientE2ETests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.Azure.Devices.Client;
using Microsoft.Azure.Devices.E2ETests.Helpers;
using Microsoft.Azure.Devices.Provisioning.Security.Samples;
using Microsoft.Azure.Devices.Provisioning.Service;
Expand All @@ -26,7 +25,7 @@ public class ProvisioningServiceClientE2ETests : E2EMsTestBase
private static readonly string s_devicePrefix = $"{nameof(ProvisioningServiceClientE2ETests)}_";

private static readonly HashSet<Type> s_retryableExceptions = new HashSet<Type> { typeof(ProvisioningServiceClientHttpException) };
private static readonly IRetryPolicy s_provisioningServiceRetryPolicy = new ProvisioningServiceRetryPolicy();
private static readonly Microsoft.Azure.Devices.Client.IRetryPolicy s_provisioningServiceRetryPolicy = new ProvisioningServiceRetryPolicy();

public enum EnrollmentType
{
Expand Down Expand Up @@ -273,7 +272,7 @@ private async Task ProvisioningServiceClient_IndividualEnrollments_Query_Ok(stri
{
using ProvisioningServiceClient provisioningServiceClient = CreateProvisioningService(proxyServerAddress);
var querySpecification = new QuerySpecification("SELECT * FROM enrollments");
using Query query = provisioningServiceClient.CreateIndividualEnrollmentQuery(querySpecification);
using Microsoft.Azure.Devices.Provisioning.Service.IQuery query = provisioningServiceClient.CreateIndividualEnrollmentQuery(querySpecification);
while (query.HasNext())
{
QueryResult queryResult = await query.NextAsync().ConfigureAwait(false);
Expand Down
9 changes: 8 additions & 1 deletion provisioning/service/src/Config/QueryResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,19 @@ public class QueryResult
/// </description></item>
/// </list>
/// </remarks>
public IEnumerable<object> Items { get; private set; }
public virtual IEnumerable<object> Items { get; private set; }

/// <summary>
/// Getter for the query result continuationToken.
/// </summary>
public string ContinuationToken { get; private set; }
/// <summary>
/// For Mocking
/// </summary>
public QueryResult()
{

}

/// <summary>
/// CONSTRUCTOR
Expand Down
98 changes: 98 additions & 0 deletions provisioning/service/src/Contract/IQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Devices.Common;

namespace Microsoft.Azure.Devices.Provisioning.Service
{


/// <summary>
/// The query iterator.
/// </summary>
/// <remarks>
/// The <see cref="Query"/> iterator is the result of the query factory for
/// <list type="bullet">
/// <item>
/// <description>
/// <see cref="ProvisioningServiceClient.CreateIndividualEnrollmentQuery(QuerySpecification, int)">IndividualEnrollment</see>
/// </description>
/// </item>
/// <item>
/// <description>
/// <see cref="ProvisioningServiceClient.CreateEnrollmentGroupQuery(QuerySpecification, int)">EnrollmentGroup</see>
/// </description>
/// </item>
/// <item>
/// <description>
/// <see cref="ProvisioningServiceClient.CreateEnrollmentGroupRegistrationStateQuery(QuerySpecification, String, int)">RegistrationStatus</see>
/// </description>
/// </item>
/// </list>
/// On all cases, the <see cref="QuerySpecification"/> contains a SQL query that must follow the
/// Query Language for the Device Provisioning Service.
///
/// Optionally, an <c>Integer</c> with the <b>page size</b>, can determine the maximum number of the items in the
/// <see cref="QueryResult"/> returned by the <see cref="NextAsync()"/>. It must be any positive integer, and if it
/// contains 0, the Device Provisioning Service will ignore it and use a standard page size.
///
/// You can use this Object as a standard iterator, just using the <c>HasNext</c> and <c>NextAsync</c> in a
/// <c>while</c> loop, up to the point where the <c>HasNext</c> contains <c>false</c>. But, keep
/// in mind that the <see cref="QueryResult"/> can contain a empty list, even if the <c>HasNext</c> contained
/// <c>true</c>. For example, image that you have 10 IndividualEnrollment in the Device Provisioning Service
/// and you created new query with the <c>PageSize</c> equals 5. In the first iteration, <c>HasNext</c>
/// will contains <c>true</c>, and the first <c>NextAsync</c> will return a <c>QueryResult</c> with
/// 5 items. After, your code will check the <c>HasNext</c>, which will contains <c>true</c> again. Now,
/// before you get the next page, somebody deletes all the IndividualEnrollment. What happened, when you call the
/// <c>NextAsync</c>, it will return a valid <c>QueryResult</c>, but the <see cref="QueryResult.Items"/>
/// will contain an empty list.
///
/// Besides the <c>Items</c>, the <c>QueryResult</c> contains the <see cref="QueryResult.ContinuationToken"/>.
/// You can also store a query context (QuerySpecification + ContinuationToken) and restart it in the future, from
/// the point where you stopped. Just recreating the query with the same <see cref="QuerySpecification"/> and calling
/// the <see cref="NextAsync(string)"/> passing the stored <c>ContinuationToken</c>.
/// </remarks>
public interface IQuery :IDisposable
{
/// <summary>
/// The number of items in the current page.
/// </summary>
public int PageSize { get; set; }

/// <summary>
/// The token to retrieve the next page.
/// </summary>
public string ContinuationToken { get; set; }

/// <summary>
/// Getter for has next.
/// </summary>
/// <remarks>
/// Contains <c>true</c> if the query is not finished in the Device Provisioning Service, and another
/// iteration with <see cref="NextAsync()"/> may return more items. Call <see cref="NextAsync()"/> after
/// a <c>true</c> <c>HasNext</c> will result in a <see cref="QueryResult"/> that can or
/// cannot contains elements. But call <see cref="NextAsync()"/> after a <c>false</c> <c>HasNext</c>
/// will result in a exception.
/// </remarks>
public bool HasNext();
/// <summary>
/// Return the next page of result for the query using a new continuationToken.
/// </summary>
/// <param name="continuationToken">the <c>string</c> with the previous continuationToken. It cannot be <c>null</c> or empty.</param>
/// <returns>The <see cref="QueryResult"/> with the next page of items for the query.</returns>
/// <exception cref="IndexOutOfRangeException">if the query does no have more pages to return.</exception>
public Task<QueryResult> NextAsync(string continuationToken);
/// <summary>
/// Getter for has next.
/// </summary>
/// <remarks>
/// Contains <c>true</c> if the query is not finished in the Device Provisioning Service, and another
/// iteration with <see cref="NextAsync()"/> may return more items. Call <see cref="NextAsync()"/> after
/// a <c>true</c> <c>HasNext</c> will result in a <see cref="QueryResult"/> that can or
/// cannot contains elements. But call <see cref="NextAsync()"/> after a <c>false</c> <c>HasNext</c>
/// will result in a exception.
/// </remarks>
public Task<QueryResult> NextAsync();
}

}
Loading