Skip to content

Commit

Permalink
[FormRecognizer] Fix: StartRecognizeCustomFormsFromUri works when URI…
Browse files Browse the repository at this point in the history
… has blank space (#12192)
  • Loading branch information
kinelski authored May 25, 2020
1 parent 00d8f23 commit e60aca3
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 11 deletions.
3 changes: 2 additions & 1 deletion sdk/formrecognizer/Azure.AI.FormRecognizer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@

### Fixes

- Custom form recognition without labels can now handle multipaged forms.
- Custom form recognition without labels can now handle multipaged forms ([#11881](https://github.com/Azure/azure-sdk-for-net/issues/11881)).
- `RecognizedForm.Pages` now only contains pages whose numbers are within `RecognizedForm.PageRange`.
- `FieldText.TextContent` cannot be `null` anymore, and it will be empty when no element is returned from the service.
- Custom form recognition with labels can now parse results from forms that do not contain all of the expected labels ([#11821](https://github.com/Azure/azure-sdk-for-net/issues/11821)).
- `FormRecognizerClient.StartRecognizeCustomFormsFromUri` now works with URIs that contain blank spaces, encoded or not ([#11564](https://github.com/Azure/azure-sdk-for-net/issues/11564)).

## 1.0.0-preview.2 (05-06-2020)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public FormRecognizerClient(Uri endpoint, AzureKeyCredential credential, FormRec

Diagnostics = new ClientDiagnostics(options);
var pipeline = HttpPipelineBuilder.Build(options, new AzureKeyCredentialPolicy(credential, Constants.AuthorizationHeader));
ServiceClient = new ServiceRestClient(Diagnostics, pipeline, endpoint.ToString());
ServiceClient = new ServiceRestClient(Diagnostics, pipeline, endpoint.AbsoluteUri);
}

/// <summary>
Expand Down Expand Up @@ -93,7 +93,7 @@ public FormRecognizerClient(Uri endpoint, TokenCredential credential, FormRecogn

Diagnostics = new ClientDiagnostics(options);
var pipeline = HttpPipelineBuilder.Build(options, new BearerTokenAuthenticationPolicy(credential, Constants.DefaultCognitiveScope));
ServiceClient = new ServiceRestClient(Diagnostics, pipeline, endpoint.ToString());
ServiceClient = new ServiceRestClient(Diagnostics, pipeline, endpoint.AbsoluteUri);
}

/// <summary>
Expand Down Expand Up @@ -169,7 +169,7 @@ public virtual RecognizeContentOperation StartRecognizeContentFromUri(Uri formFi
{
Argument.AssertNotNull(formFileUri, nameof(formFileUri));

SourcePath_internal sourcePath = new SourcePath_internal(formFileUri.ToString());
SourcePath_internal sourcePath = new SourcePath_internal(formFileUri.AbsoluteUri);
ResponseWithHeaders<ServiceAnalyzeLayoutAsyncHeaders> response = ServiceClient.AnalyzeLayoutAsync(sourcePath, cancellationToken);
return new RecognizeContentOperation(ServiceClient, response.Headers.OperationLocation);
}
Expand All @@ -187,7 +187,7 @@ public virtual async Task<RecognizeContentOperation> StartRecognizeContentFromUr
{
Argument.AssertNotNull(formFileUri, nameof(formFileUri));

SourcePath_internal sourcePath = new SourcePath_internal(formFileUri.ToString());
SourcePath_internal sourcePath = new SourcePath_internal(formFileUri.AbsoluteUri);
ResponseWithHeaders<ServiceAnalyzeLayoutAsyncHeaders> response = await ServiceClient.AnalyzeLayoutAsyncAsync(sourcePath, cancellationToken).ConfigureAwait(false);
return new RecognizeContentOperation(ServiceClient, response.Headers.OperationLocation);
}
Expand Down Expand Up @@ -251,7 +251,7 @@ public virtual async Task<RecognizeReceiptsOperation> StartRecognizeReceiptsFrom

recognizeOptions ??= new RecognizeOptions();

SourcePath_internal sourcePath = new SourcePath_internal(receiptFileUri.ToString());
SourcePath_internal sourcePath = new SourcePath_internal(receiptFileUri.AbsoluteUri);
ResponseWithHeaders<ServiceAnalyzeReceiptAsyncHeaders> response = await ServiceClient.AnalyzeReceiptAsyncAsync(includeTextDetails: recognizeOptions.IncludeTextContent, sourcePath, cancellationToken).ConfigureAwait(false);
return new RecognizeReceiptsOperation(ServiceClient, response.Headers.OperationLocation);
}
Expand All @@ -271,7 +271,7 @@ public virtual RecognizeReceiptsOperation StartRecognizeReceiptsFromUri(Uri rece

recognizeOptions ??= new RecognizeOptions();

SourcePath_internal sourcePath = new SourcePath_internal(receiptFileUri.ToString());
SourcePath_internal sourcePath = new SourcePath_internal(receiptFileUri.AbsoluteUri);
ResponseWithHeaders<ServiceAnalyzeReceiptAsyncHeaders> response = ServiceClient.AnalyzeReceiptAsync(includeTextDetails: recognizeOptions.IncludeTextContent, sourcePath, cancellationToken);
return new RecognizeReceiptsOperation(ServiceClient, response.Headers.OperationLocation);
}
Expand Down Expand Up @@ -323,7 +323,7 @@ public virtual RecognizeCustomFormsOperation StartRecognizeCustomFormsFromUri(st

recognizeOptions ??= new RecognizeOptions();

SourcePath_internal sourcePath = new SourcePath_internal(formFileUri.ToString());
SourcePath_internal sourcePath = new SourcePath_internal(formFileUri.AbsoluteUri);
ResponseWithHeaders<ServiceAnalyzeWithCustomModelHeaders> response = ServiceClient.AnalyzeWithCustomModel(guid, includeTextDetails: recognizeOptions.IncludeTextContent, sourcePath, cancellationToken);
return new RecognizeCustomFormsOperation(ServiceClient, Diagnostics, response.Headers.OperationLocation);
}
Expand Down Expand Up @@ -371,7 +371,7 @@ public virtual async Task<RecognizeCustomFormsOperation> StartRecognizeCustomFor

recognizeOptions ??= new RecognizeOptions();

SourcePath_internal sourcePath = new SourcePath_internal(formFileUri.ToString());
SourcePath_internal sourcePath = new SourcePath_internal(formFileUri.AbsoluteUri);
ResponseWithHeaders<ServiceAnalyzeWithCustomModelHeaders> response = await ServiceClient.AnalyzeWithCustomModelAsync(guid, includeTextDetails: recognizeOptions.IncludeTextContent, sourcePath, cancellationToken).ConfigureAwait(false);
return new RecognizeCustomFormsOperation(ServiceClient, Diagnostics, response.Headers.OperationLocation);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public FormTrainingClient(Uri endpoint, AzureKeyCredential credential, FormRecog

Diagnostics = new ClientDiagnostics(options);
HttpPipeline pipeline = HttpPipelineBuilder.Build(options, new AzureKeyCredentialPolicy(credential, Constants.AuthorizationHeader));
ServiceClient = new ServiceRestClient(Diagnostics, pipeline, endpoint.ToString());
ServiceClient = new ServiceRestClient(Diagnostics, pipeline, endpoint.AbsoluteUri);
}

/// <summary>
Expand Down Expand Up @@ -99,7 +99,7 @@ public FormTrainingClient(Uri endpoint, TokenCredential credential, FormRecogniz

Diagnostics = new ClientDiagnostics(options);
var pipeline = HttpPipelineBuilder.Build(options, new BearerTokenAuthenticationPolicy(credential, Constants.DefaultCognitiveScope));
ServiceClient = new ServiceRestClient(Diagnostics, pipeline, endpoint.ToString());
ServiceClient = new ServiceRestClient(Diagnostics, pipeline, endpoint.AbsoluteUri);
}

#region Training
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Azure.Core;
using Azure.Core.TestFramework;
using NUnit.Framework;

namespace Azure.AI.FormRecognizer.Tests
{
/// <summary>
/// The suite of mock tests for the <see cref="FormRecognizerClient"/> class.
/// </summary>
public class FormRecognizerClientMockTests : ClientTestBase
{
/// <summary>
/// Initializes a new instance of the <see cref="FormRecognizerClientMockTests"/> class.
/// </summary>
/// <param name="isAsync">A flag used by the Azure Core Test Framework to differentiate between tests for asynchronous and synchronous methods.</param>
public FormRecognizerClientMockTests(bool isAsync) : base(isAsync)
{
}

/// <summary>
/// Creates a fake <see cref="FormRecognizerClient" /> and instruments it to make use of the Azure Core
/// Test Framework functionalities.
/// </summary>
/// <param name="options">A set of options to apply when configuring the client.</param>
/// <returns>The instrumented <see cref="FormRecognizerClient" />.</returns>
private FormRecognizerClient CreateInstrumentedClient(FormRecognizerClientOptions options = default)
{
var fakeEndpoint = new Uri("http://localhost");
var fakeCredential = new AzureKeyCredential("fakeKey");
options ??= new FormRecognizerClientOptions();

var client = new FormRecognizerClient(fakeEndpoint, fakeCredential, options);
return InstrumentClient(client);
}

[Test]
public async Task StartRecognizeContentFromUriEncodesBlankSpaces()
{
var mockResponse = new MockResponse(202);
mockResponse.AddHeader(new HttpHeader("Operation-Location", "host/layout/analyzeResults/00000000000000000000000000000000"));

var mockTransport = new MockTransport(new[] { mockResponse, mockResponse });
var options = new FormRecognizerClientOptions() { Transport = mockTransport };
var client = CreateInstrumentedClient(options);

var encodedUriString = "https://fakeuri.com/blank%20space";
var decodedUriString = "https://fakeuri.com/blank space";

await client.StartRecognizeContentFromUriAsync(new Uri(encodedUriString));
await client.StartRecognizeContentFromUriAsync(new Uri(decodedUriString));

Assert.AreEqual(2, mockTransport.Requests.Count);

foreach (var request in mockTransport.Requests)
{
var requestBody = GetString(request.Content);

Assert.True(requestBody.Contains(encodedUriString));
Assert.False(requestBody.Contains(decodedUriString));
}
}

[Test]
public async Task StartRecognizeReceiptsFromUriEncodesBlankSpaces()
{
var mockResponse = new MockResponse(202);
mockResponse.AddHeader(new HttpHeader("Operation-Location", "host/prebuilt/receipt/analyzeResults/00000000000000000000000000000000"));

var mockTransport = new MockTransport(new[] { mockResponse, mockResponse });
var options = new FormRecognizerClientOptions() { Transport = mockTransport };
var client = CreateInstrumentedClient(options);

var encodedUriString = "https://fakeuri.com/blank%20space";
var decodedUriString = "https://fakeuri.com/blank space";

await client.StartRecognizeReceiptsFromUriAsync(new Uri(encodedUriString));
await client.StartRecognizeReceiptsFromUriAsync(new Uri(decodedUriString));

Assert.AreEqual(2, mockTransport.Requests.Count);

foreach (var request in mockTransport.Requests)
{
var requestBody = GetString(request.Content);

Assert.True(requestBody.Contains(encodedUriString));
Assert.False(requestBody.Contains(decodedUriString));
}
}

[Test]
public async Task StartRecognizeCustomFormsFromUriEncodesBlankSpaces()
{
var mockResponse = new MockResponse(202);
mockResponse.AddHeader(new HttpHeader("Operation-Location", "host/custom/models/00000000000000000000000000000000/analyzeResults/00000000000000000000000000000000"));

var mockTransport = new MockTransport(new[] { mockResponse, mockResponse });
var options = new FormRecognizerClientOptions() { Transport = mockTransport };
var client = CreateInstrumentedClient(options);

var encodedUriString = "https://fakeuri.com/blank%20space";
var decodedUriString = "https://fakeuri.com/blank space";

await client.StartRecognizeCustomFormsFromUriAsync("00000000000000000000000000000000", new Uri(encodedUriString));
await client.StartRecognizeCustomFormsFromUriAsync("00000000000000000000000000000000", new Uri(decodedUriString));

Assert.AreEqual(2, mockTransport.Requests.Count);

foreach (var request in mockTransport.Requests)
{
var requestBody = GetString(request.Content);

Assert.True(requestBody.Contains(encodedUriString));
Assert.False(requestBody.Contains(decodedUriString));
}
}

private static string GetString(RequestContent content)
{
using var stream = new MemoryStream();
content.WriteTo(stream, CancellationToken.None);

return Encoding.UTF8.GetString(stream.ToArray());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Azure.AI.FormRecognizer.Training;
using Azure.Core;
using Azure.Core.TestFramework;
using NUnit.Framework;

namespace Azure.AI.FormRecognizer.Tests
{
/// <summary>
/// The suite of mock tests for the <see cref="FormTrainingClient"/> class.
/// </summary>
public class FormTrainingClientMockTests : ClientTestBase
{
/// <summary>
/// Initializes a new instance of the <see cref="FormTrainingClientMockTests"/> class.
/// </summary>
/// <param name="isAsync">A flag used by the Azure Core Test Framework to differentiate between tests for asynchronous and synchronous methods.</param>
public FormTrainingClientMockTests(bool isAsync) : base(isAsync)
{
}

/// <summary>
/// Creates a fake <see cref="FormTrainingClient" /> and instruments it to make use of the Azure Core
/// Test Framework functionalities.
/// </summary>
/// <param name="options">A set of options to apply when configuring the client.</param>
/// <returns>The instrumented <see cref="FormTrainingClient" />.</returns>
private FormTrainingClient CreateInstrumentedClient(FormRecognizerClientOptions options = default)
{
var fakeEndpoint = new Uri("http://localhost");
var fakeCredential = new AzureKeyCredential("fakeKey");
options ??= new FormRecognizerClientOptions();

var client = new FormTrainingClient(fakeEndpoint, fakeCredential, options);
return InstrumentClient(client);
}

[Test]
public async Task StartTrainingEncodesBlankSpaces()
{
var mockResponse = new MockResponse(201);
mockResponse.AddHeader(new HttpHeader("Location", "host/custom/models/00000000000000000000000000000000"));

var mockTransport = new MockTransport(new[] { mockResponse, mockResponse });
var options = new FormRecognizerClientOptions() { Transport = mockTransport };
var client = CreateInstrumentedClient(options);

var encodedUriString = "https://fakeuri.com/blank%20space";
var decodedUriString = "https://fakeuri.com/blank space";

await client.StartTrainingAsync(new Uri(encodedUriString));
await client.StartTrainingAsync(new Uri(decodedUriString));

Assert.AreEqual(2, mockTransport.Requests.Count);

foreach (var request in mockTransport.Requests)
{
var requestBody = GetString(request.Content);

Assert.True(requestBody.Contains(encodedUriString));
Assert.False(requestBody.Contains(decodedUriString));
}
}

private static string GetString(RequestContent content)
{
using var stream = new MemoryStream();
content.WriteTo(stream, CancellationToken.None);

return Encoding.UTF8.GetString(stream.ToArray());
}
}
}

0 comments on commit e60aca3

Please sign in to comment.