< Summary

Class:Azure.AI.FormRecognizer.Models.RecognizeCustomFormsOperation
Assembly:Azure.AI.FormRecognizer
File(s):C:\Git\azure-sdk-for-net\sdk\formrecognizer\Azure.AI.FormRecognizer\src\RecognizeCustomFormsOperation.cs
Covered lines:59
Uncovered lines:4
Coverable lines:63
Total lines:265
Line coverage:93.6% (59 of 63)
Covered branches:18
Total branches:22
Branch coverage:81.8% (18 of 22)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
get_Id()-100%100%
get_Value()-66.67%75%
get_HasCompleted()-100%100%
get_HasValue()-100%100%
GetRawResponse()-100%100%
WaitForCompletionAsync(...)-0%100%
WaitForCompletionAsync(...)-100%100%
.ctor(...)-100%100%
.ctor(...)-90%50%
UpdateStatus(...)-0%100%
UpdateStatusAsync()-100%100%
UpdateStatusAsync()-100%87.5%
ConvertToRecognizedForms(...)-100%75%
ConvertUnsupervisedResult(...)-100%100%
ConvertSupervisedResult(...)-100%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\formrecognizer\Azure.AI.FormRecognizer\src\RecognizeCustomFormsOperation.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.Linq;
 7using System.Threading;
 8using System.Threading.Tasks;
 9using Azure.Core;
 10using Azure.Core.Pipeline;
 11
 12namespace Azure.AI.FormRecognizer.Models
 13{
 14    /// <summary>
 15    /// Tracks the status of a long-running operation for recognizing fields and other content from forms by using custo
 16    /// trained models.
 17    /// </summary>
 18    public class RecognizeCustomFormsOperation : Operation<RecognizedFormCollection>
 19    {
 20        /// <summary>Provides communication with the Form Recognizer Azure Cognitive Service through its REST API.</summ
 21        private readonly ServiceRestClient _serviceClient;
 22
 23        /// <summary>Provides tools for exception creation in case of failure.</summary>
 24        private readonly ClientDiagnostics _diagnostics;
 25
 26        /// <summary>The last HTTP response received from the server. <c>null</c> until the first response is received.<
 27        private Response _response;
 28
 29        /// <summary>The result of the long-running operation. <c>null</c> until result is received on status update.</s
 30        private RecognizedFormCollection _value;
 31
 32        /// <summary><c>true</c> if the long-running operation has completed. Otherwise, <c>false</c>.</summary>
 33        private bool _hasCompleted;
 34
 35        /// <summary>The ID of the model to use for recognizing form values.</summary>
 36        private readonly string _modelId;
 37
 38        /// <summary>An ID representing the operation that can be used along with <see cref="_modelId"/> to poll for the
 39        private readonly string _resultId;
 40
 41        private RequestFailedException _requestFailedException;
 42
 43        /// <summary>
 44        /// Gets an ID representing the operation that can be used to poll for the status
 45        /// of the long-running operation.
 46        /// </summary>
 447        public override string Id { get; }
 48
 49        /// <summary>
 50        /// Final result of the long-running operation.
 51        /// </summary>
 52        /// <remarks>
 53        /// This property can be accessed only after the operation completes successfully (HasValue is true).
 54        /// </remarks>
 55        public override RecognizedFormCollection Value
 56        {
 57            get
 58            {
 14059                if (HasCompleted && !HasValue)
 60#pragma warning disable CA1065 // Do not raise exceptions in unexpected locations
 061                    throw _requestFailedException;
 62#pragma warning restore CA1065 // Do not raise exceptions in unexpected locations
 63                else
 14064                    return OperationHelpers.GetValue(ref _value);
 65            }
 66        }
 67
 68        /// <summary>
 69        /// Returns true if the long-running operation completed.
 70        /// </summary>
 87071        public override bool HasCompleted => _hasCompleted;
 72
 73        /// <summary>
 74        /// Returns true if the long-running operation completed successfully and has produced final result (accessible 
 75        /// </summary>
 18876        public override bool HasValue => _value != null;
 77
 78        /// <summary>
 79        /// The last HTTP response received from the server.
 80        /// </summary>
 81        /// <remarks>
 82        /// The last response returned from the server during the lifecycle of this instance.
 83        /// An instance of <see cref="RecognizeCustomFormsOperation"/> sends requests to a server in UpdateStatusAsync, 
 84        /// Responses from these requests can be accessed using GetRawResponse.
 85        /// </remarks>
 79486        public override Response GetRawResponse() => _response;
 87
 88        /// <summary>
 89        /// Periodically calls the server till the long-running operation completes.
 90        /// </summary>
 91        /// <param name="cancellationToken">A <see cref="CancellationToken"/> used for the periodical service calls.</pa
 92        /// <returns>The last HTTP response received from the server.</returns>
 93        /// <remarks>
 94        /// This method will periodically call UpdateStatusAsync till HasCompleted is true, then return the final result
 95        /// </remarks>
 96        public override ValueTask<Response<RecognizedFormCollection>> WaitForCompletionAsync(CancellationToken cancellat
 097            this.DefaultWaitForCompletionAsync(cancellationToken);
 98
 99        /// <summary>
 100        /// Periodically calls the server till the long-running operation completes.
 101        /// </summary>
 102        /// <param name="pollingInterval">
 103        /// The interval between status requests to the server.
 104        /// The interval can change based on information returned from the server.
 105        /// For example, the server might communicate to the client that there is not reason to poll for status change s
 106        /// </param>
 107        /// <param name="cancellationToken">A <see cref="CancellationToken"/> used for the periodical service calls.</pa
 108        /// <returns>The last HTTP response received from the server.</returns>
 109        /// <remarks>
 110        /// This method will periodically call UpdateStatusAsync till HasCompleted is true, then return the final result
 111        /// </remarks>
 112        public override ValueTask<Response<RecognizedFormCollection>> WaitForCompletionAsync(TimeSpan pollingInterval, C
 84113            this.DefaultWaitForCompletionAsync(pollingInterval, cancellationToken);
 114
 115        /// <summary>
 116        /// </summary>
 117        /// <param name="operations"></param>
 118        /// <param name="diagnostics"></param>
 119        /// <param name="operationLocation"></param>
 100120        internal RecognizeCustomFormsOperation(ServiceRestClient operations, ClientDiagnostics diagnostics, string opera
 121        {
 100122            _serviceClient = operations;
 100123            _diagnostics = diagnostics;
 124
 125            // TODO: Use regex to parse ids.
 126            // https://github.com/Azure/azure-sdk-for-net/issues/11505
 127
 128            // TODO: Add validation here (should we store _resuldId and _modelId as GUIDs?)
 129            // https://github.com/Azure/azure-sdk-for-net/issues/10385
 130
 100131            string[] substrs = operationLocation.Split('/');
 132
 100133            _resultId = substrs[substrs.Length - 1];
 100134            _modelId = substrs[substrs.Length - 3];
 135
 100136            Id = string.Join("/", substrs, substrs.Length - 3, 3);
 100137        }
 138
 139        /// <summary>
 140        /// Initializes a new instance of the <see cref="RecognizeCustomFormsOperation"/> class.
 141        /// </summary>
 142        /// <param name="operationId">The ID of this operation.</param>
 143        /// <param name="client">The client used to check for completion.</param>
 8144        public RecognizeCustomFormsOperation(string operationId, FormRecognizerClient client)
 145        {
 8146            _serviceClient = client.ServiceClient;
 8147            _diagnostics = client.Diagnostics;
 148
 149            // TODO: Use regex to parse ids.
 150            // https://github.com/Azure/azure-sdk-for-net/issues/11505
 151
 152            // TODO: Add validation here (should we store _resuldId and _modelId as GUIDs?)
 153            // https://github.com/Azure/azure-sdk-for-net/issues/10385
 154
 8155            string[] substrs = operationId.Split('/');
 156
 8157            if (substrs.Length < 3)
 158            {
 0159                throw new ArgumentException($"Invalid '{nameof(operationId)}'. It should be formatted as: '{{modelId}}/a
 160            }
 161
 8162            _resultId = substrs.Last();
 8163            _modelId = substrs.First();
 164
 8165            Id = operationId;
 8166        }
 167
 168        /// <summary>
 169        /// Calls the server to get updated status of the long-running operation.
 170        /// </summary>
 171        /// <param name="cancellationToken">A <see cref="CancellationToken"/> used for the service call.</param>
 172        /// <returns>The HTTP response received from the server.</returns>
 173        /// <remarks>
 174        /// This operation will update the value returned from GetRawResponse and might update HasCompleted, HasValue, a
 175        /// </remarks>
 176        public override Response UpdateStatus(CancellationToken cancellationToken = default) =>
 0177            UpdateStatusAsync(false, cancellationToken).EnsureCompleted();
 178
 179        /// <summary>
 180        /// Calls the server to get updated status of the long-running operation.
 181        /// </summary>
 182        /// <param name="cancellationToken">A <see cref="CancellationToken"/> used for the service call.</param>
 183        /// <returns>The HTTP response received from the server.</returns>
 184        /// <remarks>
 185        /// This operation will update the value returned from GetRawResponse and might update HasCompleted, HasValue, a
 186        /// </remarks>
 187        public override async ValueTask<Response> UpdateStatusAsync(CancellationToken cancellationToken = default) =>
 734188            await UpdateStatusAsync(true, cancellationToken).ConfigureAwait(false);
 189
 190        /// <summary>
 191        /// Calls the server to get updated status of the long-running operation.
 192        /// </summary>
 193        /// <param name="async">When <c>true</c>, the method will be executed asynchronously; otherwise, it will execute
 194        /// <param name="cancellationToken">A <see cref="CancellationToken"/> used for the service call.</param>
 195        /// <returns>The HTTP response received from the server.</returns>
 196        private async ValueTask<Response> UpdateStatusAsync(bool async, CancellationToken cancellationToken)
 197        {
 734198            if (!_hasCompleted)
 199            {
 734200                using DiagnosticScope scope = _diagnostics.CreateScope($"{nameof(RecognizeCustomFormsOperation)}.{nameof
 734201                scope.Start();
 202
 203                try
 204                {
 734205                    Response<AnalyzeOperationResult> update = async
 734206                        ? await _serviceClient.GetAnalyzeFormResultAsync(new Guid(_modelId), new Guid(_resultId), cancel
 734207                        : _serviceClient.GetAnalyzeFormResult(new Guid(_modelId), new Guid(_resultId), cancellationToken
 208
 209                    // TODO: Add reasonable null checks.
 210
 734211                    _response = update.GetRawResponse();
 212
 734213                    if (update.Value.Status == OperationStatus.Succeeded)
 214                    {
 215                        // We need to first assign a value and then mark the operation as completed to avoid a race cond
 72216                        _value = ConvertToRecognizedForms(update.Value.AnalyzeResult);
 72217                        _hasCompleted = true;
 218                    }
 662219                    else if (update.Value.Status == OperationStatus.Failed)
 220                    {
 12221                        _requestFailedException = await ClientCommon
 12222                            .CreateExceptionForFailedOperationAsync(async, _diagnostics, _response, update.Value.Analyze
 12223                            .ConfigureAwait(false);
 12224                        _hasCompleted = true;
 12225                        throw _requestFailedException;
 226                    }
 722227                }
 12228                catch (Exception e)
 229                {
 12230                    scope.Failed(e);
 12231                    throw;
 232                }
 722233            }
 234
 722235            return GetRawResponse();
 722236        }
 237
 238        private static RecognizedFormCollection ConvertToRecognizedForms(AnalyzeResult analyzeResult)
 239        {
 72240            return analyzeResult.DocumentResults?.Count == 0 ?
 72241                ConvertUnsupervisedResult(analyzeResult) :
 72242                ConvertSupervisedResult(analyzeResult);
 243        }
 244
 245        private static RecognizedFormCollection ConvertUnsupervisedResult(AnalyzeResult analyzeResult)
 246        {
 32247            List<RecognizedForm> forms = new List<RecognizedForm>();
 152248            for (int pageIndex = 0; pageIndex < analyzeResult.PageResults.Count; pageIndex++)
 249            {
 44250                forms.Add(new RecognizedForm(analyzeResult.PageResults[pageIndex], analyzeResult.ReadResults, pageIndex)
 251            }
 32252            return new RecognizedFormCollection(forms);
 253        }
 254
 255        private static RecognizedFormCollection ConvertSupervisedResult(AnalyzeResult analyzeResult)
 256        {
 40257            List<RecognizedForm> forms = new List<RecognizedForm>();
 160258            foreach (var documentResult in analyzeResult.DocumentResults)
 259            {
 40260                forms.Add(new RecognizedForm(documentResult, analyzeResult.PageResults, analyzeResult.ReadResults));
 261            }
 40262            return new RecognizedFormCollection(forms);
 263        }
 264    }
 265}