Skip to content

Commit

Permalink
updates based on feedback from healthcare pageable implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
maririos committed Feb 4, 2021
1 parent 7b839f6 commit 7317feb
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 25 deletions.
56 changes: 39 additions & 17 deletions sdk/textanalytics/Azure.AI.TextAnalytics/src/AnalyzeOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,42 @@ public class AnalyzeOperation : PageableOperation<AnalyzeOperationResult>
/// </remarks>
public override AsyncPageable<AnalyzeOperationResult> Value => GetValuesAsync();

/// <summary><c>true</c> if the long-running operation has completed. Otherwise, <c>false</c>.</summary>
/// <summary>
/// <c>true</c> if the long-running operation has completed. Otherwise, <c>false</c>.
/// </summary>
private bool _hasCompleted;

/// <summary>
/// Returns true if the long-running operation completed.
/// </summary>
public override bool HasCompleted => _hasCompleted;

/// <summary>
/// If the operation has an exception, this property saves its information.
/// </summary>
private RequestFailedException _requestFailedException;

/// <summary>The last HTTP response received from the server. <c>null</c> until the first response is received.</summary>
/// <summary>
/// The last HTTP response received from the server. <c>null</c> until the first response is received.
/// </summary>
private Response _response;

/// <summary>The result of the long-running operation. <c>null</c> until result is received on status update.</summary>
/// <summary>
/// The result of the long-running operation. <c>null</c> until result is received on status update.
/// </summary>
private Page<AnalyzeOperationResult> _firstPage;

/// <summary>
/// Represents the desire of the user to request statistics.
/// This is used in every GET request.
/// </summary>
private bool? _showStats { get; }

/// <summary>
/// Provides the api version to use when doing pagination.
/// </summary>
private readonly string _apiVersion;

/// <summary>
/// Returns true if the long-running operation completed successfully and has produced final result (accessible by Value property).
/// </summary>
Expand All @@ -82,13 +100,15 @@ public AnalyzeOperation(string operationId, TextAnalyticsClient client)
/// </summary>
/// <param name="serviceClient">The client for communicating with the Form Recognizer Azure Cognitive Service through its REST API.</param>
/// <param name="diagnostics">The client diagnostics for exception creation in case of failure.</param>
/// <param name="apiversion">The specific api version to use.</param>
/// <param name="operationLocation">The address of the long-running operation. It can be obtained from the response headers upon starting the operation.</param>
/// <param name="idToIndexMap"></param>
/// <param name="showStats"></param>
internal AnalyzeOperation(TextAnalyticsRestClient serviceClient, ClientDiagnostics diagnostics, string operationLocation, IDictionary<string, int> idToIndexMap, bool? showStats = default)
internal AnalyzeOperation(TextAnalyticsRestClient serviceClient, ClientDiagnostics diagnostics, string apiversion, string operationLocation, IDictionary<string, int> idToIndexMap, bool? showStats = default)
{
_serviceClient = serviceClient;
_diagnostics = diagnostics;
_apiVersion = apiversion;
_idToIndexMap = idToIndexMap;
_showStats = showStats;

Expand Down Expand Up @@ -179,7 +199,7 @@ private async ValueTask<Response> UpdateStatusAsync(bool async, CancellationToke

if (update.Value.Status == TextAnalyticsOperationStatus.Succeeded)
{
// we need to first assign a vaue and then mark the operation as completed to avoid race conditions
// we need to first assign a value and then mark the operation as completed to avoid race conditions
var nextLink = update.Value.NextLink;
var value = Transforms.ConvertToAnalyzeOperationResult(update.Value, _idToIndexMap);
_firstPage = Page.FromValues(new List<AnalyzeOperationResult>() { value }, nextLink, _response);
Expand All @@ -203,24 +223,21 @@ private async ValueTask<Response> UpdateStatusAsync(bool async, CancellationToke
}

/// <summary>
/// Gets the final result of the long-running operation in an asynchronous way.
/// Gets the final result of the long-running operation asynchronously.
/// </summary>
/// <remarks>
/// Operation must complete successfully (HasValue is true) for it to provide values.
/// </remarks>
public override AsyncPageable<AnalyzeOperationResult> GetValuesAsync()
{
if (!HasCompleted)
throw new InvalidOperationException("The operation has not completed yet.");
if (HasCompleted && !HasValue)
throw _requestFailedException;
ValidateOperationStatus();

async Task<Page<AnalyzeOperationResult>> NextPageFunc(string nextLink, int? pageSizeHint)
{
//diagnostics scope?
try
{
Response<AnalyzeJobState> jobState = await _serviceClient.AnalyzeStatusNextPageAsync(nextLink, _showStats).ConfigureAwait(false);
Response<AnalyzeJobState> jobState = await _serviceClient.AnalyzeStatusNextPageAsync(_apiVersion, nextLink, _showStats).ConfigureAwait(false);

AnalyzeOperationResult result = Transforms.ConvertToAnalyzeOperationResult(jobState.Value, _idToIndexMap);
return Page.FromValues(new List<AnalyzeOperationResult>() { result }, jobState.Value.NextLink, jobState.GetRawResponse());
Expand All @@ -235,24 +252,21 @@ async Task<Page<AnalyzeOperationResult>> NextPageFunc(string nextLink, int? page
}

/// <summary>
/// Gets the final result of the long-running operation in an asynchronous way.
/// Gets the final result of the long-running operation synchronously.
/// </summary>
/// <remarks>
/// Operation must complete successfully (HasValue is true) for it to provide values.
/// </remarks>
public override Pageable<AnalyzeOperationResult> GetValues()
{
if (!HasCompleted)
throw new InvalidOperationException("The operation has not completed yet.");
if (HasCompleted && !HasValue)
throw _requestFailedException;
ValidateOperationStatus();

Page<AnalyzeOperationResult> NextPageFunc(string nextLink, int? pageSizeHint)
{
//diagnostics scope?
try
{
Response<AnalyzeJobState> jobState = _serviceClient.AnalyzeStatusNextPage(nextLink, _showStats);
Response<AnalyzeJobState> jobState = _serviceClient.AnalyzeStatusNextPage(_apiVersion, nextLink, _showStats);

AnalyzeOperationResult result = Transforms.ConvertToAnalyzeOperationResult(jobState.Value, _idToIndexMap);
return Page.FromValues(new List<AnalyzeOperationResult>() { result }, jobState.Value.NextLink, jobState.GetRawResponse());
Expand All @@ -265,5 +279,13 @@ Page<AnalyzeOperationResult> NextPageFunc(string nextLink, int? pageSizeHint)

return PageableHelpers.CreateEnumerable(_ => _firstPage, NextPageFunc);
}

private void ValidateOperationStatus()
{
if (!HasCompleted)
throw new InvalidOperationException("The operation has not completed yet.");
if (!HasValue)
throw _requestFailedException;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2413,7 +2413,7 @@ private AnalyzeOperation StartAnalyzeOperationBatch(MultiLanguageBatchInput batc

IDictionary<string, int> idToIndexMap = CreateIdToIndexMap(batchInput.Documents);

return new AnalyzeOperation(_serviceRestClient, _clientDiagnostics, location, idToIndexMap, options.IncludeStatistics);
return new AnalyzeOperation(_serviceRestClient, _clientDiagnostics, _options.GetVersionString(), location, idToIndexMap, options.IncludeStatistics);
}
catch (Exception e)
{
Expand Down Expand Up @@ -2451,7 +2451,7 @@ private async Task<AnalyzeOperation> StartAnalyzeOperationBatchAsync(MultiLangua

IDictionary<string, int> idToIndexMap = CreateIdToIndexMap(batchInput.Documents);

return new AnalyzeOperation(_serviceRestClient, _clientDiagnostics, location, idToIndexMap, options.IncludeStatistics);
return new AnalyzeOperation(_serviceRestClient, _clientDiagnostics, _options.GetVersionString(), location, idToIndexMap, options.IncludeStatistics);
}
catch (Exception e)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,15 @@ public Response<HealthcareJobState> HealthStatusNextPage(string apiversion, stri
}
}

internal HttpMessage CreateAnalyzeStatusNextPageRequest(string nextLink, bool? showStats)
internal HttpMessage CreateAnalyzeStatusNextPageRequest(string apiversion, string nextLink, bool? showStats)
{
var message = _pipeline.CreateMessage();
var request = message.Request;
request.Method = RequestMethod.Get;
var uri = new RawRequestUriBuilder();
uri.AppendRaw(endpoint, false);
uri.AppendRaw("/text/analytics/v3.1-preview.3", false);
uri.AppendRaw("/text/analytics/", false);
uri.AppendRaw(apiversion, false);
uri.AppendPath("/analyze/jobs/", false);
uri.AppendRawNextLink(nextLink, false);
if (showStats != null)
Expand All @@ -107,18 +108,19 @@ internal HttpMessage CreateAnalyzeStatusNextPageRequest(string nextLink, bool? s
}

/// <summary> Get the status of an analysis job. A job may consist of one or more tasks. Once all tasks are completed, the job will transition to the completed state and results will be available for each task. </summary>
/// <param name="apiVersion"> The specific api version to use. </param>
/// <param name="nextLink"> The URL to the next page of results. </param>
/// <param name="showStats"> (Optional) if set to true, response will contain request and document level statistics. </param>
/// <param name="cancellationToken"> The cancellation token to use. </param>
/// <exception cref="ArgumentNullException"> <paramref name="nextLink"/> is null. </exception>
public async Task<Response<AnalyzeJobState>> AnalyzeStatusNextPageAsync(string nextLink, bool? showStats = null, CancellationToken cancellationToken = default)
public async Task<Response<AnalyzeJobState>> AnalyzeStatusNextPageAsync(string apiVersion, string nextLink, bool? showStats = null, CancellationToken cancellationToken = default)
{
if (nextLink == null)
{
throw new ArgumentNullException(nameof(nextLink));
}

using var message = CreateAnalyzeStatusNextPageRequest(nextLink, showStats);
using var message = CreateAnalyzeStatusNextPageRequest(apiVersion, nextLink, showStats);
await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false);
switch (message.Response.Status)
{
Expand All @@ -135,18 +137,19 @@ public async Task<Response<AnalyzeJobState>> AnalyzeStatusNextPageAsync(string n
}

/// <summary> Get the status of an analysis job. A job may consist of one or more tasks. Once all tasks are completed, the job will transition to the completed state and results will be available for each task. </summary>
/// <param name="apiVersion"> The specific api version to use. </param>
/// <param name="nextLink"> The URL to the next page of results. </param>
/// <param name="showStats"> (Optional) if set to true, response will contain request and document level statistics. </param>
/// <param name="cancellationToken"> The cancellation token to use. </param>
/// <exception cref="ArgumentNullException"> <paramref name="nextLink"/> is null. </exception>
public Response<AnalyzeJobState> AnalyzeStatusNextPage(string nextLink, bool? showStats = null, CancellationToken cancellationToken = default)
public Response<AnalyzeJobState> AnalyzeStatusNextPage(string apiVersion, string nextLink, bool? showStats = null, CancellationToken cancellationToken = default)
{
if (nextLink == null)
{
throw new ArgumentNullException(nameof(nextLink));
}

using var message = CreateAnalyzeStatusNextPageRequest(nextLink, showStats);
using var message = CreateAnalyzeStatusNextPageRequest(apiVersion, nextLink, showStats);
_pipeline.Send(message, cancellationToken);
switch (message.Response.Status)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
Expand Down Expand Up @@ -257,6 +258,17 @@ public async Task AnalyzeOperationWithPagination()

AnalyzeOperation operation = await client.StartAnalyzeOperationBatchAsync(documents, operationOptions);

Assert.IsFalse(operation.HasCompleted);
Assert.IsFalse(operation.HasValue);

Assert.ThrowsAsync<InvalidOperationException>(async () => await Task.Run(() => operation.Value));
Assert.Throws<InvalidOperationException>(() => operation.GetValues());

await operation.WaitForCompletionAsync(PollingInterval);

Assert.IsTrue(operation.HasCompleted);
Assert.IsTrue(operation.HasValue);

await operation.WaitForCompletionAsync(PollingInterval);

// try async
Expand Down

0 comments on commit 7317feb

Please sign in to comment.