< Summary

Class:Azure.AI.FormRecognizer.Training.CopyModelOperation
Assembly:Azure.AI.FormRecognizer
File(s):C:\Git\azure-sdk-for-net\sdk\formrecognizer\Azure.AI.FormRecognizer\src\CopyModelOperation.cs
Covered lines:56
Uncovered lines:5
Coverable lines:61
Total lines:257
Line coverage:91.8% (56 of 61)
Covered branches:12
Total branches:16
Branch coverage:75% (12 of 16)

Metrics

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

File(s)

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

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.Linq;
 6using System.Threading;
 7using System.Threading.Tasks;
 8using Azure.AI.FormRecognizer.Models;
 9using Azure.Core;
 10using Azure.Core.Pipeline;
 11
 12namespace Azure.AI.FormRecognizer.Training
 13{
 14    /// <summary>
 15    /// Tracks the status of a long-running operation for copying a custom model into a target Form Recognizer resource.
 16    /// </summary>
 17    public class CopyModelOperation : Operation<CustomFormModelInfo>
 18    {
 19        /// <summary>Provides communication with the Form Recognizer Azure Cognitive Service through its REST API.</summ
 20        private readonly ServiceRestClient _serviceClient;
 21
 22        /// <summary>Provides tools for exception creation in case of failure.</summary>
 23        private readonly ClientDiagnostics _diagnostics;
 24
 25        /// <summary>The last HTTP response received from the server. <c>null</c> until the first response is received.<
 26        private Response _response;
 27
 28        /// <summary>The result of the long-running operation. <c>null</c> until result is received on status update.</s
 29        private CustomFormModelInfo _value;
 30
 31        /// <summary><c>true</c> if the long-running operation has completed. Otherwise, <c>false</c>.</summary>
 32        private bool _hasCompleted;
 33
 34        /// <summary>The ID of the model to use for copy.</summary>
 35        private readonly string _modelId;
 36
 37        /// <summary>The ID of the copied model.</summary>
 38        private readonly string _targetModelId;
 39
 40        /// <summary>An ID representing the operation that can be used along with <see cref="_modelId"/> to poll for the
 41        private readonly string _resultId;
 42
 43        private RequestFailedException _requestFailedException;
 44
 45        /// <summary>
 46        /// Gets an ID representing the operation that can be used to poll for the status
 47        /// of the long-running operation.
 48        /// </summary>
 449        public override string Id { get; }
 50
 51        /// <summary>
 52        /// Final result of the long-running operation.
 53        /// </summary>
 54        /// <remarks>
 55        /// This property can be accessed only after the operation completes successfully (HasValue is true).
 56        /// </remarks>
 57        public override CustomFormModelInfo Value
 58        {
 59            get
 60            {
 1661                if (HasCompleted && !HasValue)
 62#pragma warning disable CA1065 // Do not raise exceptions in unexpected locations
 063                    throw _requestFailedException;
 64#pragma warning restore CA1065 // Do not raise exceptions in unexpected locations
 65                else
 1666                    return OperationHelpers.GetValue(ref _value);
 67            }
 68        }
 69
 70        /// <summary>
 71        /// Returns true if the long-running operation completed.
 72        /// </summary>
 19473        public override bool HasCompleted => _hasCompleted;
 74
 75        /// <summary>
 76        /// Returns true if the long-running operation completed successfully and has produced final result (accessible 
 77        /// </summary>
 2478        public override bool HasValue => _value != null;
 79
 80        /// <summary>
 81        /// Initializes a new instance of the <see cref="CopyModelOperation"/> class.
 82        /// </summary>
 83        /// <param name="operationId">The ID of this operation.</param>
 84        /// <param name="targetModelId">Model ID in the target Form Recognizer resource.</param>
 85        /// <param name="client">The client used to check for completion.</param>
 886        public CopyModelOperation(string operationId, string targetModelId, FormTrainingClient client)
 87        {
 888            _serviceClient = client.ServiceClient;
 889            _diagnostics = client.Diagnostics;
 890            _targetModelId = targetModelId;
 91
 92            // TODO: Use regex to parse ids.
 93            // https://github.com/Azure/azure-sdk-for-net/issues/11505
 94
 95            // TODO: Add validation here (should we store _resuldId and _modelId as GUIDs?)
 96            // https://github.com/Azure/azure-sdk-for-net/issues/10385
 97
 898            string[] substrs = operationId.Split('/');
 99
 8100            if (substrs.Length < 3)
 101            {
 0102                throw new ArgumentException($"Invalid '{nameof(operationId)}'. It should be formatted as: '{{modelId}}/c
 103            }
 104
 8105            _resultId = substrs.Last();
 8106            _modelId = substrs.First();
 107
 8108            Id = operationId;
 8109        }
 110
 111        /// <summary>
 112        /// Initializes a new instance of the <see cref="CopyModelOperation"/> class.
 113        /// </summary>
 114        /// <param name="serviceClient">The client for communicating with the Form Recognizer Azure Cognitive Service th
 115        /// <param name="diagnostics">The client diagnostics for exception creation in case of failure.</param>
 116        /// <param name="operationLocation">The address of the long-running operation. It can be obtained from the respo
 117        /// <param name="targetModelId">Model ID in the target Form Recognizer resource.</param>
 12118        internal CopyModelOperation(ServiceRestClient serviceClient, ClientDiagnostics diagnostics, string operationLoca
 119        {
 12120            _serviceClient = serviceClient;
 12121            _diagnostics = diagnostics;
 12122            _targetModelId = targetModelId;
 123
 124            // TODO: Use regex to parse ids.
 125            // https://github.com/Azure/azure-sdk-for-net/issues/11505
 126
 127            // TODO: Add validation here (should we store _resuldId and _modelId as GUIDs?)
 128            // https://github.com/Azure/azure-sdk-for-net/issues/10385
 129
 12130            string[] substrs = operationLocation.Split('/');
 131
 12132            if (substrs.Length < 3)
 133            {
 0134                throw new ArgumentException($"Invalid '{nameof(operationLocation)}'. It should be formatted as: '{{prefi
 135            }
 136
 12137            _resultId = substrs[substrs.Length - 1];
 12138            _modelId = substrs[substrs.Length - 3];
 139
 12140            Id = string.Join("/", substrs, substrs.Length - 3, 3);
 12141        }
 142
 143        /// <summary>
 144        /// The last HTTP response received from the server.
 145        /// </summary>
 146        /// <remarks>
 147        /// The last response returned from the server during the lifecycle of this instance.
 148        /// An instance of <see cref="CopyModelOperation"/> sends requests to a server in UpdateStatusAsync, UpdateStatu
 149        /// Responses from these requests can be accessed using GetRawResponse.
 150        /// </remarks>
 190151        public override Response GetRawResponse() => _response;
 152
 153        /// <summary>
 154        /// Periodically calls the server till the long-running operation completes.
 155        /// </summary>
 156        /// <param name="cancellationToken">A <see cref="CancellationToken"/> used for the periodical service calls.</pa
 157        /// <returns>The last HTTP response received from the server.</returns>
 158        /// <remarks>
 159        /// This method will periodically call UpdateStatusAsync till HasCompleted is true, then return the final result
 160        /// </remarks>
 161        public override ValueTask<Response<CustomFormModelInfo>> WaitForCompletionAsync(CancellationToken cancellationTo
 0162            this.DefaultWaitForCompletionAsync(cancellationToken);
 163
 164        /// <summary>
 165        /// Periodically calls the server till the long-running operation completes.
 166        /// </summary>
 167        /// <param name="pollingInterval">
 168        /// The interval between status requests to the server.
 169        /// The interval can change based on information returned from the server.
 170        /// For example, the server might communicate to the client that there is not reason to poll for status change s
 171        /// </param>
 172        /// <param name="cancellationToken">A <see cref="CancellationToken"/> used for the periodical service calls.</pa
 173        /// <returns>The last HTTP response received from the server.</returns>
 174        /// <remarks>
 175        /// This method will periodically call UpdateStatusAsync till HasCompleted is true, then return the final result
 176        /// </remarks>
 177        public override ValueTask<Response<CustomFormModelInfo>> WaitForCompletionAsync(TimeSpan pollingInterval, Cancel
 12178             this.DefaultWaitForCompletionAsync(pollingInterval, cancellationToken);
 179
 180        /// <summary>
 181        /// Calls the server to get updated status of the long-running operation.
 182        /// </summary>
 183        /// <param name="cancellationToken">A <see cref="CancellationToken"/> used for the service call.</param>
 184        /// <returns>The HTTP response received from the server.</returns>
 185        /// <remarks>
 186        /// This operation will update the value returned from GetRawResponse and might update HasCompleted, HasValue, a
 187        /// </remarks>
 188        public override Response UpdateStatus(CancellationToken cancellationToken = default) =>
 0189            UpdateStatusAsync(false, cancellationToken).EnsureCompleted();
 190
 191        /// <summary>
 192        /// Calls the server to get updated status of the long-running operation.
 193        /// </summary>
 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        /// <remarks>
 197        /// This operation will update the value returned from GetRawResponse and might update HasCompleted, HasValue, a
 198        /// </remarks>
 199        public override async ValueTask<Response> UpdateStatusAsync(CancellationToken cancellationToken = default) =>
 186200            await UpdateStatusAsync(true, cancellationToken).ConfigureAwait(false);
 201
 202        /// <summary>
 203        /// Calls the server to get updated status of the long-running operation.
 204        /// </summary>
 205        /// <param name="async">When <c>true</c>, the method will be executed asynchronously; otherwise, it will execute
 206        /// <param name="cancellationToken">A <see cref="CancellationToken"/> used for the service call.</param>
 207        /// <returns>The HTTP response received from the server.</returns>
 208        private async ValueTask<Response> UpdateStatusAsync(bool async, CancellationToken cancellationToken)
 209        {
 186210            if (!_hasCompleted)
 211            {
 186212                using DiagnosticScope scope = _diagnostics.CreateScope($"{nameof(CopyModelOperation)}.{nameof(UpdateStat
 186213                scope.Start();
 214
 215                try
 216                {
 186217                    Response<CopyOperationResult> update = async
 186218                        ? await _serviceClient.GetCustomModelCopyResultAsync(new Guid(_modelId), new Guid(_resultId), ca
 186219                        : _serviceClient.GetCustomModelCopyResult(new Guid(_modelId), new Guid(_resultId), cancellationT
 220
 186221                    _response = update.GetRawResponse();
 222
 186223                    if (update.Value.Status == OperationStatus.Succeeded)
 224                    {
 225                        // We need to first assign a value and then mark the operation as completed to avoid a race cond
 8226                        _value = ConvertValue(update.Value, _targetModelId, CustomFormModelStatus.Ready);
 8227                        _hasCompleted = true;
 228                    }
 178229                    else if (update.Value.Status == OperationStatus.Failed)
 230                    {
 4231                        _requestFailedException = await ClientCommon
 4232                            .CreateExceptionForFailedOperationAsync(async, _diagnostics, _response, update.Value.CopyRes
 4233                            .ConfigureAwait(false);
 4234                        _hasCompleted = true;
 4235                        throw _requestFailedException;
 236                    }
 182237                }
 4238                catch (Exception e)
 239                {
 4240                    scope.Failed(e);
 4241                    throw;
 242                }
 182243            }
 244
 182245            return GetRawResponse();
 182246        }
 247
 248        private static CustomFormModelInfo ConvertValue(CopyOperationResult result, string modelId, CustomFormModelStatu
 249        {
 8250            return new CustomFormModelInfo(
 8251                modelId,
 8252                status,
 8253                result.CreatedDateTime,
 8254                result.LastUpdatedDateTime);
 255        }
 256    }
 257}