Skip to content

Commit 56e5a7f

Browse files
authored
[DocumentIntelligence] Renaming and fixing the behavior of TrainingOperation (#46399)
1 parent 340b722 commit 56e5a7f

File tree

6 files changed

+51
-28
lines changed

6 files changed

+51
-28
lines changed

sdk/documentintelligence/Azure.AI.DocumentIntelligence/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
### Breaking Changes
88

99
### Bugs Fixed
10+
- Fixed a bug where calling `Operation.Id` would sometimes return an `InvalidOperationException` with message "The operation ID was not present in the service response.".
1011

1112
### Other Changes
1213

sdk/documentintelligence/Azure.AI.DocumentIntelligence/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "net",
44
"TagPrefix": "net/documentintelligence/Azure.AI.DocumentIntelligence",
5-
"Tag": "net/documentintelligence/Azure.AI.DocumentIntelligence_900de40cad"
5+
"Tag": "net/documentintelligence/Azure.AI.DocumentIntelligence_0e2741b29e"
66
}

sdk/documentintelligence/Azure.AI.DocumentIntelligence/src/DocumentIntelligenceAdministrationClient.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace Azure.AI.DocumentIntelligence
1111
public partial class DocumentIntelligenceAdministrationClient
1212
{
1313
// CUSTOM CODE NOTE: we're overwriting the behavior of the BuildDocumentModel, ComposeModel,
14-
// CopyModelTo, BuildClassifier, and CopyClassifierTo APIs to return an instance of TrainingOperation.
14+
// CopyModelTo, BuildClassifier, and CopyClassifierTo APIs to return an instance of OperationWithId.
1515
// This is a workaround since Operation.Id is not supported by our generator yet (it throws a
1616
// NotSupportedException), but this ID is needed for the GetOperation API.
1717

@@ -47,7 +47,7 @@ public virtual async Task<Operation<BinaryData>> BuildDocumentModelAsync(WaitUnt
4747
{
4848
using HttpMessage message = CreateBuildDocumentModelRequest(content, context);
4949
var internalOperation = await ProtocolOperationHelpers.ProcessMessageAsync(_pipeline, message, ClientDiagnostics, "DocumentIntelligenceAdministrationClient.BuildDocumentModel", OperationFinalStateVia.OperationLocation, context, waitUntil).ConfigureAwait(false);
50-
return new TrainingOperation(internalOperation);
50+
return new OperationWithId(internalOperation);
5151
}
5252
catch (Exception e)
5353
{
@@ -88,7 +88,7 @@ public virtual Operation<BinaryData> BuildDocumentModel(WaitUntil waitUntil, Req
8888
{
8989
using HttpMessage message = CreateBuildDocumentModelRequest(content, context);
9090
var internalOperation = ProtocolOperationHelpers.ProcessMessage(_pipeline, message, ClientDiagnostics, "DocumentIntelligenceAdministrationClient.BuildDocumentModel", OperationFinalStateVia.OperationLocation, context, waitUntil);
91-
return new TrainingOperation(internalOperation);
91+
return new OperationWithId(internalOperation);
9292
}
9393
catch (Exception e)
9494
{
@@ -129,7 +129,7 @@ public virtual async Task<Operation<BinaryData>> ComposeModelAsync(WaitUntil wai
129129
{
130130
using HttpMessage message = CreateComposeModelRequest(content, context);
131131
var internalOperation = await ProtocolOperationHelpers.ProcessMessageAsync(_pipeline, message, ClientDiagnostics, "DocumentIntelligenceAdministrationClient.ComposeModel", OperationFinalStateVia.OperationLocation, context, waitUntil).ConfigureAwait(false);
132-
return new TrainingOperation(internalOperation);
132+
return new OperationWithId(internalOperation);
133133
}
134134
catch (Exception e)
135135
{
@@ -170,7 +170,7 @@ public virtual Operation<BinaryData> ComposeModel(WaitUntil waitUntil, RequestCo
170170
{
171171
using HttpMessage message = CreateComposeModelRequest(content, context);
172172
var internalOperation = ProtocolOperationHelpers.ProcessMessage(_pipeline, message, ClientDiagnostics, "DocumentIntelligenceAdministrationClient.ComposeModel", OperationFinalStateVia.OperationLocation, context, waitUntil);
173-
return new TrainingOperation(internalOperation);
173+
return new OperationWithId(internalOperation);
174174
}
175175
catch (Exception e)
176176
{
@@ -214,7 +214,7 @@ public virtual async Task<Operation<BinaryData>> CopyModelToAsync(WaitUntil wait
214214
{
215215
using HttpMessage message = CreateCopyModelToRequest(modelId, content, context);
216216
var internalOperation = await ProtocolOperationHelpers.ProcessMessageAsync(_pipeline, message, ClientDiagnostics, "DocumentIntelligenceAdministrationClient.CopyModelTo", OperationFinalStateVia.OperationLocation, context, waitUntil).ConfigureAwait(false);
217-
return new TrainingOperation(internalOperation);
217+
return new OperationWithId(internalOperation);
218218
}
219219
catch (Exception e)
220220
{
@@ -258,7 +258,7 @@ public virtual Operation<BinaryData> CopyModelTo(WaitUntil waitUntil, string mod
258258
{
259259
using HttpMessage message = CreateCopyModelToRequest(modelId, content, context);
260260
var internalOperation = ProtocolOperationHelpers.ProcessMessage(_pipeline, message, ClientDiagnostics, "DocumentIntelligenceAdministrationClient.CopyModelTo", OperationFinalStateVia.OperationLocation, context, waitUntil);
261-
return new TrainingOperation(internalOperation);
261+
return new OperationWithId(internalOperation);
262262
}
263263
catch (Exception e)
264264
{
@@ -299,7 +299,7 @@ public virtual async Task<Operation<BinaryData>> BuildClassifierAsync(WaitUntil
299299
{
300300
using HttpMessage message = CreateBuildClassifierRequest(content, context);
301301
var internalOperation = await ProtocolOperationHelpers.ProcessMessageAsync(_pipeline, message, ClientDiagnostics, "DocumentIntelligenceAdministrationClient.BuildClassifier", OperationFinalStateVia.OperationLocation, context, waitUntil).ConfigureAwait(false);
302-
return new TrainingOperation(internalOperation);
302+
return new OperationWithId(internalOperation);
303303
}
304304
catch (Exception e)
305305
{
@@ -340,7 +340,7 @@ public virtual Operation<BinaryData> BuildClassifier(WaitUntil waitUntil, Reques
340340
{
341341
using HttpMessage message = CreateBuildClassifierRequest(content, context);
342342
var internalOperation = ProtocolOperationHelpers.ProcessMessage(_pipeline, message, ClientDiagnostics, "DocumentIntelligenceAdministrationClient.BuildClassifier", OperationFinalStateVia.OperationLocation, context, waitUntil);
343-
return new TrainingOperation(internalOperation);
343+
return new OperationWithId(internalOperation);
344344
}
345345
catch (Exception e)
346346
{
@@ -384,7 +384,7 @@ public virtual async Task<Operation<BinaryData>> CopyClassifierToAsync(WaitUntil
384384
{
385385
using HttpMessage message = CreateCopyClassifierToRequest(classifierId, content, context);
386386
var internalOperation = await ProtocolOperationHelpers.ProcessMessageAsync(_pipeline, message, ClientDiagnostics, "DocumentIntelligenceAdministrationClient.CopyClassifierTo", OperationFinalStateVia.OperationLocation, context, waitUntil).ConfigureAwait(false);
387-
return new TrainingOperation(internalOperation);
387+
return new OperationWithId(internalOperation);
388388
}
389389
catch (Exception e)
390390
{
@@ -428,7 +428,7 @@ public virtual Operation<BinaryData> CopyClassifierTo(WaitUntil waitUntil, strin
428428
{
429429
using HttpMessage message = CreateCopyClassifierToRequest(classifierId, content, context);
430430
var internalOperation = ProtocolOperationHelpers.ProcessMessage(_pipeline, message, ClientDiagnostics, "DocumentIntelligenceAdministrationClient.CopyClassifierTo", OperationFinalStateVia.OperationLocation, context, waitUntil);
431-
return new TrainingOperation(internalOperation);
431+
return new OperationWithId(internalOperation);
432432
}
433433
catch (Exception e)
434434
{

sdk/documentintelligence/Azure.AI.DocumentIntelligence/src/DocumentIntelligenceClient.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace Azure.AI.DocumentIntelligence
1414
public partial class DocumentIntelligenceClient
1515
{
1616
// CUSTOM CODE NOTE: we're overwriting the behavior of the AnalyzeDocument API to
17-
// return an instance of TrainingOperation. This is a workaround since Operation.Id
17+
// return an instance of OperationWithId. This is a workaround since Operation.Id
1818
// is not supported by our generator yet (it throws a NotSupportedException), but
1919
// this ID is needed for the GetAnalyzeResultPdf and the GetAnalyzeResultImage APIs.
2020

@@ -62,18 +62,18 @@ public virtual async Task<Operation<BinaryData>> AnalyzeDocumentAsync(WaitUntil
6262
{
6363
using HttpMessage message = CreateAnalyzeDocumentRequest(modelId, content, pages, locale, stringIndexType, features, queryFields, outputContentFormat, output, context);
6464
var internalOperation = await ProtocolOperationHelpers.ProcessMessageAsync(_pipeline, message, ClientDiagnostics, "DocumentIntelligenceClient.AnalyzeDocument", OperationFinalStateVia.OperationLocation, context, WaitUntil.Started).ConfigureAwait(false);
65-
var trainingOperation = new TrainingOperation(internalOperation);
65+
var operationWithId = new OperationWithId(internalOperation);
6666

6767
// Workaround to obtain the operation ID. The operation-location header is only returned after
6868
// the first request that starts the LRO. Because of this we're setting waitUntil to 'Started'
69-
// above so we have time to extract the operation ID in the TrainingOperation constructor.
69+
// above so we have time to extract the operation ID in the OperationWithId constructor.
7070

7171
if (waitUntil == WaitUntil.Completed)
7272
{
73-
await trainingOperation.WaitForCompletionAsync(context?.CancellationToken ?? default).ConfigureAwait(false);
73+
await operationWithId.WaitForCompletionAsync(context?.CancellationToken ?? default).ConfigureAwait(false);
7474
}
7575

76-
return trainingOperation;
76+
return operationWithId;
7777
}
7878
catch (Exception e)
7979
{
@@ -126,18 +126,18 @@ public virtual Operation<BinaryData> AnalyzeDocument(WaitUntil waitUntil, string
126126
{
127127
using HttpMessage message = CreateAnalyzeDocumentRequest(modelId, content, pages, locale, stringIndexType, features, queryFields, outputContentFormat, output, context);
128128
var internalOperation = ProtocolOperationHelpers.ProcessMessage(_pipeline, message, ClientDiagnostics, "DocumentIntelligenceClient.AnalyzeDocument", OperationFinalStateVia.OperationLocation, context, WaitUntil.Started);
129-
var trainingOperation = new TrainingOperation(internalOperation);
129+
var operationWithId = new OperationWithId(internalOperation);
130130

131131
// Workaround to obtain the operation ID. The operation-location header is only returned after
132132
// the first request that starts the LRO. Because of this we're setting waitUntil to 'Started'
133-
// above so we have time to extract the operation ID in the TrainingOperation constructor.
133+
// above so we have time to extract the operation ID in the OperationWithId constructor.
134134

135135
if (waitUntil == WaitUntil.Completed)
136136
{
137-
trainingOperation.WaitForCompletion(context?.CancellationToken ?? default);
137+
operationWithId.WaitForCompletion(context?.CancellationToken ?? default);
138138
}
139139

140-
return trainingOperation;
140+
return operationWithId;
141141
}
142142
catch (Exception e)
143143
{

sdk/documentintelligence/Azure.AI.DocumentIntelligence/src/TrainingOperation.cs renamed to sdk/documentintelligence/Azure.AI.DocumentIntelligence/src/OperationWithId.cs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
// Licensed under the MIT License.
33

44
using System;
5+
using System.Text.Json;
56
using System.Text.RegularExpressions;
67
using System.Threading;
78
using System.Threading.Tasks;
89

910
namespace Azure.AI.DocumentIntelligence
1011
{
11-
internal class TrainingOperation : Operation<BinaryData>
12+
internal class OperationWithId : Operation<BinaryData>
1213
{
1314
private const string OperationIdNotFoundErrorMessage = "The operation ID was not present in the service response.";
1415

@@ -20,7 +21,7 @@ internal class TrainingOperation : Operation<BinaryData>
2021

2122
private readonly string _operationId;
2223

23-
internal TrainingOperation(Operation<BinaryData> internalOperation)
24+
internal OperationWithId(Operation<BinaryData> internalOperation)
2425
{
2526
_internalOperation = internalOperation;
2627
_operationId = GetOperationId();
@@ -53,16 +54,37 @@ private string GetOperationId()
5354
{
5455
var response = GetRawResponse();
5556

57+
// The "operation-location" header may or may not be present depending on the type of
58+
// request that was sent to the service. It depends on whether it was a GET or a POST
59+
// request, as well as whether this is an analysis or a training operation. In case the
60+
// header is missing, the operation ID must be extracted from the response body. We're
61+
// contemplating both scenarios in the code below.
62+
5663
if (response.Headers.TryGetValue("operation-location", out string operationLocation))
5764
{
5865
var match = s_locationHeaderRegex.Match(operationLocation);
5966

60-
if (!match.Success || !match.Groups[1].Success)
67+
if (match.Success && match.Groups[1].Success)
6168
{
62-
return null;
69+
return match.Groups[1].Value;
6370
}
71+
}
72+
else if (response.Content is not null)
73+
{
74+
try
75+
{
76+
using var document = JsonDocument.Parse(response.Content);
6477

65-
return match.Groups[1].Value;
78+
if (document.RootElement.TryGetProperty("operationId", out JsonElement operationIdProperty))
79+
{
80+
return operationIdProperty.GetString();
81+
}
82+
}
83+
catch (JsonException)
84+
{
85+
// Ignore the exception. Failing to extract the ID should not prevent users from
86+
// using most of this class' functionality.
87+
}
6688
}
6789

6890
return null;
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010

1111
namespace Azure.AI.DocumentIntelligence.Tests
1212
{
13-
public class TrainingOperationLiveTests : DocumentIntelligenceLiveTestBase
13+
public class OperationWithIdLiveTests : DocumentIntelligenceLiveTestBase
1414
{
1515
// Example: 3eb2e5b5-b9d3-4b5a-ac31-90d945f4b4e4
1616
private const string AnalysisOperationIdPattern = @"^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$";
1717

1818
// Example: 31466498286_3eb2e5b5-b9d3-4b5a-ac31-90d945f4b4e4
1919
private const string TrainingOperationIdPattern = @"^[0-9]+_[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$";
2020

21-
public TrainingOperationLiveTests(bool isAsync)
21+
public OperationWithIdLiveTests(bool isAsync)
2222
: base(isAsync)
2323
{
2424
}

0 commit comments

Comments
 (0)