Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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());
}
}
}