diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/CHANGELOG.md b/sdk/formrecognizer/azure-ai-formrecognizer/CHANGELOG.md index bfa17d276024..b12e7fdd2bea 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/CHANGELOG.md +++ b/sdk/formrecognizer/azure-ai-formrecognizer/CHANGELOG.md @@ -24,6 +24,7 @@ - Rename parameter `useLabelFile` to `useTrainingLabels` on `beginTraining` method in FormTrainingClients - Replace parameters `filePrefix` and `includeSubFolders` with `TrainingFileFilter` model - Rename AccountProperties `count` and `limit` to `customModelCount` and `customModelLimit` +- Added support for AAD Authentication. ### New Features - Support to copy a custom model from one Form Recognizer resource to another diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/README.md b/sdk/formrecognizer/azure-ai-formrecognizer/README.md index 17c53d18fa42..77a266eda7db 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/README.md +++ b/sdk/formrecognizer/azure-ai-formrecognizer/README.md @@ -58,32 +58,39 @@ az cognitiveservices account create \ --yes ``` ### Authenticate the client -In order to interact with the Form Recognizer service, you will need to create an instance of the `FormRecognizerClient` -class. You will need an **endpoint** and an **key** to instantiate a client object, -they can be found in the [Azure Portal][azure_portal] under the "Quickstart" in your created -Form Recognizer resource. See the full details regarding [authentication][authentication] of Cognitive Services. +In order to interact with the Form Recognizer service, you will need to create an instance of the Form Recognizer client. +Both the asynchronous and synchronous clients can be created by using `FormRecognizerClientBuilder`. Invoking `buildClient()` +will create the synchronous client, while invoking `buildAsyncClient` will create its asynchronous counterpart. -#### Get credentials -The `credential` parameter may be provided as a [`AzureKeyCredential`][azure_key_credential] from [azure-core][azure_core]. +You will need an **endpoint** and a **key** to instantiate a client object. -##### Create FormRecognizerClient with AzureKeyCredential -To use AzureKeyCredential authentication, provide the [key][key] as a string to the [AzureKeyCredential][azure_key_credential]. This can be found in the [Azure Portal][azure_portal] - under the "Quickstart" section or by running the following Azure CLI command: +##### Looking up the endpoint +You can find the **endpoint** for your Form Recognizer resource in the [Azure Portal][azure_portal] under the "Keys and Endpoint", +or [Azure CLI][azure_cli_endpoint]. +```bash +# Get the endpoint for the resource +az cognitiveservices account show --name "resource-name" --resource-group "resource-group-name" --query "endpoint" +``` + +#### Create a Form Recognizer client using AzureKeyCredential +To use `AzureKeyCredential` authentication, provide the [key][key] as a string to the [AzureKeyCredential][azure_key_credential]. +This key can be found in the [Azure Portal][azure_portal] under the "Keys and Endpoint" section in your created Form Recognizer +resource, or by running the following Azure CLI command to get the key from the Form Recognizer resource: ```bash az cognitiveservices account keys list --resource-group --name ``` Use the API key as the credential parameter to authenticate the client: - + ```java FormRecognizerClient formRecognizerClient = new FormRecognizerClientBuilder() .credential(new AzureKeyCredential("{key}")) .endpoint("{endpoint}") .buildClient(); ``` -The Azure Form Recognizer client library provides a way to **rotate the existing API key**. +The Azure Form Recognizer client library provides a way to **rotate the existing key**. - + ```java AzureKeyCredential credential = new AzureKeyCredential("{key}"); FormRecognizerClient formRecognizerClient = new FormRecognizerClientBuilder() @@ -94,6 +101,43 @@ FormRecognizerClient formRecognizerClient = new FormRecognizerClientBuilder() credential.update("{new_key}"); ``` +#### Create a Form Recognizer client with Azure Active Directory credential +Azure SDK for Java supports an Azure Identity package, making it easy to get credentials from Microsoft identity +platform. + +Authentication with AAD requires some initial setup: +* Add the Azure Identity package + +[//]: # ({x-version-update-start;com.azure:azure-identity;dependency}) +```xml + + com.azure + azure-identity + 1.0.6 + +``` +[//]: # ({x-version-update-end}) +* [Register a new Azure Active Directory application][register_AAD_application] +* [Grant access][grant_access] to Form Recognizer by assigning the `"Cognitive Services User"` role to your service principal. + +After setup, you can choose which type of [credential][azure_identity_credential_type] from azure.identity to use. +As an example, [DefaultAzureCredential][wiki_identity] can be used to authenticate the client: +Set the values of the client ID, tenant ID, and client secret of the AAD application as environment variables: +AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_CLIENT_SECRET. + +Authorization is easiest using [DefaultAzureCredential][wiki_identity]. It finds the best credential to use in its +running environment. For more information about using Azure Active Directory authorization with Form Recognizer, please +refer to [the associated documentation][aad_authorization]. + + +```java +TokenCredential credential = new DefaultAzureCredentialBuilder().build(); +FormRecognizerClient formRecognizerClient = new FormRecognizerClientBuilder() + .endpoint("{endpoint}") + .credential(credential) + .buildClient(); +``` + ## Key concepts ### FormRecognizerClient The [FormRecognizerClient][form_recognizer_sync_client] and [FormRecognizerAsyncClient][form_recognizer_async_client] @@ -143,7 +187,7 @@ The following section provides several code snippets covering some of the most c ### Recognize Forms Using a Custom Model Recognize name/value pairs and table data from forms. These models are trained with your own data, so they're tailored to your forms. You should only recognize forms of the same form type that the custom model was trained on. - + ```java String formUrl = "{file_url}"; String modelId = "{custom_trained_model_id}"; @@ -167,7 +211,7 @@ for (int i = 0; i < recognizedForms.size(); i++) { ### Recognize Content Recognize text and table structures, along with their bounding box coordinates, from documents. - + ```java String contentFileUrl = "{file_url}"; SyncPoller> recognizeContentPoller = @@ -195,7 +239,7 @@ for (int i = 0; i < contentPageResults.size(); i++) { ### Recognize receipts Recognize data from a USA sales receipts using a prebuilt model. - + ```java String receiptUrl = "https://docs.microsoft.com/en-us/azure/cognitive-services/form-recognizer/media" + "/contoso-allinone.jpg"; @@ -222,7 +266,7 @@ for (int i = 0; i < receiptPageResults.size(); i++) { Train a machine-learned model on your own form type. The resulting model will be able to recognize values from the types of forms it was trained on. Provide a container SAS url to your Azure Storage Blob container where you're storing the training documents. See details on setting this up in the [service quickstart documentation][quickstart_training]. - + ```java String trainingFilesUrl = "{training_set_SAS_URL}"; SyncPoller trainingPoller = @@ -249,7 +293,7 @@ customFormModel.getSubmodels().forEach(customFormSubmodel -> { ### Manage your models Manage the custom models attached to your account. - + ```java AtomicReference modelId = new AtomicReference<>(); // First, we see how many custom models we have, and what our limit is @@ -291,7 +335,7 @@ to provide an invalid file source URL an `ErrorResponseException` would be raise In the following code snippet, the error is handled gracefully by catching the exception and display the additional information about the error. - + ```java try { formRecognizerClient.beginRecognizeContentFromUrl("invalidSourceUrl"); @@ -318,14 +362,14 @@ The following section provides several code snippets illustrating common pattern These code samples show common scenario operations with the Azure Form Recognizer client library. The async versions of the samples show asynchronous operations with Form Recognizer. -* Recognize receipts: [RecognizeReceipts][recognize_receipts], ([async][recognize_receipts_async]) -* Recognize receipts from a URL: [RecognizeReceiptsFromUrl][recognize_receipts_from_url], ([async][recognize_receipts_from_url_async]) -* Recognize content: [RecognizeContent][recognize_content], ([async][recognize_content_async]) -* Recognize custom forms: [RecognizeCustomForms][recognize_custom_forms], ([async][recognize_custom_forms_async]) -* Train a model without labels: [TrainModelWithoutLabels][train_unlabeled_model], ([async][train_unlabeled_model_async]) -* Train a model with labels: [TrainModelWithLabels][train_labeled_model], ([async][train_labeled_model_async]) -* Manage custom models: [ManageCustomModels][manage_custom_models], ([async_version][manage_custom_models_async]) -* Copy a model between Form Recognizer resources: [CopyModel][copy_model] ([async_version][copy_model_async]) +* Recognize receipts: [RecognizeReceipts][recognize_receipts] ([async][recognize_receipts_async]) +* Recognize receipts from a URL: [RecognizeReceiptsFromUrl][recognize_receipts_from_url] ([async][recognize_receipts_from_url_async]) +* Recognize content: [RecognizeContent][recognize_content] ([async][recognize_content_async]) +* Recognize custom forms: [RecognizeCustomForms][recognize_custom_forms] ([async][recognize_custom_forms_async]) +* Train a model without labels: [TrainModelWithoutLabels][train_unlabeled_model] ([async][train_unlabeled_model_async]) +* Train a model with labels: [TrainModelWithLabels][train_labeled_model] ([async][train_labeled_model_async]) +* Manage custom models: [ManageCustomModels][manage_custom_models] ([async][manage_custom_models_async]) +* Copy a model between Form Recognizer resources: [CopyModel][copy_model] ([async][copy_model_async]) ### Additional documentation @@ -340,11 +384,14 @@ When you submit a pull request, a CLA-bot will automatically determine whether y This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For more information see the [Code of Conduct FAQ][coc_faq] or contact [opencode@microsoft.com][coc_contact] with any additional questions or comments. +[aad_authorization]: https://docs.microsoft.com/azure/cognitive-services/authentication#authenticate-with-azure-active-directory [azure_key_credential]: https://github.com/Azure/azure-sdk-for-java/blob/master/sdk/core/azure-core/src/main/java/com/azure/core/credential/AzureKeyCredential.java [key]: https://docs.microsoft.com/azure/cognitive-services/cognitive-services-apis-create-account?tabs=multiservice%2Cwindows#get-the-keys-for-your-resource [api_reference_doc]: https://aka.ms/azsdk-java-formrecognizer-ref-docs [authentication]: https://docs.microsoft.com/azure/cognitive-services/authentication +[azure_identity_credential_type]: https://github.com/Azure/azure-sdk-for-java/tree/master/sdk/identity/azure-identity#credentials [azure_cli]: https://docs.microsoft.com/azure/cognitive-services/cognitive-services-apis-create-account-cli?tabs=windows +[azure_cli_endpoint]: https://docs.microsoft.com/cli/azure/cognitiveservices/account?view=azure-cli-latest#az-cognitiveservices-account-show [azure_identity]: https://github.com/Azure/azure-sdk-for-java/tree/master/sdk/identity/azure-identity#credentials [azure_portal]: https://ms.portal.azure.com [azure_subscription]: https://azure.microsoft.com/free @@ -360,10 +407,11 @@ This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For m [form_recognizer_sync_client]: src/main/java/com/azure/ai/formrecognizer/FormRecognizerClient.java [form_training_async_client]: src/main/java/com/azure/ai/formrecognizer/training/FormTrainingAsyncClient.java [form_training_sync_client]: src/main/java/com/azure/ai/formrecognizer/training/FormTrainingClient.java +[grant_access]: https://docs.microsoft.com/azure/cognitive-services/authentication#assign-a-role-to-a-service-principal [http_clients_wiki]: https://github.com/Azure/azure-sdk-for-java/wiki/HTTP-clients -[fr_labeling_tool]: https://docs.microsoft.com/en-us/azure/cognitive-services/form-recognizer/quickstarts/label-tool -[fr_train_without_labels]: https://docs.microsoft.com/en-us/azure/cognitive-services/form-recognizer/overview#train-without-labels -[fr_train_with_labels]: https://docs.microsoft.com/en-us/azure/cognitive-services/form-recognizer/overview#train-with-labels +[fr_labeling_tool]: https://docs.microsoft.com/azure/cognitive-services/form-recognizer/quickstarts/label-tool +[fr_train_without_labels]: https://docs.microsoft.com/azure/cognitive-services/form-recognizer/overview#train-without-labels +[fr_train_with_labels]: https://docs.microsoft.com/azure/cognitive-services/form-recognizer/overview#train-with-labels [error_response_exception]: src/main/java/com/azure/ai/formrecognizer/models/ErrorResponseException.java [logging]: https://github.com/Azure/azure-sdk-for-java/wiki/Logging-with-Azure-SDK [package]: https://mvnrepository.com/artifact/com.azure/azure-ai-formrecognizer @@ -380,6 +428,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For m [recognize_receipts_from_url_async]: src/samples/java/com/azure/ai/formrecognizer/RecognizeReceiptsFromUrlAsync.java [recognize_custom_forms]: src/samples/java/com/azure/ai/formrecognizer/RecognizeCustomForms.java [recognize_custom_forms_async]: src/samples/java/com/azure/ai/formrecognizer/RecognizeCustomFormsAsync.java +[register_AAD_application]: https://docs.microsoft.com/azure/cognitive-services/authentication#assign-a-role-to-a-service-principal [train_unlabeled_model]: src/samples/java/com/azure/ai/formrecognizer/TrainModelWithoutLabels.java [train_unlabeled_model_async]: src/samples/java/com/azure/ai/formrecognizer/TrainModelWithoutLabelsAsync.java [train_labeled_model]: src/samples/java/com/azure/ai/formrecognizer/TrainModelWithLabels.java @@ -387,9 +436,10 @@ This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For m [copy_model]: src/samples/java/com/azure/ai/formrecognizer/CopyModel.java [copy_model_async]: src/samples/java/com/azure/ai/formrecognizer/CopyModelAsync.java [service_access]: https://docs.microsoft.com/azure/cognitive-services/cognitive-services-apis-create-account?tabs=multiservice%2Cwindows -[service_doc_train_unlabeled]: https://docs.microsoft.com/en-us/azure/cognitive-services/form-recognizer/overview#train-without-labels -[service_doc_train_labeled]: https://docs.microsoft.com/en-us/azure/cognitive-services/form-recognizer/overview#train-with-labels +[service_doc_train_unlabeled]: https://docs.microsoft.com/azure/cognitive-services/form-recognizer/overview#train-without-labels +[service_doc_train_labeled]: https://docs.microsoft.com/azure/cognitive-services/form-recognizer/overview#train-with-labels [source_code]: src [quickstart_training]: https://docs.microsoft.com/azure/cognitive-services/form-recognizer/quickstarts/curl-train-extract#train-a-form-recognizer-model +[wiki_identity]: https://github.com/Azure/azure-sdk-for-java/wiki/Identity-and-Authentication ![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-java%2Fsdk%2Fformrecognizer%2Fazure-ai-formrecognizer%2FREADME.png) diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/pom.xml b/sdk/formrecognizer/azure-ai-formrecognizer/pom.xml index fb1c971206cc..13e79d7d97a8 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/pom.xml +++ b/sdk/formrecognizer/azure-ai-formrecognizer/pom.xml @@ -75,5 +75,11 @@ 5.6.2 test + + com.azure + azure-identity + 1.0.6 + test + diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/src/main/java/com/azure/ai/formrecognizer/FormRecognizerClientBuilder.java b/sdk/formrecognizer/azure-ai-formrecognizer/src/main/java/com/azure/ai/formrecognizer/FormRecognizerClientBuilder.java index a3920bd782ce..a8e8e284116c 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/src/main/java/com/azure/ai/formrecognizer/FormRecognizerClientBuilder.java +++ b/sdk/formrecognizer/azure-ai-formrecognizer/src/main/java/com/azure/ai/formrecognizer/FormRecognizerClientBuilder.java @@ -7,6 +7,7 @@ import com.azure.ai.formrecognizer.implementation.FormRecognizerClientImplBuilder; import com.azure.core.annotation.ServiceClientBuilder; import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.credential.TokenCredential; import com.azure.core.http.ContentType; import com.azure.core.http.HttpClient; import com.azure.core.http.HttpHeaders; @@ -15,6 +16,7 @@ import com.azure.core.http.policy.AddDatePolicy; import com.azure.core.http.policy.AddHeadersPolicy; import com.azure.core.http.policy.AzureKeyCredentialPolicy; +import com.azure.core.http.policy.BearerTokenAuthenticationPolicy; import com.azure.core.http.policy.HttpLogDetailLevel; import com.azure.core.http.policy.HttpLogOptions; import com.azure.core.http.policy.HttpLoggingPolicy; @@ -43,7 +45,8 @@ * *

* The client needs the service endpoint of the Azure Form Recognizer to access the resource service. - * {@link #credential(AzureKeyCredential)} gives the builder access credential. + * {@link #credential(AzureKeyCredential)} or {@link #credential(TokenCredential) credential(TokenCredential)} gives + * the builder access credential. *

* *

Instantiating an asynchronous Form Recognizer Client

@@ -77,6 +80,7 @@ public final class FormRecognizerClientBuilder { private static final String NAME = "name"; private static final String VERSION = "version"; private static final RetryPolicy DEFAULT_RETRY_POLICY = new RetryPolicy("retry-after-ms", ChronoUnit.MILLIS); + private static final String DEFAULT_SCOPE = "https://cognitiveservices.azure.com/.default"; private final ClientLogger logger = new ClientLogger(FormRecognizerClientBuilder.class); private final List policies; @@ -91,6 +95,7 @@ public final class FormRecognizerClientBuilder { private HttpPipeline httpPipeline; private Configuration configuration; private RetryPolicy retryPolicy; + private TokenCredential tokenCredential; private FormRecognizerServiceVersion version; static final String OCP_APIM_SUBSCRIPTION_KEY = "Ocp-Apim-Subscription-Key"; @@ -182,7 +187,9 @@ private HttpPipeline getDefaultHttpPipeline(Configuration buildConfiguration) { policies.add(retryPolicy == null ? DEFAULT_RETRY_POLICY : retryPolicy); policies.add(new AddDatePolicy()); // Authentications - if (credential != null) { + if (tokenCredential != null) { + policies.add(new BearerTokenAuthenticationPolicy(tokenCredential, DEFAULT_SCOPE)); + } else if (credential != null) { policies.add(new AzureKeyCredentialPolicy(OCP_APIM_SUBSCRIPTION_KEY, credential)); } else { // Throw exception that credential and tokenCredential cannot be null @@ -241,6 +248,18 @@ public FormRecognizerClientBuilder credential(AzureKeyCredential apiKeyCredentia return this; } + /** + * Sets the {@link TokenCredential} used to authenticate HTTP requests. + * + * @param tokenCredential {@link TokenCredential} used to authenticate HTTP requests. + * @return The updated {@link FormRecognizerClientBuilder} object. + * @throws NullPointerException If {@code tokenCredential} is null. + */ + public FormRecognizerClientBuilder credential(TokenCredential tokenCredential) { + this.tokenCredential = Objects.requireNonNull(tokenCredential, "'tokenCredential' cannot be null."); + return this; + } + /** * Sets the logging configuration for HTTP requests and responses. * diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/src/main/java/com/azure/ai/formrecognizer/training/FormTrainingClientBuilder.java b/sdk/formrecognizer/azure-ai-formrecognizer/src/main/java/com/azure/ai/formrecognizer/training/FormTrainingClientBuilder.java index 6baf2d23bb89..6eb480d34da2 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/src/main/java/com/azure/ai/formrecognizer/training/FormTrainingClientBuilder.java +++ b/sdk/formrecognizer/azure-ai-formrecognizer/src/main/java/com/azure/ai/formrecognizer/training/FormTrainingClientBuilder.java @@ -8,6 +8,7 @@ import com.azure.ai.formrecognizer.implementation.FormRecognizerClientImplBuilder; import com.azure.core.annotation.ServiceClientBuilder; import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.credential.TokenCredential; import com.azure.core.http.ContentType; import com.azure.core.http.HttpClient; import com.azure.core.http.HttpHeaders; @@ -16,6 +17,7 @@ import com.azure.core.http.policy.AddDatePolicy; import com.azure.core.http.policy.AddHeadersPolicy; import com.azure.core.http.policy.AzureKeyCredentialPolicy; +import com.azure.core.http.policy.BearerTokenAuthenticationPolicy; import com.azure.core.http.policy.HttpLogDetailLevel; import com.azure.core.http.policy.HttpLogOptions; import com.azure.core.http.policy.HttpLoggingPolicy; @@ -43,7 +45,8 @@ * *

* The client needs the service endpoint of the Azure Form Recognizer to access the resource service. - * {@link #credential(AzureKeyCredential)} gives the builder access credential. + * {@link #credential(AzureKeyCredential)} or {@link #credential(TokenCredential) credential(TokenCredential)} gives + * the builder access credential. *

* *

Instantiating an asynchronous Form Training Client

@@ -91,9 +94,11 @@ public final class FormTrainingClientBuilder { private HttpPipeline httpPipeline; private Configuration configuration; private RetryPolicy retryPolicy; + private TokenCredential tokenCredential; private FormRecognizerServiceVersion version; - static final String OCP_APIM_SUBSCRIPTION_KEY = "Ocp-Apim-Subscription-Key"; + private static final String DEFAULT_SCOPE = "https://cognitiveservices.azure.com/.default"; + private static final String OCP_APIM_SUBSCRIPTION_KEY = "Ocp-Apim-Subscription-Key"; /** * The constructor with defaults. @@ -184,7 +189,10 @@ private HttpPipeline getDefaultHttpPipeline() { policies.add(retryPolicy == null ? DEFAULT_RETRY_POLICY : retryPolicy); policies.add(new AddDatePolicy()); // Authentications - if (credential != null) { + if (tokenCredential != null) { + // User token based policy + policies.add(new BearerTokenAuthenticationPolicy(tokenCredential, DEFAULT_SCOPE)); + } else if (credential != null) { policies.add(new AzureKeyCredentialPolicy(OCP_APIM_SUBSCRIPTION_KEY, credential)); } else { // Throw exception that credential and tokenCredential cannot be null @@ -243,6 +251,18 @@ public FormTrainingClientBuilder credential(AzureKeyCredential apiKeyCredential) return this; } + /** + * Sets the {@link TokenCredential} used to authenticate HTTP requests. + * + * @param tokenCredential {@link TokenCredential} used to authenticate HTTP requests. + * @return The updated {@link FormTrainingClientBuilder} object. + * @throws NullPointerException If {@code tokenCredential} is null. + */ + public FormTrainingClientBuilder credential(TokenCredential tokenCredential) { + this.tokenCredential = Objects.requireNonNull(tokenCredential, "'tokenCredential' cannot be null."); + return this; + } + /** * Sets the logging configuration for HTTP requests and responses. * diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/src/samples/README.md b/sdk/formrecognizer/azure-ai-formrecognizer/src/samples/README.md index aa9529a6c0d4..5434d9578b8f 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/src/samples/README.md +++ b/sdk/formrecognizer/azure-ai-formrecognizer/src/samples/README.md @@ -35,7 +35,7 @@ The following sections provide code samples covering common scenario operations - Manage the custom models in your account - [ManageCustomModels][manage_custom_models] and [ManageCustomModelsAsync][manage_custom_models_async] - Copy custom model between Form Recognizer resources - - [CopyModel](copy_model) and [CopyModelAsync](copy_model_async) + - [CopyModel][copy_model] and [CopyModelAsync][copy_model_async] ## Troubleshooting Troubleshooting steps can be found [here][SDK_README_TROUBLESHOOTING]. diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/src/samples/java/com/azure/ai/formrecognizer/Authentication.java b/sdk/formrecognizer/azure-ai-formrecognizer/src/samples/java/com/azure/ai/formrecognizer/Authentication.java new file mode 100644 index 000000000000..d3f0438ac452 --- /dev/null +++ b/sdk/formrecognizer/azure-ai-formrecognizer/src/samples/java/com/azure/ai/formrecognizer/Authentication.java @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.ai.formrecognizer; + +import com.azure.ai.formrecognizer.models.AccountProperties; +import com.azure.ai.formrecognizer.models.OperationResult; +import com.azure.ai.formrecognizer.models.RecognizedReceipt; +import com.azure.ai.formrecognizer.models.USReceipt; +import com.azure.ai.formrecognizer.training.FormTrainingClient; +import com.azure.ai.formrecognizer.training.FormTrainingClientBuilder; +import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.util.polling.SyncPoller; +import com.azure.identity.DefaultAzureCredentialBuilder; + +import java.io.IOException; +import java.util.List; + +/** + * Samples for two supported methods of authentication in Form Recognizer and Form Training clients: + * 1) Use a Form Recognizer API key with AzureKeyCredential from azure.core.credentials + * 2) Use a token credential from azure-identity to authenticate with Azure Active Directory + */ +public class Authentication { + /** + * Main method to invoke this demo. + * + * @param args Unused arguments to the program. + * + * @throws IOException Exception thrown when there is an error in reading all the bytes from the File. + */ + public static void main(String[] args) { + /* + Set the environment variables with your own values before running the sample: + + 1) AZURE_FORM_RECOGNIZER_ENDPOINT - the endpoint to your Form Recognizer resource. + 2) AZURE_FORM_RECOGNIZER_KEY - your Form Recognizer API key + 3) AZURE_CLIENT_ID - the client ID of your active directory application. + 4) AZURE_TENANT_ID - the tenant ID of your active directory application. + 5) AZURE_CLIENT_SECRET - the secret of your active directory application. + */ + // Form recognizer client: Key credential + authenticationWithKeyCredentialFormRecognizerClient(); + // Form recognizer client: Azure Active Directory + authenticationWithAzureActiveDirectoryFormRecognizerClient(); + // Form training client: Key credential + authenticationWithKeyCredentialFormTrainingClient(); + // Form training client: Azure Active Directory + authenticationWithAzureActiveDirectoryFormTrainingClient(); + } + + private static void authenticationWithKeyCredentialFormRecognizerClient() { + FormRecognizerClient formRecognizerClient = new FormRecognizerClientBuilder() + .credential(new AzureKeyCredential("{key}")) + .endpoint("{endpoint}") + .buildClient(); + beginRecognizeCustomFormsFromUrl(formRecognizerClient); + } + + private static void authenticationWithAzureActiveDirectoryFormRecognizerClient() { + FormRecognizerClient formRecognizerClient = new FormRecognizerClientBuilder() + .credential(new DefaultAzureCredentialBuilder().build()) + .endpoint("{endpoint}") + .buildClient(); + beginRecognizeCustomFormsFromUrl(formRecognizerClient); + } + + private static void authenticationWithKeyCredentialFormTrainingClient() { + FormTrainingClient formTrainingClient = new FormTrainingClientBuilder() + .credential(new AzureKeyCredential("{key}")) + .endpoint("{endpoint}") + .buildClient(); + getAccountProperties(formTrainingClient); + } + + private static void authenticationWithAzureActiveDirectoryFormTrainingClient() { + FormTrainingClient formTrainingClient = new FormTrainingClientBuilder() + .credential(new DefaultAzureCredentialBuilder().build()) + .endpoint("{endpoint}") + .buildClient(); + getAccountProperties(formTrainingClient); + } + + private static void beginRecognizeCustomFormsFromUrl(FormRecognizerClient formRecognizerClient) { + String receiptUrl = "https://raw.githubusercontent.com/Azure/azure-sdk-for-java/master/sdk/formrecognizer/azure-ai-formrecognizer/src/samples/java/sample-forms/receipts/contoso-allinone.jpg"; + + SyncPoller> recognizeReceiptPoller = + formRecognizerClient.beginRecognizeReceiptsFromUrl(receiptUrl); + + List receiptPageResults = recognizeReceiptPoller.getFinalResult(); + + for (int i = 0; i < receiptPageResults.size(); i++) { + final RecognizedReceipt recognizedReceipt = receiptPageResults.get(i); + System.out.printf("----------- Recognized Receipt page %s -----------", i); + USReceipt usReceipt = ReceiptExtensions.asUSReceipt(recognizedReceipt); + System.out.printf("Merchant Name: %s, confidence: %.2f%n", usReceipt.getMerchantName().getFieldValue(), + usReceipt.getMerchantName().getConfidence()); + System.out.printf("Merchant Address: %s, confidence: %.2f%n", usReceipt.getMerchantAddress().getName(), + usReceipt.getMerchantAddress().getConfidence()); + System.out.printf("Merchant Phone Number %s, confidence: %.2f%n", + usReceipt.getMerchantPhoneNumber().getFieldValue(), usReceipt.getMerchantPhoneNumber().getConfidence()); + System.out.printf("Total: %s confidence: %.2f%n", usReceipt.getTotal().getName(), + usReceipt.getTotal().getConfidence()); + System.out.printf("Receipt Items: %n"); + usReceipt.getReceiptItems().forEach(receiptItem -> { + if (receiptItem.getName() != null) { + System.out.printf("Name: %s, confidence: %.2f%n", receiptItem.getName().getFieldValue(), + receiptItem.getName().getConfidence()); + } + if (receiptItem.getQuantity() != null) { + System.out.printf("Quantity: %s, confidence: %.2f%n", receiptItem.getQuantity().getFieldValue(), + receiptItem.getQuantity().getConfidence()); + } + if (receiptItem.getPrice() != null) { + System.out.printf("Price: %s, confidence: %.2f%n", receiptItem.getPrice().getFieldValue(), + receiptItem.getPrice().getConfidence()); + } + if (receiptItem.getTotalPrice() != null) { + System.out.printf("Total Price: %s, confidence: %.2f%n", + receiptItem.getTotalPrice().getFieldValue(), receiptItem.getTotalPrice().getConfidence()); + } + }); + System.out.print("-----------------------------------"); + } + } + + private static void getAccountProperties(FormTrainingClient formTrainingClient) { + AccountProperties accountProperties = formTrainingClient.getAccountProperties(); + System.out.printf("Max number of models that can be trained for this account: %s%n", + accountProperties.getCustomModelLimit()); + System.out.printf("Current count of trained custom models: %d%n", accountProperties.getCustomModelCount()); + } +} diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java b/sdk/formrecognizer/azure-ai-formrecognizer/src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java index 22ba5d3f54b5..1cac39ecb317 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java +++ b/sdk/formrecognizer/azure-ai-formrecognizer/src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java @@ -15,8 +15,10 @@ import com.azure.ai.formrecognizer.training.FormTrainingClient; import com.azure.ai.formrecognizer.training.FormTrainingClientBuilder; import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.credential.TokenCredential; import com.azure.core.http.rest.PagedIterable; import com.azure.core.util.polling.SyncPoller; +import com.azure.identity.DefaultAzureCredentialBuilder; import java.util.List; import java.util.concurrent.atomic.AtomicReference; @@ -42,6 +44,17 @@ public void useAzureKeyCredentialSyncClient() { .buildClient(); } + /** + * Code snippet for getting async client using AAD authentication. + */ + public void useAadAsyncClient() { + TokenCredential credential = new DefaultAzureCredentialBuilder().build(); + FormRecognizerClient formRecognizerClient = new FormRecognizerClientBuilder() + .endpoint("{endpoint}") + .credential(credential) + .buildClient(); + } + /** * Code snippet for rotating AzureKeyCredential of the client */ diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormRecognizerAsyncClientTest.java b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormRecognizerAsyncClientTest.java index 981025cb4566..7cdcffd6b094 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormRecognizerAsyncClientTest.java +++ b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormRecognizerAsyncClientTest.java @@ -12,13 +12,8 @@ import com.azure.ai.formrecognizer.models.RecognizedForm; import com.azure.ai.formrecognizer.models.RecognizedReceipt; import com.azure.ai.formrecognizer.training.FormTrainingAsyncClient; -import com.azure.ai.formrecognizer.training.FormTrainingClientBuilder; -import com.azure.core.credential.AzureKeyCredential; import com.azure.core.exception.HttpResponseException; import com.azure.core.http.HttpClient; -import com.azure.core.http.policy.HttpLogDetailLevel; -import com.azure.core.http.policy.HttpLogOptions; -import com.azure.core.test.TestMode; import com.azure.core.util.polling.SyncPoller; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -32,7 +27,6 @@ import static com.azure.ai.formrecognizer.TestUtils.CUSTOM_FORM_FILE_LENGTH; import static com.azure.ai.formrecognizer.TestUtils.DISPLAY_NAME_WITH_ARGUMENTS; import static com.azure.ai.formrecognizer.TestUtils.FORM_LOCAL_URL; -import static com.azure.ai.formrecognizer.TestUtils.INVALID_KEY; import static com.azure.ai.formrecognizer.TestUtils.INVALID_SOURCE_URL_ERROR; import static com.azure.ai.formrecognizer.TestUtils.INVALID_URL; import static com.azure.ai.formrecognizer.TestUtils.LAYOUT_FILE_LENGTH; @@ -60,28 +54,12 @@ static void afterAll() { private FormRecognizerAsyncClient getFormRecognizerAsyncClient(HttpClient httpClient, FormRecognizerServiceVersion serviceVersion) { - FormRecognizerClientBuilder builder = new FormRecognizerClientBuilder() - .endpoint(getEndpoint()).httpClient(httpClient == null - ? interceptorManager.getPlaybackClient() : httpClient) - .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS)) - .serviceVersion(serviceVersion).addPolicy(interceptorManager.getRecordPolicy()); - AzureKeyCredential credential = (getTestMode() == TestMode.PLAYBACK) ? new AzureKeyCredential(INVALID_KEY) - : new AzureKeyCredential(getApiKey()); - builder.credential(credential); - return builder.buildAsyncClient(); + return getFormRecognizerClientBuilder(httpClient, serviceVersion).buildAsyncClient(); } private FormTrainingAsyncClient getFormTrainingAsyncClient(HttpClient httpClient, FormRecognizerServiceVersion serviceVersion) { - FormTrainingClientBuilder builder = new FormTrainingClientBuilder() - .endpoint(getEndpoint()).httpClient(httpClient == null - ? interceptorManager.getPlaybackClient() : httpClient) - .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS)) - .serviceVersion(serviceVersion).addPolicy(interceptorManager.getRecordPolicy()); - AzureKeyCredential credential = (getTestMode() == TestMode.PLAYBACK) ? new AzureKeyCredential(INVALID_KEY) - : new AzureKeyCredential(getApiKey()); - builder.credential(credential); - return builder.buildAsyncClient(); + return getFormTrainingClientBuilder(httpClient, serviceVersion).buildAsyncClient(); } /** * Verifies receipt data for a document using source as file url. diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormRecognizerClientBuilderUnitTest.java b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormRecognizerClientBuilderUnitTest.java index b6e41332f82f..c1cd3971515e 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormRecognizerClientBuilderUnitTest.java +++ b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormRecognizerClientBuilderUnitTest.java @@ -4,6 +4,7 @@ package com.azure.ai.formrecognizer; import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.credential.TokenCredential; import org.junit.jupiter.api.Test; import static com.azure.ai.formrecognizer.TestUtils.VALID_HTTPS_LOCALHOST; @@ -52,9 +53,22 @@ public void invalidProtocol() { */ @Test public void nullAzureKeyCredential() { + AzureKeyCredential azureKeyCredential = null; assertThrows(NullPointerException.class, () -> { final FormRecognizerClientBuilder builder = new FormRecognizerClientBuilder(); - builder.endpoint(VALID_HTTPS_LOCALHOST).credential(null); + builder.endpoint(VALID_HTTPS_LOCALHOST).credential(azureKeyCredential); + }); + } + + /** + * Test for null AAD credential + */ + @Test + public void nullAADCredential() { + TokenCredential tokenCredential = null; + assertThrows(NullPointerException.class, () -> { + final FormRecognizerClientBuilder builder = new FormRecognizerClientBuilder(); + builder.endpoint(VALID_HTTPS_LOCALHOST).credential(tokenCredential); }); } diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormRecognizerClientTest.java b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormRecognizerClientTest.java index 7d06965a0703..3f29134d4f98 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormRecognizerClientTest.java +++ b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormRecognizerClientTest.java @@ -12,13 +12,8 @@ import com.azure.ai.formrecognizer.models.RecognizedForm; import com.azure.ai.formrecognizer.models.RecognizedReceipt; import com.azure.ai.formrecognizer.training.FormTrainingClient; -import com.azure.ai.formrecognizer.training.FormTrainingClientBuilder; -import com.azure.core.credential.AzureKeyCredential; import com.azure.core.exception.HttpResponseException; import com.azure.core.http.HttpClient; -import com.azure.core.http.policy.HttpLogDetailLevel; -import com.azure.core.http.policy.HttpLogOptions; -import com.azure.core.test.TestMode; import com.azure.core.util.polling.SyncPoller; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -28,7 +23,6 @@ import static com.azure.ai.formrecognizer.TestUtils.CUSTOM_FORM_FILE_LENGTH; import static com.azure.ai.formrecognizer.TestUtils.DISPLAY_NAME_WITH_ARGUMENTS; import static com.azure.ai.formrecognizer.TestUtils.FORM_LOCAL_URL; -import static com.azure.ai.formrecognizer.TestUtils.INVALID_KEY; import static com.azure.ai.formrecognizer.TestUtils.INVALID_SOURCE_URL_ERROR; import static com.azure.ai.formrecognizer.TestUtils.INVALID_URL; import static com.azure.ai.formrecognizer.TestUtils.LAYOUT_FILE_LENGTH; @@ -46,30 +40,12 @@ public class FormRecognizerClientTest extends FormRecognizerClientTestBase { private FormRecognizerClient getFormRecognizerClient(HttpClient httpClient, FormRecognizerServiceVersion serviceVersion) { - FormRecognizerClientBuilder builder = new FormRecognizerClientBuilder() - .endpoint(getEndpoint()) - .httpClient(httpClient == null ? interceptorManager.getPlaybackClient() : httpClient) - .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS)) - .serviceVersion(serviceVersion) - .addPolicy(interceptorManager.getRecordPolicy()); - AzureKeyCredential credential = (getTestMode() == TestMode.PLAYBACK) - ? new AzureKeyCredential(INVALID_KEY) : new AzureKeyCredential(getApiKey()); - builder.credential(credential); - return builder.buildClient(); + return getFormRecognizerClientBuilder(httpClient, serviceVersion).buildClient(); } private FormTrainingClient getFormTrainingClient(HttpClient httpClient, FormRecognizerServiceVersion serviceVersion) { - FormTrainingClientBuilder builder = new FormTrainingClientBuilder() - .endpoint(getEndpoint()) - .httpClient(httpClient == null ? interceptorManager.getPlaybackClient() : httpClient) - .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS)) - .serviceVersion(serviceVersion) - .addPolicy(interceptorManager.getRecordPolicy()); - AzureKeyCredential credential = (getTestMode() == TestMode.PLAYBACK) - ? new AzureKeyCredential(INVALID_KEY) : new AzureKeyCredential(getApiKey()); - builder.credential(credential); - return builder.buildClient(); + return getFormTrainingClientBuilder(httpClient, serviceVersion).buildClient(); } /** diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormRecognizerClientTestBase.java b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormRecognizerClientTestBase.java index cc2741ff9013..f89c2a534591 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormRecognizerClientTestBase.java +++ b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormRecognizerClientTestBase.java @@ -20,10 +20,10 @@ import com.azure.ai.formrecognizer.models.FormField; import com.azure.ai.formrecognizer.models.FormLine; import com.azure.ai.formrecognizer.models.FormPage; +import com.azure.ai.formrecognizer.models.FormPageRange; import com.azure.ai.formrecognizer.models.FormTable; import com.azure.ai.formrecognizer.models.FormTableCell; import com.azure.ai.formrecognizer.models.FormWord; -import com.azure.ai.formrecognizer.models.FormPageRange; import com.azure.ai.formrecognizer.models.Point; import com.azure.ai.formrecognizer.models.ReceiptItemType; import com.azure.ai.formrecognizer.models.RecognizedForm; @@ -31,11 +31,17 @@ import com.azure.ai.formrecognizer.models.TextContentType; import com.azure.ai.formrecognizer.models.USReceipt; import com.azure.ai.formrecognizer.models.USReceiptItem; +import com.azure.ai.formrecognizer.training.FormTrainingClientBuilder; +import com.azure.core.credential.AzureKeyCredential; import com.azure.core.http.HttpClient; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.http.policy.HttpLogOptions; import com.azure.core.test.TestBase; +import com.azure.core.test.TestMode; import com.azure.core.test.models.NetworkCallRecord; import com.azure.core.util.Configuration; import com.azure.core.util.serializer.SerializerAdapter; +import com.azure.identity.DefaultAzureCredentialBuilder; import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; @@ -48,12 +54,12 @@ import java.util.function.Consumer; import java.util.regex.Pattern; -import static com.azure.ai.formrecognizer.FormTrainingClientTestBase.AZURE_FORM_RECOGNIZER_API_KEY; import static com.azure.ai.formrecognizer.FormTrainingClientTestBase.AZURE_FORM_RECOGNIZER_ENDPOINT; import static com.azure.ai.formrecognizer.FormTrainingClientTestBase.FORM_RECOGNIZER_TESTING_BLOB_CONTAINER_SAS_URL; import static com.azure.ai.formrecognizer.FormTrainingClientTestBase.FORM_RECOGNIZER_TRAINING_BLOB_CONTAINER_SAS_URL; import static com.azure.ai.formrecognizer.FormTrainingClientTestBase.deserializeRawResponse; import static com.azure.ai.formrecognizer.TestUtils.FORM_JPG; +import static com.azure.ai.formrecognizer.TestUtils.INVALID_KEY; import static com.azure.ai.formrecognizer.TestUtils.getFileData; import static com.azure.ai.formrecognizer.TestUtils.getSerializerAdapter; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -71,6 +77,40 @@ public abstract class FormRecognizerClientTestBase extends TestBase { static final String OCR_EXTRACTION_INVALID_URL_ERROR = "OCR extraction error: [Wrong response code: InvalidImageURL. Message: Image URL is badly formatted..]"; static final String EXPECTED_INVALID_URL_ERROR_CODE = "3003"; + FormRecognizerClientBuilder getFormRecognizerClientBuilder(HttpClient httpClient, + FormRecognizerServiceVersion serviceVersion) { + FormRecognizerClientBuilder builder = new FormRecognizerClientBuilder() + .endpoint(getEndpoint()) + .httpClient(httpClient == null ? interceptorManager.getPlaybackClient() : httpClient) + .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS)) + .serviceVersion(serviceVersion) + .addPolicy(interceptorManager.getRecordPolicy()); + + if (getTestMode() == TestMode.PLAYBACK) { + builder.credential(new AzureKeyCredential(INVALID_KEY)); + } else { + builder.credential(new DefaultAzureCredentialBuilder().build()); + } + return builder; + } + + FormTrainingClientBuilder getFormTrainingClientBuilder(HttpClient httpClient, + FormRecognizerServiceVersion serviceVersion) { + FormTrainingClientBuilder builder = new FormTrainingClientBuilder() + .endpoint(getEndpoint()) + .httpClient(httpClient == null ? interceptorManager.getPlaybackClient() : httpClient) + .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS)) + .serviceVersion(serviceVersion) + .addPolicy(interceptorManager.getRecordPolicy()); + + if (getTestMode() == TestMode.PLAYBACK) { + builder.credential(new AzureKeyCredential(INVALID_KEY)); + } else { + builder.credential(new DefaultAzureCredentialBuilder().build()); + } + return builder; + } + private static void validateReferenceElementsData(List expectedElements, List actualFormContentList, List readResults) { if (expectedElements != null && actualFormContentList != null) { @@ -578,16 +618,6 @@ static void validateMultipageReceiptData(List recognizedRecei assertEquals(ITEMIZED_RECEIPT_VALUE, receiptPage3.getReceiptType().getType()); } - /** - * Get the string of API key value based on the test running mode. - * - * @return the API key string - */ - String getApiKey() { - return interceptorManager.isPlaybackMode() ? "apiKeyInPlayback" - : Configuration.getGlobalConfiguration().get(AZURE_FORM_RECOGNIZER_API_KEY); - } - protected String getEndpoint() { return interceptorManager.isPlaybackMode() ? "https://localhost:8080" : Configuration.getGlobalConfiguration().get(AZURE_FORM_RECOGNIZER_ENDPOINT); diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormTrainingAsyncClientTest.java b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormTrainingAsyncClientTest.java index 1ecbcc6c9748..24ccd9dc68ba 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormTrainingAsyncClientTest.java +++ b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormTrainingAsyncClientTest.java @@ -7,13 +7,8 @@ import com.azure.ai.formrecognizer.models.ErrorInformation; import com.azure.ai.formrecognizer.models.OperationResult; import com.azure.ai.formrecognizer.training.FormTrainingAsyncClient; -import com.azure.ai.formrecognizer.training.FormTrainingClientBuilder; -import com.azure.core.credential.AzureKeyCredential; import com.azure.core.exception.HttpResponseException; import com.azure.core.http.HttpClient; -import com.azure.core.http.policy.HttpLogDetailLevel; -import com.azure.core.http.policy.HttpLogOptions; -import com.azure.core.test.TestMode; import com.azure.core.util.polling.SyncPoller; import io.netty.handler.codec.http.HttpResponseStatus; import org.junit.jupiter.api.AfterAll; @@ -26,11 +21,9 @@ import java.util.List; import static com.azure.ai.formrecognizer.TestUtils.DISPLAY_NAME_WITH_ARGUMENTS; -import static com.azure.ai.formrecognizer.TestUtils.INVALID_KEY; import static com.azure.ai.formrecognizer.TestUtils.INVALID_MODEL_ID; import static com.azure.ai.formrecognizer.TestUtils.INVALID_MODEL_ID_ERROR; import static com.azure.ai.formrecognizer.TestUtils.NULL_SOURCE_URL_ERROR; -import static com.azure.ai.formrecognizer.TestUtils.getExpectedAccountProperties; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -52,16 +45,7 @@ static void afterAll() { private FormTrainingAsyncClient getFormTrainingAsyncClient(HttpClient httpClient, FormRecognizerServiceVersion serviceVersion) { - FormTrainingClientBuilder builder = new FormTrainingClientBuilder() - .endpoint(getEndpoint()) - .httpClient(httpClient == null ? interceptorManager.getPlaybackClient() : httpClient) - .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS)) - .serviceVersion(serviceVersion) - .addPolicy(interceptorManager.getRecordPolicy()); - AzureKeyCredential credential = (getTestMode() == TestMode.PLAYBACK) - ? new AzureKeyCredential(INVALID_KEY) : new AzureKeyCredential(getApiKey()); - builder.credential(credential); - return builder.buildAsyncClient(); + return getFormTrainingClientBuilder(httpClient, serviceVersion).buildAsyncClient(); } /** @@ -150,8 +134,7 @@ public void getCustomModelLabeled(HttpClient httpClient, FormRecognizerServiceVe public void validGetAccountProperties(HttpClient httpClient, FormRecognizerServiceVersion serviceVersion) { client = getFormTrainingAsyncClient(httpClient, serviceVersion); StepVerifier.create(client.getAccountProperties()) - .assertNext(accountProperties -> validateAccountProperties(getExpectedAccountProperties(), - accountProperties)) + .assertNext(accountProperties -> validateAccountProperties(accountProperties)) .verifyComplete(); } @@ -163,8 +146,7 @@ public void validGetAccountProperties(HttpClient httpClient, FormRecognizerServi public void validGetAccountPropertiesWithResponse(HttpClient httpClient, FormRecognizerServiceVersion serviceVersion) { client = getFormTrainingAsyncClient(httpClient, serviceVersion); StepVerifier.create(client.getAccountProperties()) - .assertNext(accountProperties -> - validateAccountProperties(getExpectedAccountProperties(), accountProperties)) + .assertNext(accountProperties -> validateAccountProperties(accountProperties)) .verifyComplete(); } @@ -366,7 +348,7 @@ void beginTrainingInvalidModelStatus(HttpClient httpClient, FormRecognizerServic HttpResponseException httpResponseException = assertThrows(HttpResponseException.class, () -> client.beginTraining(invalidTrainingFilesUrl, useTrainingLabels).getSyncPoller().getFinalResult()); ErrorInformation errorInformation = (ErrorInformation) ((List) httpResponseException.getValue()).get(0); - assertEquals(EXPECTED_INVALID_MODEL_STATUS_MESSAGE, httpResponseException.getMessage()); + assertTrue(httpResponseException.getMessage().contains(EXPECTED_INVALID_MODEL_STATUS_MESSAGE)); assertEquals(EXPECTED_INVALID_MODEL_STATUS_ERROR_CODE, errorInformation.getCode()); assertEquals(EXPECTED_INVALID_STATUS_ERROR_INFORMATION, errorInformation.getMessage()); }); diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormTrainingClientBuilderUnitTest.java b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormTrainingClientBuilderUnitTest.java index 725b9c3a3a27..9fc64545dbe0 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormTrainingClientBuilderUnitTest.java +++ b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormTrainingClientBuilderUnitTest.java @@ -5,6 +5,7 @@ import com.azure.ai.formrecognizer.training.FormTrainingClientBuilder; import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.credential.TokenCredential; import org.junit.jupiter.api.Test; import static com.azure.ai.formrecognizer.TestUtils.VALID_HTTPS_LOCALHOST; @@ -53,9 +54,22 @@ public void invalidProtocol() { */ @Test public void nullAzureKeyCredential() { + AzureKeyCredential credential = null; assertThrows(NullPointerException.class, () -> { final FormTrainingClientBuilder builder = new FormTrainingClientBuilder(); - builder.endpoint(VALID_HTTPS_LOCALHOST).credential(null); + builder.endpoint(VALID_HTTPS_LOCALHOST).credential(credential); + }); + } + + /** + * Test for null AAD credential + */ + @Test + public void nullAADCredential() { + TokenCredential tokenCredential = null; + assertThrows(NullPointerException.class, () -> { + final FormTrainingClientBuilder builder = new FormTrainingClientBuilder(); + builder.endpoint(VALID_HTTPS_LOCALHOST).credential(tokenCredential); }); } diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormTrainingClientTest.java b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormTrainingClientTest.java index 54e760b093ed..05e6bba2f5a9 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormTrainingClientTest.java +++ b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormTrainingClientTest.java @@ -10,14 +10,9 @@ import com.azure.ai.formrecognizer.models.ErrorResponseException; import com.azure.ai.formrecognizer.models.OperationResult; import com.azure.ai.formrecognizer.training.FormTrainingClient; -import com.azure.ai.formrecognizer.training.FormTrainingClientBuilder; -import com.azure.core.credential.AzureKeyCredential; import com.azure.core.exception.HttpResponseException; import com.azure.core.http.HttpClient; -import com.azure.core.http.policy.HttpLogDetailLevel; -import com.azure.core.http.policy.HttpLogOptions; import com.azure.core.http.rest.Response; -import com.azure.core.test.TestMode; import com.azure.core.util.Context; import com.azure.core.util.polling.SyncPoller; import io.netty.handler.codec.http.HttpResponseStatus; @@ -27,11 +22,9 @@ import java.util.List; import static com.azure.ai.formrecognizer.TestUtils.DISPLAY_NAME_WITH_ARGUMENTS; -import static com.azure.ai.formrecognizer.TestUtils.INVALID_KEY; import static com.azure.ai.formrecognizer.TestUtils.INVALID_MODEL_ID; import static com.azure.ai.formrecognizer.TestUtils.INVALID_MODEL_ID_ERROR; import static com.azure.ai.formrecognizer.TestUtils.NULL_SOURCE_URL_ERROR; -import static com.azure.ai.formrecognizer.TestUtils.getExpectedAccountProperties; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -41,16 +34,7 @@ public class FormTrainingClientTest extends FormTrainingClientTestBase { private FormTrainingClient getFormTrainingClient(HttpClient httpClient, FormRecognizerServiceVersion serviceVersion) { - FormTrainingClientBuilder builder = new FormTrainingClientBuilder() - .endpoint(getEndpoint()) - .httpClient(httpClient == null ? interceptorManager.getPlaybackClient() : httpClient) - .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS)) - .serviceVersion(serviceVersion) - .addPolicy(interceptorManager.getRecordPolicy()); - AzureKeyCredential credential = (getTestMode() == TestMode.PLAYBACK) - ? new AzureKeyCredential(INVALID_KEY) : new AzureKeyCredential(getApiKey()); - builder.credential(credential); - return builder.buildClient(); + return getFormTrainingClientBuilder(httpClient, serviceVersion).buildClient(); } /** @@ -129,7 +113,7 @@ public void getCustomModelLabeled(HttpClient httpClient, FormRecognizerServiceVe @MethodSource("com.azure.ai.formrecognizer.TestUtils#getTestParameters") public void validGetAccountProperties(HttpClient httpClient, FormRecognizerServiceVersion serviceVersion) { client = getFormTrainingClient(httpClient, serviceVersion); - validateAccountProperties(getExpectedAccountProperties(), client.getAccountProperties()); + validateAccountProperties(client.getAccountProperties()); } /** @@ -141,7 +125,7 @@ public void validGetAccountPropertiesWithResponse(HttpClient httpClient, FormRec client = getFormTrainingClient(httpClient, serviceVersion); Response accountPropertiesResponse = client.getAccountPropertiesWithResponse(Context.NONE); assertEquals(accountPropertiesResponse.getStatusCode(), HttpResponseStatus.OK.code()); - validateAccountProperties(getExpectedAccountProperties(), accountPropertiesResponse.getValue()); + validateAccountProperties(accountPropertiesResponse.getValue()); } /** @@ -317,7 +301,7 @@ void beginTrainingInvalidModelStatus(HttpClient httpClient, FormRecognizerServic HttpResponseException httpResponseException = assertThrows(HttpResponseException.class, () -> client.beginTraining(invalidTrainingFilesUrl, useTrainingLabels).getFinalResult()); ErrorInformation errorInformation = (ErrorInformation) ((List) httpResponseException.getValue()).get(0); - assertEquals(EXPECTED_INVALID_MODEL_STATUS_MESSAGE, httpResponseException.getMessage()); + assertTrue(httpResponseException.getMessage().contains(EXPECTED_INVALID_MODEL_STATUS_MESSAGE)); assertEquals(EXPECTED_INVALID_MODEL_STATUS_ERROR_CODE, errorInformation.getCode()); assertEquals(EXPECTED_INVALID_STATUS_ERROR_INFORMATION, errorInformation.getMessage()); }); diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormTrainingClientTestBase.java b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormTrainingClientTestBase.java index 31f7d9263958..2807cf4a75c2 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormTrainingClientTestBase.java +++ b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/FormTrainingClientTestBase.java @@ -13,12 +13,18 @@ import com.azure.ai.formrecognizer.models.ErrorInformation; import com.azure.ai.formrecognizer.models.FormRecognizerError; import com.azure.ai.formrecognizer.models.TrainingDocumentInfo; +import com.azure.ai.formrecognizer.training.FormTrainingClientBuilder; +import com.azure.core.credential.AzureKeyCredential; import com.azure.core.http.HttpClient; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.http.policy.HttpLogOptions; import com.azure.core.test.TestBase; +import com.azure.core.test.TestMode; import com.azure.core.test.models.NetworkCallRecord; import com.azure.core.util.Configuration; import com.azure.core.util.serializer.SerializerAdapter; import com.azure.core.util.serializer.SerializerEncoding; +import com.azure.identity.DefaultAzureCredentialBuilder; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -27,6 +33,7 @@ import java.util.function.Consumer; import java.util.stream.Collectors; +import static com.azure.ai.formrecognizer.TestUtils.INVALID_KEY; import static com.azure.ai.formrecognizer.TestUtils.INVALID_RECEIPT_URL; import static com.azure.ai.formrecognizer.TestUtils.getSerializerAdapter; import static com.azure.ai.formrecognizer.implementation.models.ModelStatus.READY; @@ -41,7 +48,7 @@ public abstract class FormTrainingClientTestBase extends TestBase { "FORM_RECOGNIZER_TESTING_BLOB_CONTAINER_SAS_URL"; static final String AZURE_FORM_RECOGNIZER_API_KEY = "AZURE_FORM_RECOGNIZER_API_KEY"; static final String AZURE_FORM_RECOGNIZER_ENDPOINT = "AZURE_FORM_RECOGNIZER_ENDPOINT"; - static final String EXPECTED_INVALID_MODEL_STATUS_MESSAGE = "Invalid model created with ID: cae9d062-71e0-44a3-8630-70b32ae94f4d"; + static final String EXPECTED_INVALID_MODEL_STATUS_MESSAGE = "Invalid model created with ID"; static final String EXPECTED_INVALID_MODEL_STATUS_ERROR_CODE = "2012"; static final String EXPECTED_INVALID_STATUS_ERROR_INFORMATION = "Unable to list blobs on the Azure blob storage account."; @@ -57,6 +64,23 @@ void validateCopyAuthorizationResult(String expectedResourceId, String expectedR assertEquals(expectedResourceId, actualResult.getResourceId()); } + FormTrainingClientBuilder getFormTrainingClientBuilder(HttpClient httpClient, + FormRecognizerServiceVersion serviceVersion) { + FormTrainingClientBuilder builder = new FormTrainingClientBuilder() + .endpoint(getEndpoint()) + .httpClient(httpClient == null ? interceptorManager.getPlaybackClient() : httpClient) + .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS)) + .serviceVersion(serviceVersion) + .addPolicy(interceptorManager.getRecordPolicy()); + + if (getTestMode() == TestMode.PLAYBACK) { + builder.credential(new AzureKeyCredential(INVALID_KEY)); + } else { + builder.credential(new DefaultAzureCredentialBuilder().build()); + } + return builder; + } + private static void validateTrainingDocumentsData(List expectedTrainingDocuments, List actualTrainingDocuments) { assertEquals(expectedTrainingDocuments.size(), actualTrainingDocuments.size()); @@ -85,9 +109,8 @@ private static void validateErrorData(List expectedErrors, } } - static void validateAccountProperties(AccountProperties expectedAccountProperties, - AccountProperties actualAccountProperties) { - assertEquals(expectedAccountProperties.getCustomModelLimit(), actualAccountProperties.getCustomModelLimit()); + static void validateAccountProperties(AccountProperties actualAccountProperties) { + assertNotNull(actualAccountProperties.getCustomModelLimit()); assertNotNull(actualAccountProperties.getCustomModelCount()); } diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/TestUtils.java b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/TestUtils.java index 0246d34ea30b..3fa6b3a62f2e 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/TestUtils.java +++ b/sdk/formrecognizer/azure-ai-formrecognizer/src/test/java/com/azure/ai/formrecognizer/TestUtils.java @@ -4,7 +4,6 @@ package com.azure.ai.formrecognizer; import com.azure.ai.formrecognizer.implementation.Utility; -import com.azure.ai.formrecognizer.models.AccountProperties; import com.azure.core.http.HttpClient; import com.azure.core.http.HttpMethod; import com.azure.core.http.HttpRequest; @@ -63,10 +62,6 @@ final class TestUtils { private TestUtils() { } - static AccountProperties getExpectedAccountProperties() { - return new AccountProperties(14, 5000); - } - static InputStream getFileData(String fileName) { final HttpClient httpClient = new NettyAsyncHttpClientBuilder().build(); final HttpResponse httpResponse = diff --git a/sdk/formrecognizer/test-resources.json b/sdk/formrecognizer/test-resources.json index 1012ac5051e1..24afbed1c9c5 100644 --- a/sdk/formrecognizer/test-resources.json +++ b/sdk/formrecognizer/test-resources.json @@ -3,7 +3,11 @@ "contentVersion": "1.0.0.0", "parameters": { "baseName": { - "type": "String" + "type": "string", + "defaultValue": "[resourceGroup().name]", + "metadata": { + "description": "The base resource name." + } }, "location": { "type": "string", @@ -20,6 +24,24 @@ "description": "The principal to assign the role to. This is application object id." } }, + "tenantId": { + "type": "String", + "metadata": { + "description": "The tenant id to which the application and resources belong." + } + }, + "testApplicationId": { + "type": "String", + "metadata": { + "description": "The application client id used to run tests." + } + }, + "testApplicationSecret": { + "type": "String", + "metadata": { + "description": "The application client secret used to run tests." + } + }, "formRecognizerEndpointSuffix": { "defaultValue": ".cognitiveservices.azure.com/", "type": "String" @@ -107,6 +129,18 @@ } ], "outputs": { + "AZURE_TENANT_ID": { + "type": "String", + "value": "[parameters('tenantId')]" + }, + "AZURE_CLIENT_ID": { + "type": "String", + "value": "[parameters('testApplicationId')]" + }, + "AZURE_CLIENT_SECRET": { + "type": "String", + "value": "[parameters('testApplicationSecret')]" + }, "AZURE_FORM_RECOGNIZER_API_KEY": { "type": "string", "value": "[listKeys(resourceId('Microsoft.CognitiveServices/accounts', variables('formRecognizerBaseName')), variables('formRecognizerApiVersion')).key1]" @@ -136,4 +170,4 @@ "value": "[resourceId('Microsoft.CognitiveServices/accounts', variables('formRecognizerBaseName'))]" } } -} \ No newline at end of file +}