diff --git a/sdk/textanalytics/Azure.AI.TextAnalytics/src/AnalyzeOperation.cs b/sdk/textanalytics/Azure.AI.TextAnalytics/src/AnalyzeOperation.cs index 6460844f3767b..23d810bfcc4df 100644 --- a/sdk/textanalytics/Azure.AI.TextAnalytics/src/AnalyzeOperation.cs +++ b/sdk/textanalytics/Azure.AI.TextAnalytics/src/AnalyzeOperation.cs @@ -40,7 +40,9 @@ public class AnalyzeOperation : PageableOperation /// public override AsyncPageable Value => GetValuesAsync(); - /// true if the long-running operation has completed. Otherwise, false. + /// + /// true if the long-running operation has completed. Otherwise, false. + /// private bool _hasCompleted; /// @@ -48,16 +50,32 @@ public class AnalyzeOperation : PageableOperation /// public override bool HasCompleted => _hasCompleted; + /// + /// If the operation has an exception, this property saves its information. + /// private RequestFailedException _requestFailedException; - /// The last HTTP response received from the server. null until the first response is received. + /// + /// The last HTTP response received from the server. null until the first response is received. + /// private Response _response; - /// The result of the long-running operation. null until result is received on status update. + /// + /// The result of the long-running operation. null until result is received on status update. + /// private Page _firstPage; + /// + /// Represents the desire of the user to request statistics. + /// This is used in every GET request. + /// private bool? _showStats { get; } + /// + /// Provides the api version to use when doing pagination. + /// + private readonly string _apiVersion; + /// /// Returns true if the long-running operation completed successfully and has produced final result (accessible by Value property). /// @@ -82,13 +100,15 @@ public AnalyzeOperation(string operationId, TextAnalyticsClient client) /// /// The client for communicating with the Form Recognizer Azure Cognitive Service through its REST API. /// The client diagnostics for exception creation in case of failure. + /// The specific api version to use. /// The address of the long-running operation. It can be obtained from the response headers upon starting the operation. /// /// - internal AnalyzeOperation(TextAnalyticsRestClient serviceClient, ClientDiagnostics diagnostics, string operationLocation, IDictionary idToIndexMap, bool? showStats = default) + internal AnalyzeOperation(TextAnalyticsRestClient serviceClient, ClientDiagnostics diagnostics, string apiversion, string operationLocation, IDictionary idToIndexMap, bool? showStats = default) { _serviceClient = serviceClient; _diagnostics = diagnostics; + _apiVersion = apiversion; _idToIndexMap = idToIndexMap; _showStats = showStats; @@ -179,7 +199,7 @@ private async ValueTask 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() { value }, nextLink, _response); @@ -203,24 +223,21 @@ private async ValueTask UpdateStatusAsync(bool async, CancellationToke } /// - /// Gets the final result of the long-running operation in an asynchronous way. + /// Gets the final result of the long-running operation asynchronously. /// /// /// Operation must complete successfully (HasValue is true) for it to provide values. /// public override AsyncPageable GetValuesAsync() { - if (!HasCompleted) - throw new InvalidOperationException("The operation has not completed yet."); - if (HasCompleted && !HasValue) - throw _requestFailedException; + ValidateOperationStatus(); async Task> NextPageFunc(string nextLink, int? pageSizeHint) { //diagnostics scope? try { - Response jobState = await _serviceClient.AnalyzeStatusNextPageAsync(nextLink, _showStats).ConfigureAwait(false); + Response jobState = await _serviceClient.AnalyzeStatusNextPageAsync(_apiVersion, nextLink, _showStats).ConfigureAwait(false); AnalyzeOperationResult result = Transforms.ConvertToAnalyzeOperationResult(jobState.Value, _idToIndexMap); return Page.FromValues(new List() { result }, jobState.Value.NextLink, jobState.GetRawResponse()); @@ -235,24 +252,21 @@ async Task> NextPageFunc(string nextLink, int? page } /// - /// Gets the final result of the long-running operation in an asynchronous way. + /// Gets the final result of the long-running operation synchronously. /// /// /// Operation must complete successfully (HasValue is true) for it to provide values. /// public override Pageable GetValues() { - if (!HasCompleted) - throw new InvalidOperationException("The operation has not completed yet."); - if (HasCompleted && !HasValue) - throw _requestFailedException; + ValidateOperationStatus(); Page NextPageFunc(string nextLink, int? pageSizeHint) { //diagnostics scope? try { - Response jobState = _serviceClient.AnalyzeStatusNextPage(nextLink, _showStats); + Response jobState = _serviceClient.AnalyzeStatusNextPage(_apiVersion, nextLink, _showStats); AnalyzeOperationResult result = Transforms.ConvertToAnalyzeOperationResult(jobState.Value, _idToIndexMap); return Page.FromValues(new List() { result }, jobState.Value.NextLink, jobState.GetRawResponse()); @@ -265,5 +279,13 @@ Page 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; + } } } diff --git a/sdk/textanalytics/Azure.AI.TextAnalytics/src/TextAnalyticsClient.cs b/sdk/textanalytics/Azure.AI.TextAnalytics/src/TextAnalyticsClient.cs index b315fdf49b76b..d156b213884c0 100644 --- a/sdk/textanalytics/Azure.AI.TextAnalytics/src/TextAnalyticsClient.cs +++ b/sdk/textanalytics/Azure.AI.TextAnalytics/src/TextAnalyticsClient.cs @@ -2413,7 +2413,7 @@ private AnalyzeOperation StartAnalyzeOperationBatch(MultiLanguageBatchInput batc IDictionary 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) { @@ -2451,7 +2451,7 @@ private async Task StartAnalyzeOperationBatchAsync(MultiLangua IDictionary 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) { diff --git a/sdk/textanalytics/Azure.AI.TextAnalytics/src/TextAnalyticsRestClient.cs b/sdk/textanalytics/Azure.AI.TextAnalytics/src/TextAnalyticsRestClient.cs index 2fb12801ca9f2..eeb9d862a4d89 100644 --- a/sdk/textanalytics/Azure.AI.TextAnalytics/src/TextAnalyticsRestClient.cs +++ b/sdk/textanalytics/Azure.AI.TextAnalytics/src/TextAnalyticsRestClient.cs @@ -87,14 +87,15 @@ public Response 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) @@ -107,18 +108,19 @@ internal HttpMessage CreateAnalyzeStatusNextPageRequest(string nextLink, bool? s } /// 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. + /// The specific api version to use. /// The URL to the next page of results. /// (Optional) if set to true, response will contain request and document level statistics. /// The cancellation token to use. /// is null. - public async Task> AnalyzeStatusNextPageAsync(string nextLink, bool? showStats = null, CancellationToken cancellationToken = default) + public async Task> 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) { @@ -135,18 +137,19 @@ public async Task> AnalyzeStatusNextPageAsync(string n } /// 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. + /// The specific api version to use. /// The URL to the next page of results. /// (Optional) if set to true, response will contain request and document level statistics. /// The cancellation token to use. /// is null. - public Response AnalyzeStatusNextPage(string nextLink, bool? showStats = null, CancellationToken cancellationToken = default) + public Response 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) { diff --git a/sdk/textanalytics/Azure.AI.TextAnalytics/tests/AnalyzeOperationTests.cs b/sdk/textanalytics/Azure.AI.TextAnalytics/tests/AnalyzeOperationTests.cs index 2bb63669fc456..4e42d44c7c67c 100644 --- a/sdk/textanalytics/Azure.AI.TextAnalytics/tests/AnalyzeOperationTests.cs +++ b/sdk/textanalytics/Azure.AI.TextAnalytics/tests/AnalyzeOperationTests.cs @@ -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; @@ -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(async () => await Task.Run(() => operation.Value)); + Assert.Throws(() => operation.GetValues()); + + await operation.WaitForCompletionAsync(PollingInterval); + + Assert.IsTrue(operation.HasCompleted); + Assert.IsTrue(operation.HasValue); + await operation.WaitForCompletionAsync(PollingInterval); // try async